mirror of
https://github.com/Rohithgilla12/data-peek
synced 2026-04-21 12:57:16 +00:00
fix(ai): packaged app crashes on open from @ai-sdk provider-utils split
v0.21.0 ships a TypeError at require time: (0, import_provider_utils6.createProviderToolFactoryWithOutputSchema) is not a function Cause: PR #158 added @ai-sdk/xai, @ai-sdk/mistral, @ai-sdk/deepseek. All three pull @ai-sdk/provider-utils@4, while core ai@5 is pinned to @ai-sdk/provider-utils@3. pnpm hoisting + electron-builder's asar pack pick the wrong version at runtime and the app refuses to open. Fix: drop the three SDK packages. All three services expose OpenAI-compatible endpoints, so route them through createOpenAI with the matching base URL — same pattern GLM and Ollama already use. User- facing provider list is unchanged. Also extract the provider switch into ai-providers.ts so it can be unit-tested without pulling in Electron, and: - ai-providers.test.ts smoke-tests createProviderClient for every AIProvider. Catches any future require-time crash from an @ai-sdk package. - ai-deps.test.ts walks node_modules/@ai-sdk and asserts every package agrees on the provider-utils major version. This is the invariant that #158 violated; the suite now fails fast if it breaks again. CI guardrails: - New ci.yml runs typecheck + tests on every push/PR to main. There was no PR check before, which is how #158 landed green. - build.yml and build-artifacts.yml gate all platform builds behind a new `verify` job running the same checks, so a broken main can never produce a release artifact. - Artifact workflow now uses --frozen-lockfile like the release one.
This commit is contained in:
parent
40e7b446a8
commit
1d7183e648
9 changed files with 397 additions and 172 deletions
25
.github/workflows/build-artifacts.yml
vendored
25
.github/workflows/build-artifacts.yml
vendored
|
|
@ -15,7 +15,24 @@ on:
|
|||
- linux
|
||||
|
||||
jobs:
|
||||
# Gate every manual artifact build behind tests + typecheck. No more
|
||||
# shipping a bad dep split into an artifact that ends up on someone's
|
||||
# machine.
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
- uses: pnpm/action-setup@v4
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm --filter @data-peek/desktop test
|
||||
- run: pnpm --filter @data-peek/desktop typecheck
|
||||
|
||||
build-mac:
|
||||
needs: verify
|
||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'mac' }}
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
|
|
@ -31,7 +48,7 @@ jobs:
|
|||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build for macOS
|
||||
working-directory: apps/desktop
|
||||
|
|
@ -49,6 +66,7 @@ jobs:
|
|||
if-no-files-found: error
|
||||
|
||||
build-windows:
|
||||
needs: verify
|
||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }}
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
|
|
@ -64,7 +82,7 @@ jobs:
|
|||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build for Windows
|
||||
working-directory: apps/desktop
|
||||
|
|
@ -81,6 +99,7 @@ jobs:
|
|||
if-no-files-found: error
|
||||
|
||||
build-linux:
|
||||
needs: verify
|
||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
@ -96,7 +115,7 @@ jobs:
|
|||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build for Linux
|
||||
working-directory: apps/desktop
|
||||
|
|
|
|||
45
.github/workflows/build.yml
vendored
45
.github/workflows/build.yml
vendored
|
|
@ -10,7 +10,48 @@ permissions:
|
|||
contents: write
|
||||
|
||||
jobs:
|
||||
# Fast verification gate. Platform builds don't start until this passes,
|
||||
# so we don't burn 30min on three macOS/Windows/Linux runners just to
|
||||
# ship a broken release. This is what would have caught v0.21.0.
|
||||
verify:
|
||||
name: verify (typecheck · test)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run desktop tests
|
||||
run: pnpm --filter @data-peek/desktop test
|
||||
|
||||
- name: Typecheck desktop
|
||||
run: pnpm --filter @data-peek/desktop typecheck
|
||||
|
||||
build-mac:
|
||||
needs: verify
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
@ -22,7 +63,7 @@ jobs:
|
|||
node-version: 24
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
|
|
@ -86,6 +127,7 @@ jobs:
|
|||
if-no-files-found: error
|
||||
|
||||
build-windows:
|
||||
needs: verify
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
@ -132,6 +174,7 @@ jobs:
|
|||
if-no-files-found: error
|
||||
|
||||
build-linux:
|
||||
needs: verify
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
57
.github/workflows/ci.yml
vendored
Normal file
57
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
# Cancel in-progress CI runs on the same branch when a new commit lands —
|
||||
# the most recent commit is the only one whose result matters.
|
||||
concurrency:
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
name: typecheck · test · lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
# --frozen-lockfile ensures package.json and pnpm-lock.yaml agree —
|
||||
# this alone catches half of the "works on my machine" class of bug.
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# The dependency-consistency test (src/main/__tests__/ai-deps.test.ts)
|
||||
# runs here. If two @ai-sdk/* packages resolve conflicting
|
||||
# provider-utils majors, the suite fails before any build happens.
|
||||
- name: Run desktop tests
|
||||
run: pnpm --filter @data-peek/desktop test
|
||||
|
||||
- name: Typecheck desktop
|
||||
run: pnpm --filter @data-peek/desktop typecheck
|
||||
|
|
@ -25,12 +25,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^2.0.17",
|
||||
"@ai-sdk/deepseek": "^2.0.29",
|
||||
"@ai-sdk/google": "^2.0.12",
|
||||
"@ai-sdk/groq": "^2.0.8",
|
||||
"@ai-sdk/mistral": "^3.0.30",
|
||||
"@ai-sdk/openai": "^2.0.24",
|
||||
"@ai-sdk/xai": "^3.0.82",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
|
|
|
|||
126
apps/desktop/src/main/__tests__/ai-deps.test.ts
Normal file
126
apps/desktop/src/main/__tests__/ai-deps.test.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* AI SDK dependency consistency guard.
|
||||
*
|
||||
* The @ai-sdk/* packages all depend on @ai-sdk/provider-utils. If two
|
||||
* installed @ai-sdk packages resolve different major versions of
|
||||
* provider-utils, Electron's asar packaging can pick the wrong one at
|
||||
* runtime and the app crashes with errors like:
|
||||
*
|
||||
* TypeError: (0 , import_provider_utils6.createProviderToolFactoryWithOutputSchema)
|
||||
* is not a function
|
||||
*
|
||||
* This is exactly what shipped in v0.21.0 when @ai-sdk/xai (needing
|
||||
* provider-utils@4) was installed alongside ai@5 (pinned to
|
||||
* provider-utils@3). This test fails fast in CI if the same split
|
||||
* recurs — before anyone packages a release.
|
||||
*
|
||||
* The check runs against the real on-disk node_modules rather than a
|
||||
* parsed lockfile so it keeps working regardless of pnpm's hoisting
|
||||
* strategy.
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
interface PackageJson {
|
||||
name?: string
|
||||
version?: string
|
||||
dependencies?: Record<string, string>
|
||||
peerDependencies?: Record<string, string>
|
||||
}
|
||||
|
||||
function readPackageJson(pkgDir: string): PackageJson | null {
|
||||
const file = path.join(pkgDir, 'package.json')
|
||||
if (!fs.existsSync(file)) return null
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(file, 'utf8')) as PackageJson
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// node_modules is hoisted at the monorepo root.
|
||||
const REPO_ROOT = path.resolve(__dirname, '../../../../../')
|
||||
const AI_SDK_DIR = path.join(REPO_ROOT, 'node_modules/@ai-sdk')
|
||||
|
||||
function collectProviderUtilsRequirements(): Array<{
|
||||
package: string
|
||||
version: string
|
||||
requires: string
|
||||
}> {
|
||||
if (!fs.existsSync(AI_SDK_DIR)) return []
|
||||
|
||||
const entries = fs.readdirSync(AI_SDK_DIR, { withFileTypes: true })
|
||||
const results: Array<{ package: string; version: string; requires: string }> = []
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue
|
||||
if (entry.name === 'provider-utils' || entry.name === 'provider') continue
|
||||
|
||||
const pkg = readPackageJson(path.join(AI_SDK_DIR, entry.name))
|
||||
if (!pkg) continue
|
||||
const requires = pkg.dependencies?.['@ai-sdk/provider-utils']
|
||||
if (!requires) continue
|
||||
results.push({
|
||||
package: `@ai-sdk/${entry.name}`,
|
||||
version: pkg.version ?? 'unknown',
|
||||
requires
|
||||
})
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
function majorOf(range: string): string {
|
||||
// Strip leading ^ ~ >= <= =, take everything up to the first dot.
|
||||
const cleaned = range.replace(/^[^\d]*/, '')
|
||||
return cleaned.split('.')[0] ?? range
|
||||
}
|
||||
|
||||
describe('@ai-sdk dependency consistency', () => {
|
||||
it('all @ai-sdk/* packages agree on the major version of provider-utils', () => {
|
||||
const requirements = collectProviderUtilsRequirements()
|
||||
expect(requirements.length).toBeGreaterThan(0)
|
||||
|
||||
const majors = new Map<string, Array<{ package: string; version: string; requires: string }>>()
|
||||
for (const r of requirements) {
|
||||
const major = majorOf(r.requires)
|
||||
if (!majors.has(major)) majors.set(major, [])
|
||||
majors.get(major)!.push(r)
|
||||
}
|
||||
|
||||
if (majors.size > 1) {
|
||||
const summary = [...majors.entries()]
|
||||
.map(
|
||||
([major, pkgs]) =>
|
||||
` provider-utils@${major}.x (${pkgs.length}):\n` +
|
||||
pkgs.map((p) => ` - ${p.package}@${p.version} needs ${p.requires}`).join('\n')
|
||||
)
|
||||
.join('\n')
|
||||
|
||||
throw new Error(
|
||||
`Multiple major versions of @ai-sdk/provider-utils are installed.\n` +
|
||||
`This crashes the packaged Electron app at require-time.\n` +
|
||||
`Either downgrade the offending package or remove it:\n\n${summary}\n`
|
||||
)
|
||||
}
|
||||
|
||||
expect(majors.size).toBe(1)
|
||||
})
|
||||
|
||||
it('the resolved ai package pins a provider-utils version and matches', () => {
|
||||
const aiPkg = readPackageJson(path.join(REPO_ROOT, 'node_modules/ai'))
|
||||
expect(aiPkg).toBeTruthy()
|
||||
const aiRequires = aiPkg!.dependencies?.['@ai-sdk/provider-utils']
|
||||
expect(aiRequires).toBeTruthy()
|
||||
|
||||
const requirements = collectProviderUtilsRequirements()
|
||||
for (const r of requirements) {
|
||||
expect(
|
||||
majorOf(r.requires),
|
||||
`${r.package}@${r.version} requires provider-utils ${r.requires}, but ai requires ${aiRequires}`
|
||||
).toBe(majorOf(aiRequires!))
|
||||
}
|
||||
})
|
||||
})
|
||||
53
apps/desktop/src/main/__tests__/ai-providers.test.ts
Normal file
53
apps/desktop/src/main/__tests__/ai-providers.test.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* AI provider factory smoke test.
|
||||
*
|
||||
* Exercises createProviderClient for every declared AIProvider.
|
||||
* Catches the class of bug where an @ai-sdk/* package throws at
|
||||
* require-time (e.g. missing export in a transitive dependency) —
|
||||
* this is what blew up v0.21.0 when @ai-sdk/xai was installed against
|
||||
* a provider-utils version that lacked createProviderToolFactoryWithOutputSchema.
|
||||
*
|
||||
* If you add a provider to AIProvider in packages/shared, the
|
||||
* "covers every AIProvider" assertion below will fail until the switch
|
||||
* in ai-providers.ts is extended.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { AI_PROVIDERS, type AIConfig, type AIProvider } from '@shared/index'
|
||||
import { createProviderClient } from '../ai-providers'
|
||||
|
||||
function configFor(provider: AIProvider): AIConfig {
|
||||
return {
|
||||
provider,
|
||||
apiKey: 'test-key-not-used',
|
||||
model: provider === 'ollama' ? 'llama3' : 'test-model',
|
||||
baseUrl: undefined
|
||||
}
|
||||
}
|
||||
|
||||
describe('createProviderClient', () => {
|
||||
for (const info of AI_PROVIDERS) {
|
||||
it(`instantiates ${info.id} without throwing`, () => {
|
||||
const model = createProviderClient(configFor(info.id))
|
||||
expect(model).toBeTruthy()
|
||||
})
|
||||
}
|
||||
|
||||
it('covers every AIProvider enumerated in AI_PROVIDERS', () => {
|
||||
// If someone adds a provider to the shared union but forgets to
|
||||
// extend AI_PROVIDERS or the switch, at least one of these explodes.
|
||||
for (const info of AI_PROVIDERS) {
|
||||
expect(() => createProviderClient(configFor(info.id))).not.toThrow()
|
||||
}
|
||||
})
|
||||
|
||||
it('throws on an unknown provider (defensive)', () => {
|
||||
expect(() =>
|
||||
createProviderClient({
|
||||
provider: 'definitely-not-a-provider' as AIProvider,
|
||||
apiKey: 'x',
|
||||
model: 'y'
|
||||
})
|
||||
).toThrow()
|
||||
})
|
||||
})
|
||||
90
apps/desktop/src/main/ai-providers.ts
Normal file
90
apps/desktop/src/main/ai-providers.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* AI Provider Factory — Pure Module
|
||||
*
|
||||
* Instantiates AI SDK clients for every supported provider. Kept free of
|
||||
* Electron imports so it can be unit-tested in plain node.
|
||||
*
|
||||
* If you add a provider here, extend the AIProvider type in
|
||||
* packages/shared, and the ai-providers.test.ts suite will fail until
|
||||
* the switch is complete.
|
||||
*
|
||||
* We deliberately avoid @ai-sdk/deepseek, @ai-sdk/mistral, and
|
||||
* @ai-sdk/xai: all three currently depend on @ai-sdk/provider-utils@4
|
||||
* while the core `ai` package is on provider-utils@3, which crashes
|
||||
* the packaged app at require-time. See ai-deps.test.ts.
|
||||
*/
|
||||
|
||||
import { createOpenAI } from '@ai-sdk/openai'
|
||||
import { createAnthropic } from '@ai-sdk/anthropic'
|
||||
import { createGoogleGenerativeAI } from '@ai-sdk/google'
|
||||
import { createGroq } from '@ai-sdk/groq'
|
||||
import type { AIConfig } from '@shared/index'
|
||||
|
||||
export function createProviderClient(config: AIConfig) {
|
||||
switch (config.provider) {
|
||||
case 'openai': {
|
||||
const openai = createOpenAI({ apiKey: config.apiKey, baseURL: config.baseUrl })
|
||||
return openai(config.model)
|
||||
}
|
||||
|
||||
case 'anthropic': {
|
||||
const anthropic = createAnthropic({ apiKey: config.apiKey, baseURL: config.baseUrl })
|
||||
return anthropic(config.model)
|
||||
}
|
||||
|
||||
case 'google': {
|
||||
const google = createGoogleGenerativeAI({ apiKey: config.apiKey, baseURL: config.baseUrl })
|
||||
return google(config.model)
|
||||
}
|
||||
|
||||
case 'groq': {
|
||||
const groq = createGroq({ apiKey: config.apiKey, baseURL: config.baseUrl })
|
||||
return groq(config.model)
|
||||
}
|
||||
|
||||
case 'deepseek': {
|
||||
const deepseek = createOpenAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl || 'https://api.deepseek.com/v1'
|
||||
})
|
||||
return deepseek(config.model)
|
||||
}
|
||||
|
||||
case 'mistral': {
|
||||
const mistral = createOpenAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl || 'https://api.mistral.ai/v1'
|
||||
})
|
||||
return mistral(config.model)
|
||||
}
|
||||
|
||||
case 'xai': {
|
||||
const xai = createOpenAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl || 'https://api.x.ai/v1'
|
||||
})
|
||||
return xai(config.model)
|
||||
}
|
||||
|
||||
case 'glm': {
|
||||
const glm = createOpenAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl || 'https://open.bigmodel.cn/api/paas/v4'
|
||||
})
|
||||
return glm(config.model)
|
||||
}
|
||||
|
||||
case 'ollama': {
|
||||
const ollama = createOpenAI({
|
||||
baseURL: config.baseUrl || 'http://localhost:11434/v1',
|
||||
apiKey: 'ollama'
|
||||
})
|
||||
return ollama(config.model)
|
||||
}
|
||||
|
||||
default: {
|
||||
const _exhaustive: never = config.provider
|
||||
throw new Error(`Unknown provider: ${String(_exhaustive)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,14 +5,8 @@
|
|||
* Uses AI SDK's generateObject for typed JSON output.
|
||||
*/
|
||||
|
||||
import { createOpenAI } from '@ai-sdk/openai'
|
||||
import { createAnthropic } from '@ai-sdk/anthropic'
|
||||
import { createGoogleGenerativeAI } from '@ai-sdk/google'
|
||||
import { createGroq } from '@ai-sdk/groq'
|
||||
import { createDeepSeek } from '@ai-sdk/deepseek'
|
||||
import { createMistral } from '@ai-sdk/mistral'
|
||||
import { createXai } from '@ai-sdk/xai'
|
||||
import { generateObject, generateText } from 'ai'
|
||||
import { createProviderClient } from './ai-providers'
|
||||
import { z } from 'zod'
|
||||
import type {
|
||||
SchemaInfo,
|
||||
|
|
@ -292,87 +286,12 @@ export function clearAIConfig(): void {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the AI model instance based on provider
|
||||
* Get the AI model instance based on provider.
|
||||
* Thin wrapper around the pure factory in ai-providers.ts (kept as a
|
||||
* separate module so it can be unit-tested without Electron).
|
||||
*/
|
||||
function getModel(config: AIConfig) {
|
||||
switch (config.provider) {
|
||||
case 'openai': {
|
||||
const openai = createOpenAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return openai(config.model)
|
||||
}
|
||||
|
||||
case 'anthropic': {
|
||||
const anthropic = createAnthropic({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return anthropic(config.model)
|
||||
}
|
||||
|
||||
case 'google': {
|
||||
const google = createGoogleGenerativeAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return google(config.model)
|
||||
}
|
||||
|
||||
case 'groq': {
|
||||
const groq = createGroq({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return groq(config.model)
|
||||
}
|
||||
|
||||
case 'deepseek': {
|
||||
const deepseek = createDeepSeek({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return deepseek(config.model)
|
||||
}
|
||||
|
||||
case 'mistral': {
|
||||
const mistral = createMistral({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return mistral(config.model)
|
||||
}
|
||||
|
||||
case 'xai': {
|
||||
const xai = createXai({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl
|
||||
})
|
||||
return xai(config.model)
|
||||
}
|
||||
|
||||
case 'glm': {
|
||||
// GLM (Zhipu AI) uses OpenAI-compatible API
|
||||
const glm = createOpenAI({
|
||||
apiKey: config.apiKey,
|
||||
baseURL: config.baseUrl || 'https://open.bigmodel.cn/api/paas/v4'
|
||||
})
|
||||
return glm(config.model)
|
||||
}
|
||||
|
||||
case 'ollama': {
|
||||
// Ollama uses OpenAI-compatible API
|
||||
const ollama = createOpenAI({
|
||||
baseURL: config.baseUrl || 'http://localhost:11434/v1',
|
||||
apiKey: 'ollama' // Ollama doesn't need a real key
|
||||
})
|
||||
return ollama(config.model)
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown provider: ${config.provider}`)
|
||||
}
|
||||
return createProviderClient(config)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,24 +33,15 @@ importers:
|
|||
'@ai-sdk/anthropic':
|
||||
specifier: ^2.0.17
|
||||
version: 2.0.50(zod@3.25.76)
|
||||
'@ai-sdk/deepseek':
|
||||
specifier: ^2.0.29
|
||||
version: 2.0.29(zod@3.25.76)
|
||||
'@ai-sdk/google':
|
||||
specifier: ^2.0.12
|
||||
version: 2.0.44(zod@3.25.76)
|
||||
'@ai-sdk/groq':
|
||||
specifier: ^2.0.8
|
||||
version: 2.0.32(zod@3.25.76)
|
||||
'@ai-sdk/mistral':
|
||||
specifier: ^3.0.30
|
||||
version: 3.0.30(zod@3.25.76)
|
||||
'@ai-sdk/openai':
|
||||
specifier: ^2.0.24
|
||||
version: 2.0.74(zod@3.25.76)
|
||||
'@ai-sdk/xai':
|
||||
specifier: ^3.0.82
|
||||
version: 3.0.82(zod@3.25.76)
|
||||
'@dnd-kit/core':
|
||||
specifier: ^6.3.1
|
||||
version: 6.3.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
|
|
@ -794,12 +785,6 @@ packages:
|
|||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/deepseek@2.0.29':
|
||||
resolution: {integrity: sha512-cn4+xV0menm/4JKEDElnVGiUilHvi6AD4ZK/sY7DXP/Wb7Yb3Vr86NyYM6mGBE/Shk3mWHoHbzggVnF5x0uMEA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/gateway@2.0.17':
|
||||
resolution: {integrity: sha512-oVAG6q72KsjKlrYdLhWjRO7rcqAR8CjokAbYuyVZoCO4Uh2PH/VzZoxZav71w2ipwlXhHCNaInGYWNs889MMDA==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -818,18 +803,6 @@ packages:
|
|||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/mistral@3.0.30':
|
||||
resolution: {integrity: sha512-+j4IXRSk9E661cFSafmIr+XHOzwjFagawwzMOlSqwL6U4Sq4PCFLDF+oHbX5NUqNjUL7FD1zi/9lBIfa41pUvw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/openai-compatible@2.0.41':
|
||||
resolution: {integrity: sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/openai@2.0.74':
|
||||
resolution: {integrity: sha512-vvsL7rGoBEyQIePs630p31ebLeF+xxwLOrRKeIArHko8w7Wh9Kj3wL4Ns+PCzrEpAij31OKKDcxLQ1dSIg/qMw==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -842,26 +815,10 @@ packages:
|
|||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/provider-utils@4.0.23':
|
||||
resolution: {integrity: sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@ai-sdk/provider@2.0.0':
|
||||
resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@ai-sdk/provider@3.0.8':
|
||||
resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@ai-sdk/xai@3.0.82':
|
||||
resolution: {integrity: sha512-A0VFMufnVf4wODcT3SPQUUzvYXiIO1VhFuXj9r6z/vP4rlo+QRDPw3WSTchcz93ROQWSfBE3I6Szqz342OHi5w==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
|
||||
'@alloc/quick-lru@5.2.0':
|
||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -10619,12 +10576,6 @@ snapshots:
|
|||
'@ai-sdk/provider-utils': 3.0.18(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/deepseek@2.0.29(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.23(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/gateway@2.0.17(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
|
|
@ -10644,18 +10595,6 @@ snapshots:
|
|||
'@ai-sdk/provider-utils': 3.0.18(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/mistral@3.0.30(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.23(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/openai-compatible@2.0.41(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.23(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/openai@2.0.74(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
|
|
@ -10669,28 +10608,10 @@ snapshots:
|
|||
eventsource-parser: 3.0.6
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/provider-utils@4.0.23(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@standard-schema/spec': 1.1.0
|
||||
eventsource-parser: 3.0.6
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/provider@2.0.0':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/provider@3.0.8':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/xai@3.0.82(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/openai-compatible': 2.0.41(zod@3.25.76)
|
||||
'@ai-sdk/provider': 3.0.8
|
||||
'@ai-sdk/provider-utils': 4.0.23(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
|
||||
'@azure-rest/core-client@2.5.1':
|
||||
|
|
|
|||
Loading…
Reference in a new issue