Skip to main content

rainy_sdk/
search.rs

1//! Web Research Module
2//!
3//! This module provides types and functionality for web research via Rainy API v2.
4//! Supports multiple providers (Exa, Tavily) and configurable search depth.
5
6use crate::models::{ResearchDepth, ResearchProvider};
7use serde::{Deserialize, Serialize};
8
9/// Options for configuring a web research request
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ResearchConfig {
12    /// The search provider to use
13    #[serde(default)]
14    pub provider: ResearchProvider,
15    /// The depth of the search
16    #[serde(default)]
17    pub depth: ResearchDepth,
18    /// Maximum number of sources to include
19    #[serde(default = "default_max_sources")]
20    pub max_sources: u32,
21    /// Whether to include images in the results
22    #[serde(default)]
23    pub include_images: bool,
24    /// Process the request asynchronously
25    #[serde(default)]
26    pub async_mode: bool,
27}
28
29fn default_max_sources() -> u32 {
30    10
31}
32
33impl Default for ResearchConfig {
34    fn default() -> Self {
35        Self {
36            provider: ResearchProvider::default(),
37            depth: ResearchDepth::default(),
38            max_sources: 10,
39            include_images: false,
40            async_mode: false,
41        }
42    }
43}
44
45impl ResearchConfig {
46    /// Create a new configuration with default settings
47    pub fn new() -> Self {
48        Self::default()
49    }
50
51    /// Set the search provider
52    pub fn with_provider(mut self, provider: ResearchProvider) -> Self {
53        self.provider = provider;
54        self
55    }
56
57    /// Set the search depth
58    pub fn with_depth(mut self, depth: ResearchDepth) -> Self {
59        self.depth = depth;
60        self
61    }
62
63    /// Set maximum sources
64    pub fn with_max_sources(mut self, max: u32) -> Self {
65        self.max_sources = max;
66        self
67    }
68
69    /// Enable async mode
70    pub fn with_async(mut self, async_mode: bool) -> Self {
71        self.async_mode = async_mode;
72        self
73    }
74}
75
76/// Request body for web research
77#[derive(Debug, Clone, Serialize)]
78pub(crate) struct ResearchRequest {
79    pub topic: String,
80    pub provider: ResearchProvider,
81    pub depth: ResearchDepth,
82    #[serde(rename = "maxSources")]
83    pub max_sources: u32,
84    #[serde(rename = "async")]
85    pub async_mode: bool,
86}
87
88impl ResearchRequest {
89    pub fn new(topic: impl Into<String>, config: &ResearchConfig) -> Self {
90        Self {
91            topic: topic.into(),
92            provider: config.provider.clone(),
93            depth: config.depth.clone(),
94            max_sources: config.max_sources,
95            async_mode: config.async_mode,
96        }
97    }
98}
99
100/// Result from a research operation
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ResearchResult {
103    /// original research prompt/topic
104    pub topic: String,
105    /// Comprehensive summary/answer
106    pub content: String,
107    /// Sources used for the research
108    #[serde(default)]
109    pub sources: Vec<ResearchSource>,
110    /// Provider used for the search
111    pub provider: String,
112}
113
114/// A source used in the research
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct ResearchSource {
117    pub title: String,
118    pub url: String,
119    #[serde(default)]
120    pub snippet: Option<String>,
121}
122
123/// Response from the research API
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(untagged)]
126pub enum ResearchResponse {
127    /// Synchronous response with results
128    Sync {
129        success: bool,
130        mode: String,
131        result: String, // The actual content answer (API returns 'result' as string in some paths, check agents.ts)
132        // Wait, looking at agents.ts:
133        // return c.json({ success: true, mode: "sync", result, generatedAt: ..., provider: ... });
134        // result is the output from researchNetwork.run(prompt), which is a String.
135        // But wait, does the network return just a string or a structured object?
136        // researchNetwork.run returns a string usually (agent output).
137        generated_at: String,
138        provider: String,
139    },
140    /// Asynchronous response with task ID
141    Async {
142        success: bool,
143        mode: String,
144        #[serde(rename = "taskId")]
145        task_id: String,
146        message: String,
147    },
148}
149
150// We need to be careful about the Sync response structure.
151// In agents.ts:
152// const result = await researchNetwork.run(researchPrompt);
153// return c.json({ success: true, mode: "sync", result, ... });
154//
155// So 'result' is a string containing the markdown report.
156//
157// The SDK user probably wants a cleaner struct.
158// Let's define a clean struct for success response.
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct DeepResearchResponse {
162    pub success: bool,
163    pub mode: String,
164    pub result: Option<String>, // For sync
165    #[serde(rename = "taskId")]
166    pub task_id: Option<String>, // For async
167    #[serde(rename = "generatedAt")]
168    pub generated_at: Option<String>,
169    pub provider: Option<String>,
170    pub message: Option<String>,
171}
172
173pub use crate::search::DeepResearchResponse as ResearchApiResponse;