fix: return thinking as reasoning output item per OpenAI spec
Thinking content was previously returned as non-standard top-level fields (thinking, thinking_duration). Now follows the official OpenAI Responses API format: - Reasoning appears as a 'type: reasoning' item in the output array with summary[].text containing the thinking content - Message item follows after the reasoning item - thinking_signature kept as proxy extension (internal multi-turn data) - Removed ResponseOutput/OutputContent structs in favor of serde_json::Value for polymorphic output items
This commit is contained in:
@@ -76,7 +76,9 @@ pub(crate) struct ResponsesResponse {
|
||||
#[serde(serialize_with = "serialize_option_u64")]
|
||||
pub max_output_tokens: Option<u64>,
|
||||
pub model: String,
|
||||
pub output: Vec<ResponseOutput>,
|
||||
/// Output items — can contain both `"reasoning"` and `"message"` items.
|
||||
/// Uses serde_json::Value because reasoning and message items have different shapes.
|
||||
pub output: Vec<serde_json::Value>,
|
||||
pub parallel_tool_calls: bool,
|
||||
pub previous_response_id: Option<String>,
|
||||
pub reasoning: Reasoning,
|
||||
@@ -91,34 +93,10 @@ pub(crate) struct ResponsesResponse {
|
||||
pub user: Option<String>,
|
||||
pub metadata: serde_json::Value,
|
||||
/// Proxy extension: opaque thinking verification signature.
|
||||
/// Present for all models. Required for multi-turn chaining with thinking models.
|
||||
/// Required for multi-turn chaining with thinking models.
|
||||
/// Not part of the official OpenAI spec — internal proxy data.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thinking_signature: Option<String>,
|
||||
/// Proxy extension: the model's internal reasoning/thinking content.
|
||||
/// Available for all models (Opus, Gemini Flash, Gemini Pro).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thinking: Option<String>,
|
||||
/// Proxy extension: time spent thinking (e.g. "0.041999832s").
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thinking_duration: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct ResponseOutput {
|
||||
#[serde(rename = "type")]
|
||||
pub output_type: &'static str,
|
||||
pub id: String,
|
||||
pub status: &'static str,
|
||||
pub role: &'static str,
|
||||
pub content: Vec<OutputContent>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub(crate) struct OutputContent {
|
||||
#[serde(rename = "type")]
|
||||
pub content_type: &'static str,
|
||||
pub text: String,
|
||||
pub annotations: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
@@ -201,6 +179,47 @@ impl Default for TextFormat {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Output item builders ────────────────────────────────────────────────────
|
||||
|
||||
/// Build a reasoning output item (goes BEFORE the message item in `output`).
|
||||
/// Matches: https://platform.openai.com/docs/api-reference/responses
|
||||
pub fn build_reasoning_output(thinking_text: &str) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"id": format!("rs_{}", uuid::Uuid::new_v4().to_string().replace('-', "")),
|
||||
"type": "reasoning",
|
||||
"summary": [{
|
||||
"type": "summary_text",
|
||||
"text": thinking_text,
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a message output item.
|
||||
pub fn build_message_output(msg_id: &str, text: &str) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "message",
|
||||
"id": msg_id,
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [{
|
||||
"type": "output_text",
|
||||
"text": text,
|
||||
"annotations": [],
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
||||
/// Build an in-progress message output item (no content yet, for streaming).
|
||||
pub fn build_message_output_in_progress(msg_id: &str) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "message",
|
||||
"id": msg_id,
|
||||
"status": "in_progress",
|
||||
"role": "assistant",
|
||||
"content": [],
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Serialize Option<u64> as either the number or JSON null (not omitted).
|
||||
|
||||
Reference in New Issue
Block a user