Skip to main content

objectiveai_sdk/functions/inventions/recursive/response/streaming/
function_invention_recursive_chunk.rs

1use crate::agent;
2use serde::{Deserialize, Serialize};
3use schemars::JsonSchema;
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
6#[schemars(rename = "functions.inventions.recursive.response.streaming.FunctionInventionRecursiveChunk")]
7pub struct FunctionInventionRecursiveChunk {
8    pub id: String,
9    pub inventions: Vec<super::FunctionInventionChunk>,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    #[schemars(extend("omitempty" = true))]
12    pub inventions_errors: Option<bool>,
13    #[arbitrary(with = crate::arbitrary_util::arbitrary_u64)]
14    pub created: u64,
15    pub object: super::Object,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    #[schemars(extend("omitempty" = true))]
18    pub usage: Option<agent::completions::response::Usage>,
19}
20
21impl FunctionInventionRecursiveChunk {
22    /// Yields each inner error from the recursive chunk's wrapped
23    /// non-recursive `FunctionInventionChunk`s.
24    ///
25    /// For each wrapped invention:
26    /// 1. If the wrapped invention has its own `.error`, yields one item
27    ///    with `agent_completion_index: None`.
28    /// 2. Then yields each item from the wrapped invention's
29    ///    `inner_errors()`, re-tagged with this wrapper's
30    ///    `function_invention_index` and `agent_completion_index:
31    ///    Some(non_recursive.agent_completion_index)`.
32    ///
33    /// Lazy and zero-allocation; collect with `.collect::<Vec<_>>()` if
34    /// you need to retain the items past the chunk's lifetime.
35    pub fn inner_errors(&self) -> impl Iterator<Item = super::InnerError<'_>> {
36        self.inventions.iter().flat_map(|wrapper| {
37            let function_invention_index = wrapper.index;
38            let own = wrapper.inner.error.as_ref().map(move |error| super::InnerError {
39                function_invention_index,
40                agent_completion_index: None,
41                error: std::borrow::Cow::Borrowed(error),
42            });
43            let nested = wrapper.inner.inner_errors().map(move |non_recursive| {
44                super::InnerError {
45                    function_invention_index,
46                    agent_completion_index: Some(non_recursive.agent_completion_index),
47                    error: non_recursive.error,
48                }
49            });
50            own.into_iter().chain(nested)
51        })
52    }
53
54    pub fn push(
55        &mut self,
56        FunctionInventionRecursiveChunk {
57            inventions,
58            inventions_errors,
59            usage,
60            ..
61        }: &FunctionInventionRecursiveChunk,
62    ) {
63        self.push_inventions(inventions);
64        if let Some(true) = inventions_errors {
65            self.inventions_errors = Some(true);
66        }
67        match (&mut self.usage, usage) {
68            (Some(self_usage), Some(other_usage)) => {
69                self_usage.push(other_usage);
70            }
71            (None, Some(other_usage)) => {
72                self.usage = Some(other_usage.clone());
73            }
74            _ => {}
75        }
76    }
77
78    fn push_inventions(
79        &mut self,
80        other_inventions: &[super::FunctionInventionChunk],
81    ) {
82        fn push_invention(
83            inventions: &mut Vec<super::FunctionInventionChunk>,
84            other: &super::FunctionInventionChunk,
85        ) {
86            fn find_invention(
87                inventions: &mut Vec<super::FunctionInventionChunk>,
88                index: u64,
89            ) -> Option<&mut super::FunctionInventionChunk> {
90                for invention in inventions {
91                    if invention.index == index {
92                        return Some(invention);
93                    }
94                }
95                None
96            }
97            if let Some(existing) = find_invention(inventions, other.index) {
98                existing.push(other);
99            } else {
100                inventions.push(other.clone());
101            }
102        }
103        for other in other_inventions {
104            push_invention(&mut self.inventions, other);
105        }
106    }
107
108    /// Produces the [`LogFile`]s for the log file structure.
109    ///
110    /// Returns `(reference, files)`. All paths relative to `logs/`.
111    #[cfg(feature = "filesystem")]
112    pub fn produce_files(&self) -> Option<(serde_json::Value, Vec<crate::filesystem::logs::LogFile>)> {
113        use crate::filesystem::logs::LogFile;
114        const ROUTE: &str = "functions/inventions/recursive";
115
116        let id = &self.id;
117        if id.is_empty() {
118            return None;
119        }
120
121        let mut files: Vec<LogFile> = Vec::new();
122        let mut invention_refs: Vec<serde_json::Value> = Vec::new();
123
124        for invention in &self.inventions {
125            let (reference, invention_files) = invention.produce_files();
126            invention_refs.push(reference);
127            files.extend(invention_files);
128        }
129
130        // Serialize a shell without inventions to avoid double-serialization
131        let shell = FunctionInventionRecursiveChunk {
132            id: self.id.clone(),
133            inventions: Vec::new(),
134            inventions_errors: self.inventions_errors,
135            created: self.created,
136            object: self.object,
137            usage: self.usage.clone(),
138        };
139        let mut root = serde_json::to_value(&shell).unwrap();
140        root["inventions"] = serde_json::Value::Array(invention_refs);
141
142        let root_file = LogFile {
143            route: ROUTE.to_string(),
144            id: id.clone(),
145            message_index: None,
146            media_index: None,
147            extension: "json".to_string(),
148            content: serde_json::to_vec_pretty(&root).unwrap(),
149        };
150        let reference = serde_json::json!({ "type": "reference", "path": root_file.path() });
151        files.push(root_file);
152
153        Some((reference, files))
154    }
155}