gproxy_protocol/wire/openai/chat/
reasoning.rs1use super::content::present_nullable;
4use crate::Rest;
5use serde::{Deserialize, Serialize};
6
7#[derive(
8 Debug, Clone, PartialEq, Eq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
9)]
10#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
11pub enum ReasoningDetailKind {
12 #[serde(rename = "reasoning.text")]
13 Text,
14 #[serde(rename = "reasoning.summary")]
15 Summary,
16 #[serde(rename = "reasoning.encrypted")]
17 Encrypted,
18}
19#[derive(
22 Debug, Clone, PartialEq, Eq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
23)]
24#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
25pub struct ReasoningDetail {
26 #[serde(rename = "type")]
27 pub type_: ReasoningDetailKind,
28 #[serde(
29 default,
30 deserialize_with = "present_nullable",
31 skip_serializing_if = "Option::is_none"
32 )]
33 pub text: Option<Option<String>>,
34 #[serde(
35 default,
36 deserialize_with = "present_nullable",
37 skip_serializing_if = "Option::is_none"
38 )]
39 pub signature: Option<Option<String>>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub summary: Option<String>,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub data: Option<String>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub format: Option<String>,
46 #[serde(
47 default,
48 deserialize_with = "present_nullable",
49 skip_serializing_if = "Option::is_none"
50 )]
51 pub id: Option<Option<String>>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub index: Option<i64>,
54 #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
55 pub rest: Rest,
56}
57
58pub fn visible_reasoning(
59 content: &Option<Option<String>>,
60 reasoning: &Option<Option<String>>,
61 details: &Option<Option<Vec<ReasoningDetail>>>,
62) -> Option<String> {
63 if let Some(text) = super::reasoning_text(content, reasoning).filter(|s| !s.is_empty()) {
64 return Some(text.into());
65 }
66 let texts: Vec<_> = details
67 .as_ref()
68 .and_then(Option::as_ref)
69 .into_iter()
70 .flatten()
71 .filter_map(|d| match d.type_ {
72 ReasoningDetailKind::Text => d.text.as_ref().and_then(Option::as_deref),
73 ReasoningDetailKind::Summary => d.summary.as_deref(),
74 ReasoningDetailKind::Encrypted => None,
75 })
76 .collect();
77 if texts.is_empty() {
78 super::reasoning_text(content, reasoning).map(str::to_owned)
79 } else {
80 Some(texts.join(""))
81 }
82}