Cover ChatGPT Responses tool call IDs
This commit is contained in:
@@ -169,8 +169,18 @@ impl AgentRuntime for ChatGPTSubscriptionRuntime {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rig_core::client::CompletionClient;
|
||||
use rig_core::completion::{AssistantContent, CompletionModel, Message};
|
||||
use rig_core::message::{ToolResultContent, UserContent};
|
||||
use rig_core::providers::chatgpt::ChatGPTAuth;
|
||||
use rig_core::test_utils::RecordingHttpClient;
|
||||
|
||||
use super::*;
|
||||
|
||||
const COMPLETED_RESPONSE: &str = r#"data: {"type":"response.output_text.delta","delta":"ok"}
|
||||
data: {"type":"response.completed","response":{"id":"resp_test","object":"response","created_at":1,"status":"completed","error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"model":"gpt-5.3-codex","usage":{"input_tokens":1,"input_tokens_details":{"cached_tokens":0},"output_tokens":1,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":2},"output":[{"type":"message","id":"msg_test","status":"completed","role":"assistant","content":[{"type":"output_text","annotations":[],"text":"ok"}]}],"tools":[]}}
|
||||
data: [DONE]"#;
|
||||
|
||||
#[test]
|
||||
fn runtime_descriptor_identifies_chatgpt_subscription() {
|
||||
let runtime = ChatGPTSubscriptionRuntime::new(ChatGPTSubscriptionRuntimeConfig {
|
||||
@@ -216,4 +226,64 @@ mod tests {
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn chatgpt_follow_up_request_preserves_responses_call_ids() {
|
||||
let http_client = RecordingHttpClient::new(COMPLETED_RESPONSE);
|
||||
let client = chatgpt::Client::builder()
|
||||
.api_key(ChatGPTAuth::AccessToken {
|
||||
access_token: "test-token".to_string(),
|
||||
account_id: None,
|
||||
})
|
||||
.http_client(http_client.clone())
|
||||
.build()
|
||||
.expect("client should build");
|
||||
let model = client.completion_model("gpt-5.3-codex");
|
||||
let assistant_tool_call = AssistantContent::tool_call_with_call_id(
|
||||
"fc_native_1",
|
||||
"call_native_1".to_string(),
|
||||
"read_files",
|
||||
serde_json::json!({"files": ["Cargo.toml"]}),
|
||||
);
|
||||
let tool_result = UserContent::tool_result_with_call_id(
|
||||
"fc_native_1",
|
||||
"call_native_1".to_string(),
|
||||
rig_core::OneOrMany::one(ToolResultContent::text("contents")),
|
||||
);
|
||||
let chat_history = rig_core::OneOrMany::many(vec![
|
||||
Message::Assistant {
|
||||
id: None,
|
||||
content: rig_core::OneOrMany::one(assistant_tool_call),
|
||||
},
|
||||
Message::User {
|
||||
content: rig_core::OneOrMany::one(tool_result),
|
||||
},
|
||||
Message::user("Continue."),
|
||||
])
|
||||
.expect("history should contain messages");
|
||||
|
||||
model
|
||||
.completion(rig_core::completion::CompletionRequest {
|
||||
model: Some("gpt-5.3-codex".to_string()),
|
||||
preamble: None,
|
||||
chat_history,
|
||||
documents: Vec::new(),
|
||||
tools: Vec::new(),
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
tool_choice: None,
|
||||
additional_params: None,
|
||||
output_schema: None,
|
||||
record_telemetry_content: false,
|
||||
})
|
||||
.await
|
||||
.expect("request should reach the mocked provider");
|
||||
|
||||
let requests = http_client.requests();
|
||||
assert_eq!(requests.len(), 1);
|
||||
let body: serde_json::Value = serde_json::from_slice(&requests[0].body).unwrap();
|
||||
let input = body["input"].as_array().expect("input should be an array");
|
||||
assert_eq!(input[0]["call_id"], "call_native_1");
|
||||
assert_eq!(input[1]["call_id"], "call_native_1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,4 +310,20 @@ mod tests {
|
||||
assert_eq!(call.id, "call_123");
|
||||
assert_eq!(call.name, "read_files");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn domain_tool_call_falls_back_to_wire_id_without_responses_call_id() {
|
||||
let tool_call = rig_core::message::ToolCall::new(
|
||||
"fc_item_123".to_string(),
|
||||
rig_core::message::ToolFunction {
|
||||
name: "read_files".to_string(),
|
||||
arguments: serde_json::json!({"files": ["Cargo.toml"]}),
|
||||
},
|
||||
);
|
||||
|
||||
let call = domain_tool_call(tool_call);
|
||||
|
||||
assert_eq!(call.id, "fc_item_123");
|
||||
assert_eq!(call.name, "read_files");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,6 +432,10 @@ assigned to the phase that owns the affected flow before the related work is con
|
||||
monitor.
|
||||
- [x] Monitor teardown now clears orphaned in-memory state when completion metadata is missing.
|
||||
- [x] Refresh requests ignore completed or no-longer-long-running blocks.
|
||||
- [ ] ChatGPT subscription follow-up: a reported OAuth-backed tool turn failed because the
|
||||
Responses request lacked `call_id`. Rig's stream fallback and the serialized assistant/tool
|
||||
follow-up are now covered by hermetic tests; complete a fresh authenticated end-to-end check and
|
||||
investigate any remaining loss in the app-owned history handoff.
|
||||
- [x] Open-source project presentation: structure the About page around Galaxy’s local-first
|
||||
identity, audit the repository’s license and third-party notices, and make the root metadata,
|
||||
contribution guidance, and license files agree on an explicit license split (the repository
|
||||
|
||||
Reference in New Issue
Block a user