fix(model-bridge): handle SDK context hints and Responses metadata
This commit is contained in:
1 parent
194853ac01
commit
2cfd53b923
4 files changed
+98
-5
No files matched your search
@@ -43,10 +43,25 @@ export function historyKey(content: Json[]): string {
|
||||
function replayKey(prefix: string, content: Json[]): string {
|
||||
return createHash('sha256').update(prefix).digest('hex') + ':' + createHash('sha256').update(historyKey(content)).digest('hex')
|
||||
}
|
||||
function validateContextManagement(value: unknown): void {
|
||||
if (value === undefined || value === null) return
|
||||
requireValue(record(value) && Object.keys(value).every(key => key === 'edits'), 'Unsupported Messages context_management')
|
||||
if (value.edits === undefined) return
|
||||
requireValue(Array.isArray(value.edits), 'Invalid Messages context_management edits')
|
||||
// Adaptive thinking adds "keep all" even for custom models. It is a no-op;
|
||||
// actual server-side clearing/compaction cannot be translated across protocols.
|
||||
for (const edit of value.edits) {
|
||||
requireValue(record(edit) && Object.keys(edit).every(key => key === 'type' || key === 'keep')
|
||||
&& edit.type === 'clear_thinking_20251015'
|
||||
&& (edit.keep === 'all' || record(edit.keep) && edit.keep.type === 'all' && Object.keys(edit.keep).length === 1),
|
||||
'Unsupported Messages context_management edit: only keeping all thinking is supported')
|
||||
}
|
||||
}
|
||||
export function convertRequest(body: Json, model: ResolvedModel, context: ConversionContext): Json {
|
||||
const { config: c, profile: p } = model, responses = c.api === 'openai-responses'
|
||||
requireValue(Array.isArray(body.messages), 'messages must be an array')
|
||||
for (const key of ['mcp_servers', 'container', 'context_management']) requireValue(body[key] === undefined, `Unsupported Messages field: ${key}`)
|
||||
for (const key of ['mcp_servers', 'container']) requireValue(body[key] === undefined, `Unsupported Messages field: ${key}`)
|
||||
validateContextManagement(body.context_management)
|
||||
const input: Json[] = []
|
||||
const prefix: Json[] = []
|
||||
context.responsePrefix = undefined
|
||||
@@ -56,8 +71,10 @@ export function convertRequest(body: Json, model: ResolvedModel, context: Conver
|
||||
if (system.length) input.push({ role: 'system', content: system.map(b => b.text).join('\n\n') })
|
||||
}
|
||||
for (const message of body.messages) {
|
||||
requireValue(record(message) && ['user', 'assistant'].includes(message.role), 'Invalid message role')
|
||||
requireValue(record(message) && ['user', 'assistant', 'system'].includes(message.role), 'Invalid message role')
|
||||
const role = message.role, content = blocks(message.content)
|
||||
// The SDK also appends environment instructions as an in-history system message.
|
||||
if (role === 'system') requireValue(content.every(b => b.type === 'text' && typeof b.text === 'string'), 'Only text system blocks are supported')
|
||||
const prior = JSON.stringify(prefix)
|
||||
prefix.push({ role, content: JSON.parse(historyKey(content)) })
|
||||
if (responses && role === 'assistant') {
|
||||
|
||||
@@ -136,6 +136,9 @@ export async function streamResponse(upstream: Response, response: ServerRespons
|
||||
await context.persist?.()
|
||||
finish = converted.stop_reason; usage = converted.usage; completed = true; break
|
||||
}
|
||||
// Gateway metadata carries no model content. Keep reading to the terminal
|
||||
// event so reasoning history is saved before the SDK starts its next turn.
|
||||
case 'response.metadata':
|
||||
case 'response.created': case 'response.in_progress': case 'response.queued':
|
||||
case 'response.content_part.added': case 'response.content_part.done':
|
||||
case 'response.reasoning_summary_part.added': case 'response.reasoning_summary_part.done':
|
||||
|
||||
Reference in new issue
Block a user