Skip to main content

rust_genai/
deep_research.rs

1//! Deep Research convenience wrapper (Preview).
2
3use std::pin::Pin;
4use std::sync::Arc;
5
6use futures_util::Stream;
7use rust_genai_types::interactions::{
8    AgentConfig, CreateInteractionConfig, DeepResearchAgentConfig, Interaction, InteractionInput,
9    InteractionSseEvent, InteractionThinkingSummaries, Tool,
10};
11
12use crate::client::ClientInner;
13use crate::error::Result;
14use crate::interactions::Interactions;
15
16const DEEP_RESEARCH_AGENT: &str = "deep-research-pro-preview-12-2025";
17
18#[derive(Clone)]
19pub struct DeepResearch {
20    inner: Arc<ClientInner>,
21}
22
23impl DeepResearch {
24    pub(crate) const fn new(inner: Arc<ClientInner>) -> Self {
25        Self { inner }
26    }
27
28    /// 启动 Deep Research(默认配置)。
29    ///
30    /// # Errors
31    ///
32    /// 当创建交互请求失败或服务端返回错误时返回错误。
33    pub async fn start(&self, input: impl Into<InteractionInput>) -> Result<Interaction> {
34        let mut config = CreateInteractionConfig::new_agent(DEEP_RESEARCH_AGENT, input);
35        apply_deep_research_defaults(&mut config);
36        Interactions::new(self.inner.clone()).create(config).await
37    }
38
39    /// 启动 Deep Research(自定义配置)。
40    ///
41    /// # Errors
42    ///
43    /// 当创建交互请求失败或服务端返回错误时返回错误。
44    pub async fn start_with_config(
45        &self,
46        mut config: CreateInteractionConfig,
47    ) -> Result<Interaction> {
48        apply_deep_research_defaults(&mut config);
49        Interactions::new(self.inner.clone()).create(config).await
50    }
51
52    /// 流式启动 Deep Research(自定义配置)。
53    ///
54    /// # Errors
55    ///
56    /// 当创建流式交互请求失败或服务端返回错误时返回错误。
57    pub async fn stream_with_config(
58        &self,
59        mut config: CreateInteractionConfig,
60    ) -> Result<Pin<Box<dyn Stream<Item = Result<InteractionSseEvent>> + Send>>> {
61        apply_deep_research_defaults(&mut config);
62        Interactions::new(self.inner.clone())
63            .create_stream(config)
64            .await
65    }
66}
67
68fn apply_deep_research_defaults(config: &mut CreateInteractionConfig) {
69    // Ensure this wrapper always targets the Deep Research agent.
70    if config.agent.is_none() {
71        config.agent = Some(DEEP_RESEARCH_AGENT.to_string());
72    }
73    config.model = None;
74    config.generation_config = None;
75
76    if config.background.is_none() {
77        config.background = Some(true);
78    }
79    if config.store.is_none() {
80        config.store = Some(true);
81    }
82
83    // Agent-specific config defaults.
84    match &mut config.agent_config {
85        None => {
86            config.agent_config = Some(AgentConfig::DeepResearch(DeepResearchAgentConfig {
87                thinking_summaries: Some(InteractionThinkingSummaries::Auto),
88                ..Default::default()
89            }));
90        }
91        Some(AgentConfig::DeepResearch(cfg)) => {
92            if cfg.thinking_summaries.is_none() {
93                cfg.thinking_summaries = Some(InteractionThinkingSummaries::Auto);
94            }
95        }
96        Some(AgentConfig::Dynamic(_)) => {}
97    }
98
99    if config.tools.is_none() {
100        config.tools = Some(vec![Tool::GoogleSearch]);
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn apply_defaults_sets_missing_fields() {
110        let mut config = CreateInteractionConfig::new_agent(
111            DEEP_RESEARCH_AGENT,
112            InteractionInput::Text("hi".into()),
113        );
114        apply_deep_research_defaults(&mut config);
115        assert_eq!(config.agent.as_deref(), Some(DEEP_RESEARCH_AGENT));
116        assert!(config.model.is_none());
117        assert_eq!(config.background, Some(true));
118        assert_eq!(config.store, Some(true));
119        assert!(config.tools.is_some());
120
121        match config.agent_config {
122            Some(AgentConfig::DeepResearch(cfg)) => {
123                assert_eq!(
124                    cfg.thinking_summaries,
125                    Some(InteractionThinkingSummaries::Auto)
126                );
127            }
128            _ => panic!("expected deep research agent config"),
129        }
130    }
131
132    #[test]
133    fn apply_defaults_respects_existing_fields() {
134        let mut config = CreateInteractionConfig::new_agent(
135            DEEP_RESEARCH_AGENT,
136            InteractionInput::Text("hi".into()),
137        );
138        config.background = Some(false);
139        config.store = Some(false);
140        config.agent_config = Some(AgentConfig::DeepResearch(DeepResearchAgentConfig {
141            thinking_summaries: Some(InteractionThinkingSummaries::NoneValue),
142            ..Default::default()
143        }));
144        config.tools = Some(Vec::new());
145        apply_deep_research_defaults(&mut config);
146        assert_eq!(config.agent.as_deref(), Some(DEEP_RESEARCH_AGENT));
147        assert!(config.model.is_none());
148        assert_eq!(config.background, Some(false));
149        assert_eq!(config.store, Some(false));
150        match config.agent_config {
151            Some(AgentConfig::DeepResearch(cfg)) => {
152                assert_eq!(
153                    cfg.thinking_summaries,
154                    Some(InteractionThinkingSummaries::NoneValue)
155                );
156            }
157            _ => panic!("expected deep research agent config"),
158        }
159        assert!(config.tools.as_ref().unwrap().is_empty());
160    }
161}