mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
cleanup
This commit is contained in:
@@ -1,356 +0,0 @@
|
||||
/// Spike test for OpenAI Responses API with gpt-5.1-codex
|
||||
///
|
||||
/// This is a standalone program to validate the Responses API works
|
||||
/// before integrating it into the provider system.
|
||||
///
|
||||
/// Run with: cargo run --example test_responses_api
|
||||
use anyhow::Result;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Response {
|
||||
id: String,
|
||||
object: String,
|
||||
created_at: i64,
|
||||
status: String,
|
||||
model: String,
|
||||
output: Vec<OutputItem>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
reasoning: Option<ReasoningInfo>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
usage: Option<Usage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum OutputItem {
|
||||
Reasoning {
|
||||
id: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
summary: Option<Vec<String>>,
|
||||
},
|
||||
Message {
|
||||
id: String,
|
||||
status: String,
|
||||
role: String,
|
||||
content: Vec<ContentBlock>,
|
||||
},
|
||||
FunctionCall {
|
||||
id: String,
|
||||
status: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
call_id: Option<String>,
|
||||
name: String,
|
||||
arguments: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum ContentBlock {
|
||||
OutputText {
|
||||
text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
annotations: Option<Vec<Value>>,
|
||||
},
|
||||
ToolCall {
|
||||
id: String,
|
||||
name: String,
|
||||
input: Value,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ReasoningInfo {
|
||||
effort: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
summary: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Usage {
|
||||
input_tokens: i32,
|
||||
output_tokens: i32,
|
||||
total_tokens: i32,
|
||||
}
|
||||
|
||||
async fn test_simple_request() -> Result<()> {
|
||||
println!("=== Testing Simple Responses API Request ===\n");
|
||||
|
||||
let api_key =
|
||||
std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY environment variable not set");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&format!("Bearer {}", api_key))?,
|
||||
);
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
|
||||
let payload = json!({
|
||||
"model": "gpt-5.1-codex",
|
||||
"input": "Write a simple hello world function in Python",
|
||||
"instructions": "You are a helpful coding assistant. Provide clear, concise code."
|
||||
});
|
||||
|
||||
println!("Request payload:");
|
||||
println!("{}\n", serde_json::to_string_pretty(&payload)?);
|
||||
|
||||
let response = client
|
||||
.post("https://api.openai.com/v1/responses")
|
||||
.headers(headers)
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let status = response.status();
|
||||
println!("Response status: {}\n", status);
|
||||
|
||||
let response_text = response.text().await?;
|
||||
println!("Raw response:");
|
||||
println!("{}\n", response_text);
|
||||
|
||||
if status.is_success() {
|
||||
let parsed: Response = serde_json::from_str(&response_text)?;
|
||||
println!("Parsed response:");
|
||||
println!(" ID: {}", parsed.id);
|
||||
println!(" Model: {}", parsed.model);
|
||||
println!(" Status: {}", parsed.status);
|
||||
println!(" Output items: {}", parsed.output.len());
|
||||
|
||||
for item in &parsed.output {
|
||||
match item {
|
||||
OutputItem::Reasoning { id, .. } => {
|
||||
println!(" - Reasoning block (id: {})", id);
|
||||
}
|
||||
OutputItem::Message {
|
||||
id, role, content, ..
|
||||
} => {
|
||||
println!(" - Message (id: {}, role: {})", id, role);
|
||||
for content_block in content {
|
||||
match content_block {
|
||||
ContentBlock::OutputText { text, .. } => {
|
||||
println!(" Text: {}", text);
|
||||
}
|
||||
ContentBlock::ToolCall { name, .. } => {
|
||||
println!(" Tool call: {}", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OutputItem::FunctionCall {
|
||||
id,
|
||||
name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
" - Function call (id: {}, name: {}, args: {})",
|
||||
id, name, arguments
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reasoning) = parsed.reasoning {
|
||||
println!(" Reasoning effort: {}", reasoning.effort);
|
||||
}
|
||||
if let Some(usage) = parsed.usage {
|
||||
println!(
|
||||
" Usage: input={}, output={}, total={}",
|
||||
usage.input_tokens, usage.output_tokens, usage.total_tokens
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_with_tools() -> Result<()> {
|
||||
println!("\n=== Testing Responses API with Tools ===\n");
|
||||
|
||||
let api_key =
|
||||
std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY environment variable not set");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&format!("Bearer {}", api_key))?,
|
||||
);
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
|
||||
let payload = json!({
|
||||
"model": "gpt-5.1-codex",
|
||||
"input": "What's 25 * 17? Use the calculator.",
|
||||
"instructions": "You are a helpful assistant with access to tools.",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "calculator",
|
||||
"description": "Perform basic arithmetic operations",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"operation": {
|
||||
"type": "string",
|
||||
"enum": ["add", "subtract", "multiply", "divide"],
|
||||
"description": "The operation to perform"
|
||||
},
|
||||
"a": {
|
||||
"type": "number",
|
||||
"description": "First number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"description": "Second number"
|
||||
}
|
||||
},
|
||||
"required": ["operation", "a", "b"]
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
println!("Request payload:");
|
||||
println!("{}\n", serde_json::to_string_pretty(&payload)?);
|
||||
|
||||
let response = client
|
||||
.post("https://api.openai.com/v1/responses")
|
||||
.headers(headers)
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let status = response.status();
|
||||
println!("Response status: {}\n", status);
|
||||
|
||||
let response_text = response.text().await?;
|
||||
println!("Raw response:");
|
||||
println!("{}\n", response_text);
|
||||
|
||||
if status.is_success() {
|
||||
let parsed: Response = serde_json::from_str(&response_text)?;
|
||||
println!("Parsed response:");
|
||||
println!(" ID: {}", parsed.id);
|
||||
println!(" Model: {}", parsed.model);
|
||||
println!(" Output items: {}", parsed.output.len());
|
||||
|
||||
for item in &parsed.output {
|
||||
match item {
|
||||
OutputItem::Reasoning { id, .. } => {
|
||||
println!(" - Reasoning block (id: {})", id);
|
||||
}
|
||||
OutputItem::Message {
|
||||
id, role, content, ..
|
||||
} => {
|
||||
println!(" - Message (id: {}, role: {})", id, role);
|
||||
for content_block in content {
|
||||
match content_block {
|
||||
ContentBlock::OutputText { text, .. } => {
|
||||
println!(" Text: {}", text);
|
||||
}
|
||||
ContentBlock::ToolCall { name, input, .. } => {
|
||||
println!(" Tool call: {} with input: {}", name, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OutputItem::FunctionCall {
|
||||
id,
|
||||
name,
|
||||
arguments,
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
" - Function call (id: {}, name: {}, args: {})",
|
||||
id, name, arguments
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_structured_input() -> Result<()> {
|
||||
println!("\n=== Testing Responses API with Structured Input ===\n");
|
||||
|
||||
let api_key =
|
||||
std::env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY environment variable not set");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
AUTHORIZATION,
|
||||
HeaderValue::from_str(&format!("Bearer {}", api_key))?,
|
||||
);
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
|
||||
// Using structured input similar to messages format
|
||||
let payload = json!({
|
||||
"model": "gpt-5.1-codex",
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Write a function to calculate fibonacci numbers"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"instructions": "You are an expert programmer. Write clean, efficient code."
|
||||
});
|
||||
|
||||
println!("Request payload:");
|
||||
println!("{}\n", serde_json::to_string_pretty(&payload)?);
|
||||
|
||||
let response = client
|
||||
.post("https://api.openai.com/v1/responses")
|
||||
.headers(headers)
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let status = response.status();
|
||||
println!("Response status: {}\n", status);
|
||||
|
||||
let response_text = response.text().await?;
|
||||
println!("Raw response:");
|
||||
println!("{}\n", response_text);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
println!("OpenAI Responses API Spike Test for gpt-5.1-codex\n");
|
||||
println!("================================================\n");
|
||||
|
||||
// Test 1: Simple request
|
||||
if let Err(e) = test_simple_request().await {
|
||||
eprintln!("Simple request test failed: {}", e);
|
||||
}
|
||||
|
||||
// Test 2: Request with tools
|
||||
if let Err(e) = test_with_tools().await {
|
||||
eprintln!("Tools test failed: {}", e);
|
||||
}
|
||||
|
||||
// Test 3: Structured input
|
||||
if let Err(e) = test_structured_input().await {
|
||||
eprintln!("Structured input test failed: {}", e);
|
||||
}
|
||||
|
||||
println!("\n=== Spike Test Complete ===");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -681,7 +681,6 @@ where
|
||||
// Responses API Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
/// Create a request for the Responses API endpoint
|
||||
pub fn create_responses_request(
|
||||
model_config: &ModelConfig,
|
||||
system: &str,
|
||||
@@ -744,7 +743,6 @@ pub fn create_responses_request(
|
||||
"instructions": system,
|
||||
});
|
||||
|
||||
// Format tools for responses API - flat structure without nested "function"
|
||||
if !tools.is_empty() {
|
||||
let tools_spec: Vec<Value> = tools
|
||||
.iter()
|
||||
@@ -781,7 +779,6 @@ pub fn create_responses_request(
|
||||
Ok(payload)
|
||||
}
|
||||
|
||||
/// Convert Responses API response to internal Message format
|
||||
pub fn responses_api_to_message(response: &ResponsesApiResponse) -> anyhow::Result<Message> {
|
||||
let mut content = Vec::new();
|
||||
|
||||
@@ -838,13 +835,11 @@ pub fn responses_api_to_message(response: &ResponsesApiResponse) -> anyhow::Resu
|
||||
|
||||
let mut message = Message::new(Role::Assistant, chrono::Utc::now().timestamp(), content);
|
||||
|
||||
// Add response ID if available
|
||||
message = message.with_id(response.id.clone());
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
/// Get usage from Responses API response
|
||||
pub fn get_responses_usage(response: &ResponsesApiResponse) -> Usage {
|
||||
response.usage.as_ref().map_or_else(Usage::default, |u| {
|
||||
Usage::new(
|
||||
|
||||
@@ -44,7 +44,8 @@ pub const OPEN_AI_KNOWN_MODELS: &[(&str, usize)] = &[
|
||||
("gpt-3.5-turbo", 16_385),
|
||||
("gpt-4-turbo", 128_000),
|
||||
("o4-mini", 128_000),
|
||||
("gpt-5.1-codex", 128_000),
|
||||
("gpt-5.1-codex", 400_000),
|
||||
("gpt-5-codex", 400_000),
|
||||
];
|
||||
|
||||
pub const OPEN_AI_DOC_URL: &str = "https://platform.openai.com/docs/models";
|
||||
@@ -189,7 +190,9 @@ impl OpenAiProvider {
|
||||
}
|
||||
|
||||
fn uses_responses_api(model_name: &str) -> bool {
|
||||
model_name.starts_with("gpt-5")
|
||||
model_name.starts_with("gpt-5-codex")
|
||||
|| model_name.starts_with("gpt-5.1-codex")
|
||||
|| model_name.starts_with("gpt-5-codex")
|
||||
}
|
||||
|
||||
async fn post(&self, payload: &Value) -> Result<Value, ProviderError> {
|
||||
|
||||
Reference in New Issue
Block a user