Skip to main content

outfox_openai/spec/chatkit/
thread.rs

1use serde::{Deserialize, Serialize};
2
3/// Represents a ChatKit thread and its current status.
4#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
5pub struct ThreadResource {
6    /// Identifier of the thread.
7    pub id: String,
8    /// Type discriminator that is always `chatkit.thread`.
9    #[serde(default = "default_thread_object")]
10    pub object: String,
11    /// Unix timestamp (in seconds) for when the thread was created.
12    pub created_at: u64,
13    /// Optional human-readable title for the thread. Defaults to null when no title has been
14    /// generated.
15    pub title: Option<String>,
16    /// Current status for the thread. Defaults to `active` for newly created threads.
17    #[serde(flatten)]
18    pub status: ThreadStatus,
19    /// Free-form string that identifies your end user who owns the thread.
20    pub user: String,
21    /// Thread items (only present when retrieving a thread)
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub items: Option<ThreadItemListResource>,
24}
25
26fn default_thread_object() -> String {
27    "chatkit.thread".to_string()
28}
29
30/// Current status for the thread.
31#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
32#[serde(tag = "type", rename_all = "snake_case")]
33pub enum ThreadStatus {
34    /// Indicates that a thread is active.
35    Active,
36    /// Indicates that a thread is locked and cannot accept new input.
37    Locked { reason: Option<String> },
38    /// Indicates that a thread has been closed.
39    Closed { reason: Option<String> },
40}
41
42/// A paginated list of ChatKit threads.
43#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
44pub struct ThreadListResource {
45    /// The type of object returned, must be `list`.
46    #[serde(default = "default_list_object")]
47    pub object: String,
48    /// A list of items
49    pub data: Vec<ThreadResource>,
50    /// The ID of the first item in the list.
51    pub first_id: Option<String>,
52    /// The ID of the last item in the list.
53    pub last_id: Option<String>,
54    /// Whether there are more items available.
55    pub has_more: bool,
56}
57
58fn default_list_object() -> String {
59    "list".to_string()
60}
61
62/// Confirmation payload returned after deleting a thread.
63#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
64pub struct DeletedThreadResource {
65    /// Identifier of the deleted thread.
66    pub id: String,
67    /// Type discriminator that is always `chatkit.thread.deleted`.
68    #[serde(default = "default_deleted_object")]
69    pub object: String,
70    /// Indicates that the thread has been deleted.
71    pub deleted: bool,
72}
73
74fn default_deleted_object() -> String {
75    "chatkit.thread.deleted".to_string()
76}
77
78/// A paginated list of thread items rendered for the ChatKit API.
79#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
80pub struct ThreadItemListResource {
81    /// The type of object returned, must be `list`.
82    #[serde(default = "default_list_object")]
83    pub object: String,
84    /// A list of items
85    pub data: Vec<ThreadItem>,
86    /// The ID of the first item in the list.
87    pub first_id: Option<String>,
88    /// The ID of the last item in the list.
89    pub last_id: Option<String>,
90    /// Whether there are more items available.
91    pub has_more: bool,
92}
93
94/// The thread item - discriminated union based on type field.
95#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
96#[serde(tag = "type", rename_all = "snake_case")]
97pub enum ThreadItem {
98    /// User-authored messages within a thread.
99    #[serde(rename = "chatkit.user_message")]
100    UserMessage(UserMessageItem),
101    /// Assistant-authored message within a thread.
102    #[serde(rename = "chatkit.assistant_message")]
103    AssistantMessage(AssistantMessageItem),
104    /// Thread item that renders a widget payload.
105    #[serde(rename = "chatkit.widget")]
106    Widget(WidgetMessageItem),
107    /// Record of a client side tool invocation initiated by the assistant.
108    #[serde(rename = "chatkit.client_tool_call")]
109    ClientToolCall(ClientToolCallItem),
110    /// Task emitted by the workflow to show progress and status updates.
111    #[serde(rename = "chatkit.task")]
112    Task(TaskItem),
113    /// Collection of workflow tasks grouped together in the thread.
114    #[serde(rename = "chatkit.task_group")]
115    TaskGroup(TaskGroupItem),
116}
117
118/// User-authored messages within a thread.
119#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
120pub struct UserMessageItem {
121    /// Identifier of the thread item.
122    pub id: String,
123    /// Type discriminator that is always `chatkit.thread_item`.
124    #[serde(default = "default_thread_item_object")]
125    pub object: String,
126    /// Unix timestamp (in seconds) for when the item was created.
127    pub created_at: u64,
128    /// Identifier of the parent thread.
129    pub thread_id: String,
130    /// Ordered content elements supplied by the user.
131    pub content: Vec<UserMessageContent>,
132    /// Attachments associated with the user message. Defaults to an empty list.
133    #[serde(default)]
134    pub attachments: Vec<Attachment>,
135    /// Inference overrides applied to the message. Defaults to null when unset.
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub inference_options: Option<InferenceOptions>,
138}
139
140fn default_thread_item_object() -> String {
141    "chatkit.thread_item".to_string()
142}
143
144/// Content blocks that comprise a user message.
145#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
146#[serde(tag = "type", rename_all = "snake_case")]
147pub enum UserMessageContent {
148    /// Text block that a user contributed to the thread.
149    #[serde(rename = "input_text")]
150    InputText { text: String },
151    /// Quoted snippet that the user referenced in their message.
152    #[serde(rename = "quoted_text")]
153    QuotedText { text: String },
154}
155
156/// Attachment metadata included on thread items.
157#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
158pub struct Attachment {
159    /// Attachment discriminator.
160    #[serde(rename = "type")]
161    pub attachment_type: AttachmentType,
162    /// Identifier for the attachment.
163    pub id: String,
164    /// Original display name for the attachment.
165    pub name: String,
166    /// MIME type of the attachment.
167    pub mime_type: String,
168    /// Preview URL for rendering the attachment inline.
169    pub preview_url: Option<String>,
170}
171
172/// Attachment discriminator.
173#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
174#[serde(rename_all = "snake_case")]
175pub enum AttachmentType {
176    Image,
177    File,
178}
179
180/// Model and tool overrides applied when generating the assistant response.
181#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
182pub struct InferenceOptions {
183    /// Preferred tool to invoke. Defaults to null when ChatKit should auto-select.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub tool_choice: Option<ToolChoice>,
186    /// Model name that generated the response. Defaults to null when using the session default.
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub model: Option<String>,
189}
190
191/// Tool selection that the assistant should honor when executing the item.
192#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
193pub struct ToolChoice {
194    /// Identifier of the requested tool.
195    pub id: String,
196}
197
198/// Assistant-authored message within a thread.
199#[derive(Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
200pub struct AssistantMessageItem {
201    /// Identifier of the thread item.
202    pub id: String,
203    /// Type discriminator that is always `chatkit.thread_item`.
204    #[serde(default = "default_thread_item_object")]
205    pub object: String,
206    /// Unix timestamp (in seconds) for when the item was created.
207    pub created_at: u64,
208    /// Identifier of the parent thread.
209    pub thread_id: String,
210    /// Ordered assistant response segments.
211    pub content: Vec<ResponseOutputText>,
212}
213
214/// Assistant response text accompanied by optional annotations.
215#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
216pub struct ResponseOutputText {
217    /// Type discriminator that is always `output_text`.
218    #[serde(default = "default_output_text_type")]
219    pub kind: String,
220    /// Assistant generated text.
221    pub text: String,
222    /// Ordered list of annotations attached to the response text.
223    #[serde(default)]
224    pub annotations: Vec<Annotation>,
225}
226
227fn default_output_text_type() -> String {
228    "output_text".to_string()
229}
230
231/// Annotation object describing a cited source.
232#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
233#[serde(tag = "type", rename_all = "snake_case")]
234pub enum Annotation {
235    /// Annotation that references an uploaded file.
236    #[serde(rename = "file")]
237    File(FileAnnotation),
238    /// Annotation that references a URL.
239    #[serde(rename = "url")]
240    Url(UrlAnnotation),
241}
242
243/// Annotation that references an uploaded file.
244#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
245pub struct FileAnnotation {
246    /// Type discriminator that is always `file` for this annotation.
247    #[serde(default = "default_file_annotation_type")]
248    pub kind: String,
249    /// File attachment referenced by the annotation.
250    pub source: FileAnnotationSource,
251}
252
253fn default_file_annotation_type() -> String {
254    "file".to_string()
255}
256
257/// Attachment source referenced by an annotation.
258#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
259pub struct FileAnnotationSource {
260    /// Type discriminator that is always `file`.
261    #[serde(default = "default_file_source_type")]
262    pub kind: String,
263    /// Filename referenced by the annotation.
264    pub filename: String,
265}
266
267fn default_file_source_type() -> String {
268    "file".to_string()
269}
270
271/// Annotation that references a URL.
272#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
273pub struct UrlAnnotation {
274    /// Type discriminator that is always `url` for this annotation.
275    #[serde(default = "default_url_annotation_type")]
276    pub kind: String,
277    /// URL referenced by the annotation.
278    pub source: UrlAnnotationSource,
279}
280
281fn default_url_annotation_type() -> String {
282    "url".to_string()
283}
284
285/// URL backing an annotation entry.
286#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
287pub struct UrlAnnotationSource {
288    /// Type discriminator that is always `url`.
289    #[serde(default = "default_url_source_type")]
290    pub kind: String,
291    /// URL referenced by the annotation.
292    pub url: String,
293}
294
295fn default_url_source_type() -> String {
296    "url".to_string()
297}
298
299/// Thread item that renders a widget payload.
300#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
301pub struct WidgetMessageItem {
302    /// Identifier of the thread item.
303    pub id: String,
304    /// Type discriminator that is always `chatkit.thread_item`.
305    #[serde(default = "default_thread_item_object")]
306    pub object: String,
307    /// Unix timestamp (in seconds) for when the item was created.
308    pub created_at: u64,
309    /// Identifier of the parent thread.
310    pub thread_id: String,
311    /// Serialized widget payload rendered in the UI.
312    pub widget: String,
313}
314
315/// Record of a client side tool invocation initiated by the assistant.
316#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
317pub struct ClientToolCallItem {
318    /// Identifier of the thread item.
319    pub id: String,
320    /// Type discriminator that is always `chatkit.thread_item`.
321    #[serde(default = "default_thread_item_object")]
322    pub object: String,
323    /// Unix timestamp (in seconds) for when the item was created.
324    pub created_at: u64,
325    /// Identifier of the parent thread.
326    pub thread_id: String,
327    /// Execution status for the tool call.
328    pub status: ClientToolCallStatus,
329    /// Identifier for the client tool call.
330    pub call_id: String,
331    /// Tool name that was invoked.
332    pub name: String,
333    /// JSON-encoded arguments that were sent to the tool.
334    pub arguments: String,
335    /// JSON-encoded output captured from the tool. Defaults to null while execution is in
336    /// progress.
337    pub output: Option<String>,
338}
339
340/// Execution status for the tool call.
341#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
342#[serde(rename_all = "snake_case")]
343pub enum ClientToolCallStatus {
344    InProgress,
345    Completed,
346}
347
348/// Task emitted by the workflow to show progress and status updates.
349#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
350pub struct TaskItem {
351    /// Identifier of the thread item.
352    pub id: String,
353    /// Type discriminator that is always `chatkit.thread_item`.
354    #[serde(default = "default_thread_item_object")]
355    pub object: String,
356    /// Unix timestamp (in seconds) for when the item was created.
357    pub created_at: u64,
358    /// Identifier of the parent thread.
359    pub thread_id: String,
360    /// Subtype for the task.
361    pub task_type: TaskType,
362    /// Optional heading for the task. Defaults to null when not provided.
363    pub heading: Option<String>,
364    /// Optional summary that describes the task. Defaults to null when omitted.
365    pub summary: Option<String>,
366}
367
368/// Subtype for the task.
369#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
370#[serde(rename_all = "snake_case")]
371pub enum TaskType {
372    Custom,
373    Thought,
374}
375
376/// Collection of workflow tasks grouped together in the thread.
377#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
378pub struct TaskGroupItem {
379    /// Identifier of the thread item.
380    pub id: String,
381    /// Type discriminator that is always `chatkit.thread_item`.
382    #[serde(default = "default_thread_item_object")]
383    pub object: String,
384    /// Unix timestamp (in seconds) for when the item was created.
385    pub created_at: u64,
386    /// Identifier of the parent thread.
387    pub thread_id: String,
388    /// Tasks included in the group.
389    pub tasks: Vec<TaskGroupTask>,
390}
391
392/// Task entry that appears within a TaskGroup.
393#[derive(Clone, Serialize, Debug, Deserialize, PartialEq)]
394pub struct TaskGroupTask {
395    /// Subtype for the grouped task.
396    pub task_type: TaskType,
397    /// Optional heading for the grouped task. Defaults to null when not provided.
398    pub heading: Option<String>,
399    /// Optional summary that describes the grouped task. Defaults to null when omitted.
400    pub summary: Option<String>,
401}