iachat/scripts/captain_codex_poc/test_tools.rb
Rodribm10 df56ee8115 chore(captain): PoC Codex OAuth device flow + Responses streaming
PoC validado com conta ChatGPT Plus e client_id do Hermes. Device flow
OAuth funciona, gera access_token + refresh_token auto-refresh. Chat e
function calling funcionaram em gpt-5.4, gpt-5.4-mini, gpt-5.2 e
gpt-5.3-codex.

Descobertas pro adapter final:
- Endpoint: /responses (não /chat/completions)
- Streaming obrigatório (stream: true)
- store: false obrigatório
- Sem temperature/top_p (modelos reasoning)
- input[] no lugar de messages[]
- instructions top-level no lugar de system role
- Tools sem wrapping function: {}
- Output via events response.output_item.done (não response.completed)

Pasta scripts/captain_codex_poc/ está excluída do Rubocop (scripts
standalone, não rodam em contexto Rails).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 14:56:57 -03:00

67 lines
2.2 KiB
Ruby
Executable File

#!/usr/bin/env ruby
# Go/no-go crítico: function calling via /responses.
require_relative 'codex_client'
# Formato Responses API: tools sem wrapping `function: {...}`
TOOLS = [
{
type: 'function',
name: 'gerar_pix',
description: 'Gera um Pix para pagamento de reserva de hotel. Use apenas após ter CPF e nome do hóspede.',
strict: false,
parameters: {
type: 'object',
properties: {
cpf: { type: 'string', description: 'CPF do hóspede, apenas dígitos' },
nome: { type: 'string', description: 'Nome completo do hóspede' },
valor: { type: 'number', description: 'Valor em reais' },
descricao: { type: 'string', description: 'Descrição da reserva' }
},
required: %w[cpf nome valor descricao]
}
}
].freeze
SYSTEM = 'Você é um recepcionista de hotel. Quando tiver CPF e nome do hóspede, chame a tool gerar_pix para emitir o pagamento. Nunca invente dados.'.freeze
USER = 'Oi, quero fechar a reserva. Meu nome é Rodrigo Borba Machado, CPF 123.456.789-00. O valor era R$ 320 por uma diária no Prime.'.freeze
MODELS_TO_TRY = %w[gpt-5.4 gpt-5.4-mini gpt-5.2 gpt-5.3-codex].freeze
client = CodexPoc::Client.new
MODELS_TO_TRY.each do |model|
puts "=== Function calling — modelo: #{model} ==="
begin
resp = client.responses(
model: model,
system_prompt: SYSTEM,
user_messages: USER,
tools: TOOLS
)
out = CodexPoc::Client.extract(resp)
if out[:tool_calls].empty?
warn "[FAIL] #{model} NÃO chamou a tool. Texto retornado:"
warn " #{out[:text][0, 400]}"
else
call = out[:tool_calls].first
begin
args = JSON.parse(call[:arguments])
puts "[PASS] #{model} chamou tool '#{call[:name]}' com args:"
puts JSON.pretty_generate(args)
rescue JSON::ParserError => e
warn "[FAIL] #{model} chamou tool mas args não são JSON: #{call[:arguments].inspect} (#{e.message})"
end
end
puts
rescue CodexPoc::Error => e
warn "FALHOU para #{model}: #{e.message[0, 300]}"
puts
end
end
puts '=== Fim do teste de function calling ==='
puts 'GO/NO-GO: se ao menos gpt-5.4 (ou gpt-5.3-codex) passou com args JSON válidos, seguimos.'