feat: explicit provider field replaces URL sniffing
LlmConfig.provider ("ollama" | "openai", empty = sniff URL so legacy
configs keep working). Settings presets set it — a custom-port Ollama
no longer falls into the OpenAI branch and fails confusingly.
Also: test_connection now accepts an optional config override — the
wizard was passing one that Rust silently ignored, so it tested the
saved config instead of the URL the user just typed.
This commit is contained in:
@@ -37,7 +37,7 @@ async fn generate_inner(state: tauri::State<'_, AppState>, req: GenerateRequest)
|
||||
// consistent with the user's world bible. Shared path = every caller.
|
||||
let messages = inject_lore(&state, &client, &config, messages, &req.rag_query).await;
|
||||
|
||||
if llm::is_ollama(&config.api_url) {
|
||||
if llm::is_ollama(&config.provider, &config.api_url) {
|
||||
call_ollama(&client, &config, &messages, temperature, max_tokens).await
|
||||
} else {
|
||||
call_openai(&client, &config, &messages, temperature, max_tokens).await
|
||||
@@ -65,7 +65,7 @@ pub async fn generate_stream(
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// For now, we do a non-streaming call and emit the full response as one token
|
||||
// Real SSE streaming from Ollama/OpenAI can be added later
|
||||
let result = if llm::is_ollama(&config.api_url) {
|
||||
let result = if llm::is_ollama(&config.provider, &config.api_url) {
|
||||
call_ollama(&client, &config, &messages, temperature, max_tokens).await
|
||||
} else {
|
||||
call_openai(&client, &config, &messages, temperature, max_tokens).await
|
||||
@@ -123,14 +123,22 @@ pub struct ConnectionTest {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn test_connection(state: tauri::State<'_, AppState>) -> Result<ConnectionTest, String> {
|
||||
let config = state.config.lock().map_err(|e| e.to_string())?.clone();
|
||||
pub async fn test_connection(
|
||||
state: tauri::State<'_, AppState>,
|
||||
config: Option<crate::llm::LlmConfig>,
|
||||
) -> Result<ConnectionTest, String> {
|
||||
// Optional override: the first-run wizard tests a config it hasn't saved
|
||||
// yet. None = use the persisted state (Settings panel saves first anyway).
|
||||
let config = match config {
|
||||
Some(c) => c,
|
||||
None => state.config.lock().map_err(|e| e.to_string())?.clone(),
|
||||
};
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(8))
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if llm::is_ollama(&config.api_url) {
|
||||
if llm::is_ollama(&config.provider, &config.api_url) {
|
||||
let url = format!("{}/api/tags", config.api_url.trim_end_matches('/'));
|
||||
let res = client.get(&url).send().await.map_err(|e| e.to_string())?;
|
||||
if !res.status().is_success() {
|
||||
|
||||
@@ -25,6 +25,12 @@ pub struct AppState {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LlmConfig {
|
||||
/// API dialect: "ollama" (native /api/*) or "openai" (/v1/*).
|
||||
/// Empty = sniff the URL (legacy configs + defaults). Provider presets
|
||||
/// set it explicitly — a custom-port Ollama would otherwise be sniffed
|
||||
/// as OpenAI and fail confusingly. serde default: old stored configs.
|
||||
#[serde(default)]
|
||||
pub provider: String,
|
||||
pub api_url: String,
|
||||
pub api_key: String,
|
||||
pub model: String,
|
||||
@@ -42,6 +48,7 @@ impl Default for LlmConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// Default to Ollama local server; also works with LM Studio, llama.cpp server, or OpenAI
|
||||
provider: String::new(),
|
||||
api_url: "http://localhost:11434".to_string(),
|
||||
api_key: String::new(),
|
||||
model: "llama3.2".to_string(),
|
||||
@@ -100,9 +107,18 @@ pub struct OllamaChatResponse {
|
||||
pub done: bool,
|
||||
}
|
||||
|
||||
// ─── Helper: detect if we're talking to Ollama ───────────────
|
||||
// ─── Helper: are we talking Ollama? ───────────────────────
|
||||
|
||||
pub fn is_ollama(url: &str) -> bool {
|
||||
/// True when we should speak Ollama's native /api/* protocol. An explicit
|
||||
/// provider on the config wins; empty falls back to URL sniffing so legacy
|
||||
/// and default configs keep working.
|
||||
pub fn is_ollama(provider: &str, url: &str) -> bool {
|
||||
if provider.eq_ignore_ascii_case("ollama") {
|
||||
return true;
|
||||
}
|
||||
if provider.eq_ignore_ascii_case("openai") {
|
||||
return false;
|
||||
}
|
||||
url.contains("localhost:11434") || url.contains("127.0.0.1:11434")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user