1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5pub type AnswersByToolCall = HashMap<String, HashMap<String, String>>;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Workspace {
14 pub id: String,
16 pub name: String,
18 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
20 pub created_at: i64,
21 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
23 pub updated_at: i64,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct WorkspacesResponse {
29 pub workspaces: Vec<Workspace>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Agent {
36 pub uid: String,
39 pub name: String,
41 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
43 pub description: String,
44 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
46 pub mode: String,
47 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
49 pub icon: String,
50 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
52 pub is_published: bool,
53 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
55 pub published_at: i64,
56 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
58 pub created_at: i64,
59 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
61 pub updated_at: i64,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct AgentsResponse {
67 pub agents: Vec<Agent>,
69 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
71 pub total: i32,
72}
73
74#[derive(Debug, Serialize, Default, Clone)]
76pub struct GetAgentsOptions {
77 #[serde(skip_serializing_if = "Option::is_none")]
78 page: Option<i32>,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 limit: Option<i32>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 name: Option<String>,
83}
84
85impl GetAgentsOptions {
86 #[inline]
88 pub fn new() -> Self {
89 Default::default()
90 }
91
92 #[inline]
94 #[must_use]
95 pub fn page(self, page: i32) -> Self {
96 Self {
97 page: Some(page),
98 ..self
99 }
100 }
101
102 #[inline]
104 #[must_use]
105 pub fn limit(self, limit: i32) -> Self {
106 Self {
107 limit: Some(limit),
108 ..self
109 }
110 }
111
112 #[inline]
114 #[must_use]
115 pub fn name(self, name: impl Into<String>) -> Self {
116 Self {
117 name: Some(name.into()),
118 ..self
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
125#[serde(rename_all = "snake_case")]
126pub enum ConversationStatus {
127 Succeeded,
129 Interrupted,
132 Failed,
134 Stopped,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct Reference {
141 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
143 pub index: i32,
144 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
146 pub original_index: i32,
147 #[serde(
149 default,
150 deserialize_with = "crate::serde_utils::null_as_default",
151 rename = "type"
152 )]
153 pub ref_type: String,
154 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
156 pub id: String,
157 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
160 pub title: String,
161 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
164 pub url: String,
165 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
170 pub content: Option<serde_json::Value>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct Question {
176 pub question: String,
178 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
180 pub options: Vec<QuestionOption>,
181 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
183 pub multi_select: bool,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct QuestionOption {
189 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
191 pub label: String,
192 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
194 pub description: String,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct Interrupt {
201 pub node_id: String,
203 pub tool_call_id: String,
205 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
207 pub questions: Vec<Question>,
208 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
210 pub interactions: Vec<HumanInteraction>,
211 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
213 pub message_id: i64,
214 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
216 pub chat_id: i64,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct HumanInteraction {
222 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
224 pub tool_call_id: String,
225 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
227 pub interrupt_id: String,
228 #[serde(
230 default,
231 deserialize_with = "crate::serde_utils::null_as_default",
232 rename = "type"
233 )]
234 pub interaction_type: String,
235 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
237 pub tool_name: String,
238 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
240 pub questions: Vec<Question>,
241 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
243 pub tool_args: serde_json::Value,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct AgentError {
249 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
251 pub code: i32,
252 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
254 pub message: String,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ConversationResponse {
262 pub chat_uid: String,
265 #[serde(deserialize_with = "crate::serde_utils::deserialize_string_or_int_as_string")]
268 pub message_id: String,
269 pub status: ConversationStatus,
271 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
273 pub answer: String,
274 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
276 pub references: Option<Vec<Reference>>,
277 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
280 pub further_questions: Option<Vec<String>>,
281 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
283 pub elapsed_time: f64,
284 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
286 pub interrupt: Option<Interrupt>,
287 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
289 pub error: Option<AgentError>,
290}
291
292impl ConversationResponse {
293 pub(crate) fn from_stream_parts(
297 started: Option<(String, String)>,
298 payload: WorkflowFinishedPayload,
299 ) -> Self {
300 let (chat_uid, message_id) = started.unwrap_or_default();
301 let error = (payload.status == ConversationStatus::Failed).then_some(AgentError {
302 code: payload.error_code,
303 message: payload.error_message,
304 });
305 Self {
306 chat_uid,
307 message_id,
308 status: payload.status,
309 answer: payload.outputs.answer.unwrap_or_default(),
310 references: payload.outputs.references,
311 further_questions: payload.outputs.further_questions,
312 elapsed_time: payload.elapsed_time,
313 interrupt: None,
314 error,
315 }
316 }
317
318 pub(crate) fn from_stream_interrupt(
327 started: Option<(String, String)>,
328 interrupt: Interrupt,
329 ) -> Self {
330 let (chat_uid, message_id) = started.unwrap_or_default();
331 Self {
332 chat_uid,
333 message_id,
334 status: ConversationStatus::Interrupted,
335 answer: String::new(),
336 references: None,
337 further_questions: None,
338 elapsed_time: 0.0,
339 interrupt: Some(interrupt),
340 error: None,
341 }
342 }
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct ChatStartedPayload {
348 pub chat_uid: String,
350 #[serde(deserialize_with = "crate::serde_utils::deserialize_string_or_int_as_string")]
354 pub message_id: String,
355 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
357 pub chat_id: i64,
358 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
360 pub error: String,
361 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
363 pub error_message: String,
364}
365
366#[derive(Debug, Clone, Default, Serialize, Deserialize)]
369pub struct MessagePayload {
370 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
372 pub text: String,
373 #[serde(
376 default,
377 deserialize_with = "crate::serde_utils::null_as_default",
378 rename = "type"
379 )]
380 pub message_type: String,
381 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
385 pub key: String,
386 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
388 pub started_at: i64,
389 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
391 pub stage: String,
392 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
395 pub stage_title: String,
396 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
399 pub stage_finished_title: String,
400 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
402 pub outputs: Option<serde_json::Value>,
403}
404
405#[derive(Debug, Clone, Default, Serialize, Deserialize)]
407pub struct WorkflowOutputs {
408 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
410 pub answer: Option<String>,
411 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
413 pub references: Option<Vec<Reference>>,
414 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
417 pub further_questions: Option<Vec<String>>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct WorkflowFinishedPayload {
425 pub status: ConversationStatus,
427 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
429 pub elapsed_time: f64,
430 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
432 pub outputs: WorkflowOutputs,
433 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
435 pub error: String,
436 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
438 pub error_code: i32,
439 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
441 pub error_message: String,
442 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
444 pub error_args: Option<serde_json::Value>,
445 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
447 pub process_data: Vec<serde_json::Value>,
448}
449
450#[derive(Debug, Clone, Default, Serialize, Deserialize)]
452pub struct WorkflowStartedInputs {
453 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
455 pub chat_id: i64,
456 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
458 pub chat_uid: String,
459 #[serde(
462 default,
463 deserialize_with = "crate::serde_utils::deserialize_string_or_int_as_string"
464 )]
465 pub message_id: String,
466 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
468 pub query: String,
469}
470
471#[derive(Debug, Clone, Default, Serialize, Deserialize)]
474pub struct WorkflowStartedPayload {
475 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
477 pub hit_cache: bool,
478 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
480 pub inputs: WorkflowStartedInputs,
481 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
483 pub started_at: i64,
484 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
486 pub workflow_id: i64,
487}
488
489#[derive(Debug, Clone, Default, Serialize, Deserialize)]
492pub struct ChatFinishedPayload {
493 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
495 pub chat_id: i64,
496 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
498 pub chat_uid: String,
499 #[serde(
502 default,
503 deserialize_with = "crate::serde_utils::deserialize_string_or_int_as_string"
504 )]
505 pub message_id: String,
506 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
508 pub error: String,
509 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
511 pub error_message: String,
512}
513
514#[derive(Debug, Clone, Default, Serialize, Deserialize)]
518pub struct ChatTitleUpdatedPayload {
519 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
521 pub chat_id: i64,
522 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
524 pub chat_uid: String,
525 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
527 pub source: String,
528 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
530 pub title: String,
531 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
533 pub updated_at: i64,
534}
535
536#[derive(Debug, Clone, Default, Serialize, Deserialize)]
541pub struct ThinkingStartedPayload {
542 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
544 pub started_at: i64,
545}
546
547#[derive(Debug, Clone, Default, Serialize, Deserialize)]
550pub struct ThinkingFinishedPayload {
551 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
553 pub finished_at: i64,
554 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
556 pub elapsed_time: i32,
557}
558
559#[derive(Debug, Clone, Default, Serialize, Deserialize)]
563pub struct NodeToolUseStartedPayload {
564 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
566 pub tool_use_id: String,
567 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
569 pub tool_name: String,
570 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
573 pub tool_func_name: String,
574 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
576 pub tool_args: String,
577 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
580 pub tips: String,
581 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
583 pub tip_chips: Vec<String>,
584 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
587 pub iteration: i32,
588 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
590 pub started_at: i64,
591}
592
593#[derive(Debug, Clone, Default, Serialize, Deserialize)]
596pub struct NodeToolUseOutputs {
597 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
599 pub references: Option<Vec<Reference>>,
600 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
602 pub reference_domains: Option<Vec<String>>,
603 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
605 pub query: Option<String>,
606 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
608 pub text: Option<String>,
609 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
611 pub tool_args: Option<serde_json::Value>,
612 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
614 pub data: Option<serde_json::Value>,
615}
616
617#[derive(Debug, Clone, Default, Serialize, Deserialize)]
620pub struct NodeToolUseFinishedPayload {
621 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
623 pub tool_use_id: String,
624 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
626 pub status: String,
627 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
629 pub error: String,
630 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
632 pub elapsed_time: f64,
633 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
635 pub started_at: i64,
636 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
638 pub tool_name: String,
639 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
641 pub tool_func_name: String,
642 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
644 pub tool_args: String,
645 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
647 pub tool_type: String,
648 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
650 pub tips: String,
651 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
653 pub tip_chips: Vec<String>,
654 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
656 pub iteration: i32,
657 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
659 pub is_thinking: bool,
660 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
662 pub outputs: NodeToolUseOutputs,
663}
664
665#[derive(Debug, Clone, Default, Serialize, Deserialize)]
669pub struct SubagentStartedPayload {
670 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
672 pub node_id: String,
673 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
675 pub tool_use_id: String,
676 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
678 pub started_at: i64,
679 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
681 pub goal: String,
682 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
684 pub prompt: String,
685 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
687 pub subagent_id: String,
688 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
690 pub tools: Vec<serde_json::Value>,
691}
692
693#[derive(Debug, Clone, Default, Serialize, Deserialize)]
697pub struct SubagentProgressPayload {
698 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
700 pub node_id: String,
701 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
703 pub parent_tool_call_id: String,
704 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
706 pub subagent_tool_name: String,
707 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
709 pub subagent_tool_args: String,
710 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
712 pub subagent_status: String,
713 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
715 pub subagent_duration_ms: i64,
716 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
718 pub subagent_iteration: i32,
719 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
721 pub started_at: i64,
722}
723
724#[derive(Debug, Clone, Default, Serialize, Deserialize)]
726pub struct SubagentOutputs {
727 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
729 pub goal: Option<String>,
730 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
732 pub result: Option<String>,
733 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
735 pub subagent_tools: Option<Vec<serde_json::Value>>,
736}
737
738#[derive(Debug, Clone, Default, Serialize, Deserialize)]
740pub struct SubagentFinishedPayload {
741 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
743 pub node_id: String,
744 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
746 pub tool_use_id: String,
747 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
749 pub status: String,
750 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
752 pub started_at: i64,
753 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
755 pub elapsed_time: f64,
756 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
758 pub error: String,
759 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
762 pub outputs: SubagentOutputs,
763}
764
765#[derive(Debug, Clone, Default, Serialize, Deserialize)]
769pub struct AgentToolStartedPayload {
770 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
772 pub node_id: String,
773 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
775 pub tool_use_id: String,
776 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
778 pub agent_tool_name: String,
779 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
781 pub title: String,
782 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
784 pub started_at: i64,
785 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
787 pub tool_args: String,
788 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
790 pub tool_name: String,
791 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
793 pub tips: String,
794 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
796 pub tip_chips: Vec<String>,
797 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
799 pub is_thinking: bool,
800}
801
802#[derive(Debug, Clone, Default, Serialize, Deserialize)]
805pub struct AgentToolProgressPayload {
806 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
808 pub node_id: String,
809 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
811 pub parent_tool_call_id: String,
812 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
814 pub agent_tool_name: String,
815 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
817 pub inner_tool_name: String,
818 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
820 pub inner_tool_args: String,
821 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
823 pub status: String,
824 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
826 pub duration_ms: i64,
827 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
829 pub started_at: i64,
830 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
832 pub is_thinking: bool,
833}
834
835#[derive(Debug, Clone, Default, Serialize, Deserialize)]
837pub struct AgentToolFinishedPayload {
838 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
840 pub node_id: String,
841 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
843 pub tool_use_id: String,
844 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
846 pub agent_tool_name: String,
847 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
849 pub status: String,
850 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
852 pub started_at: i64,
853 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
855 pub elapsed_time: f64,
856 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
858 pub error: String,
859 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
861 pub tool_args: String,
862 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
864 pub outputs: Option<serde_json::Value>,
865 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
867 pub tool_type: String,
868 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
870 pub tips: String,
871 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
873 pub tip_chips: Vec<String>,
874 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
876 pub is_thinking: bool,
877}
878
879#[derive(Debug, Clone, Default, Serialize, Deserialize)]
883pub struct QueryMaskedPayload {
884 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
886 pub raw_query: String,
887 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
889 pub masked_query: String,
890}
891
892#[derive(Debug, Clone, Default, Serialize, Deserialize)]
895pub struct PlanChangedPayload {
896 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
898 pub node_id: String,
899 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
901 pub started_at: i64,
902 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
904 pub outputs: Option<serde_json::Value>,
905 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
908 pub tool_name: String,
909}
910
911#[derive(Debug, Clone, Default, Serialize, Deserialize)]
915pub struct ContextCompressStartedPayload {
916 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
918 pub started_at: String,
919 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
921 pub inputs: Option<serde_json::Value>,
922}
923
924#[derive(Debug, Clone, Default, Serialize, Deserialize)]
927pub struct ContextCompressFinishedPayload {
928 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
930 pub created_at: String,
931 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
933 pub inputs: Option<serde_json::Value>,
934 #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")]
936 pub outputs: Option<serde_json::Value>,
937}
938
939#[derive(Debug, Clone)]
967pub enum ConversationStreamEvent {
968 ChatStarted(ChatStartedPayload),
970 WorkflowStarted(WorkflowStartedPayload),
974 Message(MessagePayload),
976 Ping,
979 ThinkingStarted(ThinkingStartedPayload),
981 ThinkingFinished(ThinkingFinishedPayload),
983 NodeToolUseStarted(NodeToolUseStartedPayload),
985 NodeToolUseFinished(NodeToolUseFinishedPayload),
987 SubagentStarted(SubagentStartedPayload),
989 SubagentProgress(SubagentProgressPayload),
991 SubagentFinished(SubagentFinishedPayload),
993 AgentToolStarted(AgentToolStartedPayload),
995 AgentToolProgress(AgentToolProgressPayload),
997 AgentToolFinished(AgentToolFinishedPayload),
999 HumanInteractionRequired(ConversationResponse),
1005 QueryMasked(QueryMaskedPayload),
1007 PlanChanged(PlanChangedPayload),
1009 ContextCompressStarted(ContextCompressStartedPayload),
1012 ContextCompressFinished(ContextCompressFinishedPayload),
1014 ChatFinished(ChatFinishedPayload),
1017 WorkflowFinished(ConversationResponse),
1025 ChatTitleUpdated(ChatTitleUpdatedPayload),
1029 Other {
1034 event: String,
1036 data: serde_json::Value,
1038 },
1039}
1040
1041#[cfg(test)]
1042mod tests {
1043 use super::*;
1044
1045 #[test]
1046 fn workflow_finished_tolerates_null_outputs() {
1047 let payload: WorkflowFinishedPayload =
1051 serde_json::from_str(r#"{"status":"succeeded","outputs":null}"#).unwrap();
1052 assert!(payload.outputs.answer.is_none());
1053
1054 let payload: WorkflowFinishedPayload =
1056 serde_json::from_str(r#"{"status":"succeeded"}"#).unwrap();
1057 assert!(payload.outputs.answer.is_none());
1058 }
1059
1060 #[test]
1061 fn streamed_vec_fields_tolerate_null_sequences() {
1062 let payload: WorkflowFinishedPayload =
1066 serde_json::from_str(r#"{"status":"succeeded","process_data":null}"#).unwrap();
1067 assert!(payload.process_data.is_empty());
1068
1069 let payload: NodeToolUseStartedPayload =
1070 serde_json::from_str(r#"{"tip_chips":null}"#).unwrap();
1071 assert!(payload.tip_chips.is_empty());
1072
1073 let payload: SubagentStartedPayload = serde_json::from_str(r#"{"tools":null}"#).unwrap();
1074 assert!(payload.tools.is_empty());
1075 }
1076
1077 const SUCCEEDED_JSON: &str = r#"{
1080 "chat_uid": "ct_9f2c1a5b",
1081 "message_id": "42",
1082 "status": "succeeded",
1083 "answer": "Tesla (TSLA.US) recently...",
1084 "references": [
1085 { "index": 1, "title": "...", "url": "..." }
1086 ],
1087 "elapsed_time": 3.21
1088 }"#;
1089
1090 const INTERRUPTED_JSON: &str = r#"{
1092 "chat_uid": "ct_9f2c1a5b",
1093 "message_id": "43",
1094 "status": "interrupted",
1095 "answer": "",
1096 "references": null,
1097 "elapsed_time": 1.05,
1098 "interrupt": {
1099 "node_id": "n_ask_human",
1100 "tool_call_id": "call_abc123",
1101 "questions": [
1102 {
1103 "question": "Which time range would you like to check?",
1104 "options": [
1105 { "description": "Past week" },
1106 { "description": "Past month" }
1107 ],
1108 "multi_select": false
1109 }
1110 ],
1111 "message_id": 43,
1112 "chat_id": 1001
1113 }
1114 }"#;
1115
1116 #[test]
1117 fn deserialize_succeeded_conversation_response() {
1118 let resp: ConversationResponse = serde_json::from_str(SUCCEEDED_JSON).unwrap();
1119 assert_eq!(resp.chat_uid, "ct_9f2c1a5b");
1120 assert_eq!(resp.message_id, "42");
1121 assert_eq!(resp.status, ConversationStatus::Succeeded);
1122 assert_eq!(resp.answer, "Tesla (TSLA.US) recently...");
1123 assert_eq!(resp.references.as_ref().unwrap().len(), 1);
1124 assert_eq!(resp.references.as_ref().unwrap()[0].index, 1);
1125 assert!((resp.elapsed_time - 3.21).abs() < f64::EPSILON);
1126 assert!(resp.interrupt.is_none());
1127 assert!(resp.error.is_none());
1128 }
1129
1130 #[test]
1131 fn deserialize_interrupted_conversation_response() {
1132 let resp: ConversationResponse = serde_json::from_str(INTERRUPTED_JSON).unwrap();
1133 assert_eq!(resp.status, ConversationStatus::Interrupted);
1134 let interrupt = resp.interrupt.expect("interrupt");
1135 assert_eq!(interrupt.node_id, "n_ask_human");
1136 assert_eq!(interrupt.tool_call_id, "call_abc123");
1137 assert_eq!(interrupt.message_id, 43);
1138 assert_eq!(interrupt.chat_id, 1001);
1139 assert_eq!(interrupt.questions.len(), 1);
1140 assert_eq!(interrupt.questions[0].options.len(), 2);
1141 assert!(!interrupt.questions[0].multi_select);
1142 }
1143
1144 #[test]
1145 fn deserialize_chat_started_payload_with_numeric_message_id() {
1146 let json = r#"{"chat_uid":"ct_9f2c1a5b","message_id":42}"#;
1149 let payload: ChatStartedPayload = serde_json::from_str(json).unwrap();
1150 assert_eq!(payload.chat_uid, "ct_9f2c1a5b");
1151 assert_eq!(payload.message_id, "42");
1152 }
1153
1154 #[test]
1155 fn deserialize_message_payload() {
1156 let json = r#"{"text":"Tesla"}"#;
1157 let payload: MessagePayload = serde_json::from_str(json).unwrap();
1158 assert_eq!(payload.text, "Tesla");
1159 }
1160
1161 #[test]
1162 fn deserialize_message_payload_with_full_fields() {
1163 let json =
1165 r#"{"text":"Tesla","type":"answer","key":"n_llm_1:answer","started_at":1752048000}"#;
1166 let payload: MessagePayload = serde_json::from_str(json).unwrap();
1167 assert_eq!(payload.text, "Tesla");
1168 assert_eq!(payload.message_type, "answer");
1169 assert_eq!(payload.key, "n_llm_1:answer");
1170 assert_eq!(payload.started_at, 1752048000);
1171 }
1172
1173 #[test]
1174 fn deserialize_workflow_finished_payload() {
1175 let json = r#"{"status":"succeeded","elapsed_time":3.21,"outputs":{"answer":"Tesla (TSLA.US) recently...","further_questions":["What is Tesla's P/E?","How did Q3 deliveries look?"]}}"#;
1176 let payload: WorkflowFinishedPayload = serde_json::from_str(json).unwrap();
1177 assert_eq!(payload.status, ConversationStatus::Succeeded);
1178 assert!((payload.elapsed_time - 3.21).abs() < f64::EPSILON);
1179 assert_eq!(
1180 payload.outputs.answer.as_deref(),
1181 Some("Tesla (TSLA.US) recently...")
1182 );
1183 assert_eq!(
1184 payload.outputs.further_questions.as_deref(),
1185 Some(
1186 [
1187 "What is Tesla's P/E?".to_string(),
1188 "How did Q3 deliveries look?".to_string(),
1189 ]
1190 .as_slice()
1191 )
1192 );
1193
1194 let resp = ConversationResponse::from_stream_parts(
1195 Some(("ct_9f2c1a5b".to_string(), "42".to_string())),
1196 payload,
1197 );
1198 assert_eq!(resp.chat_uid, "ct_9f2c1a5b");
1199 assert_eq!(resp.message_id, "42");
1200 assert_eq!(resp.answer, "Tesla (TSLA.US) recently...");
1201 assert_eq!(resp.further_questions.as_ref().unwrap().len(), 2);
1203 assert!(resp.interrupt.is_none());
1204 assert!(resp.error.is_none());
1205 }
1206
1207 #[test]
1208 fn deserialize_workflow_finished_payload_with_failure() {
1209 let json = r#"{"status":"failed","elapsed_time":0.8,"error":"upstream timeout","error_code":500,"error_message":"Something went wrong, please try again"}"#;
1212 let payload: WorkflowFinishedPayload = serde_json::from_str(json).unwrap();
1213 assert_eq!(payload.status, ConversationStatus::Failed);
1214 assert_eq!(payload.error, "upstream timeout");
1215 assert_eq!(payload.error_code, 500);
1216 assert_eq!(
1217 payload.error_message,
1218 "Something went wrong, please try again"
1219 );
1220
1221 let resp = ConversationResponse::from_stream_parts(None, payload);
1222 assert_eq!(resp.status, ConversationStatus::Failed);
1223 let error = resp.error.expect("error");
1224 assert_eq!(error.code, 500);
1225 assert_eq!(error.message, "Something went wrong, please try again");
1226 }
1227
1228 #[test]
1229 fn conversation_response_from_stream_interrupt() {
1230 let json = r#"{"node_id":"n_ask_human","tool_call_id":"call_abc123","questions":[{"question":"Which time range would you like to check?","options":[{"description":"Past week"},{"description":"Past month"}],"multi_select":false}],"message_id":43,"chat_id":1001}"#;
1235 let interrupt: Interrupt = serde_json::from_str(json).unwrap();
1236
1237 let resp = ConversationResponse::from_stream_interrupt(
1238 Some(("ct_9f2c1a5b".to_string(), "43".to_string())),
1239 interrupt,
1240 );
1241 assert_eq!(resp.chat_uid, "ct_9f2c1a5b");
1242 assert_eq!(resp.message_id, "43");
1243 assert_eq!(resp.status, ConversationStatus::Interrupted);
1244 let interrupt = resp.interrupt.expect("interrupt");
1245 assert_eq!(interrupt.node_id, "n_ask_human");
1246 assert_eq!(interrupt.questions.len(), 1);
1247 }
1248
1249 #[test]
1250 fn deserialize_node_tool_use_finished_payload() {
1251 let json = r#"{"tool_use_id":"call_abc123","status":"succeeded","elapsed_time":1.42,"tool_name":"Web Search","tool_func_name":"web_search","tool_args":"{\"query\":\"TSLA stock news\"}","tool_type":"builtin","tips":"Searched the web","iteration":1,"is_thinking":true,"outputs":{"query":"TSLA stock news","references":[{"index":1,"title":"...","url":"..."}]}}"#;
1252 let payload: NodeToolUseFinishedPayload = serde_json::from_str(json).unwrap();
1253 assert_eq!(payload.tool_use_id, "call_abc123");
1254 assert_eq!(payload.status, "succeeded");
1255 assert_eq!(payload.tool_func_name, "web_search");
1256 assert!(payload.is_thinking);
1257 assert_eq!(payload.outputs.query.as_deref(), Some("TSLA stock news"));
1258 assert_eq!(payload.outputs.references.as_ref().unwrap().len(), 1);
1259 }
1260
1261 #[test]
1262 fn deserialize_reference_with_nested_content() {
1263 let json = r#"{"type":"NewsArticle","id":"295354885","index":1,"original_index":10,"content":{"source":"智通财经","description":"Jefferies cut Tesla's target.","published_at":"2026-08-10T03:45:02Z","source_url":"https://example.com/a","title":""}}"#;
1267 let r: Reference = serde_json::from_str(json).unwrap();
1268 assert_eq!(r.index, 1);
1269 assert_eq!(r.original_index, 10);
1270 assert_eq!(r.ref_type, "NewsArticle");
1271 assert_eq!(r.id, "295354885");
1272 let content = r.content.expect("content");
1273 assert_eq!(content["source"], "智通财经");
1274 assert_eq!(content["published_at"], "2026-08-10T03:45:02Z");
1275 }
1276
1277 #[test]
1278 fn deserialize_reference_flat_shape_still_works() {
1279 let r: Reference = serde_json::from_str(r#"{"index":1,"title":"t","url":"u"}"#).unwrap();
1281 assert_eq!(r.index, 1);
1282 assert_eq!(r.title, "t");
1283 assert_eq!(r.url, "u");
1284 assert!(r.content.is_none());
1285 assert_eq!(r.ref_type, "");
1286 }
1287
1288 #[test]
1289 fn deserialize_plan_changed_payload_picks_up_sibling_tool_name() {
1290 let mut payload: PlanChangedPayload =
1291 serde_json::from_str(r#"{"node_id":"n_plan","started_at":1752048000}"#).unwrap();
1292 payload.tool_name = "planner".to_string();
1295 assert_eq!(payload.node_id, "n_plan");
1296 assert_eq!(payload.tool_name, "planner");
1297 }
1298
1299 #[test]
1300 fn deserialize_workspaces_response() {
1301 let json = r#"{
1302 "workspaces": [
1303 { "id": "1001", "name": "My Workspace", "created_at": 1742000000, "updated_at": 1742001000 }
1304 ]
1305 }"#;
1306 let resp: WorkspacesResponse = serde_json::from_str(json).unwrap();
1307 assert_eq!(resp.workspaces.len(), 1);
1308 assert_eq!(resp.workspaces[0].id, "1001");
1309 }
1310
1311 #[test]
1312 fn deserialize_agents_response() {
1313 let json = r#"{
1314 "agents": [
1315 {
1316 "uid": "ag_7d3f9b2c",
1317 "name": "US Stock Analyst",
1318 "description": "Answers US stock questions with market and fundamental data",
1319 "mode": "chat",
1320 "icon": "https://cdn.longbridge.com/icons/agent.png",
1321 "is_published": true,
1322 "published_at": 1742000000,
1323 "created_at": 1741000000,
1324 "updated_at": 1742001000
1325 }
1326 ],
1327 "total": 12
1328 }"#;
1329 let resp: AgentsResponse = serde_json::from_str(json).unwrap();
1330 assert_eq!(resp.total, 12);
1331 assert_eq!(resp.agents[0].uid, "ag_7d3f9b2c");
1332 assert!(resp.agents[0].is_published);
1333 }
1334
1335 #[test]
1336 fn deserialize_interrupt_treats_null_questions_as_empty() {
1337 let interrupt: Interrupt = serde_json::from_str(
1338 r#"{"node_id":"approval","tool_call_id":"call-1","questions":null}"#,
1339 )
1340 .unwrap();
1341 assert!(interrupt.questions.is_empty());
1342 }
1343
1344 #[test]
1345 fn deserialize_question_treats_null_options_as_empty() {
1346 let question: Question =
1347 serde_json::from_str(r#"{"question":"Continue?","options":null}"#).unwrap();
1348 assert!(question.options.is_empty());
1349 }
1350
1351 #[test]
1352 fn deserialize_human_interaction_preserves_labels_and_answer_keys() {
1353 let interrupt: Interrupt = serde_json::from_str(
1354 r#"{
1355 "node_id":"ask",
1356 "tool_call_id":"call-1",
1357 "questions":[],
1358 "interactions":[{
1359 "tool_call_id":"call-1",
1360 "interrupt_id":"call-1",
1361 "type":"ask_human",
1362 "tool_name":"AskHuman",
1363 "questions":[{
1364 "question":"从哪个方向开始?",
1365 "options":[{"label":"看行情","description":"比较当前价格"}],
1366 "multi_select":false
1367 }],
1368 "tool_args":{}
1369 }]
1370 }"#,
1371 )
1372 .unwrap();
1373 assert_eq!(interrupt.interactions[0].interrupt_id, "call-1");
1374 assert_eq!(
1375 interrupt.interactions[0].questions[0].options[0].label,
1376 "看行情"
1377 );
1378 assert_eq!(
1379 interrupt.interactions[0].questions[0].options[0].description,
1380 "比较当前价格"
1381 );
1382 }
1383}