mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-12 23:02:41 +00:00
Extract ContentPart::OPENAI_REASONING and OPENAI_MESSAGE constants
Replace 16 raw string literal usages of "openai_reasoning" and "openai_message" across openai.rs and history.rs with constants defined on ContentPart, eliminating typo risk and centralizing the kind identifiers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ca71b2895d
commit
e73803039c
3 changed files with 22 additions and 17 deletions
|
|
@ -39,7 +39,7 @@ impl History {
|
|||
for turn in &mut self.turns {
|
||||
if let Turn::Assistant { provider_parts, .. } = turn {
|
||||
provider_parts.retain(|p| {
|
||||
!matches!(p, ContentPart::Other { kind, .. } if kind == "openai_reasoning" || kind == "openai_message")
|
||||
!matches!(p, ContentPart::Other { kind, .. } if kind == ContentPart::OPENAI_REASONING || kind == ContentPart::OPENAI_MESSAGE)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -285,7 +285,7 @@ mod tests {
|
|||
fn assistant_turn_preserves_provider_parts() {
|
||||
let mut history = History::default();
|
||||
let reasoning_item = ContentPart::Other {
|
||||
kind: "openai_reasoning".to_string(),
|
||||
kind: ContentPart::OPENAI_REASONING.to_string(),
|
||||
data: serde_json::json!({"type": "reasoning", "id": "rs_abc"}),
|
||||
};
|
||||
let tc = ToolCall::new("call_1", "search", serde_json::json!({}));
|
||||
|
|
@ -301,7 +301,7 @@ mod tests {
|
|||
assert_eq!(messages.len(), 1);
|
||||
// Provider parts come first, then tool calls
|
||||
assert!(
|
||||
matches!(&messages[0].content[0], ContentPart::Other { kind, .. } if kind == "openai_reasoning")
|
||||
matches!(&messages[0].content[0], ContentPart::Other { kind, .. } if kind == ContentPart::OPENAI_REASONING)
|
||||
);
|
||||
assert!(matches!(&messages[0].content[1], ContentPart::ToolCall(_)));
|
||||
}
|
||||
|
|
@ -421,7 +421,7 @@ mod tests {
|
|||
timestamp: SystemTime::now(),
|
||||
});
|
||||
let reasoning = ContentPart::Other {
|
||||
kind: "openai_reasoning".into(),
|
||||
kind: ContentPart::OPENAI_REASONING.into(),
|
||||
data: serde_json::json!({"type": "reasoning", "id": "rs_abc"}),
|
||||
};
|
||||
let tc = ToolCall::new("call_1", "search", serde_json::json!({}));
|
||||
|
|
@ -501,7 +501,7 @@ mod tests {
|
|||
content: format!("response {i}"),
|
||||
tool_calls: vec![],
|
||||
provider_parts: vec![ContentPart::Other {
|
||||
kind: "openai_reasoning".into(),
|
||||
kind: ContentPart::OPENAI_REASONING.into(),
|
||||
data: serde_json::json!({"type": "reasoning", "id": format!("rs_{i}")}),
|
||||
}],
|
||||
usage: Usage::default(),
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ fn translate_input(messages: &[Message]) -> (Option<String>, Vec<serde_json::Val
|
|||
// required so that reasoning items can find their "required following
|
||||
// item" during Responses API round-tripping.
|
||||
let has_opaque_message = msg.content.iter().any(|p| {
|
||||
matches!(p, ContentPart::Other { kind, .. } if kind == "openai_message")
|
||||
matches!(p, ContentPart::Other { kind, .. } if kind == ContentPart::OPENAI_MESSAGE)
|
||||
});
|
||||
for part in &msg.content {
|
||||
match part {
|
||||
|
|
@ -261,7 +261,7 @@ fn translate_input(messages: &[Message]) -> (Option<String>, Vec<serde_json::Val
|
|||
}
|
||||
// Round-trip opaque items back to the API
|
||||
ContentPart::Other { kind, data }
|
||||
if kind == "openai_reasoning" || kind == "openai_message" =>
|
||||
if kind == ContentPart::OPENAI_REASONING || kind == ContentPart::OPENAI_MESSAGE =>
|
||||
{
|
||||
input.push(data.clone());
|
||||
}
|
||||
|
|
@ -414,7 +414,7 @@ fn parse_output(output: &[serde_json::Value]) -> (Vec<ContentPart>, bool) {
|
|||
// The item's `id` and `status` fields are required so that reasoning
|
||||
// items preceding it can find their "required following item."
|
||||
parts.push(ContentPart::Other {
|
||||
kind: "openai_message".to_string(),
|
||||
kind: ContentPart::OPENAI_MESSAGE.to_string(),
|
||||
data: item.clone(),
|
||||
});
|
||||
if let Some(content) = item.get("content").and_then(|c| c.as_array()) {
|
||||
|
|
@ -433,7 +433,7 @@ fn parse_output(output: &[serde_json::Value]) -> (Vec<ContentPart>, bool) {
|
|||
}
|
||||
Some("reasoning") => {
|
||||
parts.push(ContentPart::Other {
|
||||
kind: "openai_reasoning".to_string(),
|
||||
kind: ContentPart::OPENAI_REASONING.to_string(),
|
||||
data: item.clone(),
|
||||
});
|
||||
}
|
||||
|
|
@ -827,14 +827,14 @@ fn handle_response_completed(
|
|||
// Reasoning items must precede function calls for Responses API round-trip
|
||||
for item in &state.reasoning_items {
|
||||
content_parts.push(ContentPart::Other {
|
||||
kind: "openai_reasoning".to_string(),
|
||||
kind: ContentPart::OPENAI_REASONING.to_string(),
|
||||
data: item.clone(),
|
||||
});
|
||||
}
|
||||
// Preserve full message output items for Responses API round-tripping
|
||||
for item in &state.message_items {
|
||||
content_parts.push(ContentPart::Other {
|
||||
kind: "openai_message".to_string(),
|
||||
kind: ContentPart::OPENAI_MESSAGE.to_string(),
|
||||
data: item.clone(),
|
||||
});
|
||||
}
|
||||
|
|
@ -1330,7 +1330,7 @@ mod tests {
|
|||
// First part is the reasoning item
|
||||
match &parts[0] {
|
||||
ContentPart::Other { kind, data } => {
|
||||
assert_eq!(kind, "openai_reasoning");
|
||||
assert_eq!(kind, ContentPart::OPENAI_REASONING);
|
||||
assert_eq!(data["type"], "reasoning");
|
||||
assert_eq!(data["id"], "rs_abc123");
|
||||
}
|
||||
|
|
@ -1367,8 +1367,8 @@ mod tests {
|
|||
assert!(has_tool_calls);
|
||||
// reasoning + openai_message + text + function_call
|
||||
assert_eq!(parts.len(), 4);
|
||||
assert!(matches!(&parts[0], ContentPart::Other { kind, .. } if kind == "openai_reasoning"));
|
||||
assert!(matches!(&parts[1], ContentPart::Other { kind, data } if kind == "openai_message" && data["id"] == "msg_xyz"));
|
||||
assert!(matches!(&parts[0], ContentPart::Other { kind, .. } if kind == ContentPart::OPENAI_REASONING));
|
||||
assert!(matches!(&parts[1], ContentPart::Other { kind, data } if kind == ContentPart::OPENAI_MESSAGE && data["id"] == "msg_xyz"));
|
||||
assert!(matches!(&parts[2], ContentPart::Text(t) if t == "Hello"));
|
||||
assert!(matches!(&parts[3], ContentPart::ToolCall(_)));
|
||||
}
|
||||
|
|
@ -1387,7 +1387,7 @@ mod tests {
|
|||
role: Role::Assistant,
|
||||
content: vec![
|
||||
ContentPart::Other {
|
||||
kind: "openai_reasoning".to_string(),
|
||||
kind: ContentPart::OPENAI_REASONING.to_string(),
|
||||
data: reasoning,
|
||||
},
|
||||
ContentPart::ToolCall(tc),
|
||||
|
|
@ -1431,11 +1431,11 @@ mod tests {
|
|||
role: Role::Assistant,
|
||||
content: vec![
|
||||
ContentPart::Other {
|
||||
kind: "openai_reasoning".to_string(),
|
||||
kind: ContentPart::OPENAI_REASONING.to_string(),
|
||||
data: reasoning,
|
||||
},
|
||||
ContentPart::Other {
|
||||
kind: "openai_message".to_string(),
|
||||
kind: ContentPart::OPENAI_MESSAGE.to_string(),
|
||||
data: opaque_message,
|
||||
},
|
||||
ContentPart::text("Checking now."),
|
||||
|
|
|
|||
|
|
@ -223,6 +223,11 @@ impl<'de> Deserialize<'de> for ContentPart {
|
|||
}
|
||||
|
||||
impl ContentPart {
|
||||
/// Kind string for opaque OpenAI reasoning output items.
|
||||
pub const OPENAI_REASONING: &str = "openai_reasoning";
|
||||
/// Kind string for opaque OpenAI message output items.
|
||||
pub const OPENAI_MESSAGE: &str = "openai_message";
|
||||
|
||||
pub fn text(text: impl Into<String>) -> Self {
|
||||
Self::Text(text.into())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue