use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize)]
pub struct DeepSearchRequest {
pub query: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_num_results: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fast_mode: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub relevance_threshold: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub included_sources: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub excluded_sources: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_length: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_tool_call: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url_only: Option<bool>,
}
impl DeepSearchRequest {
pub fn new(query: impl Into<String>) -> Self {
Self {
query: query.into(),
max_num_results: None,
search_type: None,
fast_mode: None,
max_price: None,
relevance_threshold: None,
included_sources: None,
excluded_sources: None,
category: None,
response_length: None,
country_code: None,
is_tool_call: None,
start_date: None,
end_date: None,
url_only: None,
}
}
pub fn with_max_results(mut self, max: u8) -> Self {
self.max_num_results = Some(max);
self
}
pub fn with_search_type(mut self, search_type: impl Into<String>) -> Self {
self.search_type = Some(search_type.into());
self
}
pub fn with_fast_mode(mut self, enabled: bool) -> Self {
self.fast_mode = Some(enabled);
self
}
pub fn with_max_price(mut self, price: f64) -> Self {
self.max_price = Some(price);
self
}
pub fn with_relevance_threshold(mut self, threshold: f64) -> Self {
self.relevance_threshold = Some(threshold);
self
}
pub fn with_response_length(mut self, length: impl Into<String>) -> Self {
self.response_length = Some(length.into());
self
}
pub fn with_included_sources(mut self, sources: Vec<String>) -> Self {
self.included_sources = Some(sources);
self
}
pub fn with_excluded_sources(mut self, sources: Vec<String>) -> Self {
self.excluded_sources = Some(sources);
self
}
pub fn with_category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
pub fn with_country_code(mut self, code: impl Into<String>) -> Self {
self.country_code = Some(code.into());
self
}
pub fn with_is_tool_call(mut self, is_tool_call: bool) -> Self {
self.is_tool_call = Some(is_tool_call);
self
}
pub fn with_date_range(mut self, start: impl Into<String>, end: impl Into<String>) -> Self {
self.start_date = Some(start.into());
self.end_date = Some(end.into());
self
}
pub fn with_url_only(mut self, url_only: bool) -> Self {
self.url_only = Some(url_only);
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepSearchResponse {
pub success: bool,
pub error: Option<String>,
pub tx_id: Option<String>,
pub query: Option<String>,
pub results: Option<Vec<SearchResult>>,
pub results_by_source: Option<ResultsBySource>,
pub total_deduction_pcm: Option<f64>,
pub total_deduction_dollars: Option<f64>,
pub total_characters: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SearchResult {
pub id: Option<String>,
pub title: Option<String>,
pub url: Option<String>,
pub content: Option<String>,
pub description: Option<String>,
pub source: Option<String>,
pub source_type: Option<String>,
pub data_type: Option<String>,
pub length: Option<i32>,
pub price: Option<f64>,
pub image_url: Option<serde_json::Value>,
pub publication_date: Option<String>,
pub doi: Option<String>,
pub citation: Option<String>,
pub citation_count: Option<i32>,
pub authors: Option<Vec<String>>,
pub relevance_score: Option<f64>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ResultsBySource {
pub web: Option<i32>,
pub proprietary: Option<i32>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ContentsRequest {
pub urls: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_length: Option<ResponseLength>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extract_effort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<SummaryOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_price_dollars: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseLength {
Preset(String),
Custom(i32),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SummaryOption {
Boolean(bool),
Instructions(String),
Schema(serde_json::Value),
}
impl ContentsRequest {
pub fn new(urls: Vec<String>) -> Self {
Self {
urls,
response_length: None,
extract_effort: None,
summary: None,
max_price_dollars: None,
}
}
pub fn with_response_length(mut self, length: impl Into<String>) -> Self {
self.response_length = Some(ResponseLength::Preset(length.into()));
self
}
pub fn with_custom_response_length(mut self, chars: i32) -> Self {
self.response_length = Some(ResponseLength::Custom(chars));
self
}
pub fn with_extract_effort(mut self, effort: impl Into<String>) -> Self {
self.extract_effort = Some(effort.into());
self
}
pub fn with_summary(mut self, enabled: bool) -> Self {
self.summary = Some(SummaryOption::Boolean(enabled));
self
}
pub fn with_summary_instructions(mut self, instructions: impl Into<String>) -> Self {
self.summary = Some(SummaryOption::Instructions(instructions.into()));
self
}
pub fn with_summary_schema(mut self, schema: serde_json::Value) -> Self {
self.summary = Some(SummaryOption::Schema(schema));
self
}
pub fn with_max_price_dollars(mut self, max_price: f64) -> Self {
self.max_price_dollars = Some(max_price);
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ContentsResponse {
pub success: bool,
pub error: Option<String>,
pub tx_id: Option<String>,
pub results: Option<Vec<ContentResult>>,
pub urls_requested: Option<i32>,
pub urls_processed: Option<i32>,
pub urls_failed: Option<i32>,
pub total_cost_dollars: Option<f64>,
pub total_characters: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ContentResult {
pub title: Option<String>,
pub url: Option<String>,
pub content: Option<serde_json::Value>,
pub description: Option<String>,
pub publication_date: Option<String>,
pub images: Option<Vec<String>>,
pub cost_dollars: Option<f64>,
pub characters: Option<i32>,
}
#[derive(Debug, Clone, Serialize)]
pub struct AnswerRequest {
pub query: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_instructions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub structured_output: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fast_mode: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data_max_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub included_sources: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub excluded_sources: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub streaming: Option<bool>,
}
impl AnswerRequest {
pub fn new(query: impl Into<String>) -> Self {
Self {
query: query.into(),
system_instructions: None,
structured_output: None,
search_type: None,
fast_mode: None,
data_max_price: None,
included_sources: None,
excluded_sources: None,
start_date: None,
end_date: None,
country_code: None,
streaming: Some(false), }
}
pub fn with_system_instructions(mut self, instructions: impl Into<String>) -> Self {
self.system_instructions = Some(instructions.into());
self
}
pub fn with_structured_output(mut self, schema: serde_json::Value) -> Self {
self.structured_output = Some(schema);
self
}
pub fn with_search_type(mut self, search_type: impl Into<String>) -> Self {
self.search_type = Some(search_type.into());
self
}
pub fn with_fast_mode(mut self, enabled: bool) -> Self {
self.fast_mode = Some(enabled);
self
}
pub fn with_data_max_price(mut self, price: f64) -> Self {
self.data_max_price = Some(price);
self
}
pub fn with_included_sources(mut self, sources: Vec<String>) -> Self {
self.included_sources = Some(sources);
self
}
pub fn with_excluded_sources(mut self, sources: Vec<String>) -> Self {
self.excluded_sources = Some(sources);
self
}
pub fn with_date_range(mut self, start: impl Into<String>, end: impl Into<String>) -> Self {
self.start_date = Some(start.into());
self.end_date = Some(end.into());
self
}
pub fn with_country_code(mut self, code: impl Into<String>) -> Self {
self.country_code = Some(code.into());
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnswerResponse {
pub success: bool,
pub error: Option<String>,
pub ai_tx_id: Option<String>,
pub original_query: Option<String>,
pub contents: Option<serde_json::Value>,
pub data_type: Option<String>,
pub search_results: Option<Vec<AnswerSearchResult>>,
pub search_metadata: Option<AnswerSearchMetadata>,
pub ai_usage: Option<AiUsage>,
pub cost: Option<AnswerCost>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnswerSearchResult {
pub title: Option<String>,
pub url: Option<String>,
pub snippet: Option<String>,
pub date: Option<String>,
pub length: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnswerSearchMetadata {
pub search_tx_id: Option<String>,
pub result_count: Option<i32>,
pub total_characters: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AiUsage {
pub input_tokens: Option<i32>,
pub output_tokens: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnswerCost {
pub total_dollars: Option<f64>,
pub search_dollars: Option<f64>,
pub ai_dollars: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum DeepResearchMode {
Fast,
Lite,
Heavy,
}
impl Default for DeepResearchMode {
fn default() -> Self {
DeepResearchMode::Lite
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum DeepResearchStatus {
Queued,
Running,
Completed,
Failed,
Cancelled,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepResearchFileAttachment {
pub data: String,
pub filename: String,
#[serde(rename = "mediaType")]
pub media_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepResearchMCPServerConfig {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "toolPrefix")]
pub tool_prefix: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none", rename = "allowedTools")]
pub allowed_tools: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepResearchSearchConfig {
#[serde(skip_serializing_if = "Option::is_none", rename = "searchType")]
pub search_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "includedSources")]
pub included_sources: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DeepResearchCreateRequest {
pub input: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<DeepResearchMode>,
#[serde(skip_serializing_if = "Option::is_none", rename = "outputFormats")]
pub output_formats: Option<Vec<serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strategy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search: Option<DeepResearchSearchConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub urls: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub files: Option<Vec<DeepResearchFileAttachment>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "mcpServers")]
pub mcp_servers: Option<Vec<DeepResearchMCPServerConfig>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "codeExecution")]
pub code_execution: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", rename = "previousReports")]
pub previous_reports: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "webhookUrl")]
pub webhook_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Value>,
}
impl DeepResearchCreateRequest {
pub fn new(input: impl Into<String>) -> Self {
Self {
input: input.into(),
model: None,
output_formats: None,
strategy: None,
search: None,
urls: None,
files: None,
mcp_servers: None,
code_execution: None,
previous_reports: None,
webhook_url: None,
metadata: None,
}
}
pub fn with_mode(mut self, mode: DeepResearchMode) -> Self {
self.model = Some(mode);
self
}
pub fn with_output_formats(mut self, formats: Vec<String>) -> Self {
self.output_formats = Some(formats.into_iter().map(serde_json::Value::String).collect());
self
}
pub fn with_structured_output(mut self, schema: serde_json::Value) -> Self {
self.output_formats = Some(vec![schema]);
self
}
pub fn with_strategy(mut self, strategy: impl Into<String>) -> Self {
self.strategy = Some(strategy.into());
self
}
pub fn with_search(mut self, search: DeepResearchSearchConfig) -> Self {
self.search = Some(search);
self
}
pub fn with_urls(mut self, urls: Vec<String>) -> Self {
self.urls = Some(urls);
self
}
pub fn with_files(mut self, files: Vec<DeepResearchFileAttachment>) -> Self {
self.files = Some(files);
self
}
pub fn with_mcp_servers(mut self, servers: Vec<DeepResearchMCPServerConfig>) -> Self {
self.mcp_servers = Some(servers);
self
}
pub fn with_code_execution(mut self, enabled: bool) -> Self {
self.code_execution = Some(enabled);
self
}
pub fn with_previous_reports(mut self, report_ids: Vec<String>) -> Self {
self.previous_reports = Some(report_ids);
self
}
pub fn with_webhook_url(mut self, url: impl Into<String>) -> Self {
self.webhook_url = Some(url.into());
self
}
pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
self.metadata = Some(metadata);
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchCreateResponse {
pub success: bool,
pub deepresearch_id: Option<String>,
pub status: Option<DeepResearchStatus>,
pub model: Option<DeepResearchMode>,
pub created_at: Option<String>,
pub metadata: Option<serde_json::Value>,
pub public: Option<bool>,
pub webhook_secret: Option<String>,
pub message: Option<String>,
pub error: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchProgress {
pub current_step: i32,
pub total_steps: i32,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchSource {
pub title: String,
pub url: String,
pub snippet: Option<String>,
pub description: Option<String>,
pub source: Option<String>,
pub org_id: Option<String>,
pub price: Option<f64>,
pub id: Option<String>,
pub doi: Option<String>,
pub category: Option<String>,
pub word_count: Option<i32>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchImage {
pub image_id: String,
pub image_type: String,
pub deepresearch_id: String,
pub title: String,
pub description: Option<String>,
pub image_url: String,
pub s3_key: String,
pub created_at: i64,
pub chart_type: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchUsage {
pub search_cost: f64,
pub contents_cost: f64,
pub ai_cost: f64,
pub compute_cost: f64,
pub total_cost: f64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchStatusResponse {
pub success: bool,
pub deepresearch_id: Option<String>,
pub status: Option<DeepResearchStatus>,
pub query: Option<String>,
pub mode: Option<DeepResearchMode>,
pub output_formats: Option<Vec<serde_json::Value>>,
pub created_at: Option<i64>,
pub public: Option<bool>,
pub progress: Option<DeepResearchProgress>,
pub messages: Option<Vec<serde_json::Value>>,
pub completed_at: Option<i64>,
pub output: Option<serde_json::Value>,
pub output_type: Option<String>,
pub pdf_url: Option<String>,
pub images: Option<Vec<DeepResearchImage>>,
pub sources: Option<Vec<DeepResearchSource>>,
pub usage: Option<DeepResearchUsage>,
pub error: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchListResponse {
pub success: bool,
pub data: Option<Vec<DeepResearchTaskListItem>>,
pub error: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchTaskListItem {
pub deepresearch_id: String,
pub query: String,
pub status: DeepResearchStatus,
pub created_at: i64,
pub public: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DeepResearchOperationResponse {
pub success: bool,
pub message: Option<String>,
pub deepresearch_id: Option<String>,
pub error: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_request_builder() {
let request = DeepSearchRequest::new("quantum computing")
.with_max_results(10)
.with_search_type("web")
.with_fast_mode(true);
assert_eq!(request.query, "quantum computing");
assert_eq!(request.max_num_results, Some(10));
assert_eq!(request.search_type, Some("web".to_string()));
assert_eq!(request.fast_mode, Some(true));
}
#[test]
fn test_request_serialization() {
let request = DeepSearchRequest::new("test query")
.with_max_results(5);
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("test query"));
assert!(json.contains("max_num_results"));
}
}