From d53c86df94888b3eb4f1caab3494c696f8135d40 Mon Sep 17 00:00:00 2001 From: Rodribm10 Date: Wed, 22 Apr 2026 15:27:37 -0300 Subject: [PATCH] fix(captain): always include instructions in Codex responses body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex endpoint retorna HTTP 400 "Instructions are required" quando o campo vem ausente. Agora sempre incluímos o campo — string com espaço quando não há system message no request. Validado end-to-end: curl → /codex/v1/chat/completions → proxy traduz → Codex devolve streaming SSE → proxy agrega → JSON Chat Completions. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../app/services/captain/codex/translator.rb | 4 +++- .../services/captain/codex/translator_spec.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/enterprise/app/services/captain/codex/translator.rb b/enterprise/app/services/captain/codex/translator.rb index 8a703322a..035759491 100644 --- a/enterprise/app/services/captain/codex/translator.rb +++ b/enterprise/app/services/captain/codex/translator.rb @@ -15,10 +15,12 @@ class Captain::Codex::Translator body = { model: chat_body['model'] || chat_body[:model], input: input, + # Codex exige o campo instructions sempre. Se não vier system message, + # usamos uma string com um espaço pra satisfazer o endpoint sem influenciar o comportamento. + instructions: instructions.presence || ' ', store: false, stream: true # Codex exige streaming sempre — o Client agrega. } - body[:instructions] = instructions if instructions body[:max_output_tokens] = chat_body['max_tokens'] if chat_body['max_tokens'] tools = chat_body['tools'] || chat_body[:tools] diff --git a/spec/enterprise/services/captain/codex/translator_spec.rb b/spec/enterprise/services/captain/codex/translator_spec.rb index 22e448ae7..7fcd8248c 100644 --- a/spec/enterprise/services/captain/codex/translator_spec.rb +++ b/spec/enterprise/services/captain/codex/translator_spec.rb @@ -130,6 +130,20 @@ RSpec.describe Captain::Codex::Translator do expect(result.keys).not_to include(:temperature, :top_p, :frequency_penalty) end + + it 'always includes instructions (empty placeholder when no system message)' do + chat = { + 'model' => 'gpt-5.4', + 'messages' => [{ 'role' => 'user', 'content' => 'Oi' }] + } + + result = described_class.chat_to_responses(chat) + + # Codex exige o campo instructions mesmo sem system message. + expect(result).to have_key(:instructions) + expect(result[:instructions]).not_to be_nil + expect(result[:instructions]).not_to eq('') + end end describe '.responses_to_chat' do