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>
67 lines
2.2 KiB
Ruby
Executable File
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.'
|