using OpenAICompatible

This commit is contained in:
munimunigamer 2026-01-31 21:42:36 -06:00
parent 769de2995b
commit faff7495b6
5 changed files with 77 additions and 11 deletions

59
package-lock.json generated
View file

@ -11,6 +11,7 @@
"dependencies": {
"@ai-sdk/anthropic": "^3.0.29",
"@ai-sdk/openai": "^3.0.21",
"@ai-sdk/openai-compatible": "^2.0.25",
"@chutes-ai/ai-sdk-provider": "^0.1.2",
"@openrouter/ai-sdk-provider": "^2.1.1",
"@tauri-apps/api": "^2",
@ -105,6 +106,51 @@
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/openai-compatible": {
"version": "2.0.25",
"resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-2.0.25.tgz",
"integrity": "sha512-8PHEsigICF3wz62slGnAVuepDq+ASHN5MTAVk+qSvINnF1P1712OQ8TthK92eS+oJLabZujMo0jq1umM5y5YMA==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "3.0.6",
"@ai-sdk/provider-utils": "4.0.12"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.6.tgz",
"integrity": "sha512-hSfoJtLtpMd7YxKM+iTqlJ0ZB+kJ83WESMiWuWrNVey3X8gg97x0OdAAaeAeclZByCX3UdPOTqhvJdK8qYA3ww==",
"license": "Apache-2.0",
"dependencies": {
"json-schema": "^0.4.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider-utils": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.12.tgz",
"integrity": "sha512-sdC3eUTa5W4r/bISlF3nxmM6zc8mV7Nj3mWI9iUO0cib70h0Zr52Tz5gGzO6HcDirbKVTR2ywmZb61MHU68prA==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "3.0.6",
"@standard-schema/spec": "^1.1.0",
"eventsource-parser": "^3.0.6"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"zod": "^3.25.76 || ^4.1.8"
}
},
"node_modules/@ai-sdk/provider": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.5.tgz",
@ -1082,6 +1128,7 @@
"integrity": "sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@sveltejs/acorn-typescript": "^1.0.5",
@ -1121,6 +1168,7 @@
"integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
"debug": "^4.4.1",
@ -1776,6 +1824,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -1788,6 +1837,7 @@
"resolved": "https://registry.npmjs.org/ai/-/ai-6.0.59.tgz",
"integrity": "sha512-9SfCvcr4kVk4t8ZzIuyHpuL1hFYKsYMQfBSbBq3dipXPa+MphARvI8wHEjNaRqYl3JOsJbWxEBIMqHL0L92mUA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@ai-sdk/gateway": "3.0.27",
"@ai-sdk/provider": "3.0.5",
@ -1930,6 +1980,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@ -2685,6 +2736,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -2712,6 +2764,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@ -2902,6 +2955,7 @@
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.46.1.tgz",
"integrity": "sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
@ -3018,7 +3072,8 @@
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
@ -3094,6 +3149,7 @@
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -3163,6 +3219,7 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",

View file

@ -15,6 +15,7 @@
"dependencies": {
"@ai-sdk/anthropic": "^3.0.29",
"@ai-sdk/openai": "^3.0.21",
"@ai-sdk/openai-compatible": "^2.0.25",
"@chutes-ai/ai-sdk-provider": "^0.1.2",
"@openrouter/ai-sdk-provider": "^2.1.1",
"@tauri-apps/api": "^2",

View file

@ -6,6 +6,7 @@
*/
import { ToolLoopAgent, type StopCondition, type ToolSet, type StepResult } from 'ai';
import type { LanguageModelV3 } from '@ai-sdk/provider';
import type { ProviderOptions } from '@ai-sdk/provider-utils';
import { settings } from '$lib/stores/settings.svelte';
import { createProviderFromProfile } from '../providers';
@ -22,7 +23,7 @@ export interface ResolvedAgentConfig {
preset: GenerationPreset;
profile: APIProfile;
providerType: ProviderType;
model: ReturnType<ReturnType<typeof createProviderFromProfile>['chat']>;
model: LanguageModelV3;
providerOptions: ProviderOptions | undefined;
}
@ -42,7 +43,8 @@ export function resolveAgentConfig(presetId: string): ResolvedAgentConfig {
}
const provider = createProviderFromProfile(profile);
const model = provider.chat(preset.model);
// Call provider directly - all providers support provider(modelId) syntax
const model = provider(preset.model) as LanguageModelV3;
const providerOptions = buildProviderOptions(preset, profile.providerType);
return { preset, profile, providerType: profile.providerType, model, providerOptions };

View file

@ -9,11 +9,12 @@ import { generateText, streamText, Output, generateImage as sdkGenerateImage } f
import { createOpenAI } from '@ai-sdk/openai';
import { createChutes } from '@chutes-ai/ai-sdk-provider';
import { createPollinations } from 'ai-sdk-pollinations';
import type { LanguageModelV3 } from '@ai-sdk/provider';
import type { ProviderOptions } from '@ai-sdk/provider-utils';
import type { z } from 'zod';
import { settings } from '$lib/stores/settings.svelte';
import { createProviderFromProfile } from './providers';
import { PROVIDER_CAPABILITIES, IMAGE_MODEL_DEFAULTS } from './providers/defaults';
import { PROVIDER_CAPABILITIES } from './providers/defaults';
import type { ProviderType, GenerationPreset, ReasoningEffort, APIProfile } from '$lib/types';
import { createLogger } from '../core/config';
@ -55,7 +56,7 @@ const PROVIDER_OPTIONS_KEY: Record<ProviderType, string> = {
openai: 'openai',
anthropic: 'anthropic',
google: 'google',
nanogpt: 'openai', // NanoGPT is OpenAI-compatible
nanogpt: 'nanogpt',
chutes: 'chutes',
pollinations: 'pollinations',
};
@ -141,7 +142,7 @@ interface ResolvedConfig {
preset: GenerationPreset;
profile: APIProfile;
providerType: ProviderType;
model: ReturnType<ReturnType<typeof createProviderFromProfile>['chat']>;
model: LanguageModelV3;
providerOptions: ProviderOptions | undefined;
}
@ -159,7 +160,8 @@ function resolveConfig(presetId: string): ResolvedConfig {
}
const provider = createProviderFromProfile(profile);
const model = provider.chat(preset.model);
// Call provider directly - all providers support provider(modelId) syntax
const model = provider(preset.model) as LanguageModelV3;
const providerOptions = buildProviderOptions(preset, profile.providerType);
return { preset, profile, providerType: profile.providerType, model, providerOptions };
@ -172,7 +174,7 @@ function resolveConfig(presetId: string): ResolvedConfig {
interface NarrativeConfig {
profile: APIProfile;
providerType: ProviderType;
model: ReturnType<ReturnType<typeof createProviderFromProfile>['chat']>;
model: LanguageModelV3;
temperature: number;
maxTokens: number;
providerOptions: ProviderOptions | undefined;
@ -191,7 +193,8 @@ function resolveNarrativeConfig(): NarrativeConfig {
const provider = createProviderFromProfile(profile);
const modelId = settings.apiSettings.defaultModel;
const model = provider.chat(modelId);
// Call provider directly - all providers support provider(modelId) syntax
const model = provider(modelId) as LanguageModelV3;
// Build a minimal preset-like object for provider options
const narrativePreset: GenerationPreset = {

View file

@ -6,6 +6,7 @@
*/
import { createOpenAI } from '@ai-sdk/openai';
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { createAnthropic } from '@ai-sdk/anthropic';
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { createChutes } from '@chutes-ai/ai-sdk-provider';
@ -49,7 +50,7 @@ const DEFAULT_BASE_URLS: Record<ProviderType, string | undefined> = {
* ```typescript
* const profile = settings.getProfile(profileId);
* const provider = createProviderFromProfile(profile);
* const model = provider.chat('gpt-4o');
* const model = provider('gpt-4o'); // All providers are callable
*
* const result = await generateText({ model, prompt: '...' });
* ```
@ -90,9 +91,11 @@ export function createProviderFromProfile(profile: APIProfile) {
case 'nanogpt':
// NanoGPT is OpenAI-compatible with custom base URL
return createOpenAI({
return createOpenAICompatible({
name: 'nanogpt',
apiKey: profile.apiKey,
baseURL: baseURL ?? NANOGPT_API_URL,
supportsStructuredOutputs: true,
fetch,
});