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