From 4d9800cb197840085820e2a97505d536a31a42e2 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Tue, 18 Nov 2025 15:18:54 +1100 Subject: [PATCH] cleanup --- crates/goose/examples/test_responses_api.rs | 356 ------------------- crates/goose/src/providers/formats/openai.rs | 5 - crates/goose/src/providers/openai.rs | 7 +- 3 files changed, 5 insertions(+), 363 deletions(-) delete mode 100644 crates/goose/examples/test_responses_api.rs diff --git a/crates/goose/examples/test_responses_api.rs b/crates/goose/examples/test_responses_api.rs deleted file mode 100644 index 8183b28349..0000000000 --- a/crates/goose/examples/test_responses_api.rs +++ /dev/null @@ -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, - #[serde(skip_serializing_if = "Option::is_none")] - reasoning: Option, - #[serde(skip_serializing_if = "Option::is_none")] - usage: Option, -} - -#[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>, - }, - Message { - id: String, - status: String, - role: String, - content: Vec, - }, - FunctionCall { - id: String, - status: String, - #[serde(skip_serializing_if = "Option::is_none")] - call_id: Option, - 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>, - }, - ToolCall { - id: String, - name: String, - input: Value, - }, -} - -#[derive(Debug, Serialize, Deserialize)] -struct ReasoningInfo { - effort: String, - #[serde(skip_serializing_if = "Option::is_none")] - summary: Option, -} - -#[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(()) -} diff --git a/crates/goose/src/providers/formats/openai.rs b/crates/goose/src/providers/formats/openai.rs index b08521955e..a9544f5213 100644 --- a/crates/goose/src/providers/formats/openai.rs +++ b/crates/goose/src/providers/formats/openai.rs @@ -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 = 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 { 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( diff --git a/crates/goose/src/providers/openai.rs b/crates/goose/src/providers/openai.rs index 1a9d4e8dfb..8875c7e6ff 100644 --- a/crates/goose/src/providers/openai.rs +++ b/crates/goose/src/providers/openai.rs @@ -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 {