Cover ChatGPT Responses tool call IDs
This commit is contained in:
@@ -169,8 +169,18 @@ impl AgentRuntime for ChatGPTSubscriptionRuntime {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
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::*;
|
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]
|
#[test]
|
||||||
fn runtime_descriptor_identifies_chatgpt_subscription() {
|
fn runtime_descriptor_identifies_chatgpt_subscription() {
|
||||||
let runtime = ChatGPTSubscriptionRuntime::new(ChatGPTSubscriptionRuntimeConfig {
|
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.id, "call_123");
|
||||||
assert_eq!(call.name, "read_files");
|
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.
|
monitor.
|
||||||
- [x] Monitor teardown now clears orphaned in-memory state when completion metadata is missing.
|
- [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.
|
- [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
|
- [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,
|
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
|
contribution guidance, and license files agree on an explicit license split (the repository
|
||||||
|
|||||||
Reference in New Issue
Block a user