Skip to main content

objectiveai_sdk/functions/inventions/response/streaming/
function_invention_chunk.rs

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