98049282eb
* OpenRouter: new OpenAI-compatible provider with chat + embedding model routing, health check via /models, and a fetchOpenRouterModels() helper for the settings UI. API key, base URL and model fields are persisted in AISettings, surfaced in AIPanel, indexed by commandRegistry, and added to backupService's credential allow-list so the key round-trips through encrypted backups. * utils/httpFetch: introduce getAIFetch() as the single decision point for outbound AI traffic. In Tauri it returns @tauri-apps/plugin-http's fetch (Rust/reqwest transport, no renderer CORS preflight, no Android cleartext block); on the web build it falls back to window.fetch. OllamaProvider is migrated end-to-end — both ai-sdk-ollama streaming and the /api/tags health probe — and the new OpenRouterProvider uses the same path, so any future provider only has to call getAIFetch(). * Tests: unit tests for OpenRouter provider behavior (model selection, availability, health check) and a backup-settings round-trip test ensuring openrouterApiKey is treated as a credential field.
32 lines
1.5 KiB
TypeScript
32 lines
1.5 KiB
TypeScript
import { fetch as tauriFetch } from '@tauri-apps/plugin-http';
|
|
import { isTauriAppPlatform } from '@/services/environment';
|
|
|
|
/**
|
|
* AI providers need to call arbitrary third-party HTTP endpoints
|
|
* (OpenRouter, OpenAI-compatible proxies, self-hosted Ollama, internal
|
|
* LLM gateways, etc.). In a browser/webview context the standard
|
|
* `window.fetch` is subject to CORS preflight rules AND — on Android —
|
|
* the platform's cleartext-traffic policy. Neither restriction makes
|
|
* sense for an AI provider call: the user explicitly typed the endpoint
|
|
* URL into our settings and authenticated to it themselves, so we want
|
|
* the same semantics as `curl` or `reqwest` (raw HTTP, no Origin header,
|
|
* no preflight, no cleartext block).
|
|
*
|
|
* `@tauri-apps/plugin-http` gives us exactly that: the request is sent
|
|
* from the Rust side via reqwest, bypassing the renderer entirely. We
|
|
* expose a single helper so every provider goes through the same
|
|
* decision rather than each file re-implementing the platform check.
|
|
*
|
|
* For web builds (no Tauri runtime) we fall back to `window.fetch` and
|
|
* rely on the upstream server to send the right `Access-Control-Allow-*`
|
|
* headers — there is no alternative in that environment.
|
|
*/
|
|
export const getAIFetch = (): typeof fetch => {
|
|
if (isTauriAppPlatform()) {
|
|
// tauriFetch matches the standard `fetch` signature, so ai-sdk
|
|
// providers can take it directly via their `fetch` option.
|
|
return tauriFetch as unknown as typeof fetch;
|
|
}
|
|
return window.fetch.bind(window);
|
|
};
|