fix(captain): translate response_format to text.format on Codex proxy
Sem isso o Codex devolvia texto puro e o reaction_emoji do JSON estruturado nunca chegava ao ResponseBuilderJob — quebrava a ferramenta de reagir mensagens com emoji. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9e8550dd45
commit
0ecfce5c27
@ -18,9 +18,14 @@ class Captain::Codex::Translator
|
|||||||
# chat_body: hash no formato OpenAI Chat Completions.
|
# chat_body: hash no formato OpenAI Chat Completions.
|
||||||
# Retorna hash pro POST /responses.
|
# Retorna hash pro POST /responses.
|
||||||
def self.chat_to_responses(chat_body)
|
def self.chat_to_responses(chat_body)
|
||||||
messages = chat_body['messages'] || chat_body[:messages] || []
|
instructions, input = split_system(chat_body['messages'] || chat_body[:messages] || [])
|
||||||
instructions, input = split_system(messages)
|
body = base_body(chat_body, instructions, input)
|
||||||
|
apply_tools!(body, chat_body)
|
||||||
|
apply_response_format!(body, chat_body['response_format'] || chat_body[:response_format])
|
||||||
|
body
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.base_body(chat_body, instructions, input)
|
||||||
body = {
|
body = {
|
||||||
model: override_model(chat_body['model'] || chat_body[:model]),
|
model: override_model(chat_body['model'] || chat_body[:model]),
|
||||||
input: input,
|
input: input,
|
||||||
@ -31,16 +36,43 @@ class Captain::Codex::Translator
|
|||||||
stream: true # Codex exige streaming sempre — o Client agrega.
|
stream: true # Codex exige streaming sempre — o Client agrega.
|
||||||
}
|
}
|
||||||
body[:max_output_tokens] = chat_body['max_tokens'] if chat_body['max_tokens']
|
body[:max_output_tokens] = chat_body['max_tokens'] if chat_body['max_tokens']
|
||||||
|
|
||||||
tools = chat_body['tools'] || chat_body[:tools]
|
|
||||||
if tools.present?
|
|
||||||
body[:tools] = tools.map { |t| translate_tool(t) }
|
|
||||||
body[:tool_choice] = translate_tool_choice(chat_body['tool_choice'] || chat_body[:tool_choice])
|
|
||||||
end
|
|
||||||
|
|
||||||
body
|
body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.apply_tools!(body, chat_body)
|
||||||
|
tools = chat_body['tools'] || chat_body[:tools]
|
||||||
|
return if tools.blank?
|
||||||
|
|
||||||
|
body[:tools] = tools.map { |t| translate_tool(t) }
|
||||||
|
body[:tool_choice] = translate_tool_choice(chat_body['tool_choice'] || chat_body[:tool_choice])
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.apply_response_format!(body, response_format)
|
||||||
|
text_format = translate_response_format(response_format)
|
||||||
|
body[:text] = { format: text_format } if text_format
|
||||||
|
end
|
||||||
|
|
||||||
|
# Chat Completions: { type: 'json_schema', json_schema: { name, schema, strict } }
|
||||||
|
# Responses API: { type: 'json_schema', name, schema, strict } (sem wrapper json_schema)
|
||||||
|
# Também aceita { type: 'json_object' } (sem schema).
|
||||||
|
def self.translate_response_format(format)
|
||||||
|
return nil if format.blank?
|
||||||
|
|
||||||
|
format = format.stringify_keys
|
||||||
|
case format['type']
|
||||||
|
when 'json_schema'
|
||||||
|
js = (format['json_schema'] || {}).stringify_keys
|
||||||
|
{
|
||||||
|
type: 'json_schema',
|
||||||
|
name: js['name'] || 'response',
|
||||||
|
schema: js['schema'] || {},
|
||||||
|
strict: js.fetch('strict', true)
|
||||||
|
}
|
||||||
|
when 'json_object'
|
||||||
|
{ type: 'json_object' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Separa a(s) mensagem(ns) system do resto.
|
# Separa a(s) mensagem(ns) system do resto.
|
||||||
# Várias system messages viram uma única instruction com \n\n.
|
# Várias system messages viram uma única instruction com \n\n.
|
||||||
def self.split_system(messages)
|
def self.split_system(messages)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user