use crate::agent::completions::response;
use serde::{Deserialize, Serialize};
use schemars::JsonSchema;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
#[schemars(rename = "agent.completions.response.unary.AgentCompletion")]
pub struct AgentCompletion {
pub id: String,
pub created: u64,
pub messages: Vec<super::Message>,
pub object: super::Object,
pub usage: response::Usage,
pub upstream: crate::agent::Upstream,
pub error: Option<crate::error::ResponseError>,
pub continuation: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(extend("omitempty" = true))]
pub messages_queued: Option<bool>,
}
impl AgentCompletion {
pub fn normalize_for_tests(&mut self) {
use crate::agent::completions::message::{RichContent, RichContentPart};
self.id = String::new();
self.created = 0;
for msg in &mut self.messages {
match msg {
super::Message::Assistant(asst) => {
asst.upstream_id = String::new();
asst.created = 0;
}
super::Message::Tool(tool) => {
match &mut tool.inner.content {
RichContent::Text(s) => {
*s = strip_agent_id_lines(s);
}
RichContent::Parts(parts) => {
for p in parts {
if let RichContentPart::Text { text } = p {
*text = strip_agent_id_lines(text);
}
}
}
}
}
}
}
if let Some(s) = &mut self.continuation {
if let Some(mut c) = crate::agent::Continuation::try_from_string(s) {
match &mut c {
crate::agent::Continuation::Openrouter(x) => {
x.mcp_sessions.clear()
}
crate::agent::Continuation::ClaudeAgentSdk(x) => {
x.mcp_sessions.clear()
}
crate::agent::Continuation::CodexSdk(x) => {
x.mcp_sessions.clear()
}
crate::agent::Continuation::Mock(x) => {
x.mcp_sessions.clear()
}
}
*s = c.to_string();
}
}
}
}
fn strip_agent_id_lines(text: &str) -> String {
let mut out: String = text
.lines()
.map(|line| {
let Ok(serde_json::Value::Object(mut obj)) =
serde_json::from_str::<serde_json::Value>(line)
else {
return line.to_string();
};
if obj.remove("agent_id").is_none() {
return line.to_string();
}
serde_json::to_string(&serde_json::Value::Object(obj))
.unwrap_or_else(|_| line.to_string())
})
.collect::<Vec<_>>()
.join("\n");
if text.ends_with('\n') {
out.push('\n');
}
out
}
impl From<response::streaming::AgentCompletionChunk> for AgentCompletion {
fn from(
response::streaming::AgentCompletionChunk {
id,
created,
messages,
object,
usage,
upstream,
error,
continuation,
messages_queued,
}: response::streaming::AgentCompletionChunk,
) -> Self {
Self {
id,
created,
messages: messages.into_iter().map(Into::into).collect(),
object: object.into(),
usage: usage.unwrap_or_default(),
upstream,
error,
continuation,
messages_queued,
}
}
}