1use crate::provider::gemini::attachments::{
5 GeminiFileRef, PrepareAttachmentRequest, PrepareAttachmentResult,
6 PrepareSubmissionAttachmentsRequest, PrepareSubmissionAttachmentsResult,
7};
8use crate::runtime::BrainRuntimeState;
9
10pub struct ImageThreadCredentialSnapshot {
11 credential: crate::provider::gemini::attachments::ActiveCredential,
12}
13
14impl std::fmt::Debug for ImageThreadCredentialSnapshot {
15 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 formatter.write_str("ImageThreadCredentialSnapshot([REDACTED])")
17 }
18}
19
20pub struct BrainService {
21 runtime: BrainRuntimeState,
22}
23
24impl BrainService {
25 pub fn new() -> Self {
26 Self {
27 runtime: BrainRuntimeState::new(),
28 }
29 }
30
31 pub async fn prepare_attachment(
32 &self,
33 request: PrepareAttachmentRequest,
34 ) -> PrepareAttachmentResult {
35 crate::provider::gemini::attachments::prepare_attachment(&self.runtime, request).await
36 }
37
38 pub async fn attachment_preparation_snapshot(
39 &self,
40 job_id: &str,
41 ) -> Option<PrepareAttachmentResult> {
42 crate::provider::gemini::attachments::attachment_preparation_snapshot(&self.runtime, job_id)
43 .await
44 }
45
46 pub async fn cancel_attachment(&self, job_id: String) -> Result<(), String> {
47 crate::provider::gemini::attachments::cancel_attachment(&self.runtime, &job_id).await;
48 Ok(())
49 }
50
51 pub async fn cancel_all_attachment_jobs(&self) -> Result<(), String> {
52 crate::provider::gemini::attachments::cancel_all_attachment_jobs(&self.runtime).await;
53 Ok(())
54 }
55
56 pub async fn prepare_submission_attachments(
57 &self,
58 request: PrepareSubmissionAttachmentsRequest,
59 ) -> PrepareSubmissionAttachmentsResult {
60 crate::provider::gemini::attachments::prepare_submission_attachments(&self.runtime, request)
61 .await
62 }
63
64 pub async fn suggest_thread_title(
65 &self,
66 thread_id: String,
67 model_candidates: Vec<String>,
68 ) -> Result<String, String> {
69 crate::provider::gemini::commands::generation::suggest_thread_title(
70 &self.runtime,
71 thread_id,
72 model_candidates,
73 )
74 .await
75 }
76
77 pub async fn suggest_thread_title_from_text(
78 &self,
79 text: String,
80 model_candidates: Vec<String>,
81 ) -> Result<String, String> {
82 crate::provider::gemini::commands::generation::generate_thread_title_from_text(
83 model_candidates,
84 text,
85 )
86 .await
87 }
88
89 pub async fn capture_image_thread_credential(
92 &self,
93 ) -> Result<Option<ImageThreadCredentialSnapshot>, String> {
94 Ok(
95 crate::provider::gemini::attachments::capture_image_thread_credential()
96 .await?
97 .map(|credential| ImageThreadCredentialSnapshot { credential }),
98 )
99 }
100
101 pub async fn ensure_thread_image_uploaded_with_snapshot(
104 &self,
105 snapshot: &ImageThreadCredentialSnapshot,
106 image_path: String,
107 ) -> Result<GeminiFileRef, String> {
108 let cancel_token = tokio_util::sync::CancellationToken::new();
109 Ok(
110 crate::provider::gemini::attachments::ensure_file_uploaded_for_credential(
111 &self.runtime,
112 &snapshot.credential,
113 &image_path,
114 &cancel_token,
115 )
116 .await?
117 .file_ref,
118 )
119 }
120
121 pub async fn suggest_thread_title_from_file_with_snapshot(
124 &self,
125 snapshot: &ImageThreadCredentialSnapshot,
126 file: GeminiFileRef,
127 model_candidates: Vec<String>,
128 ) -> Result<String, String> {
129 crate::provider::gemini::commands::generation::generate_thread_title_from_image(
130 snapshot.credential.api_key(),
131 model_candidates,
132 file.file_uri,
133 file.mime_type,
134 )
135 .await
136 }
137
138 pub async fn build_model_attempt_plan(
139 &self,
140 model_id: String,
141 effort: String,
142 ) -> Result<Vec<String>, String> {
143 crate::provider::gemini::models::build_attempt_plan(&model_id, &effort)
144 }
145}
146
147impl Default for BrainService {
148 fn default() -> Self {
149 Self::new()
150 }
151}