增加注入提示词功能
This commit is contained in:
parent
f193c48ce1
commit
ae059ffcb8
10 changed files with 160 additions and 20 deletions
|
|
@ -135,6 +135,8 @@ def add_mapping():
|
|||
'backend': data.get('backend', 'auto'),
|
||||
'target_url': data.get('target_url', ''),
|
||||
'api_key': data.get('api_key', ''),
|
||||
'custom_instructions': data.get('custom_instructions', ''),
|
||||
'instructions_position': data.get('instructions_position', 'prepend'),
|
||||
}
|
||||
return _save_and_respond(s, f'映射已添加: {name}')
|
||||
|
||||
|
|
@ -157,6 +159,8 @@ def update_mapping(name):
|
|||
'backend': data.get('backend', 'auto'),
|
||||
'target_url': data.get('target_url', ''),
|
||||
'api_key': data.get('api_key', ''),
|
||||
'custom_instructions': data.get('custom_instructions', ''),
|
||||
'instructions_position': data.get('instructions_position', 'prepend'),
|
||||
}
|
||||
if new_name != name:
|
||||
del mappings[name]
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ from routes.common import (
|
|||
build_responses_target,
|
||||
build_route_context,
|
||||
chat_error_chunk,
|
||||
inject_instructions_anthropic,
|
||||
inject_instructions_cc,
|
||||
inject_instructions_responses,
|
||||
log_route_context,
|
||||
log_usage,
|
||||
sse_data_message,
|
||||
|
|
@ -108,6 +111,7 @@ def _handle_openai_backend(ctx: RouteContext, payload: dict[str, Any]):
|
|||
)
|
||||
|
||||
payload = normalize_request(payload, ctx.upstream_model)
|
||||
payload = inject_instructions_cc(payload, ctx.custom_instructions, ctx.instructions_position)
|
||||
_dbg(
|
||||
f'标准化完成:模型={payload.get("model")} '
|
||||
f'工具数={len(payload.get("tools", []))}'
|
||||
|
|
@ -194,6 +198,7 @@ def _handle_responses_backend(ctx: RouteContext, payload: dict[str, Any]):
|
|||
"""
|
||||
responses_payload = cc_to_responses_request(payload)
|
||||
responses_payload['model'] = ctx.upstream_model
|
||||
responses_payload = inject_instructions_responses(responses_payload, ctx.custom_instructions, ctx.instructions_position)
|
||||
_dbg(
|
||||
'已转换为 Responses 请求:字段=' + str(list(responses_payload.keys()))
|
||||
+ f' 输入项数={len(responses_payload.get("input", []))}'
|
||||
|
|
@ -270,6 +275,7 @@ def _handle_anthropic_backend(ctx: RouteContext, payload: dict[str, Any]):
|
|||
"""处理走 Anthropic Messages 后端的聊天补全请求。"""
|
||||
payload['model'] = ctx.upstream_model
|
||||
anthropic_payload = cc_to_messages_request(payload)
|
||||
anthropic_payload = inject_instructions_anthropic(anthropic_payload, ctx.custom_instructions, ctx.instructions_position)
|
||||
_dbg(
|
||||
'已转换为 Messages 请求:字段=' + str(list(anthropic_payload.keys()))
|
||||
+ f' 消息数={len(anthropic_payload.get("messages", []))}'
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ class RouteContext:
|
|||
"""数据面路由使用的标准请求上下文。
|
||||
|
||||
路由层会先根据客户端模型名解析出统一上下文,后续处理函数只需要关心
|
||||
上游模型、后端类型、目标地址、鉴权信息和流式标记,而不必重复访问配置层。
|
||||
上游模型、后端类型、目标地址、鉴权信息、流式标记和自定义指令,
|
||||
而不必重复访问配置层。
|
||||
"""
|
||||
|
||||
client_model: str
|
||||
|
|
@ -31,6 +32,8 @@ class RouteContext:
|
|||
target_url: str
|
||||
api_key: str
|
||||
is_stream: bool
|
||||
custom_instructions: str
|
||||
instructions_position: str
|
||||
|
||||
|
||||
def build_route_context(client_model: str, is_stream: bool) -> RouteContext:
|
||||
|
|
@ -43,6 +46,8 @@ def build_route_context(client_model: str, is_stream: bool) -> RouteContext:
|
|||
target_url=mapping['target_url'],
|
||||
api_key=mapping['api_key'],
|
||||
is_stream=is_stream,
|
||||
custom_instructions=mapping.get('custom_instructions', ''),
|
||||
instructions_position=mapping.get('instructions_position', 'prepend'),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -120,3 +125,71 @@ def chat_error_chunk(message: str, error_type: str = 'upstream_error') -> str:
|
|||
def responses_error_event(message: str) -> str:
|
||||
"""构造 Responses 流式接口使用的错误事件。"""
|
||||
return sse_event_message('error', {'error': message})
|
||||
|
||||
|
||||
# ─── 自定义指令注入 ──────────────────────────────
|
||||
|
||||
|
||||
def _merge_text(custom: str, existing: str, position: str) -> str:
|
||||
"""根据 position 决定自定义指令与原有内容的拼接顺序。"""
|
||||
if not existing:
|
||||
return custom
|
||||
if position == 'append':
|
||||
return existing + '\n\n' + custom
|
||||
return custom + '\n\n' + existing
|
||||
|
||||
|
||||
def inject_instructions_cc(payload: dict[str, Any], instructions: str, position: str = 'prepend') -> dict[str, Any]:
|
||||
"""向 Chat Completions 请求注入自定义指令。
|
||||
|
||||
position='prepend' 时放在 system 消息开头,'append' 时放在末尾。
|
||||
"""
|
||||
if not instructions:
|
||||
return payload
|
||||
|
||||
messages = payload.get('messages', [])
|
||||
if messages and messages[0].get('role') == 'system':
|
||||
first = messages[0]
|
||||
original = first.get('content') or ''
|
||||
first['content'] = _merge_text(instructions, original, position)
|
||||
else:
|
||||
messages.insert(0, {'role': 'system', 'content': instructions})
|
||||
payload['messages'] = messages
|
||||
|
||||
logger.info('已注入自定义指令到 CC system 消息 (%d 字符, %s)', len(instructions), position)
|
||||
return payload
|
||||
|
||||
|
||||
def inject_instructions_responses(payload: dict[str, Any], instructions: str, position: str = 'prepend') -> dict[str, Any]:
|
||||
"""向 Responses 请求注入自定义指令(写入 instructions 字段)。
|
||||
|
||||
position='prepend' 时放在 instructions 开头,'append' 时放在末尾。
|
||||
"""
|
||||
if not instructions:
|
||||
return payload
|
||||
|
||||
existing = payload.get('instructions') or ''
|
||||
payload['instructions'] = _merge_text(instructions, existing, position)
|
||||
|
||||
logger.info('已注入自定义指令到 Responses instructions (%d 字符, %s)', len(instructions), position)
|
||||
return payload
|
||||
|
||||
|
||||
def inject_instructions_anthropic(payload: dict[str, Any], instructions: str, position: str = 'prepend') -> dict[str, Any]:
|
||||
"""向 Anthropic Messages 请求注入自定义指令(写入 system 字段)。
|
||||
|
||||
position='prepend' 时放在 system 开头,'append' 时放在末尾。
|
||||
"""
|
||||
if not instructions:
|
||||
return payload
|
||||
|
||||
existing = payload.get('system') or ''
|
||||
if isinstance(existing, list):
|
||||
existing = '\n'.join(
|
||||
block.get('text', '') for block in existing
|
||||
if isinstance(block, dict) and block.get('type') == 'text'
|
||||
)
|
||||
payload['system'] = _merge_text(instructions, existing, position)
|
||||
|
||||
logger.info('已注入自定义指令到 Anthropic system (%d 字符, %s)', len(instructions), position)
|
||||
return payload
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from flask import Blueprint, request, jsonify
|
|||
|
||||
import settings
|
||||
from config import Config
|
||||
from routes.common import inject_instructions_anthropic
|
||||
from utils.http import build_anthropic_headers, forward_request, sse_response
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -29,11 +30,16 @@ def messages_passthrough():
|
|||
|
||||
logger.info(f'[透传] model={model} 流式={is_stream}')
|
||||
|
||||
url_base = settings.get_url()
|
||||
api_key = settings.get_key()
|
||||
mapping = settings.resolve_model(model)
|
||||
url_base = mapping['target_url']
|
||||
api_key = mapping['api_key']
|
||||
custom_instructions = mapping.get('custom_instructions', '')
|
||||
instructions_position = mapping.get('instructions_position', 'prepend')
|
||||
headers = build_anthropic_headers(api_key)
|
||||
url = f'{url_base.rstrip("/")}/v1/messages'
|
||||
|
||||
payload = inject_instructions_anthropic(payload, custom_instructions, instructions_position)
|
||||
|
||||
if not is_stream:
|
||||
resp, err = forward_request(url, headers, payload)
|
||||
if err:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ from routes.common import (
|
|||
build_openai_target,
|
||||
build_responses_target,
|
||||
build_route_context,
|
||||
inject_instructions_anthropic,
|
||||
inject_instructions_cc,
|
||||
inject_instructions_responses,
|
||||
log_route_context,
|
||||
log_usage,
|
||||
responses_error_event,
|
||||
|
|
@ -73,6 +76,7 @@ def _build_cc_payload(payload: dict[str, Any], ctx: RouteContext) -> dict[str, A
|
|||
"""
|
||||
cc_payload = responses_to_cc(payload)
|
||||
cc_payload['model'] = ctx.upstream_model
|
||||
cc_payload = inject_instructions_cc(cc_payload, ctx.custom_instructions, ctx.instructions_position)
|
||||
_dbg(
|
||||
'已转换为聊天补全中间表示:字段=' + str(list(cc_payload.keys()))
|
||||
+ f' 消息数={len(cc_payload.get("messages", []))}'
|
||||
|
|
@ -171,6 +175,7 @@ def _handle_responses_backend(ctx: RouteContext, payload: dict[str, Any]):
|
|||
"""
|
||||
payload = dict(payload)
|
||||
payload['model'] = ctx.upstream_model
|
||||
payload = inject_instructions_responses(payload, ctx.custom_instructions, ctx.instructions_position)
|
||||
url, headers = build_responses_target(ctx)
|
||||
|
||||
if ctx.is_stream:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue