#[cfg(not(target_arch = "wasm32"))]
use crate::config::GeminiConfig;
#[cfg(not(target_arch = "wasm32"))]
use crate::error::GeminiError;
#[cfg(not(target_arch = "wasm32"))]
use crate::files::upload_file;
#[cfg(not(target_arch = "wasm32"))]
use aither_attachments::{FileCache, default_cache_dir};
#[cfg(not(target_arch = "wasm32"))]
use aither_core::llm::{Attachment, Message};
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
#[cfg(not(target_arch = "wasm32"))]
use url::Url;
#[cfg(not(target_arch = "wasm32"))]
pub async fn resolve_messages(
cfg: &GeminiConfig,
messages: Vec<Message>,
) -> Result<Vec<Message>, GeminiError> {
if messages.iter().all(|msg| msg.attachments().is_empty()) {
return Ok(messages);
}
let mut cache = FileCache::open(default_cache_dir())
.await
.map_err(GeminiError::from)?;
let mut cache_dirty = cache.prune_expired();
let mut resolved = Vec::with_capacity(messages.len());
for message in messages {
let Message::User {
content,
attachments,
} = message
else {
resolved.push(message);
continue;
};
let mut next_attachments = Vec::with_capacity(attachments.len());
for attachment in attachments {
let resolved_url = resolve_attachment(cfg, &mut cache, &attachment).await?;
cache_dirty |= resolved_url.cache_updated;
next_attachments.push(attachment.with_url(resolved_url.url));
}
resolved.push(Message::User {
content,
attachments: next_attachments,
});
}
if cache_dirty {
cache.save().await.map_err(GeminiError::from)?;
}
Ok(resolved)
}
#[cfg(not(target_arch = "wasm32"))]
struct ResolvedUrl {
url: Url,
cache_updated: bool,
}
#[cfg(not(target_arch = "wasm32"))]
async fn resolve_attachment(
cfg: &GeminiConfig,
cache: &mut FileCache,
attachment: &Attachment,
) -> Result<ResolvedUrl, GeminiError> {
let url = attachment.url();
match url.scheme() {
"file" => {
let path = url.to_file_path().map_err(|()| {
GeminiError::Api("Attachment file URL could not be converted to path".to_string())
})?;
resolve_file_attachment(cfg, cache, &path).await
}
"http" | "https" | "data" => Ok(ResolvedUrl {
url: url.clone(),
cache_updated: false,
}),
other => Err(GeminiError::Api(format!(
"Unsupported attachment URL scheme: {other}"
))),
}
}
#[cfg(not(target_arch = "wasm32"))]
async fn resolve_file_attachment(
cfg: &GeminiConfig,
cache: &mut FileCache,
path: &Path,
) -> Result<ResolvedUrl, GeminiError> {
let provider = "gemini";
if let Some(entry) = cache.get(path, provider).await.map_err(GeminiError::from)? {
let url = Url::parse(&entry.reference)
.map_err(|e| GeminiError::Api(format!("Invalid cached Gemini file URL: {e}")))?;
return Ok(ResolvedUrl {
url,
cache_updated: false,
});
}
let file = upload_file(cfg, path).await?;
if !file.is_ready() {
return Err(GeminiError::Api(format!(
"Gemini file upload not ready (state: {:?})",
file.state
)));
}
if file.uri.is_empty() {
return Err(GeminiError::Api(
"Gemini file upload missing URI".to_string(),
));
}
let expires_at = file.expiration();
cache
.insert(path, provider, file.uri.clone(), expires_at)
.await
.map_err(GeminiError::from)?;
let url = Url::parse(&file.uri)
.map_err(|e| GeminiError::Api(format!("Invalid Gemini file URI: {e}")))?;
Ok(ResolvedUrl {
url,
cache_updated: true,
})
}