feat: add Fireworks AI provider for chat agent

Routes chat-completions through a managed OpenAI-compatible inference
endpoint as an alternative to local Ollama, useful when the agent needs
fast multi-hop reasoning that local hardware can't sustain.

- backend: rename `call_ollama_chat_messages` → `call_chat_messages`,
  dispatch by provider; add `call_fireworks` branch (Bearer auth,
  `response_format: json_object` mapped from internal `format="json"`)
  and `list_fireworks_models` Tauri command
- settings: extend `AiProvider` enum + `AiSettings.fireworks_api_key`
  (serde-default for legacy config compat); Fireworks base URL hardcoded
- UI: provider selector in both popover and AppSettingsSheet (only
  ollama+fireworks shown; legacy openai/anthropic kept for serde-compat
  but normalized to ollama in UI); password input + dynamic model list
  for Fireworks; switching provider clears stale model selection
- 4 unit tests: serde round-trip, legacy settings deserialization,
  Fireworks chat-completions parsing, models-list parsing
This commit is contained in:
2026-05-06 23:04:10 +03:00
parent 532ebf3b44
commit 96a54edcd0
10 changed files with 524 additions and 65 deletions

View File

@@ -7,14 +7,19 @@ pub enum AiProvider {
Ollama,
OpenAi,
Anthropic,
Fireworks,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AiSettings {
pub provider: AiProvider,
pub ollama_url: String,
#[serde(default)]
pub openai_api_key: Option<String>,
#[serde(default)]
pub anthropic_api_key: Option<String>,
#[serde(default)]
pub fireworks_api_key: Option<String>,
pub model: String,
}
@@ -25,11 +30,14 @@ impl Default for AiSettings {
ollama_url: "http://localhost:11434".to_string(),
openai_api_key: None,
anthropic_api_key: None,
fireworks_api_key: None,
model: String::new(),
}
}
}
/// Generic chat message used by all chat providers (Ollama, Fireworks, OpenAI-compatible).
/// `{role, content}` shape is identical across these APIs.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OllamaChatMessage {
pub role: String,
@@ -55,7 +63,48 @@ pub struct OllamaTagsResponse {
pub models: Vec<OllamaModel>,
}
/// Generic chat-model descriptor exposed to the UI dropdown.
/// Reused as the return shape for both Ollama and Fireworks model listings.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OllamaModel {
pub name: String,
}
// ---------------------------------------------------------------------------
// Fireworks (OpenAI-compatible chat-completions)
// ---------------------------------------------------------------------------
#[derive(Debug, Clone, Serialize)]
pub struct FireworksChatRequest {
pub model: String,
pub messages: Vec<OllamaChatMessage>,
pub temperature: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<FireworksResponseFormat>,
}
#[derive(Debug, Clone, Serialize)]
pub struct FireworksResponseFormat {
#[serde(rename = "type")]
pub kind: String,
}
#[derive(Debug, Deserialize)]
pub struct FireworksChatResponse {
pub choices: Vec<FireworksChoice>,
}
#[derive(Debug, Deserialize)]
pub struct FireworksChoice {
pub message: OllamaChatMessage,
}
#[derive(Debug, Deserialize)]
pub struct FireworksModelsResponse {
pub data: Vec<FireworksModelEntry>,
}
#[derive(Debug, Deserialize)]
pub struct FireworksModelEntry {
pub id: String,
}