1use crate::models::{ResearchDepth, ResearchProvider};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11#[serde(rename_all = "lowercase")]
12pub enum ThinkingLevel {
13 Minimal,
15 Low,
17 Medium,
19 High,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ResearchConfig {
26 #[serde(default)]
28 pub provider: ResearchProvider,
29 #[serde(default)]
31 pub depth: ResearchDepth,
32 #[serde(default = "default_max_sources")]
34 pub max_sources: u32,
35 #[serde(default)]
37 pub include_images: bool,
38 #[serde(default)]
40 pub async_mode: bool,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub model: Option<String>,
44 #[serde(
46 default,
47 skip_serializing_if = "Option::is_none",
48 rename = "thinkingLevel"
49 )]
50 pub thinking_level: Option<ThinkingLevel>,
51}
52
53fn default_max_sources() -> u32 {
54 10
55}
56
57impl Default for ResearchConfig {
58 fn default() -> Self {
59 Self {
60 provider: ResearchProvider::default(),
61 depth: ResearchDepth::default(),
62 max_sources: 10,
63 include_images: false,
64 async_mode: false,
65 model: None,
66 thinking_level: None,
67 }
68 }
69}
70
71impl ResearchConfig {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn with_provider(mut self, provider: ResearchProvider) -> Self {
79 self.provider = provider;
80 self
81 }
82
83 pub fn with_depth(mut self, depth: ResearchDepth) -> Self {
85 self.depth = depth;
86 self
87 }
88
89 pub fn with_max_sources(mut self, max: u32) -> Self {
91 self.max_sources = max;
92 self
93 }
94
95 pub fn with_async(mut self, async_mode: bool) -> Self {
97 self.async_mode = async_mode;
98 self
99 }
100
101 pub fn with_model(mut self, model: impl Into<String>) -> Self {
103 self.model = Some(model.into());
104 self
105 }
106
107 pub fn with_thinking_level(mut self, level: ThinkingLevel) -> Self {
109 self.thinking_level = Some(level);
110 self
111 }
112}
113
114#[derive(Debug, Clone, Serialize)]
116pub(crate) struct ResearchRequest {
117 pub topic: String,
118 pub provider: ResearchProvider,
119 pub depth: ResearchDepth,
120 #[serde(rename = "maxSources")]
121 pub max_sources: u32,
122 #[serde(rename = "async")]
123 pub async_mode: bool,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub model: Option<String>,
126 #[serde(skip_serializing_if = "Option::is_none", rename = "thinkingLevel")]
127 pub thinking_level: Option<ThinkingLevel>,
128}
129
130impl ResearchRequest {
131 pub fn new(topic: impl Into<String>, config: &ResearchConfig) -> Self {
132 Self {
133 topic: topic.into(),
134 provider: config.provider.clone(),
135 depth: config.depth.clone(),
136 max_sources: config.max_sources,
137 async_mode: config.async_mode,
138 model: config.model.clone(),
139 thinking_level: config.thinking_level.clone(),
140 }
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct ResearchResult {
147 pub topic: String,
149 pub content: String,
151 #[serde(default)]
153 pub sources: Vec<ResearchSource>,
154 pub provider: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct ResearchSource {
161 pub title: String,
163 pub url: String,
165 #[serde(default)]
167 pub snippet: Option<String>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172#[serde(untagged)]
173pub enum ResearchResponse {
174 Sync {
176 success: bool,
178 mode: String,
180 result: String,
182 generated_at: String,
184 provider: String,
186 },
187 Async {
189 success: bool,
191 mode: String,
193 #[serde(rename = "taskId")]
195 task_id: String,
196 message: String,
198 },
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct DeepResearchResponse {
214 pub success: bool,
216 pub mode: String,
218 pub result: Option<serde_json::Value>,
220 #[serde(rename = "taskId")]
222 pub task_id: Option<String>,
223 #[serde(rename = "generatedAt")]
225 pub generated_at: Option<String>,
226 pub provider: Option<String>,
228 pub message: Option<String>,
230}
231
232pub use crate::search::DeepResearchResponse as ResearchApiResponse;