@xopcai/xopc 0.0.131 → 0.0.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -26
- package/README.zh-CN.md +25 -28
- package/dist/browser-ext/icons/icon-128.png +0 -0
- package/dist/browser-ext/icons/icon-16.png +0 -0
- package/dist/browser-ext/icons/icon-32.png +0 -0
- package/dist/browser-ext/icons/icon-48.png +0 -0
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +5 -8
- package/dist/extensions/weixin/src/messaging/process-message.js.map +1 -1
- package/dist/extensions/weixin/src/plugin.js +11 -1
- package/dist/extensions/weixin/src/plugin.js.map +1 -1
- package/dist/extensions/weixin/src/routing-integration.d.ts +7 -0
- package/dist/extensions/weixin/src/routing-integration.js +17 -0
- package/dist/extensions/weixin/src/routing-integration.js.map +1 -0
- package/dist/gateway/static/root/apple-touch-icon.png +0 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BJYeTxQ9.js → agent-avatar-dicebear-Ab81VpCx.js} +1 -1
- package/dist/gateway/static/root/assets/agents-HCGFJG8I.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Bj8iJ-7d.js → app-management-settings-panel-DMI_sbnc.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-6TjISbHB.js → appearance-settings-oQiCWQIQ.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-DC4YwXLL.js → apps-page-CRYuPIdb.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-2G62raBg.js → archive-plugin-BaKuWJws.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-kLNJMEJh.js → attachment-load-DYCFtbAA.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CpcZSSD8.js → automation-suggestion-card-C6ZIz_B1.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-DjbtV2VP.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-C_zSrWow.js → binary-plugins-Bdg1hfhV.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-D_PMrT-l.js → block-editor-CEgvbzpG.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-C89wKfPd.js → browser-settings-page-KcwlS_iE.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-api-CPFtajl7.js → capability-presets-api-CC3vmrm5.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-DWw31-4v.js → capability-presets-settings-panel-Di4uiKbv.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-BUi8syv2.js → channel-recipient-api-RANYrzfa.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DMa0BEWf.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-BG_j4Ci-.js +1 -0
- package/dist/gateway/static/root/assets/{connectors-page-DCE-PgQF.js → connectors-page-DIZKYaMj.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BYV16wez.js → desktop-pet-CNnlqbkx.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DGIaFqPR.js → desktop-pet-settings-ukJCc0LA.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-BEhDuShd.js → directory-picker-path-field-8hoieEyt.js} +1 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BOrBSNLL.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BIjLZMAQ.js → extension-debug-page-D6Jc0hYZ.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Cxv4Bgj5.js → extension-page-CJMhBASX.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DNd0tRFl.js → extension-settings-page-Dt-FWyMN.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-Bl7zMnmd.js → fetch-6HXXdO77.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BiqKIkkp.js → gateway-config-api-CyYi-a3k.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-D_Cvasqi.js → gateway-settings-yJvCmOBC.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-CPOgTcpv.js → gateway-startup-retry-BE1M9fH_.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-B3PLFUAl.js +1 -0
- package/dist/gateway/static/root/assets/{goal-detail-page-DF8QhTHH.js → goal-detail-page-BQXW29kk.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-config-api-h26_-2Vm.js → goals-config-api-FU_bdYvR.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-BzBKHZJg.js → goals-page-BmppU6mC.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-settings-BFG1uTI8.js → goals-settings-BsXWV6CN.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-BqwSCZLD.js +1 -0
- package/dist/gateway/static/root/assets/index-DXUkWmeK.css +1 -0
- package/dist/gateway/static/root/assets/{index-DOetFpTt.js → index-DqFBdoK-.js} +41 -41
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Dpx1ovO6.js → keyboard-shortcuts-settings-D8J0hkTt.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-3QO3elfx.js → logs-page-Cr5ptDtc.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-Ba4EsPkV.js → markdown-split-w-hIAZ9S.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-lA5rvtCq.js → media-plugins-DjFnDy3R.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-Bq4REfIj.js → messages-DR9ODkb8.js} +2 -2
- package/dist/gateway/static/root/assets/{models-hub-panel-BBG87CHZ.js → models-hub-panel-DEsMnITi.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-DrUGE-Hn.js → notes-page-D7bHXaeR.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-workbench-eAIE8iKV.js → notes-workbench-DiuG4Ov7.js} +2 -2
- package/dist/gateway/static/root/assets/{page-tabs-Cy7bX7Ce.js → page-tabs-smjfSAwc.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-B9_iql49.js → preview-open-alternatives-v02THugE.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-Us3zDRE6.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-BcvW8R0Z.js +1 -0
- package/dist/gateway/static/root/assets/{remote-access-hub-C29EWM0P.js → remote-access-hub-Bun2k0V-.js} +1 -1
- package/dist/gateway/static/root/assets/{save-bar-controls-nMloZ0mp.js → save-bar-controls-CD7LPbLU.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BuajpVNF.js → schedule-field-Cf3p_M0z.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-IeXV_yQk.js → schema-form-D8XdvcNH.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-DHHVDqsy.js → sessions-page-Bh44TVld.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BhyLZ0IW.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-ByixWwvu.js → settings-collapsible-section-BVpPSgMs.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-C-5-n-jM.js → settings-form-section-GAwBG9Vp.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-CTA5Gje4.js → settings-loading-skeleton-CGxa3aCb.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-DzqxeemF.js → settings-page-bZKl6uRR.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-DgFTf6nM.js → settings-page-layout-DfbMLJgN.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-svpHcJ62.js → setup-status-panel-DBIruh16.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-IOjU69FC.js → share-preview-page-BglBMvA0.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-C0fR-Oto.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BG-FEo9B.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-DoonnYRs.js → skills-page-CwwZOoCH.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-BMDD7FXg.js → system-settings-panel-CTT0BkbS.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-DayamJ56.js → typed-models-lib-Fa9PjI0-.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-ChYwFiqo.js → use-channel-catalog-BCBfSoHx.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-Dd0X3mPv.js → user-profile-settings-panel-pCK-aMqu.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-D9v9ZpK4.js → utils-aleluS3U.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-QXGvjTwa.js → voice-api-key-field-Fh4mqUIK.js} +1 -1
- package/dist/gateway/static/root/assets/{work-item-detail-page-CP-5OUYI.js → work-item-detail-page-C0_3nId3.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-IidqhbUI.js → workflow-run-setup-panel-r969H1r2.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-start-dialog-BQQkr8YD.js → workflow-start-dialog-C1GbcWN0.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-DtVIjVSX.js → workflows-page-DsPM3HEI.js} +1 -1
- package/dist/gateway/static/root/favicon-16x16.png +0 -0
- package/dist/gateway/static/root/favicon-32x32.png +0 -0
- package/dist/gateway/static/root/favicon-icon.svg +179 -15
- package/dist/gateway/static/root/favicon.ico +0 -0
- package/dist/gateway/static/root/favicon.png +0 -0
- package/dist/gateway/static/root/favicon.svg +179 -15
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/gateway/static/root/logo-dark.svg +179 -15
- package/dist/gateway/static/root/logo.svg +179 -15
- package/dist/gateway/static/root/pwa-192x192.png +0 -0
- package/dist/gateway/static/root/pwa-512x512.png +0 -0
- package/dist/package.js +1 -1
- package/dist/src/agent/image/tool-model-config.js +5 -5
- package/dist/src/agent/image/tool-model-config.js.map +1 -1
- package/dist/src/agent/starter-agents.d.ts +1 -0
- package/dist/src/agent/starter-agents.js +38 -11
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/transcript/thinking-types.js +2 -0
- package/dist/src/agent/transcript/thinking-types.js.map +1 -1
- package/dist/src/auth/oauth/google-oauth/google-gemini-cli.d.ts +1 -1
- package/dist/src/auth/oauth/google-oauth/google-gemini-cli.js.map +1 -1
- package/dist/src/chat-commands/builtins/model.js +3 -3
- package/dist/src/chat-commands/builtins/model.js.map +1 -1
- package/dist/src/cli/commands/models.js +9 -0
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/commands/agents.config.d.ts +2 -0
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/schema.js +4 -2
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/gateway/hono/routes/status.js +2 -2
- package/dist/src/gateway/hono/routes/status.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +1 -1
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/providers/domestic-presets.d.ts +5 -15
- package/dist/src/providers/domestic-presets.js +31 -49
- package/dist/src/providers/domestic-presets.js.map +1 -1
- package/dist/src/providers/index.js +19 -5
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.js.map +1 -1
- package/dist/src/providers/model-supplements.d.ts +6 -0
- package/dist/src/providers/model-supplements.js +22 -0
- package/dist/src/providers/model-supplements.js.map +1 -0
- package/dist/src/providers/presentation.js +25 -26
- package/dist/src/providers/presentation.js.map +1 -1
- package/dist/src/voice/tts/summarize.js +1 -1
- package/dist/src/voice/tts/summarize.js.map +1 -1
- package/package.json +5 -5
- package/dist/gateway/static/root/assets/agents-kVsY4Iz_.js +0 -2
- package/dist/gateway/static/root/assets/automations-page-MZ8m4bYP.js +0 -4
- package/dist/gateway/static/root/assets/channels-settings-DOYzcMAE.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BIkgLFDH.js +0 -3
- package/dist/gateway/static/root/assets/goal-create-dialog-DZ68qzpm.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-5vV8vTva.js +0 -1
- package/dist/gateway/static/root/assets/index-W4ogAiHt.css +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-Cwct6pCV.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BXZlzV8t.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-hDvmobsT.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Ddg8gHPN.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DcNOEJsK.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["getPiAiModel","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/index.ts"],"sourcesContent":["/**\n * Model provider module - integrates built-in models with custom models from models.json\n */\n\nimport {\n\tgetModel as getPiAiModel,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype Api,\n} from '@earendil-works/pi-ai/compat';\nimport { getAgentDefaultModelRef, type Config } from '../config/schema.js';\nimport { getModelRegistry } from './model-registry.js';\nimport { CredentialResolver, resolveApiKey, hasCredentials } from '../auth/credentials.js';\nimport { hasProviderAuthOnDiskSync } from '../auth/sync-provider-auth.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport type { ProviderModelDefinition } from '../extensions/types/providers.js';\n\nexport { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';\n\n/** Sentinel base URL: model is served by an extension {@link ProviderPluginRegistry} provider. */\nexport const EXTENSION_PROVIDER_BASE_URL = 'extension://provider-plugin';\nconst OPENAI_CODEX_CANONICAL_BASE_URL = 'https://chatgpt.com/backend-api/codex';\n\nfunction normalizeProviderModel(model: Model<Api>): Model<Api> {\n\tif (model.provider === 'openai-codex' && model.api === 'openai-codex-responses') {\n\t\treturn { ...model, baseUrl: OPENAI_CODEX_CANONICAL_BASE_URL } as Model<Api>;\n\t}\n\treturn model;\n}\n\n/** Map a plugin registry model to the pi-ai {@link Model} shape. */\nexport function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api> {\n\treturn {\n\t\tprovider: providerId,\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\tapi: 'openai-completions' as Api,\n\t\tbaseUrl: EXTENSION_PROVIDER_BASE_URL,\n\t\treasoning: false,\n\t\tinput: definition.supportsImages ? (['text', 'image'] as ('text' | 'image')[]) : (['text'] as ('text' | 'image')[]),\n\t\tcontextWindow: definition.contextWindow ?? 128000,\n\t\tmaxTokens: definition.maxOutputTokens ?? 4096,\n\t\tcost: {\n\t\t\tinput: definition.pricing?.input ?? 0,\n\t\t\toutput: definition.pricing?.output ?? 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t} as Model<Api>;\n}\n\n/**\n * Get API key synchronously: checks registry (models.json) first, then environment variables.\n * Use this for Agent's getApiKey callback which must be synchronous.\n */\nexport function getApiKeySync(provider: string): string | undefined {\n const pluginRegistry = getProviderRegistry();\n if (pluginRegistry.has(provider)) return 'extension-managed';\n\n const registry = getModelRegistry();\n const registryKey = registry.getApiKey(provider);\n if (registryKey) {\n return registryKey;\n }\n return getApiKeyFromEnv(provider);\n}\n\n/**\n * Resolve model reference. Supports:\n * - \"provider/modelId\" format\n * - \"modelId\" auto-detection via pi-ai or custom models\n * @throws if model not found\n */\nexport function resolveModel(ref: string): Model<Api> {\n\tconst trimmedRef = typeof ref === 'string' ? ref.trim() : '';\n\tif (!trimmedRef) {\n\t\tthrow new Error(\n\t\t\t'No default model configured. Choose a model in onboarding or set agents.capabilityPresets.default.models.roles.<role>.model.',\n\t\t);\n\t}\n\n\t// First try ModelRegistry (includes custom models)\n\tconst registry = getModelRegistry();\n\tconst customModel = registry.resolve(trimmedRef);\n\tif (customModel) {\n\t\treturn customModel;\n\t}\n\n\tif (trimmedRef.includes('/')) {\n\t\tconst [provider, modelId] = trimmedRef.split('/');\n\t\tconst piAiModel = getPiAiModel(provider as any, modelId as any);\n\t\tif (piAiModel) return normalizeProviderModel(piAiModel as Model<Api>);\n\n\t\tconst pluginRegistry = getProviderRegistry();\n\t\tconst plugin = pluginRegistry.get(provider);\n\t\tif (plugin) {\n\t\t\tconst pluginModel = plugin.models.find(m => m.id === modelId);\n\t\t\tif (pluginModel) return pluginModelToModel(provider, pluginModel);\n\t\t}\n\t\tthrow new Error(`Model not found: ${trimmedRef}`);\n\t}\n\n\tfor (const provider of getPiAiProviders()) {\n\t\ttry {\n\t\t\tconst models = getPiAiModels(provider);\n\t\t\tconst found = models.find(m => m.id === trimmedRef);\n\t\t\tif (found) return normalizeProviderModel(found as Model<Api>);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst pluginRegistry = getProviderRegistry();\n\tfor (const plugin of pluginRegistry.listAll()) {\n\t\tconst found = plugin.models.find(m => m.id === trimmedRef);\n\t\tif (found) return pluginModelToModel(plugin.id, found);\n\t}\n\n\tthrow new Error(`Model not found: ${trimmedRef}. Use format: provider/model-id`);\n}\n\nexport function getModelsByProvider(provider: string): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst fromRegistry = registry.getAll().filter(m => m.provider === provider).map(normalizeProviderModel);\n\tconst plugin = getProviderRegistry().get(provider);\n\tif (!plugin) return fromRegistry;\n\tconst pluginModels = plugin.models.map(m => pluginModelToModel(provider, m));\n\treturn [...fromRegistry, ...pluginModels];\n}\n\nexport function getAllProviders(): string[] {\n\tconst registry = getModelRegistry();\n\tconst providers = new Set<string>();\n\n\t// Add built-in providers\n\tfor (const p of getPiAiProviders()) {\n\t\tproviders.add(p);\n\t}\n\n\t// Add custom providers from registry\n\tfor (const m of registry.getAll()) {\n\t\tproviders.add(m.provider);\n\t}\n\n\tfor (const plugin of getProviderRegistry().listAll()) {\n\t\tproviders.add(plugin.id);\n\t}\n\n\treturn Array.from(providers);\n}\n\nexport async function getApiKey(provider: string): Promise<string | undefined> {\n\tif (getProviderRegistry().has(provider)) return 'extension-managed';\n\n\t// Use new credential resolver first (checks: agent private > global > oauth > env)\n\tconst credentialKey = await resolveApiKey(provider);\n\tif (credentialKey) {\n\t\treturn credentialKey;\n\t}\n\n\t// Check registry for custom providers (from models.json)\n\tconst registry = getModelRegistry();\n\tconst registryKey = registry.getApiKey(provider);\n\tif (registryKey) {\n\t\treturn registryKey;\n\t}\n\n\t// Fallback to environment variables\n\treturn getApiKeyFromEnv(provider);\n}\n\n/**\n * Synchronous version for use in non-async contexts\n * Only checks environment variables and registry, not credential system\n */\nexport function isProviderConfiguredSync(provider: string): boolean {\n\tif (getProviderRegistry().has(provider)) return true;\n\n\t// Check registry for custom providers\n\tconst registry = getModelRegistry();\n\tif (registry.getApiKey(provider)) {\n\t\treturn true;\n\t}\n\t// Check environment variables\n\tif (getApiKeyFromEnv(provider)) {\n\t\treturn true;\n\t}\n\t// Gateway UI / CLI store keys in auth-profiles.json (async CredentialResolver); sync path for fallback list\n\treturn hasProviderAuthOnDiskSync(provider);\n}\n\nexport async function isProviderConfigured(provider: string): Promise<boolean> {\n if (getProviderRegistry().has(provider)) return true;\n\n // Check registry first for custom providers (from models.json)\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return true;\n }\n return await hasCredentials(provider);\n}\n\n/** Where runtime {@link getApiKey} resolves the key from (no secret values). */\nexport type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';\n\nexport type ProviderAuthMode = ProviderActiveKeySource;\nexport type ProviderAuthStatus = 'connected' | 'expired' | 'not_connected';\n\nexport interface ProviderAuthState {\n authMode: ProviderAuthMode;\n authStatus: ProviderAuthStatus;\n expiresAt?: number;\n}\n\nexport async function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource> {\n const authState = await getProviderAuthState(provider);\n return authState.authMode;\n}\n\nexport async function getProviderAuthState(provider: string): Promise<ProviderAuthState> {\n if (getProviderRegistry().has(provider)) {\n return { authMode: 'extension', authStatus: 'connected' };\n }\n\n const resolver = new CredentialResolver();\n const fromCredentials = await resolver.resolveApiKeySource(provider);\n if (fromCredentials === 'agent') return { authMode: 'agent', authStatus: 'connected' };\n if (fromCredentials === 'global') return { authMode: 'gateway', authStatus: 'connected' };\n if (fromCredentials === 'oauth') {\n const token = await resolver.loadOAuthTokenRecord(provider);\n const expired = Boolean(token?.expiresAt && token.expiresAt < Date.now());\n return {\n authMode: 'oauth',\n authStatus: expired ? 'expired' : 'connected',\n ...(token?.expiresAt ? { expiresAt: token.expiresAt } : {}),\n };\n }\n if (fromCredentials === 'env') return { authMode: 'env', authStatus: 'connected' };\n\n const expiredOAuthToken = await resolver.loadOAuthTokenRecord(provider);\n if (expiredOAuthToken?.expiresAt && expiredOAuthToken.expiresAt < Date.now()) {\n return {\n authMode: 'oauth',\n authStatus: 'expired',\n expiresAt: expiredOAuthToken.expiresAt,\n };\n }\n\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return { authMode: 'models_json', authStatus: 'connected' };\n }\n\n return { authMode: 'none', authStatus: 'not_connected' };\n}\n\nexport async function getConfiguredProviders(): Promise<string[]> {\n\tconst allProviders = getAllProviders();\n\tconst configured: string[] = [];\n\tfor (const p of allProviders) {\n\t\tif (await isProviderConfigured(p)) {\n\t\t\tconfigured.push(p);\n\t\t}\n\t}\n\treturn configured;\n}\n\nexport function getAllModels(): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst registryModels = registry.getAll().map(normalizeProviderModel);\n\tconst pluginProviders = getProviderRegistry().listAll();\n\tif (pluginProviders.length === 0) return registryModels;\n\n\tconst existingIds = new Set(registryModels.map(m => `${m.provider}/${m.id}`));\n\tconst merged: Model<Api>[] = [...registryModels];\n\tfor (const plugin of pluginProviders) {\n\t\tfor (const model of plugin.models) {\n\t\t\tconst compositeId = `${plugin.id}/${model.id}`;\n\t\t\tif (!existingIds.has(compositeId)) {\n\t\t\t\tmerged.push(pluginModelToModel(plugin.id, model));\n\t\t\t\texistingIds.add(compositeId);\n\t\t\t}\n\t\t}\n\t}\n\treturn merged;\n}\n\nexport async function getAvailableModels(): Promise<readonly Model<Api>[]> {\n\tconst allModels = getAllModels();\n\tconst pluginRegistry = getProviderRegistry();\n\tconst available: Model<Api>[] = [];\n\tconst configuredByProvider = new Map<string, boolean>();\n\n\tfor (const model of allModels) {\n\t\tif (pluginRegistry.has(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t\tcontinue;\n\t\t}\n\t\tlet configured = configuredByProvider.get(model.provider);\n\t\tif (configured === undefined) {\n\t\t\tconfigured = await isProviderConfigured(model.provider);\n\t\t\tconfiguredByProvider.set(model.provider, configured);\n\t\t}\n\t\tif (configured) {\n\t\t\tavailable.push(model);\n\t\t}\n\t}\n\treturn available;\n}\n\nexport type { Model, Api } from '@earendil-works/pi-ai';\n\nexport type ProviderCategory = 'common' | 'domestic' | 'specialty' | 'oauth' | 'enterprise' | 'extension';\n\nexport interface ProviderMeta {\n name: string;\n category: ProviderCategory;\n supportsOAuth?: boolean;\n supportsApiKey?: boolean;\n}\n\nexport const PROVIDER_META: Record<string, ProviderMeta> = {\n 'openai': { name: 'OpenAI', category: 'common', supportsApiKey: true },\n 'anthropic': { name: 'Anthropic', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'deepseek': { name: 'DeepSeek', category: 'domestic', supportsApiKey: true },\n 'google': { name: 'Google AI', category: 'common', supportsApiKey: true },\n 'groq': { name: 'Groq', category: 'common', supportsApiKey: true },\n 'minimax': { name: 'MiniMax', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'minimax-cn': { name: 'MiniMax CN', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'kimi-coding': { name: 'Kimi For Coding', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'xai': { name: 'xAI', category: 'specialty', supportsApiKey: true },\n 'mistral': { name: 'Mistral AI', category: 'specialty', supportsApiKey: true },\n 'cerebras': { name: 'Cerebras', category: 'specialty', supportsApiKey: true },\n 'openrouter': { name: 'OpenRouter', category: 'specialty', supportsApiKey: true },\n 'huggingface': { name: 'Hugging Face', category: 'specialty', supportsApiKey: true },\n moonshotai: { name: 'Moonshot AI (Kimi · International)', category: 'domestic', supportsApiKey: true },\n 'moonshotai-cn': { name: 'Moonshot AI (Kimi · China)', category: 'domestic', supportsApiKey: true },\n fireworks: { name: 'Fireworks AI', category: 'specialty', supportsApiKey: true },\n together: { name: 'Together AI', category: 'specialty', supportsApiKey: true },\n 'cloudflare-workers-ai': { name: 'Cloudflare Workers AI', category: 'enterprise', supportsApiKey: true },\n 'cloudflare-ai-gateway': { name: 'Cloudflare AI Gateway', category: 'enterprise', supportsApiKey: true },\n xiaomi: { name: 'Xiaomi Mimo', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-cn': { name: 'Xiaomi Token Plan (CN)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-ams': { name: 'Xiaomi Token Plan (AMS)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-sgp': { name: 'Xiaomi Token Plan (SGP)', category: 'domestic', supportsApiKey: true },\n 'opencode': { name: 'OpenCode', category: 'specialty', supportsApiKey: true },\n 'opencode-go': { name: 'OpenCode Go', category: 'specialty', supportsApiKey: true },\n 'dashscope': { name: 'DashScope (Alibaba)', category: 'domestic', supportsApiKey: true },\n 'dashscope-cn': { name: 'Alibaba Bailian / DashScope China', category: 'domestic', supportsApiKey: true },\n 'dashscope-intl': { name: 'Alibaba Bailian / DashScope International', category: 'domestic', supportsApiKey: true },\n 'volcengine-ark': { name: 'Volcengine Ark', category: 'domestic', supportsApiKey: true },\n 'volcengine-plan': { name: 'Volcengine Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'byteplus-plan': { name: 'BytePlus Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun': { name: 'StepFun', category: 'domestic', supportsApiKey: true },\n 'stepfun-cn': { name: 'StepFun China', category: 'domestic', supportsApiKey: true },\n 'stepfun-intl': { name: 'StepFun International', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan': { name: 'StepFun Step Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-cn': { name: 'StepFun Step Plan China', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-intl': { name: 'StepFun Step Plan International', category: 'domestic', supportsApiKey: true },\n 'ant-ling': { name: 'Ant Ling', category: 'domestic', supportsApiKey: true },\n 'zhipu-cn': { name: 'Zhipu GLM (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-global': { name: 'Zhipu GLM Coding Plan (International)', category: 'domestic', supportsApiKey: true },\n 'zhipu-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n /** International GLM (api.z.ai). Auth: API key (ZAI_API_KEY); no published OAuth for this HTTP API. */\n 'zai': { name: 'Zhipu GLM (International · z.ai)', category: 'domestic', supportsApiKey: true },\n 'amazon-bedrock': { name: 'Amazon Bedrock', category: 'enterprise', supportsApiKey: true },\n 'azure-openai-responses': { name: 'Azure OpenAI', category: 'enterprise', supportsApiKey: true },\n 'google-vertex': { name: 'Google Vertex AI', category: 'enterprise', supportsApiKey: true },\n 'vercel-ai-gateway': { name: 'Vercel AI Gateway', category: 'enterprise', supportsApiKey: true },\n 'github-copilot': { name: 'GitHub Copilot (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'openai-codex': { name: 'OpenAI Codex (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-gemini-cli': { name: 'Google Gemini CLI (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-antigravity': { name: 'Google Antigravity (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n};\n\nexport function getSortedProviders(): string[] {\n const all = getAllProviders();\n const catOrder: Record<ProviderCategory, number> = {\n common: 0,\n domestic: 1,\n specialty: 2,\n enterprise: 3,\n oauth: 4,\n extension: 5,\n };\n const pluginRegistry = getProviderRegistry();\n\n return [...all].sort((a, b) => {\n const catA = pluginRegistry.has(a) ? 'extension' : (PROVIDER_META[a]?.category ?? 'specialty');\n const catB = pluginRegistry.has(b) ? 'extension' : (PROVIDER_META[b]?.category ?? 'specialty');\n if (catOrder[catA] !== catOrder[catB]) {\n return catOrder[catA] - catOrder[catB];\n }\n return a.localeCompare(b);\n });\n}\n\nexport function getProviderDisplayName(provider: string): string {\n const plugin = getProviderRegistry().get(provider);\n if (plugin) return plugin.name;\n return PROVIDER_META[provider]?.name || provider;\n}\n\nexport function providerSupportsOAuth(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsOAuth ?? false;\n}\n\nexport function providerSupportsApiKey(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsApiKey ?? true;\n}\n\n// ============================================\n// Dynamic Default Model Resolution\n// ============================================\n\n/**\n * Get a default model reference.\n * Priority:\n * 1. Effective model inherited from the global default preset / agent presets / agent override\n * 2. Empty string only when no default model is present in the loaded config\n */\nexport async function getDefaultModel(config?: Config | null | undefined): Promise<string> {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n const availableModels = await getAvailableModels();\n const configured = availableModels.find(m =>\n `${m.provider}/${m.id}` === modelRef ||\n m.id === modelRef\n );\n if (configured) {\n return `${configured.provider}/${configured.id}`;\n }\n return modelRef;\n }\n\n return '';\n}\n\n/**\n * Synchronous default model resolution for constructors and sync code paths.\n * Uses catalog/registry only (no async credential checks).\n *\n * When no model is present in the loaded config, returns an empty string so\n * setup flows can prompt for the global default model.\n */\nexport function getDefaultModelSync(config?: Config | null | undefined): string {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n return modelRef;\n }\n\n return '';\n}\n\n// Re-export ModelRegistry for advanced use cases\nexport { ModelRegistry, getModelRegistry, resetModelRegistry, prewarmModelRegistry } from './model-registry.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["getPiAiModel","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/index.ts"],"sourcesContent":["/**\n * Model provider module - integrates built-in models with custom models from models.json\n */\n\nimport {\n\tgetModel as getPiAiModel,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype Api,\n} from '@earendil-works/pi-ai/compat';\nimport { getAgentDefaultModelRef, type Config } from '../config/schema.js';\nimport { getModelRegistry } from './model-registry.js';\nimport { CredentialResolver, resolveApiKey, hasCredentials } from '../auth/credentials.js';\nimport { hasProviderAuthOnDiskSync } from '../auth/sync-provider-auth.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\nimport { getSupplementalModels } from './model-supplements.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport type { ProviderModelDefinition } from '../extensions/types/providers.js';\n\nexport { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';\n\n/** Sentinel base URL: model is served by an extension {@link ProviderPluginRegistry} provider. */\nexport const EXTENSION_PROVIDER_BASE_URL = 'extension://provider-plugin';\nconst OPENAI_CODEX_CANONICAL_BASE_URL = 'https://chatgpt.com/backend-api/codex';\n\nfunction normalizeProviderModel(model: Model<Api>): Model<Api> {\n\tif (model.provider === 'openai-codex' && model.api === 'openai-codex-responses') {\n\t\treturn { ...model, baseUrl: OPENAI_CODEX_CANONICAL_BASE_URL } as Model<Api>;\n\t}\n\treturn model;\n}\n\n/** Map a plugin registry model to the pi-ai {@link Model} shape. */\nexport function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api> {\n\treturn {\n\t\tprovider: providerId,\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\tapi: 'openai-completions' as Api,\n\t\tbaseUrl: EXTENSION_PROVIDER_BASE_URL,\n\t\treasoning: false,\n\t\tinput: definition.supportsImages ? (['text', 'image'] as ('text' | 'image')[]) : (['text'] as ('text' | 'image')[]),\n\t\tcontextWindow: definition.contextWindow ?? 128000,\n\t\tmaxTokens: definition.maxOutputTokens ?? 4096,\n\t\tcost: {\n\t\t\tinput: definition.pricing?.input ?? 0,\n\t\t\toutput: definition.pricing?.output ?? 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t},\n\t} as Model<Api>;\n}\n\n/**\n * Get API key synchronously: checks registry (models.json) first, then environment variables.\n * Use this for Agent's getApiKey callback which must be synchronous.\n */\nexport function getApiKeySync(provider: string): string | undefined {\n const pluginRegistry = getProviderRegistry();\n if (pluginRegistry.has(provider)) return 'extension-managed';\n\n const registry = getModelRegistry();\n const registryKey = registry.getApiKey(provider);\n if (registryKey) {\n return registryKey;\n }\n return getApiKeyFromEnv(provider);\n}\n\n/**\n * Resolve model reference. Supports:\n * - \"provider/modelId\" format\n * - \"modelId\" auto-detection via pi-ai or custom models\n * @throws if model not found\n */\nexport function resolveModel(ref: string): Model<Api> {\n\tconst trimmedRef = typeof ref === 'string' ? ref.trim() : '';\n\tif (!trimmedRef) {\n\t\tthrow new Error(\n\t\t\t'No default model configured. Choose a model in onboarding or set agents.capabilityPresets.default.models.roles.<role>.model.',\n\t\t);\n\t}\n\n\t// First try ModelRegistry (includes custom models)\n\tconst registry = getModelRegistry();\n\tconst customModel = registry.resolve(trimmedRef);\n\tif (customModel) {\n\t\treturn customModel;\n\t}\n\n\tif (trimmedRef.includes('/')) {\n\t\tconst [provider, modelId] = trimmedRef.split('/');\n\t\tconst piAiModel = getPiAiModel(provider as any, modelId as any);\n\t\tif (piAiModel) return normalizeProviderModel(piAiModel as Model<Api>);\n\n\t\tconst supplementalModel = getSupplementalModels().find(m => m.provider === provider && m.id === modelId);\n\t\tif (supplementalModel) return normalizeProviderModel(supplementalModel);\n\n\t\tconst pluginRegistry = getProviderRegistry();\n\t\tconst plugin = pluginRegistry.get(provider);\n\t\tif (plugin) {\n\t\t\tconst pluginModel = plugin.models.find(m => m.id === modelId);\n\t\t\tif (pluginModel) return pluginModelToModel(provider, pluginModel);\n\t\t}\n\t\tthrow new Error(`Model not found: ${trimmedRef}`);\n\t}\n\n\tfor (const provider of getPiAiProviders()) {\n\t\ttry {\n\t\t\tconst models = getPiAiModels(provider);\n\t\t\tconst found = models.find(m => m.id === trimmedRef);\n\t\t\tif (found) return normalizeProviderModel(found as Model<Api>);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst supplementalModel = getSupplementalModels().find(m => m.id === trimmedRef);\n\tif (supplementalModel) return normalizeProviderModel(supplementalModel);\n\n\tconst pluginRegistry = getProviderRegistry();\n\tfor (const plugin of pluginRegistry.listAll()) {\n\t\tconst found = plugin.models.find(m => m.id === trimmedRef);\n\t\tif (found) return pluginModelToModel(plugin.id, found);\n\t}\n\n\tthrow new Error(`Model not found: ${trimmedRef}. Use format: provider/model-id`);\n}\n\nexport function getModelsByProvider(provider: string): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst fromRegistry = registry.getAll().filter(m => m.provider === provider).map(normalizeProviderModel);\n\tconst existingIds = new Set(fromRegistry.map(m => m.id));\n\tconst supplementalModels = getSupplementalModels()\n\t\t.filter(m => m.provider === provider && !existingIds.has(m.id))\n\t\t.map(normalizeProviderModel);\n\tconst plugin = getProviderRegistry().get(provider);\n\tif (!plugin) return [...fromRegistry, ...supplementalModels];\n\tconst pluginModels = plugin.models.map(m => pluginModelToModel(provider, m));\n\treturn [...fromRegistry, ...supplementalModels, ...pluginModels];\n}\n\nexport function getAllProviders(): string[] {\n\tconst registry = getModelRegistry();\n\tconst providers = new Set<string>();\n\n\t// Add built-in providers\n\tfor (const p of getPiAiProviders()) {\n\t\tproviders.add(p);\n\t}\n\n\t// Add custom providers from registry\n\tfor (const m of registry.getAll()) {\n\t\tproviders.add(m.provider);\n\t}\n\n\tfor (const plugin of getProviderRegistry().listAll()) {\n\t\tproviders.add(plugin.id);\n\t}\n\n\treturn Array.from(providers);\n}\n\nexport async function getApiKey(provider: string): Promise<string | undefined> {\n\tif (getProviderRegistry().has(provider)) return 'extension-managed';\n\n\t// Use new credential resolver first (checks: agent private > global > oauth > env)\n\tconst credentialKey = await resolveApiKey(provider);\n\tif (credentialKey) {\n\t\treturn credentialKey;\n\t}\n\n\t// Check registry for custom providers (from models.json)\n\tconst registry = getModelRegistry();\n\tconst registryKey = registry.getApiKey(provider);\n\tif (registryKey) {\n\t\treturn registryKey;\n\t}\n\n\t// Fallback to environment variables\n\treturn getApiKeyFromEnv(provider);\n}\n\n/**\n * Synchronous version for use in non-async contexts\n * Only checks environment variables and registry, not credential system\n */\nexport function isProviderConfiguredSync(provider: string): boolean {\n\tif (getProviderRegistry().has(provider)) return true;\n\n\t// Check registry for custom providers\n\tconst registry = getModelRegistry();\n\tif (registry.getApiKey(provider)) {\n\t\treturn true;\n\t}\n\t// Check environment variables\n\tif (getApiKeyFromEnv(provider)) {\n\t\treturn true;\n\t}\n\t// Gateway UI / CLI store keys in auth-profiles.json (async CredentialResolver); sync path for fallback list\n\treturn hasProviderAuthOnDiskSync(provider);\n}\n\nexport async function isProviderConfigured(provider: string): Promise<boolean> {\n if (getProviderRegistry().has(provider)) return true;\n\n // Check registry first for custom providers (from models.json)\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return true;\n }\n return await hasCredentials(provider);\n}\n\n/** Where runtime {@link getApiKey} resolves the key from (no secret values). */\nexport type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';\n\nexport type ProviderAuthMode = ProviderActiveKeySource;\nexport type ProviderAuthStatus = 'connected' | 'expired' | 'not_connected';\n\nexport interface ProviderAuthState {\n authMode: ProviderAuthMode;\n authStatus: ProviderAuthStatus;\n expiresAt?: number;\n}\n\nexport async function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource> {\n const authState = await getProviderAuthState(provider);\n return authState.authMode;\n}\n\nexport async function getProviderAuthState(provider: string): Promise<ProviderAuthState> {\n if (getProviderRegistry().has(provider)) {\n return { authMode: 'extension', authStatus: 'connected' };\n }\n\n const resolver = new CredentialResolver();\n const fromCredentials = await resolver.resolveApiKeySource(provider);\n if (fromCredentials === 'agent') return { authMode: 'agent', authStatus: 'connected' };\n if (fromCredentials === 'global') return { authMode: 'gateway', authStatus: 'connected' };\n if (fromCredentials === 'oauth') {\n const token = await resolver.loadOAuthTokenRecord(provider);\n const expired = Boolean(token?.expiresAt && token.expiresAt < Date.now());\n return {\n authMode: 'oauth',\n authStatus: expired ? 'expired' : 'connected',\n ...(token?.expiresAt ? { expiresAt: token.expiresAt } : {}),\n };\n }\n if (fromCredentials === 'env') return { authMode: 'env', authStatus: 'connected' };\n\n const expiredOAuthToken = await resolver.loadOAuthTokenRecord(provider);\n if (expiredOAuthToken?.expiresAt && expiredOAuthToken.expiresAt < Date.now()) {\n return {\n authMode: 'oauth',\n authStatus: 'expired',\n expiresAt: expiredOAuthToken.expiresAt,\n };\n }\n\n const registry = getModelRegistry();\n if (registry.getApiKey(provider)) {\n return { authMode: 'models_json', authStatus: 'connected' };\n }\n\n return { authMode: 'none', authStatus: 'not_connected' };\n}\n\nexport async function getConfiguredProviders(): Promise<string[]> {\n\tconst allProviders = getAllProviders();\n\tconst configured: string[] = [];\n\tfor (const p of allProviders) {\n\t\tif (await isProviderConfigured(p)) {\n\t\t\tconfigured.push(p);\n\t\t}\n\t}\n\treturn configured;\n}\n\nexport function getAllModels(): readonly Model<Api>[] {\n\tconst registry = getModelRegistry();\n\tconst registryModels = registry.getAll().map(normalizeProviderModel);\n\tconst existingIds = new Set(registryModels.map(m => `${m.provider}/${m.id}`));\n\tconst supplementalModels = getSupplementalModels()\n\t\t.filter(m => !existingIds.has(`${m.provider}/${m.id}`))\n\t\t.map(normalizeProviderModel);\n\tfor (const model of supplementalModels) {\n\t\texistingIds.add(`${model.provider}/${model.id}`);\n\t}\n\tconst pluginProviders = getProviderRegistry().listAll();\n\tif (pluginProviders.length === 0) return [...registryModels, ...supplementalModels];\n\n\tconst merged: Model<Api>[] = [...registryModels, ...supplementalModels];\n\tfor (const plugin of pluginProviders) {\n\t\tfor (const model of plugin.models) {\n\t\t\tconst compositeId = `${plugin.id}/${model.id}`;\n\t\t\tif (!existingIds.has(compositeId)) {\n\t\t\t\tmerged.push(pluginModelToModel(plugin.id, model));\n\t\t\t\texistingIds.add(compositeId);\n\t\t\t}\n\t\t}\n\t}\n\treturn merged;\n}\n\nexport async function getAvailableModels(): Promise<readonly Model<Api>[]> {\n\tconst allModels = getAllModels();\n\tconst pluginRegistry = getProviderRegistry();\n\tconst available: Model<Api>[] = [];\n\tconst configuredByProvider = new Map<string, boolean>();\n\n\tfor (const model of allModels) {\n\t\tif (pluginRegistry.has(model.provider)) {\n\t\t\tavailable.push(model);\n\t\t\tcontinue;\n\t\t}\n\t\tlet configured = configuredByProvider.get(model.provider);\n\t\tif (configured === undefined) {\n\t\t\tconfigured = await isProviderConfigured(model.provider);\n\t\t\tconfiguredByProvider.set(model.provider, configured);\n\t\t}\n\t\tif (configured) {\n\t\t\tavailable.push(model);\n\t\t}\n\t}\n\treturn available;\n}\n\nexport type { Model, Api } from '@earendil-works/pi-ai';\n\nexport type ProviderCategory = 'common' | 'domestic' | 'specialty' | 'oauth' | 'enterprise' | 'extension';\n\nexport interface ProviderMeta {\n name: string;\n category: ProviderCategory;\n supportsOAuth?: boolean;\n supportsApiKey?: boolean;\n}\n\nexport const PROVIDER_META: Record<string, ProviderMeta> = {\n 'openai': { name: 'OpenAI', category: 'common', supportsApiKey: true },\n 'anthropic': { name: 'Anthropic', category: 'common', supportsApiKey: true, supportsOAuth: true },\n 'deepseek': { name: 'DeepSeek', category: 'domestic', supportsApiKey: true },\n 'google': { name: 'Google AI', category: 'common', supportsApiKey: true },\n 'groq': { name: 'Groq', category: 'common', supportsApiKey: true },\n 'minimax': { name: 'MiniMax', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'minimax-cn': { name: 'MiniMax CN', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'kimi-coding': { name: 'Kimi For Coding', category: 'domestic', supportsApiKey: true, supportsOAuth: true },\n 'xai': { name: 'xAI', category: 'specialty', supportsApiKey: true },\n 'mistral': { name: 'Mistral AI', category: 'specialty', supportsApiKey: true },\n 'cerebras': { name: 'Cerebras', category: 'specialty', supportsApiKey: true },\n 'openrouter': { name: 'OpenRouter', category: 'specialty', supportsApiKey: true },\n 'huggingface': { name: 'Hugging Face', category: 'specialty', supportsApiKey: true },\n moonshotai: { name: 'Moonshot AI (Kimi · International)', category: 'domestic', supportsApiKey: true },\n 'moonshotai-cn': { name: 'Moonshot AI (Kimi · China)', category: 'domestic', supportsApiKey: true },\n fireworks: { name: 'Fireworks AI', category: 'specialty', supportsApiKey: true },\n together: { name: 'Together AI', category: 'specialty', supportsApiKey: true },\n 'cloudflare-workers-ai': { name: 'Cloudflare Workers AI', category: 'enterprise', supportsApiKey: true },\n 'cloudflare-ai-gateway': { name: 'Cloudflare AI Gateway', category: 'enterprise', supportsApiKey: true },\n xiaomi: { name: 'Xiaomi Mimo', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-cn': { name: 'Xiaomi Token Plan (CN)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-ams': { name: 'Xiaomi Token Plan (AMS)', category: 'domestic', supportsApiKey: true },\n 'xiaomi-token-plan-sgp': { name: 'Xiaomi Token Plan (SGP)', category: 'domestic', supportsApiKey: true },\n 'opencode': { name: 'OpenCode', category: 'specialty', supportsApiKey: true },\n 'opencode-go': { name: 'OpenCode Go', category: 'specialty', supportsApiKey: true },\n 'dashscope': { name: 'DashScope (Alibaba)', category: 'domestic', supportsApiKey: true },\n 'dashscope-cn': { name: 'Alibaba Bailian / DashScope China', category: 'domestic', supportsApiKey: true },\n 'dashscope-intl': { name: 'Alibaba Bailian / DashScope International', category: 'domestic', supportsApiKey: true },\n 'volcengine-ark': { name: 'Volcengine Ark', category: 'domestic', supportsApiKey: true },\n 'volcengine-plan': { name: 'Volcengine Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'byteplus-plan': { name: 'BytePlus Doubao Coding Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun': { name: 'StepFun', category: 'domestic', supportsApiKey: true },\n 'stepfun-cn': { name: 'StepFun China', category: 'domestic', supportsApiKey: true },\n 'stepfun-intl': { name: 'StepFun International', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan': { name: 'StepFun Step Plan', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-cn': { name: 'StepFun Step Plan China', category: 'domestic', supportsApiKey: true },\n 'stepfun-plan-intl': { name: 'StepFun Step Plan International', category: 'domestic', supportsApiKey: true },\n 'ant-ling': { name: 'Ant Ling', category: 'domestic', supportsApiKey: true },\n 'zhipu-cn': { name: 'Zhipu GLM (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n 'zai-coding-global': { name: 'Zhipu GLM Coding Plan (International)', category: 'domestic', supportsApiKey: true },\n 'zhipu-coding-cn': { name: 'Zhipu GLM Coding Plan (China)', category: 'domestic', supportsApiKey: true },\n /** International GLM (api.z.ai). Auth: API key (ZAI_API_KEY); no published OAuth for this HTTP API. */\n 'zai': { name: 'Zhipu GLM (International · z.ai)', category: 'domestic', supportsApiKey: true },\n 'amazon-bedrock': { name: 'Amazon Bedrock', category: 'enterprise', supportsApiKey: true },\n 'azure-openai-responses': { name: 'Azure OpenAI', category: 'enterprise', supportsApiKey: true },\n 'google-vertex': { name: 'Google Vertex AI', category: 'enterprise', supportsApiKey: true },\n 'vercel-ai-gateway': { name: 'Vercel AI Gateway', category: 'enterprise', supportsApiKey: true },\n 'github-copilot': { name: 'GitHub Copilot (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'openai-codex': { name: 'OpenAI Codex (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-gemini-cli': { name: 'Google Gemini CLI (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n 'google-antigravity': { name: 'Google Antigravity (OAuth)', category: 'oauth', supportsOAuth: true, supportsApiKey: false },\n};\n\nexport function getSortedProviders(): string[] {\n const all = getAllProviders();\n const catOrder: Record<ProviderCategory, number> = {\n common: 0,\n domestic: 1,\n specialty: 2,\n enterprise: 3,\n oauth: 4,\n extension: 5,\n };\n const pluginRegistry = getProviderRegistry();\n\n return [...all].sort((a, b) => {\n const catA = pluginRegistry.has(a) ? 'extension' : (PROVIDER_META[a]?.category ?? 'specialty');\n const catB = pluginRegistry.has(b) ? 'extension' : (PROVIDER_META[b]?.category ?? 'specialty');\n if (catOrder[catA] !== catOrder[catB]) {\n return catOrder[catA] - catOrder[catB];\n }\n return a.localeCompare(b);\n });\n}\n\nexport function getProviderDisplayName(provider: string): string {\n const plugin = getProviderRegistry().get(provider);\n if (plugin) return plugin.name;\n return PROVIDER_META[provider]?.name || provider;\n}\n\nexport function providerSupportsOAuth(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsOAuth ?? false;\n}\n\nexport function providerSupportsApiKey(provider: string): boolean {\n return PROVIDER_META[provider]?.supportsApiKey ?? true;\n}\n\n// ============================================\n// Dynamic Default Model Resolution\n// ============================================\n\n/**\n * Get a default model reference.\n * Priority:\n * 1. Effective model inherited from the global default preset / agent presets / agent override\n * 2. Empty string only when no default model is present in the loaded config\n */\nexport async function getDefaultModel(config?: Config | null | undefined): Promise<string> {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n const availableModels = await getAvailableModels();\n const configured = availableModels.find(m =>\n `${m.provider}/${m.id}` === modelRef ||\n m.id === modelRef\n );\n if (configured) {\n return `${configured.provider}/${configured.id}`;\n }\n return modelRef;\n }\n\n return '';\n}\n\n/**\n * Synchronous default model resolution for constructors and sync code paths.\n * Uses catalog/registry only (no async credential checks).\n *\n * When no model is present in the loaded config, returns an empty string so\n * setup flows can prompt for the global default model.\n */\nexport function getDefaultModelSync(config?: Config | null | undefined): string {\n const modelRef = config ? getAgentDefaultModelRef(config) : undefined;\n if (modelRef) {\n return modelRef;\n }\n\n return '';\n}\n\n// Re-export ModelRegistry for advanced use cases\nexport { ModelRegistry, getModelRegistry, resetModelRegistry, prewarmModelRegistry } from './model-registry.js';\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-registry.js","names":["getModelsJsonPath","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/model-registry.ts"],"sourcesContent":["/**\n * Model Registry - manages built-in and custom models from models.json\n * \n * Features:\n * - Loads built-in models from @earendil-works/pi-ai\n * - Loads custom models from ~/.xopc/models.json\n * - Supports provider baseUrl/headers overrides\n * - Supports per-model overrides (modelOverrides)\n * - API key resolution with shell commands, env vars, and literals\n * - Hot reload support\n */\n\nimport {\n\ttype Api,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n\ttype KnownProvider,\n} from '@earendil-works/pi-ai/compat';\nimport { existsSync, readFileSync } from 'fs';\nimport { resolveConfigValue, resolveHeaders } from '../config/resolve-config-value.js';\nimport { getModelsJsonPath } from '../config/models-json.js';\nimport type {\n\tModelsJsonConfig,\n\tModelOverride,\n} from '../config/models-json.js';\nimport { validateModelsConfig, getDefaultModelValues } from '../config/models-json.js';\nimport { createLogger } from '../utils/logger.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\n\nconst log = createLogger('ModelRegistry');\n\n/** Provider override config (baseUrl, headers, apiKey) without custom models */\ninterface ProviderOverride {\n\tbaseUrl?: string;\n\theaders?: Record<string, string>;\n\tapiKey?: string;\n}\n\n/** Result of loading custom models from models.json */\ninterface CustomModelsResult {\n\tmodels: Model<Api>[];\n\t/** Providers with baseUrl/headers/apiKey overrides for built-in models */\n\toverrides: Map<string, ProviderOverride>;\n\t/** Per-model overrides: provider -> modelId -> override */\n\tmodelOverrides: Map<string, Map<string, ModelOverride>>;\n\t/** Custom provider API key configs for fallback resolution */\n\tapiKeyConfigs: Map<string, string>;\n\terror: string | undefined;\n}\n\nfunction emptyCustomModelsResult(error?: string): CustomModelsResult {\n\treturn {\n\t\tmodels: [],\n\t\toverrides: new Map(),\n\t\tmodelOverrides: new Map(),\n\t\tapiKeyConfigs: new Map(),\n\t\terror,\n\t};\n}\n\n/**\n * Deep merge compat settings\n */\nfunction mergeCompat(\n\tbaseCompat: Model<Api>['compat'],\n\toverrideCompat: ModelOverride['compat'],\n): Model<Api>['compat'] | undefined {\n\tif (!overrideCompat) return baseCompat;\n\n\tconst base = baseCompat as Record<string, unknown> | undefined;\n\tconst override = overrideCompat as Record<string, unknown>;\n\tconst merged = { ...base, ...override } as Record<string, unknown>;\n\n\t// Deep merge nested routing objects\n\tconst baseCompletions = base as { openRouterRouting?: unknown; vercelGatewayRouting?: unknown } | undefined;\n\tconst overrideCompletions = override as { openRouterRouting?: unknown; vercelGatewayRouting?: unknown };\n\n\tif (baseCompletions?.openRouterRouting || overrideCompletions?.openRouterRouting) {\n\t\t(merged as { openRouterRouting: unknown }).openRouterRouting = {\n\t\t\t...((baseCompletions?.openRouterRouting as object) || {}),\n\t\t\t...((overrideCompletions?.openRouterRouting as object) || {}),\n\t\t};\n\t}\n\n\tif (baseCompletions?.vercelGatewayRouting || overrideCompletions?.vercelGatewayRouting) {\n\t\t(merged as { vercelGatewayRouting: unknown }).vercelGatewayRouting = {\n\t\t\t...((baseCompletions?.vercelGatewayRouting as object) || {}),\n\t\t\t...((overrideCompletions?.vercelGatewayRouting as object) || {}),\n\t\t};\n\t}\n\n\treturn merged as Model<Api>['compat'];\n}\n\n/**\n * Apply a model override to a model\n */\nfunction applyModelOverride(model: Model<Api>, override: ModelOverride): Model<Api> {\n\tconst result = { ...model };\n\n\tif (override.name !== undefined) result.name = override.name;\n\tif (override.reasoning !== undefined) result.reasoning = override.reasoning;\n\tif (override.input !== undefined) result.input = override.input as ('text' | 'image')[];\n\tif (override.contextWindow !== undefined) result.contextWindow = override.contextWindow;\n\tif (override.maxTokens !== undefined) result.maxTokens = override.maxTokens;\n\n\t// Merge cost (partial override)\n\tif (override.cost) {\n\t\tresult.cost = {\n\t\t\tinput: override.cost.input ?? model.cost.input,\n\t\t\toutput: override.cost.output ?? model.cost.output,\n\t\t\tcacheRead: override.cost.cacheRead ?? model.cost.cacheRead,\n\t\t\tcacheWrite: override.cost.cacheWrite ?? model.cost.cacheWrite,\n\t\t};\n\t}\n\n\t// Merge headers\n\tif (override.headers) {\n\t\tconst resolvedHeaders = resolveHeaders(override.headers);\n\t\tresult.headers = resolvedHeaders ? { ...model.headers, ...resolvedHeaders } : model.headers;\n\t}\n\n\t// Deep merge compat\n\tresult.compat = mergeCompat(model.compat, override.compat);\n\n\treturn result;\n}\n\n/**\n * Model Registry - manages all models (built-in + custom)\n */\nexport class ModelRegistry {\n\tprivate models: Model<Api>[] = [];\n\tprivate customProviderApiKeys: Map<string, string> = new Map();\n\tprivate loadError: string | undefined = undefined;\n\tprivate loaded = false;\n\tprivate prewarmPromise: Promise<void> | null = null;\n\n\t// Performance caches\n\tprivate _availableModelsCache: Model<Api>[] | undefined;\n\tprivate _authStatusCache: Map<string, boolean> = new Map();\n\n\tconstructor(private modelsJsonPath: string = getModelsJsonPath()) {\n\t\t// Catalog loads on first use or via prewarm() — keeps gateway constructor off the hot path.\n\t}\n\n\tisLoaded(): boolean {\n\t\treturn this.loaded;\n\t}\n\n\t/** Synchronously materialize the catalog (idempotent). */\n\tensureLoaded(): void {\n\t\tif (this.loaded) {\n\t\t\treturn;\n\t\t}\n\t\tthis.loadModels();\n\t\tthis.loaded = true;\n\t}\n\n\t/** Background-friendly catalog load (idempotent). */\n\tprewarm(): Promise<void> {\n\t\tif (this.loaded) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tif (this.prewarmPromise) {\n\t\t\treturn this.prewarmPromise;\n\t\t}\n\t\tthis.prewarmPromise = Promise.resolve().then(() => {\n\t\t\tthis.ensureLoaded();\n\t\t});\n\t\treturn this.prewarmPromise;\n\t}\n\n\t/**\n\t * Reload models from disk (built-in + custom from models.json)\n\t * Clears all caches\n\t */\n\trefresh(): void {\n\t\tthis.customProviderApiKeys.clear();\n\t\tthis.loadError = undefined;\n\t\tthis._availableModelsCache = undefined;\n\t\tthis._authStatusCache.clear();\n\t\tthis.prewarmPromise = null;\n\t\tthis.loadModels();\n\t\tthis.loaded = true;\n\t\tlog.info('Model registry refreshed');\n\t}\n\n\t/**\n\t * Get any error from loading models.json (undefined if no error)\n\t */\n\tgetError(): string | undefined {\n\t\treturn this.loadError;\n\t}\n\n\t/**\n\t * Get all models (built-in + custom)\n\t * Results are cached until refresh() is called\n\t */\n\tgetAll(): readonly Model<Api>[] {\n\t\tthis.ensureLoaded();\n\t\treturn this.models;\n\t}\n\n\t/**\n\t * Get only models that have auth configured\n\t * Results are cached on first call until refresh()\n\t */\n\tgetAvailable(): readonly Model<Api>[] {\n\t\tthis.ensureLoaded();\n\t\t// Lazy initialization with caching\n\t\tif (!this._availableModelsCache) {\n\t\t\tthis._availableModelsCache = this.models.filter((m) => this.hasAuth(m.provider));\n\t\t}\n\t\treturn this._availableModelsCache;\n\t}\n\n\t/**\n\t * Find a model by provider and ID\n\t */\n\tfind(provider: string, modelId: string): Model<Api> | undefined {\n\t\tthis.ensureLoaded();\n\t\treturn this.models.find((m) => m.provider === provider && m.id === modelId);\n\t}\n\n\t/**\n\t * Resolve a model reference (provider/modelId or just modelId)\n\t */\n\tresolve(ref: string): Model<Api> | undefined {\n\t\tthis.ensureLoaded();\n\t\t// Handle provider/modelId format\n\t\tif (ref.includes('/')) {\n\t\t\tconst [provider, modelId] = ref.split('/');\n\t\t\treturn this.find(provider, modelId);\n\t\t}\n\n\t\t// Try to find by model ID only (search all providers)\n\t\tfor (const model of this.models) {\n\t\t\tif (model.id === ref) {\n\t\t\t\treturn model;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get API key for a provider (for custom providers from models.json)\n\t */\n\tgetApiKey(provider: string): string | undefined {\n\t\tthis.ensureLoaded();\n\t\t// First check custom provider configs\n\t\tconst keyConfig = this.customProviderApiKeys.get(provider);\n\t\tif (keyConfig) {\n\t\t\treturn resolveConfigValue(keyConfig);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Check if a provider has auth configured\n\t * Results are cached until refresh() is called\n\t */\n\tprivate hasAuth(provider: string): boolean {\n\t\t// Check cache first\n\t\tif (this._authStatusCache.has(provider)) {\n\t\t\treturn this._authStatusCache.get(provider)!;\n\t\t}\n\n\t\t// Compute and cache\n\t\tlet result: boolean;\n\t\t\n\t\t// Check custom provider configs from models.json first\n\t\tif (this.customProviderApiKeys.has(provider)) {\n\t\t\tconst key = this.getApiKey(provider);\n\t\t\tresult = !!key;\n\t\t} else {\n\t\t\t// Also check environment variables\n\t\t\tconst envKey = getApiKeyFromEnv(provider);\n\t\t\tresult = !!envKey;\n\t\t}\n\n\t\tthis._authStatusCache.set(provider, result);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Load all models\n\t * Clears caches to ensure fresh data\n\t */\n\tprivate loadModels(): void {\n\t\t// Clear caches before loading\n\t\tthis._availableModelsCache = undefined;\n\t\tthis._authStatusCache.clear();\n\n\t\t// Load custom models and overrides from models.json\n\t\tconst {\n\t\t\tmodels: customModels,\n\t\t\toverrides,\n\t\t\tmodelOverrides,\n\t\t\tapiKeyConfigs,\n\t\t\terror,\n\t\t} = this.loadCustomModels();\n\n\t\tif (error) {\n\t\t\tthis.loadError = error;\n\t\t\tlog.warn({ error }, 'Failed to load models.json, using built-in models only');\n\t\t}\n\n\t\t// Store API key configs for fallback resolution\n\t\tthis.customProviderApiKeys = apiKeyConfigs;\n\n\t\t// Load built-in models with overrides applied\n\t\tconst builtInModels = this.loadBuiltInModels(overrides, modelOverrides);\n\n\t\t// Merge custom models\n\t\tthis.models = this.mergeCustomModels(builtInModels, customModels);\n\n\t\tlog.debug(\n\t\t\t{ builtIn: builtInModels.length, custom: customModels.length, total: this.models.length },\n\t\t\t'Models loaded'\n\t\t);\n\t}\n\n\t/**\n\t * Load built-in models and apply provider/model overrides\n\t */\n\tprivate loadBuiltInModels(\n\t\toverrides: Map<string, ProviderOverride>,\n\t\tmodelOverrides: Map<string, Map<string, ModelOverride>>,\n\t): Model<Api>[] {\n\t\treturn getPiAiProviders().flatMap((provider) => {\n\t\t\ttry {\n\t\t\t\tconst models = getPiAiModels(provider as KnownProvider) as Model<Api>[];\n\t\t\t\tconst providerOverride = overrides.get(provider);\n\t\t\t\tconst perModelOverrides = modelOverrides.get(provider);\n\n\t\t\t\treturn models.map((m) => {\n\t\t\t\t\tlet model = m;\n\n\t\t\t\t\t// Apply provider-level baseUrl/headers override\n\t\t\t\t\tif (providerOverride) {\n\t\t\t\t\t\tconst resolvedHeaders = resolveHeaders(providerOverride.headers);\n\t\t\t\t\t\tmodel = {\n\t\t\t\t\t\t\t...model,\n\t\t\t\t\t\t\tbaseUrl: providerOverride.baseUrl ?? model.baseUrl,\n\t\t\t\t\t\t\theaders: resolvedHeaders ? { ...model.headers, ...resolvedHeaders } : model.headers,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Apply per-model override\n\t\t\t\t\tconst modelOverride = perModelOverrides?.get(m.id);\n\t\t\t\t\tif (modelOverride) {\n\t\t\t\t\t\tmodel = applyModelOverride(model, modelOverride);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn model;\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst errorMsg = err instanceof Error ? err.message : String(err);\n\t\t\t\tlog.error({ provider, error: errorMsg }, 'Failed to load built-in models for provider');\n\t\t\t\treturn [];\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Merge custom models into built-in list (custom wins on conflicts)\n\t */\n\tprivate mergeCustomModels(builtInModels: Model<Api>[], customModels: Model<Api>[]): Model<Api>[] {\n\t\tconst merged = [...builtInModels];\n\t\tfor (const customModel of customModels) {\n\t\t\tconst existingIndex = merged.findIndex(\n\t\t\t\t(m) => m.provider === customModel.provider && m.id === customModel.id\n\t\t\t);\n\t\t\tif (existingIndex >= 0) {\n\t\t\t\tmerged[existingIndex] = customModel;\n\t\t\t} else {\n\t\t\t\tmerged.push(customModel);\n\t\t\t}\n\t\t}\n\t\treturn merged;\n\t}\n\n\t/**\n\t * Load custom models from models.json\n\t */\n\tprivate loadCustomModels(): CustomModelsResult {\n\t\tif (!existsSync(this.modelsJsonPath)) {\n\t\t\treturn emptyCustomModelsResult();\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = readFileSync(this.modelsJsonPath, 'utf-8');\n\t\t\tconst config: ModelsJsonConfig = JSON.parse(content);\n\n\t\t\t// Validate schema\n\t\t\tconst validation = validateModelsConfig(config);\n\t\t\tif (!validation.valid) {\n\t\t\t\tconst errors = validation.errors\n\t\t\t\t\t.map((e) => ` - ${e.path}: ${e.message}`)\n\t\t\t\t\t.join('\\n');\n\t\t\t\treturn emptyCustomModelsResult(\n\t\t\t\t\t`Invalid models.json schema:\\n${errors}\\n\\nFile: ${this.modelsJsonPath}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst overrides = new Map<string, ProviderOverride>();\n\t\t\tconst modelOverrides = new Map<string, Map<string, ModelOverride>>();\n\t\t\tconst apiKeyConfigs = new Map<string, string>();\n\n\t\t\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\t\t\t// Apply provider-level baseUrl/headers/apiKey override\n\t\t\t\tif (providerConfig.baseUrl || providerConfig.headers || providerConfig.apiKey) {\n\t\t\t\t\toverrides.set(providerName, {\n\t\t\t\t\t\tbaseUrl: providerConfig.baseUrl,\n\t\t\t\t\t\theaders: providerConfig.headers,\n\t\t\t\t\t\tapiKey: providerConfig.apiKey,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Store API key config for fallback resolver\n\t\t\t\tif (providerConfig.apiKey) {\n\t\t\t\t\tapiKeyConfigs.set(providerName, providerConfig.apiKey);\n\t\t\t\t}\n\n\t\t\t\t// Store model overrides\n\t\t\t\tif (providerConfig.modelOverrides) {\n\t\t\t\t\tmodelOverrides.set(providerName, new Map(Object.entries(providerConfig.modelOverrides)));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tmodels: this.parseModels(config),\n\t\t\t\toverrides,\n\t\t\t\tmodelOverrides,\n\t\t\t\tapiKeyConfigs,\n\t\t\t\terror: undefined,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\treturn emptyCustomModelsResult(\n\t\t\t\t\t`Failed to parse models.json: ${error.message}\\n\\nFile: ${this.modelsJsonPath}`\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn emptyCustomModelsResult(\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${this.modelsJsonPath}`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Parse custom models from config\n\t */\n\tprivate parseModels(config: ModelsJsonConfig): Model<Api>[] {\n\t\tconst models: Model<Api>[] = [];\n\t\tconst defaults = getDefaultModelValues();\n\n\t\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\t\tconst modelDefs = providerConfig.models ?? [];\n\t\t\tif (modelDefs.length === 0) continue;\n\n\t\t\tfor (const modelDef of modelDefs) {\n\t\t\t\tconst api = modelDef.api || providerConfig.api;\n\t\t\t\tif (!api) continue;\n\n\t\t\t\t// Merge headers: provider headers are base, model headers override\n\t\t\t\t// Resolve env vars and shell commands in header values\n\t\t\t\tconst providerHeaders = resolveHeaders(providerConfig.headers);\n\t\t\t\tconst modelHeaders = resolveHeaders(modelDef.headers);\n\t\t\t\tlet headers =\n\t\t\t\t\tproviderHeaders || modelHeaders ? { ...providerHeaders, ...modelHeaders } : undefined;\n\n\t\t\t\t// If authHeader is true, add Authorization header with resolved API key\n\t\t\t\tif (providerConfig.authHeader && providerConfig.apiKey) {\n\t\t\t\t\tconst resolvedKey = resolveConfigValue(providerConfig.apiKey);\n\t\t\t\t\tif (resolvedKey) {\n\t\t\t\t\t\theaders = { ...headers, Authorization: `Bearer ${resolvedKey}` };\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmodels.push({\n\t\t\t\t\tid: modelDef.id,\n\t\t\t\t\tname: modelDef.name ?? modelDef.id,\n\t\t\t\t\tapi: api as Api,\n\t\t\t\t\tprovider: providerName,\n\t\t\t\t\tbaseUrl: providerConfig.baseUrl!,\n\t\t\t\t\treasoning: modelDef.reasoning ?? false,\n\t\t\t\t\tinput: (modelDef.input ?? defaults.input) as ('text' | 'image')[],\n\t\t\t\t\tcost: modelDef.cost ?? defaults.cost,\n\t\t\t\t\tcontextWindow: modelDef.contextWindow ?? defaults.contextWindow,\n\t\t\t\t\tmaxTokens: modelDef.maxTokens ?? defaults.maxTokens,\n\t\t\t\t\theaders,\n\t\t\t\t\tcompat: modelDef.compat as Model<Api>['compat'],\n\t\t\t\t} as Model<Api>);\n\t\t\t}\n\t\t}\n\n\t\treturn models;\n\t}\n}\n\n// ============================================\n// Global Registry Instance\n// ============================================\n\nlet globalRegistry: ModelRegistry | undefined;\n\nexport function getModelRegistry(): ModelRegistry {\n\tif (!globalRegistry) {\n\t\tglobalRegistry = new ModelRegistry();\n\t}\n\treturn globalRegistry;\n}\n\nexport function resetModelRegistry(): void {\n\tglobalRegistry = undefined;\n}\n\n/** Schedule catalog load on a background turn (gateway post-ready sidecar). */\nexport function prewarmModelRegistry(): Promise<void> {\n\treturn getModelRegistry().prewarm();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model-registry.js","names":["getModelsJsonPath","getPiAiProviders","getPiAiModels"],"sources":["../../../src/providers/model-registry.ts"],"sourcesContent":["/**\n * Model Registry - manages built-in and custom models from models.json\n * \n * Features:\n * - Loads built-in models from @earendil-works/pi-ai\n * - Loads custom models from ~/.xopc/models.json\n * - Supports provider baseUrl/headers overrides\n * - Supports per-model overrides (modelOverrides)\n * - API key resolution with shell commands, env vars, and literals\n * - Hot reload support\n */\n\nimport {\n\ttype Api,\n\tgetModels as getPiAiModels,\n\tgetProviders as getPiAiProviders,\n\ttype Model,\n} from '@earendil-works/pi-ai/compat';\nimport { existsSync, readFileSync } from 'fs';\nimport { resolveConfigValue, resolveHeaders } from '../config/resolve-config-value.js';\nimport { getModelsJsonPath } from '../config/models-json.js';\nimport type {\n\tModelsJsonConfig,\n\tModelOverride,\n} from '../config/models-json.js';\nimport { validateModelsConfig, getDefaultModelValues } from '../config/models-json.js';\nimport { createLogger } from '../utils/logger.js';\nimport { getApiKeyFromEnv } from './env-keys.js';\n\nconst log = createLogger('ModelRegistry');\n\n/** Provider override config (baseUrl, headers, apiKey) without custom models */\ninterface ProviderOverride {\n\tbaseUrl?: string;\n\theaders?: Record<string, string>;\n\tapiKey?: string;\n}\n\n/** Result of loading custom models from models.json */\ninterface CustomModelsResult {\n\tmodels: Model<Api>[];\n\t/** Providers with baseUrl/headers/apiKey overrides for built-in models */\n\toverrides: Map<string, ProviderOverride>;\n\t/** Per-model overrides: provider -> modelId -> override */\n\tmodelOverrides: Map<string, Map<string, ModelOverride>>;\n\t/** Custom provider API key configs for fallback resolution */\n\tapiKeyConfigs: Map<string, string>;\n\terror: string | undefined;\n}\n\nfunction emptyCustomModelsResult(error?: string): CustomModelsResult {\n\treturn {\n\t\tmodels: [],\n\t\toverrides: new Map(),\n\t\tmodelOverrides: new Map(),\n\t\tapiKeyConfigs: new Map(),\n\t\terror,\n\t};\n}\n\n/**\n * Deep merge compat settings\n */\nfunction mergeCompat(\n\tbaseCompat: Model<Api>['compat'],\n\toverrideCompat: ModelOverride['compat'],\n): Model<Api>['compat'] | undefined {\n\tif (!overrideCompat) return baseCompat;\n\n\tconst base = baseCompat as Record<string, unknown> | undefined;\n\tconst override = overrideCompat as Record<string, unknown>;\n\tconst merged = { ...base, ...override } as Record<string, unknown>;\n\n\t// Deep merge nested routing objects\n\tconst baseCompletions = base as { openRouterRouting?: unknown; vercelGatewayRouting?: unknown } | undefined;\n\tconst overrideCompletions = override as { openRouterRouting?: unknown; vercelGatewayRouting?: unknown };\n\n\tif (baseCompletions?.openRouterRouting || overrideCompletions?.openRouterRouting) {\n\t\t(merged as { openRouterRouting: unknown }).openRouterRouting = {\n\t\t\t...((baseCompletions?.openRouterRouting as object) || {}),\n\t\t\t...((overrideCompletions?.openRouterRouting as object) || {}),\n\t\t};\n\t}\n\n\tif (baseCompletions?.vercelGatewayRouting || overrideCompletions?.vercelGatewayRouting) {\n\t\t(merged as { vercelGatewayRouting: unknown }).vercelGatewayRouting = {\n\t\t\t...((baseCompletions?.vercelGatewayRouting as object) || {}),\n\t\t\t...((overrideCompletions?.vercelGatewayRouting as object) || {}),\n\t\t};\n\t}\n\n\treturn merged as Model<Api>['compat'];\n}\n\n/**\n * Apply a model override to a model\n */\nfunction applyModelOverride(model: Model<Api>, override: ModelOverride): Model<Api> {\n\tconst result = { ...model };\n\n\tif (override.name !== undefined) result.name = override.name;\n\tif (override.reasoning !== undefined) result.reasoning = override.reasoning;\n\tif (override.input !== undefined) result.input = override.input as ('text' | 'image')[];\n\tif (override.contextWindow !== undefined) result.contextWindow = override.contextWindow;\n\tif (override.maxTokens !== undefined) result.maxTokens = override.maxTokens;\n\n\t// Merge cost (partial override)\n\tif (override.cost) {\n\t\tresult.cost = {\n\t\t\tinput: override.cost.input ?? model.cost.input,\n\t\t\toutput: override.cost.output ?? model.cost.output,\n\t\t\tcacheRead: override.cost.cacheRead ?? model.cost.cacheRead,\n\t\t\tcacheWrite: override.cost.cacheWrite ?? model.cost.cacheWrite,\n\t\t};\n\t}\n\n\t// Merge headers\n\tif (override.headers) {\n\t\tconst resolvedHeaders = resolveHeaders(override.headers);\n\t\tresult.headers = resolvedHeaders ? { ...model.headers, ...resolvedHeaders } : model.headers;\n\t}\n\n\t// Deep merge compat\n\tresult.compat = mergeCompat(model.compat, override.compat);\n\n\treturn result;\n}\n\n/**\n * Model Registry - manages all models (built-in + custom)\n */\nexport class ModelRegistry {\n\tprivate models: Model<Api>[] = [];\n\tprivate customProviderApiKeys: Map<string, string> = new Map();\n\tprivate loadError: string | undefined = undefined;\n\tprivate loaded = false;\n\tprivate prewarmPromise: Promise<void> | null = null;\n\n\t// Performance caches\n\tprivate _availableModelsCache: Model<Api>[] | undefined;\n\tprivate _authStatusCache: Map<string, boolean> = new Map();\n\n\tconstructor(private modelsJsonPath: string = getModelsJsonPath()) {\n\t\t// Catalog loads on first use or via prewarm() — keeps gateway constructor off the hot path.\n\t}\n\n\tisLoaded(): boolean {\n\t\treturn this.loaded;\n\t}\n\n\t/** Synchronously materialize the catalog (idempotent). */\n\tensureLoaded(): void {\n\t\tif (this.loaded) {\n\t\t\treturn;\n\t\t}\n\t\tthis.loadModels();\n\t\tthis.loaded = true;\n\t}\n\n\t/** Background-friendly catalog load (idempotent). */\n\tprewarm(): Promise<void> {\n\t\tif (this.loaded) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tif (this.prewarmPromise) {\n\t\t\treturn this.prewarmPromise;\n\t\t}\n\t\tthis.prewarmPromise = Promise.resolve().then(() => {\n\t\t\tthis.ensureLoaded();\n\t\t});\n\t\treturn this.prewarmPromise;\n\t}\n\n\t/**\n\t * Reload models from disk (built-in + custom from models.json)\n\t * Clears all caches\n\t */\n\trefresh(): void {\n\t\tthis.customProviderApiKeys.clear();\n\t\tthis.loadError = undefined;\n\t\tthis._availableModelsCache = undefined;\n\t\tthis._authStatusCache.clear();\n\t\tthis.prewarmPromise = null;\n\t\tthis.loadModels();\n\t\tthis.loaded = true;\n\t\tlog.info('Model registry refreshed');\n\t}\n\n\t/**\n\t * Get any error from loading models.json (undefined if no error)\n\t */\n\tgetError(): string | undefined {\n\t\treturn this.loadError;\n\t}\n\n\t/**\n\t * Get all models (built-in + custom)\n\t * Results are cached until refresh() is called\n\t */\n\tgetAll(): readonly Model<Api>[] {\n\t\tthis.ensureLoaded();\n\t\treturn this.models;\n\t}\n\n\t/**\n\t * Get only models that have auth configured\n\t * Results are cached on first call until refresh()\n\t */\n\tgetAvailable(): readonly Model<Api>[] {\n\t\tthis.ensureLoaded();\n\t\t// Lazy initialization with caching\n\t\tif (!this._availableModelsCache) {\n\t\t\tthis._availableModelsCache = this.models.filter((m) => this.hasAuth(m.provider));\n\t\t}\n\t\treturn this._availableModelsCache;\n\t}\n\n\t/**\n\t * Find a model by provider and ID\n\t */\n\tfind(provider: string, modelId: string): Model<Api> | undefined {\n\t\tthis.ensureLoaded();\n\t\treturn this.models.find((m) => m.provider === provider && m.id === modelId);\n\t}\n\n\t/**\n\t * Resolve a model reference (provider/modelId or just modelId)\n\t */\n\tresolve(ref: string): Model<Api> | undefined {\n\t\tthis.ensureLoaded();\n\t\t// Handle provider/modelId format\n\t\tif (ref.includes('/')) {\n\t\t\tconst [provider, modelId] = ref.split('/');\n\t\t\treturn this.find(provider, modelId);\n\t\t}\n\n\t\t// Try to find by model ID only (search all providers)\n\t\tfor (const model of this.models) {\n\t\t\tif (model.id === ref) {\n\t\t\t\treturn model;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get API key for a provider (for custom providers from models.json)\n\t */\n\tgetApiKey(provider: string): string | undefined {\n\t\tthis.ensureLoaded();\n\t\t// First check custom provider configs\n\t\tconst keyConfig = this.customProviderApiKeys.get(provider);\n\t\tif (keyConfig) {\n\t\t\treturn resolveConfigValue(keyConfig);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Check if a provider has auth configured\n\t * Results are cached until refresh() is called\n\t */\n\tprivate hasAuth(provider: string): boolean {\n\t\t// Check cache first\n\t\tif (this._authStatusCache.has(provider)) {\n\t\t\treturn this._authStatusCache.get(provider)!;\n\t\t}\n\n\t\t// Compute and cache\n\t\tlet result: boolean;\n\t\t\n\t\t// Check custom provider configs from models.json first\n\t\tif (this.customProviderApiKeys.has(provider)) {\n\t\t\tconst key = this.getApiKey(provider);\n\t\t\tresult = !!key;\n\t\t} else {\n\t\t\t// Also check environment variables\n\t\t\tconst envKey = getApiKeyFromEnv(provider);\n\t\t\tresult = !!envKey;\n\t\t}\n\n\t\tthis._authStatusCache.set(provider, result);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Load all models\n\t * Clears caches to ensure fresh data\n\t */\n\tprivate loadModels(): void {\n\t\t// Clear caches before loading\n\t\tthis._availableModelsCache = undefined;\n\t\tthis._authStatusCache.clear();\n\n\t\t// Load custom models and overrides from models.json\n\t\tconst {\n\t\t\tmodels: customModels,\n\t\t\toverrides,\n\t\t\tmodelOverrides,\n\t\t\tapiKeyConfigs,\n\t\t\terror,\n\t\t} = this.loadCustomModels();\n\n\t\tif (error) {\n\t\t\tthis.loadError = error;\n\t\t\tlog.warn({ error }, 'Failed to load models.json, using built-in models only');\n\t\t}\n\n\t\t// Store API key configs for fallback resolution\n\t\tthis.customProviderApiKeys = apiKeyConfigs;\n\n\t\t// Load built-in models with overrides applied\n\t\tconst builtInModels = this.loadBuiltInModels(overrides, modelOverrides);\n\n\t\t// Merge custom models\n\t\tthis.models = this.mergeCustomModels(builtInModels, customModels);\n\n\t\tlog.debug(\n\t\t\t{ builtIn: builtInModels.length, custom: customModels.length, total: this.models.length },\n\t\t\t'Models loaded'\n\t\t);\n\t}\n\n\t/**\n\t * Load built-in models and apply provider/model overrides\n\t */\n\tprivate loadBuiltInModels(\n\t\toverrides: Map<string, ProviderOverride>,\n\t\tmodelOverrides: Map<string, Map<string, ModelOverride>>,\n\t): Model<Api>[] {\n\t\treturn getPiAiProviders().flatMap((provider) => {\n\t\t\ttry {\n\t\t\t\tconst models = getPiAiModels(provider as any) as Model<Api>[];\n\t\t\t\tconst providerOverride = overrides.get(provider);\n\t\t\t\tconst perModelOverrides = modelOverrides.get(provider);\n\n\t\t\t\treturn models.map((m) => {\n\t\t\t\t\tlet model = m;\n\n\t\t\t\t\t// Apply provider-level baseUrl/headers override\n\t\t\t\t\tif (providerOverride) {\n\t\t\t\t\t\tconst resolvedHeaders = resolveHeaders(providerOverride.headers);\n\t\t\t\t\t\tmodel = {\n\t\t\t\t\t\t\t...model,\n\t\t\t\t\t\t\tbaseUrl: providerOverride.baseUrl ?? model.baseUrl,\n\t\t\t\t\t\t\theaders: resolvedHeaders ? { ...model.headers, ...resolvedHeaders } : model.headers,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Apply per-model override\n\t\t\t\t\tconst modelOverride = perModelOverrides?.get(m.id);\n\t\t\t\t\tif (modelOverride) {\n\t\t\t\t\t\tmodel = applyModelOverride(model, modelOverride);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn model;\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconst errorMsg = err instanceof Error ? err.message : String(err);\n\t\t\t\tlog.error({ provider, error: errorMsg }, 'Failed to load built-in models for provider');\n\t\t\t\treturn [];\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Merge custom models into built-in list (custom wins on conflicts)\n\t */\n\tprivate mergeCustomModels(builtInModels: Model<Api>[], customModels: Model<Api>[]): Model<Api>[] {\n\t\tconst merged = [...builtInModels];\n\t\tfor (const customModel of customModels) {\n\t\t\tconst existingIndex = merged.findIndex(\n\t\t\t\t(m) => m.provider === customModel.provider && m.id === customModel.id\n\t\t\t);\n\t\t\tif (existingIndex >= 0) {\n\t\t\t\tmerged[existingIndex] = customModel;\n\t\t\t} else {\n\t\t\t\tmerged.push(customModel);\n\t\t\t}\n\t\t}\n\t\treturn merged;\n\t}\n\n\t/**\n\t * Load custom models from models.json\n\t */\n\tprivate loadCustomModels(): CustomModelsResult {\n\t\tif (!existsSync(this.modelsJsonPath)) {\n\t\t\treturn emptyCustomModelsResult();\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = readFileSync(this.modelsJsonPath, 'utf-8');\n\t\t\tconst config: ModelsJsonConfig = JSON.parse(content);\n\n\t\t\t// Validate schema\n\t\t\tconst validation = validateModelsConfig(config);\n\t\t\tif (!validation.valid) {\n\t\t\t\tconst errors = validation.errors\n\t\t\t\t\t.map((e) => ` - ${e.path}: ${e.message}`)\n\t\t\t\t\t.join('\\n');\n\t\t\t\treturn emptyCustomModelsResult(\n\t\t\t\t\t`Invalid models.json schema:\\n${errors}\\n\\nFile: ${this.modelsJsonPath}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst overrides = new Map<string, ProviderOverride>();\n\t\t\tconst modelOverrides = new Map<string, Map<string, ModelOverride>>();\n\t\t\tconst apiKeyConfigs = new Map<string, string>();\n\n\t\t\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\t\t\t// Apply provider-level baseUrl/headers/apiKey override\n\t\t\t\tif (providerConfig.baseUrl || providerConfig.headers || providerConfig.apiKey) {\n\t\t\t\t\toverrides.set(providerName, {\n\t\t\t\t\t\tbaseUrl: providerConfig.baseUrl,\n\t\t\t\t\t\theaders: providerConfig.headers,\n\t\t\t\t\t\tapiKey: providerConfig.apiKey,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Store API key config for fallback resolver\n\t\t\t\tif (providerConfig.apiKey) {\n\t\t\t\t\tapiKeyConfigs.set(providerName, providerConfig.apiKey);\n\t\t\t\t}\n\n\t\t\t\t// Store model overrides\n\t\t\t\tif (providerConfig.modelOverrides) {\n\t\t\t\t\tmodelOverrides.set(providerName, new Map(Object.entries(providerConfig.modelOverrides)));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tmodels: this.parseModels(config),\n\t\t\t\toverrides,\n\t\t\t\tmodelOverrides,\n\t\t\t\tapiKeyConfigs,\n\t\t\t\terror: undefined,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof SyntaxError) {\n\t\t\t\treturn emptyCustomModelsResult(\n\t\t\t\t\t`Failed to parse models.json: ${error.message}\\n\\nFile: ${this.modelsJsonPath}`\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn emptyCustomModelsResult(\n\t\t\t\t`Failed to load models.json: ${error instanceof Error ? error.message : error}\\n\\nFile: ${this.modelsJsonPath}`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Parse custom models from config\n\t */\n\tprivate parseModels(config: ModelsJsonConfig): Model<Api>[] {\n\t\tconst models: Model<Api>[] = [];\n\t\tconst defaults = getDefaultModelValues();\n\n\t\tfor (const [providerName, providerConfig] of Object.entries(config.providers)) {\n\t\t\tconst modelDefs = providerConfig.models ?? [];\n\t\t\tif (modelDefs.length === 0) continue;\n\n\t\t\tfor (const modelDef of modelDefs) {\n\t\t\t\tconst api = modelDef.api || providerConfig.api;\n\t\t\t\tif (!api) continue;\n\n\t\t\t\t// Merge headers: provider headers are base, model headers override\n\t\t\t\t// Resolve env vars and shell commands in header values\n\t\t\t\tconst providerHeaders = resolveHeaders(providerConfig.headers);\n\t\t\t\tconst modelHeaders = resolveHeaders(modelDef.headers);\n\t\t\t\tlet headers =\n\t\t\t\t\tproviderHeaders || modelHeaders ? { ...providerHeaders, ...modelHeaders } : undefined;\n\n\t\t\t\t// If authHeader is true, add Authorization header with resolved API key\n\t\t\t\tif (providerConfig.authHeader && providerConfig.apiKey) {\n\t\t\t\t\tconst resolvedKey = resolveConfigValue(providerConfig.apiKey);\n\t\t\t\t\tif (resolvedKey) {\n\t\t\t\t\t\theaders = { ...headers, Authorization: `Bearer ${resolvedKey}` };\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmodels.push({\n\t\t\t\t\tid: modelDef.id,\n\t\t\t\t\tname: modelDef.name ?? modelDef.id,\n\t\t\t\t\tapi: api as Api,\n\t\t\t\t\tprovider: providerName,\n\t\t\t\t\tbaseUrl: providerConfig.baseUrl!,\n\t\t\t\t\treasoning: modelDef.reasoning ?? false,\n\t\t\t\t\tinput: (modelDef.input ?? defaults.input) as ('text' | 'image')[],\n\t\t\t\t\tcost: modelDef.cost ?? defaults.cost,\n\t\t\t\t\tcontextWindow: modelDef.contextWindow ?? defaults.contextWindow,\n\t\t\t\t\tmaxTokens: modelDef.maxTokens ?? defaults.maxTokens,\n\t\t\t\t\theaders,\n\t\t\t\t\tcompat: modelDef.compat as Model<Api>['compat'],\n\t\t\t\t} as Model<Api>);\n\t\t\t}\n\t\t}\n\n\t\treturn models;\n\t}\n}\n\n// ============================================\n// Global Registry Instance\n// ============================================\n\nlet globalRegistry: ModelRegistry | undefined;\n\nexport function getModelRegistry(): ModelRegistry {\n\tif (!globalRegistry) {\n\t\tglobalRegistry = new ModelRegistry();\n\t}\n\treturn globalRegistry;\n}\n\nexport function resetModelRegistry(): void {\n\tglobalRegistry = undefined;\n}\n\n/** Schedule catalog load on a background turn (gateway post-ready sidecar). */\nexport function prewarmModelRegistry(): Promise<void> {\n\treturn getModelRegistry().prewarm();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Api, type Model } from '@earendil-works/pi-ai/compat';
|
|
2
|
+
/**
|
|
3
|
+
* Small local supplement for official model aliases that may lag in pi-ai.
|
|
4
|
+
* Keep this list narrow: full provider catalogs should come from pi-ai.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getSupplementalModels(): Model<Api>[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { getModel } from "@earendil-works/pi-ai/compat";
|
|
3
|
+
//#region src/providers/model-supplements.ts
|
|
4
|
+
/**
|
|
5
|
+
* Small local supplement for official model aliases that may lag in pi-ai.
|
|
6
|
+
* Keep this list narrow: full provider catalogs should come from pi-ai.
|
|
7
|
+
*/
|
|
8
|
+
function getSupplementalModels() {
|
|
9
|
+
const gpt56Sol = getModel("openai", "gpt-5.6-sol");
|
|
10
|
+
if (!gpt56Sol) return [];
|
|
11
|
+
return [{
|
|
12
|
+
...gpt56Sol,
|
|
13
|
+
id: "gpt-5.6",
|
|
14
|
+
name: "GPT-5.6"
|
|
15
|
+
}];
|
|
16
|
+
}
|
|
17
|
+
var init_model_supplements = __esmMin((() => {}));
|
|
18
|
+
//#endregion
|
|
19
|
+
init_model_supplements();
|
|
20
|
+
export { getSupplementalModels, init_model_supplements };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=model-supplements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-supplements.js","names":["getPiAiModel"],"sources":["../../../src/providers/model-supplements.ts"],"sourcesContent":["import {\n getModel as getPiAiModel,\n type Api,\n type Model,\n} from '@earendil-works/pi-ai/compat';\n\n/**\n * Small local supplement for official model aliases that may lag in pi-ai.\n * Keep this list narrow: full provider catalogs should come from pi-ai.\n */\nexport function getSupplementalModels(): Model<Api>[] {\n const gpt56Sol = getPiAiModel('openai' as never, 'gpt-5.6-sol' as never) as Model<Api> | undefined;\n if (!gpt56Sol) return [];\n\n return [\n {\n ...gpt56Sol,\n id: 'gpt-5.6',\n name: 'GPT-5.6',\n },\n ];\n}\n"],"mappings":";;;;;;;AAUA,SAAgB,wBAAsC;CACpD,MAAM,WAAWA,SAAa,UAAmB,cAAuB;AACxE,KAAI,CAAC,SAAU,QAAO,EAAE;AAExB,QAAO,CACL;EACE,GAAG;EACH,IAAI;EACJ,MAAM;EACP,CACF"}
|
|
@@ -73,30 +73,37 @@ const PROVIDER_HINTS = {
|
|
|
73
73
|
};
|
|
74
74
|
const RECOMMENDED_MODEL_PATTERNS = {
|
|
75
75
|
openai: [
|
|
76
|
+
/^gpt-5\.6$/i,
|
|
77
|
+
/^gpt-5\.6-sol$/i,
|
|
78
|
+
/^gpt-5\.6-terra$/i,
|
|
79
|
+
/^gpt-5\.6-luna$/i,
|
|
76
80
|
/^gpt-5\.5$/i,
|
|
77
|
-
/^gpt-5\.
|
|
78
|
-
/^gpt-5\.4-mini$/i,
|
|
79
|
-
/^gpt-5\.4-pro$/i
|
|
81
|
+
/^gpt-5\.5-pro$/i
|
|
80
82
|
],
|
|
81
83
|
"openai-codex": [
|
|
82
|
-
/^gpt-5\.
|
|
83
|
-
/^gpt-5\.
|
|
84
|
-
/^gpt-5\.
|
|
84
|
+
/^gpt-5\.6-sol$/i,
|
|
85
|
+
/^gpt-5\.6-terra$/i,
|
|
86
|
+
/^gpt-5\.6-luna$/i,
|
|
87
|
+
/^gpt-5\.5$/i
|
|
85
88
|
],
|
|
86
89
|
anthropic: [
|
|
87
|
-
/^claude-sonnet-
|
|
88
|
-
/^claude-opus-4
|
|
89
|
-
/^claude-
|
|
90
|
+
/^claude-sonnet-5$/i,
|
|
91
|
+
/^claude-opus-4-8$/i,
|
|
92
|
+
/^claude-fable-5$/i,
|
|
93
|
+
/^claude-sonnet-4-6$/i,
|
|
94
|
+
/^claude-sonnet-4-5$/i
|
|
90
95
|
],
|
|
91
96
|
google: [
|
|
92
|
-
/^gemini-3
|
|
93
|
-
/^gemini-
|
|
94
|
-
/^gemini-
|
|
97
|
+
/^gemini-3\.5-flash$/i,
|
|
98
|
+
/^gemini-3\.1-pro-preview$/i,
|
|
99
|
+
/^gemini-3-flash-preview$/i,
|
|
100
|
+
/^gemini-3-pro-preview$/i
|
|
95
101
|
],
|
|
96
102
|
"google-vertex": [
|
|
97
|
-
/^gemini-3
|
|
98
|
-
/^gemini-
|
|
99
|
-
/^gemini-
|
|
103
|
+
/^gemini-3\.5-flash$/i,
|
|
104
|
+
/^gemini-3\.1-pro-preview$/i,
|
|
105
|
+
/^gemini-3-flash-preview$/i,
|
|
106
|
+
/^gemini-3-pro-preview$/i
|
|
100
107
|
],
|
|
101
108
|
xai: [
|
|
102
109
|
/^grok-4/i,
|
|
@@ -135,17 +142,9 @@ const RECOMMENDED_MODEL_PATTERNS = {
|
|
|
135
142
|
/^anthropic\/claude-sonnet-4/i,
|
|
136
143
|
/^google\/gemini-3/i
|
|
137
144
|
],
|
|
138
|
-
moonshotai: [
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
/^kimi-k2\.5$/i
|
|
142
|
-
],
|
|
143
|
-
"moonshotai-cn": [
|
|
144
|
-
/^kimi-k2\.7-code$/i,
|
|
145
|
-
/^kimi-k2\.6$/i,
|
|
146
|
-
/^kimi-k2\.5$/i
|
|
147
|
-
],
|
|
148
|
-
"kimi-coding": [/^kimi-k2\.7-code$/i, /^kimi-for-coding$/i],
|
|
145
|
+
moonshotai: [/^kimi-k2\.7-code$/i, /^kimi-k2\.6$/i],
|
|
146
|
+
"moonshotai-cn": [/^kimi-k2\.7-code$/i, /^kimi-k2\.6$/i],
|
|
147
|
+
"kimi-coding": [/^k2p7$/i, /^kimi-for-coding$/i],
|
|
149
148
|
stepfun: [
|
|
150
149
|
/^step-3\.7-flash$/i,
|
|
151
150
|
/^step-3\.5-flash-2603$/i,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation.js","names":[],"sources":["../../../src/providers/presentation.ts"],"sourcesContent":["import type { Api, Model } from '@earendil-works/pi-ai';\n\nimport {\n getAllModels,\n getAllProviders,\n getModelsByProvider,\n getProviderDisplayName,\n isProviderConfigured,\n PROVIDER_META,\n type ProviderCategory,\n} from './index.js';\nimport {\n getDomesticProviderPreset,\n getDomesticProviderPresetIds,\n type DomesticProviderModelPreset,\n} from './domestic-presets.js';\nimport { getProviderRegistry } from './plugin-registry.js';\n\nexport interface ModelCatalogView {\n ref: string;\n provider: string;\n id: string;\n name: string;\n reasoning: boolean;\n input: Array<'text' | 'image'>;\n contextWindow?: number;\n maxTokens?: number;\n vision: boolean;\n recommended: boolean;\n}\n\nexport interface ProviderCatalogView {\n id: string;\n name: string;\n category: ProviderCategory;\n supportsOAuth: boolean;\n supportsApiKey: boolean;\n configured: boolean;\n onboardingFeatured: boolean;\n recommendedModels: ModelCatalogView[];\n modelCount: number;\n hint?: string;\n}\n\nconst FEATURED_PROVIDER_ORDER = [\n 'openai',\n 'anthropic',\n 'xai',\n 'google',\n 'openrouter',\n 'dashscope-cn',\n 'dashscope-intl',\n 'volcengine-ark',\n 'volcengine-plan',\n 'byteplus-plan',\n 'deepseek',\n 'moonshotai',\n 'moonshotai-cn',\n 'kimi-coding',\n 'stepfun-cn',\n 'stepfun-intl',\n 'stepfun-plan-cn',\n 'stepfun-plan-intl',\n 'xiaomi',\n 'xiaomi-token-plan-cn',\n 'ant-ling',\n 'zhipu-cn',\n 'zai',\n 'zai-coding-cn',\n 'zhipu-coding-cn',\n 'minimax',\n 'minimax-cn',\n];\n\nconst PROVIDER_HINTS: Record<string, string> = {\n openai: 'Frontier OpenAI models from the official API.',\n anthropic: 'Claude models for coding, long context, and reasoning.',\n xai: 'Grok models from xAI.',\n google: 'Gemini models with strong multimodal and long-context support.',\n openrouter: 'Route many hosted models through one API key.',\n dashscope: 'DashScope service provider for Alibaba Model Studio.',\n 'dashscope-cn': 'Alibaba Bailian Qwen models through the China OpenAI-compatible endpoint.',\n 'dashscope-intl': 'Alibaba Bailian Qwen models through the international OpenAI-compatible endpoint.',\n 'volcengine-ark': 'Volcengine Ark OpenAI-compatible endpoints; use your Ark endpoint id as the model id.',\n 'volcengine-plan': 'Doubao coding-plan models through Volcengine Ark.',\n 'byteplus-plan': 'Doubao coding-plan models through BytePlus.',\n deepseek: 'DeepSeek V4 models with strong price/performance.',\n moonshotai: 'Kimi models through Moonshot International.',\n 'moonshotai-cn': 'Kimi models through Moonshot China.',\n 'kimi-coding': 'Kimi dedicated coding endpoint with Anthropic-compatible requests.',\n stepfun: 'StepFun OpenAI-compatible endpoint.',\n 'stepfun-cn': 'StepFun China OpenAI-compatible endpoint.',\n 'stepfun-intl': 'StepFun international OpenAI-compatible endpoint.',\n 'stepfun-plan': 'StepFun Step Plan coding/planning endpoint.',\n 'stepfun-plan-cn': 'StepFun Step Plan China coding/planning endpoint.',\n 'stepfun-plan-intl': 'StepFun Step Plan international coding/planning endpoint.',\n xiaomi: 'Xiaomi MiMo OpenAI-compatible model access.',\n 'xiaomi-token-plan-cn': 'Xiaomi MiMo Token Plan China endpoint.',\n 'xiaomi-token-plan-ams': 'Xiaomi MiMo Token Plan Amsterdam endpoint.',\n 'xiaomi-token-plan-sgp': 'Xiaomi MiMo Token Plan Singapore endpoint.',\n 'ant-ling': 'Ant Ling models through a domestic OpenAI-compatible endpoint.',\n 'zhipu-cn': 'Zhipu GLM China through the BigModel OpenAI-compatible API.',\n zai: 'Zhipu GLM International through z.ai.',\n 'zai-coding-cn': 'Zhipu GLM coding-plan China endpoint from pi-ai.',\n 'zai-coding-global': 'Zhipu GLM coding-plan international endpoint.',\n 'zhipu-coding-cn': 'Zhipu GLM coding-plan China endpoint.',\n minimax: 'MiniMax text models; Anthropic-compatible endpoint is preferred for agent workflows.',\n 'minimax-cn': 'MiniMax China endpoint for domestic access.',\n 'openai-codex': 'OpenAI Codex models through OAuth.',\n 'google-gemini-cli': 'Gemini through Google Cloud Code Assist OAuth.',\n 'google-antigravity': 'Gemini/Antigravity OAuth model access.',\n};\n\nconst RECOMMENDED_MODEL_PATTERNS: Record<string, RegExp[]> = {\n openai: [/^gpt-5\\.5$/i, /^gpt-5\\.4$/i, /^gpt-5\\.4-mini$/i, /^gpt-5\\.4-pro$/i],\n 'openai-codex': [/^gpt-5\\.5$/i, /^gpt-5\\.4$/i, /^gpt-5\\.4-mini$/i],\n anthropic: [/^claude-sonnet-4/i, /^claude-opus-4/i, /^claude-haiku-4/i],\n google: [/^gemini-3/i, /^gemini-2\\.5-pro$/i, /^gemini-2\\.5-flash$/i],\n 'google-vertex': [/^gemini-3/i, /^gemini-2\\.5-pro$/i, /^gemini-2\\.5-flash$/i],\n xai: [/^grok-4/i, /^grok-3/i, /^grok-code/i],\n dashscope: [/^qwen3\\.7-plus$/i, /^qwen3\\.7-max$/i, /^qwen3\\.6-flash$/i],\n 'dashscope-cn': [/^qwen3\\.7-plus$/i, /^qwen3\\.7-max$/i, /^qwen3\\.6-flash$/i],\n 'dashscope-intl': [/^qwen3\\.7-plus$/i, /^qwen3\\.7-max$/i, /^qwen3\\.6-flash$/i],\n 'volcengine-ark': [/^ep-your-endpoint-id$/i],\n 'volcengine-plan': [/^ark-code-latest$/i, /^doubao-seed-2\\.0-code$/i, /^doubao-seed-2\\.0-pro$/i],\n 'byteplus-plan': [/^ark-code-latest$/i, /^doubao-seed-2\\.0-code$/i, /^doubao-seed-2\\.0-pro$/i],\n deepseek: [/^deepseek-v4-flash$/i, /^deepseek-v4-pro$/i],\n openrouter: [/^openai\\/gpt-5/i, /^anthropic\\/claude-sonnet-4/i, /^google\\/gemini-3/i],\n moonshotai: [/^kimi-k2\\.7-code$/i, /^kimi-k2\\.6$/i, /^kimi-k2\\.5$/i],\n 'moonshotai-cn': [/^kimi-k2\\.7-code$/i, /^kimi-k2\\.6$/i, /^kimi-k2\\.5$/i],\n 'kimi-coding': [/^kimi-k2\\.7-code$/i, /^kimi-for-coding$/i],\n stepfun: [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-cn': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-intl': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-plan': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-plan-cn': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-plan-intl': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n xiaomi: [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'xiaomi-token-plan-cn': [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'xiaomi-token-plan-ams': [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'xiaomi-token-plan-sgp': [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'ant-ling': [/^Ling-2\\.6-1T$/i, /^Ling-2\\.6-flash$/i, /^Ring-2\\.6-1T$/i],\n 'zhipu-cn': [/^glm-5\\.2$/i],\n zai: [/^glm-5\\.2$/i],\n 'zai-coding-cn': [/^glm-5\\.2$/i, /^glm-5\\.1$/i, /^glm-5-turbo$/i],\n 'zai-coding-global': [/^glm-5\\.2$/i],\n 'zhipu-coding-cn': [/^glm-5\\.2$/i],\n minimax: [/^MiniMax-M3$/i, /^MiniMax-M2\\.7-highspeed$/i, /^MiniMax-M2\\.7$/i],\n 'minimax-cn': [/^MiniMax-M3$/i, /^MiniMax-M2\\.7-highspeed$/i, /^MiniMax-M2\\.7$/i],\n};\n\nfunction modelRef(model: Pick<Model<Api>, 'provider' | 'id'>): string {\n return `${model.provider}/${model.id}`;\n}\n\nfunction recommendedRank(provider: string, modelId: string): number {\n const patterns = RECOMMENDED_MODEL_PATTERNS[provider] ?? [];\n const idx = patterns.findIndex((pattern) => pattern.test(modelId));\n return idx === -1 ? Number.POSITIVE_INFINITY : idx;\n}\n\nexport function isRecommendedModel(provider: string, modelId: string): boolean {\n return Number.isFinite(recommendedRank(provider, modelId));\n}\n\nexport function modelToCatalogView(model: Model<Api>): ModelCatalogView {\n const input = (model.input ?? ['text']) as Array<'text' | 'image'>;\n return {\n ref: modelRef(model),\n provider: model.provider,\n id: model.id,\n name: model.name || model.id,\n reasoning: model.reasoning ?? false,\n input,\n ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),\n ...(model.maxTokens ? { maxTokens: model.maxTokens } : {}),\n vision: input.includes('image'),\n recommended: isRecommendedModel(model.provider, model.id),\n };\n}\n\nfunction presetModelToCatalogView(provider: string, model: DomesticProviderModelPreset): ModelCatalogView {\n const input = model.input ?? ['text'];\n return {\n ref: `${provider}/${model.id}`,\n provider,\n id: model.id,\n name: model.name || model.id,\n reasoning: model.reasoning ?? false,\n input,\n ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),\n ...(model.maxTokens ? { maxTokens: model.maxTokens } : {}),\n vision: input.includes('image'),\n recommended: isRecommendedModel(provider, model.id),\n };\n}\n\nexport function sortModelsForPicker(models: readonly Model<Api>[]): Model<Api>[] {\n return [...models].sort((a, b) => {\n const aRank = recommendedRank(a.provider, a.id);\n const bRank = recommendedRank(b.provider, b.id);\n if (aRank !== bRank) return aRank - bRank;\n if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);\n return (a.name || a.id).localeCompare(b.name || b.id, undefined, { sensitivity: 'base' });\n });\n}\n\nexport function getRecommendedModelsForProvider(provider: string, limit = 4): ModelCatalogView[] {\n const models = sortModelsForPicker(getModelsByProvider(provider));\n const fromRegistry = models\n .filter((model) => isRecommendedModel(provider, model.id))\n .slice(0, limit)\n .map(modelToCatalogView);\n if (fromRegistry.length > 0) return fromRegistry;\n\n const preset = getDomesticProviderPreset(provider);\n if (!preset) return [];\n return preset.models\n .filter((model) => isRecommendedModel(provider, model.id))\n .slice(0, limit)\n .map((model) => presetModelToCatalogView(provider, model));\n}\n\nexport function getProviderHint(provider: string): string | undefined {\n return PROVIDER_HINTS[provider];\n}\n\nexport function getOnboardingFeaturedProviders(): string[] {\n const providers = new Set([...getAllProviders(), ...getDomesticProviderPresetIds()]);\n return FEATURED_PROVIDER_ORDER.filter((provider) => providers.has(provider));\n}\n\nexport function sortProvidersForPicker(providers: readonly string[]): string[] {\n const featuredRank = new Map(FEATURED_PROVIDER_ORDER.map((provider, index) => [provider, index] as const));\n return [...providers].sort((a, b) => {\n const aRank = featuredRank.get(a) ?? Number.POSITIVE_INFINITY;\n const bRank = featuredRank.get(b) ?? Number.POSITIVE_INFINITY;\n if (aRank !== bRank) return aRank - bRank;\n const catA = PROVIDER_META[a]?.category ?? 'specialty';\n const catB = PROVIDER_META[b]?.category ?? 'specialty';\n const catOrder: Record<ProviderCategory, number> = {\n common: 0,\n domestic: 1,\n specialty: 2,\n enterprise: 3,\n oauth: 4,\n extension: 5,\n };\n if (catOrder[catA] !== catOrder[catB]) return catOrder[catA] - catOrder[catB];\n return getProviderDisplayName(a).localeCompare(getProviderDisplayName(b), undefined, { sensitivity: 'base' });\n });\n}\n\nexport async function getProviderCatalogViews(): Promise<ProviderCatalogView[]> {\n const pluginRegistry = getProviderRegistry();\n const providers = sortProvidersForPicker([...new Set([...getAllProviders(), ...getDomesticProviderPresetIds()])]);\n const modelCounts = new Map<string, number>();\n for (const model of getAllModels()) {\n modelCounts.set(model.provider, (modelCounts.get(model.provider) ?? 0) + 1);\n }\n\n return Promise.all(\n providers.map(async (provider) => {\n const plugin = pluginRegistry.get(provider);\n const meta = PROVIDER_META[provider];\n const domesticPreset = getDomesticProviderPreset(provider);\n return {\n id: provider,\n name: plugin?.name ?? domesticPreset?.displayName ?? getProviderDisplayName(provider),\n category: plugin ? 'extension' : (meta?.category ?? (domesticPreset ? 'domestic' : 'specialty')),\n supportsOAuth: plugin ? false : (meta?.supportsOAuth ?? false),\n supportsApiKey: plugin ? false : (meta?.supportsApiKey ?? true),\n configured: plugin ? true : await isProviderConfigured(provider),\n onboardingFeatured: FEATURED_PROVIDER_ORDER.includes(provider),\n recommendedModels: getRecommendedModelsForProvider(provider),\n modelCount: modelCounts.get(provider) ?? 0,\n ...(getProviderHint(provider) ? { hint: getProviderHint(provider) } : {}),\n };\n }),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation.js","names":[],"sources":["../../../src/providers/presentation.ts"],"sourcesContent":["import type { Api, Model } from '@earendil-works/pi-ai';\n\nimport {\n getAllModels,\n getAllProviders,\n getModelsByProvider,\n getProviderDisplayName,\n isProviderConfigured,\n PROVIDER_META,\n type ProviderCategory,\n} from './index.js';\nimport {\n getDomesticProviderPreset,\n getDomesticProviderPresetIds,\n type DomesticProviderModelPreset,\n} from './domestic-presets.js';\nimport { getProviderRegistry } from './plugin-registry.js';\n\nexport interface ModelCatalogView {\n ref: string;\n provider: string;\n id: string;\n name: string;\n reasoning: boolean;\n input: Array<'text' | 'image'>;\n contextWindow?: number;\n maxTokens?: number;\n vision: boolean;\n recommended: boolean;\n}\n\nexport interface ProviderCatalogView {\n id: string;\n name: string;\n category: ProviderCategory;\n supportsOAuth: boolean;\n supportsApiKey: boolean;\n configured: boolean;\n onboardingFeatured: boolean;\n recommendedModels: ModelCatalogView[];\n modelCount: number;\n hint?: string;\n}\n\nconst FEATURED_PROVIDER_ORDER = [\n 'openai',\n 'anthropic',\n 'xai',\n 'google',\n 'openrouter',\n 'dashscope-cn',\n 'dashscope-intl',\n 'volcengine-ark',\n 'volcengine-plan',\n 'byteplus-plan',\n 'deepseek',\n 'moonshotai',\n 'moonshotai-cn',\n 'kimi-coding',\n 'stepfun-cn',\n 'stepfun-intl',\n 'stepfun-plan-cn',\n 'stepfun-plan-intl',\n 'xiaomi',\n 'xiaomi-token-plan-cn',\n 'ant-ling',\n 'zhipu-cn',\n 'zai',\n 'zai-coding-cn',\n 'zhipu-coding-cn',\n 'minimax',\n 'minimax-cn',\n];\n\nconst PROVIDER_HINTS: Record<string, string> = {\n openai: 'Frontier OpenAI models from the official API.',\n anthropic: 'Claude models for coding, long context, and reasoning.',\n xai: 'Grok models from xAI.',\n google: 'Gemini models with strong multimodal and long-context support.',\n openrouter: 'Route many hosted models through one API key.',\n dashscope: 'DashScope service provider for Alibaba Model Studio.',\n 'dashscope-cn': 'Alibaba Bailian Qwen models through the China OpenAI-compatible endpoint.',\n 'dashscope-intl': 'Alibaba Bailian Qwen models through the international OpenAI-compatible endpoint.',\n 'volcengine-ark': 'Volcengine Ark OpenAI-compatible endpoints; use your Ark endpoint id as the model id.',\n 'volcengine-plan': 'Doubao coding-plan models through Volcengine Ark.',\n 'byteplus-plan': 'Doubao coding-plan models through BytePlus.',\n deepseek: 'DeepSeek V4 models with strong price/performance.',\n moonshotai: 'Kimi models through Moonshot International.',\n 'moonshotai-cn': 'Kimi models through Moonshot China.',\n 'kimi-coding': 'Kimi dedicated coding endpoint with Anthropic-compatible requests.',\n stepfun: 'StepFun OpenAI-compatible endpoint.',\n 'stepfun-cn': 'StepFun China OpenAI-compatible endpoint.',\n 'stepfun-intl': 'StepFun international OpenAI-compatible endpoint.',\n 'stepfun-plan': 'StepFun Step Plan coding/planning endpoint.',\n 'stepfun-plan-cn': 'StepFun Step Plan China coding/planning endpoint.',\n 'stepfun-plan-intl': 'StepFun Step Plan international coding/planning endpoint.',\n xiaomi: 'Xiaomi MiMo OpenAI-compatible model access.',\n 'xiaomi-token-plan-cn': 'Xiaomi MiMo Token Plan China endpoint.',\n 'xiaomi-token-plan-ams': 'Xiaomi MiMo Token Plan Amsterdam endpoint.',\n 'xiaomi-token-plan-sgp': 'Xiaomi MiMo Token Plan Singapore endpoint.',\n 'ant-ling': 'Ant Ling models through a domestic OpenAI-compatible endpoint.',\n 'zhipu-cn': 'Zhipu GLM China through the BigModel OpenAI-compatible API.',\n zai: 'Zhipu GLM International through z.ai.',\n 'zai-coding-cn': 'Zhipu GLM coding-plan China endpoint from pi-ai.',\n 'zai-coding-global': 'Zhipu GLM coding-plan international endpoint.',\n 'zhipu-coding-cn': 'Zhipu GLM coding-plan China endpoint.',\n minimax: 'MiniMax text models; Anthropic-compatible endpoint is preferred for agent workflows.',\n 'minimax-cn': 'MiniMax China endpoint for domestic access.',\n 'openai-codex': 'OpenAI Codex models through OAuth.',\n 'google-gemini-cli': 'Gemini through Google Cloud Code Assist OAuth.',\n 'google-antigravity': 'Gemini/Antigravity OAuth model access.',\n};\n\nconst RECOMMENDED_MODEL_PATTERNS: Record<string, RegExp[]> = {\n openai: [/^gpt-5\\.6$/i, /^gpt-5\\.6-sol$/i, /^gpt-5\\.6-terra$/i, /^gpt-5\\.6-luna$/i, /^gpt-5\\.5$/i, /^gpt-5\\.5-pro$/i],\n 'openai-codex': [/^gpt-5\\.6-sol$/i, /^gpt-5\\.6-terra$/i, /^gpt-5\\.6-luna$/i, /^gpt-5\\.5$/i],\n anthropic: [/^claude-sonnet-5$/i, /^claude-opus-4-8$/i, /^claude-fable-5$/i, /^claude-sonnet-4-6$/i, /^claude-sonnet-4-5$/i],\n google: [/^gemini-3\\.5-flash$/i, /^gemini-3\\.1-pro-preview$/i, /^gemini-3-flash-preview$/i, /^gemini-3-pro-preview$/i],\n 'google-vertex': [/^gemini-3\\.5-flash$/i, /^gemini-3\\.1-pro-preview$/i, /^gemini-3-flash-preview$/i, /^gemini-3-pro-preview$/i],\n xai: [/^grok-4/i, /^grok-3/i, /^grok-code/i],\n dashscope: [/^qwen3\\.7-plus$/i, /^qwen3\\.7-max$/i, /^qwen3\\.6-flash$/i],\n 'dashscope-cn': [/^qwen3\\.7-plus$/i, /^qwen3\\.7-max$/i, /^qwen3\\.6-flash$/i],\n 'dashscope-intl': [/^qwen3\\.7-plus$/i, /^qwen3\\.7-max$/i, /^qwen3\\.6-flash$/i],\n 'volcengine-ark': [/^ep-your-endpoint-id$/i],\n 'volcengine-plan': [/^ark-code-latest$/i, /^doubao-seed-2\\.0-code$/i, /^doubao-seed-2\\.0-pro$/i],\n 'byteplus-plan': [/^ark-code-latest$/i, /^doubao-seed-2\\.0-code$/i, /^doubao-seed-2\\.0-pro$/i],\n deepseek: [/^deepseek-v4-flash$/i, /^deepseek-v4-pro$/i],\n openrouter: [/^openai\\/gpt-5/i, /^anthropic\\/claude-sonnet-4/i, /^google\\/gemini-3/i],\n moonshotai: [/^kimi-k2\\.7-code$/i, /^kimi-k2\\.6$/i],\n 'moonshotai-cn': [/^kimi-k2\\.7-code$/i, /^kimi-k2\\.6$/i],\n 'kimi-coding': [/^k2p7$/i, /^kimi-for-coding$/i],\n stepfun: [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-cn': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-intl': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-plan': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-plan-cn': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n 'stepfun-plan-intl': [/^step-3\\.7-flash$/i, /^step-3\\.5-flash-2603$/i, /^step-3\\.5-flash$/i],\n xiaomi: [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'xiaomi-token-plan-cn': [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'xiaomi-token-plan-ams': [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'xiaomi-token-plan-sgp': [/^mimo-v2\\.5-pro$/i, /^mimo-v2\\.5-pro-ultraspeed$/i, /^mimo-v2\\.5$/i],\n 'ant-ling': [/^Ling-2\\.6-1T$/i, /^Ling-2\\.6-flash$/i, /^Ring-2\\.6-1T$/i],\n 'zhipu-cn': [/^glm-5\\.2$/i],\n zai: [/^glm-5\\.2$/i],\n 'zai-coding-cn': [/^glm-5\\.2$/i, /^glm-5\\.1$/i, /^glm-5-turbo$/i],\n 'zai-coding-global': [/^glm-5\\.2$/i],\n 'zhipu-coding-cn': [/^glm-5\\.2$/i],\n minimax: [/^MiniMax-M3$/i, /^MiniMax-M2\\.7-highspeed$/i, /^MiniMax-M2\\.7$/i],\n 'minimax-cn': [/^MiniMax-M3$/i, /^MiniMax-M2\\.7-highspeed$/i, /^MiniMax-M2\\.7$/i],\n};\n\nfunction modelRef(model: Pick<Model<Api>, 'provider' | 'id'>): string {\n return `${model.provider}/${model.id}`;\n}\n\nfunction recommendedRank(provider: string, modelId: string): number {\n const patterns = RECOMMENDED_MODEL_PATTERNS[provider] ?? [];\n const idx = patterns.findIndex((pattern) => pattern.test(modelId));\n return idx === -1 ? Number.POSITIVE_INFINITY : idx;\n}\n\nexport function isRecommendedModel(provider: string, modelId: string): boolean {\n return Number.isFinite(recommendedRank(provider, modelId));\n}\n\nexport function modelToCatalogView(model: Model<Api>): ModelCatalogView {\n const input = (model.input ?? ['text']) as Array<'text' | 'image'>;\n return {\n ref: modelRef(model),\n provider: model.provider,\n id: model.id,\n name: model.name || model.id,\n reasoning: model.reasoning ?? false,\n input,\n ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),\n ...(model.maxTokens ? { maxTokens: model.maxTokens } : {}),\n vision: input.includes('image'),\n recommended: isRecommendedModel(model.provider, model.id),\n };\n}\n\nfunction presetModelToCatalogView(provider: string, model: DomesticProviderModelPreset): ModelCatalogView {\n const input = model.input ?? ['text'];\n return {\n ref: `${provider}/${model.id}`,\n provider,\n id: model.id,\n name: model.name || model.id,\n reasoning: model.reasoning ?? false,\n input,\n ...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),\n ...(model.maxTokens ? { maxTokens: model.maxTokens } : {}),\n vision: input.includes('image'),\n recommended: isRecommendedModel(provider, model.id),\n };\n}\n\nexport function sortModelsForPicker(models: readonly Model<Api>[]): Model<Api>[] {\n return [...models].sort((a, b) => {\n const aRank = recommendedRank(a.provider, a.id);\n const bRank = recommendedRank(b.provider, b.id);\n if (aRank !== bRank) return aRank - bRank;\n if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);\n return (a.name || a.id).localeCompare(b.name || b.id, undefined, { sensitivity: 'base' });\n });\n}\n\nexport function getRecommendedModelsForProvider(provider: string, limit = 4): ModelCatalogView[] {\n const models = sortModelsForPicker(getModelsByProvider(provider));\n const fromRegistry = models\n .filter((model) => isRecommendedModel(provider, model.id))\n .slice(0, limit)\n .map(modelToCatalogView);\n if (fromRegistry.length > 0) return fromRegistry;\n\n const preset = getDomesticProviderPreset(provider);\n if (!preset) return [];\n return preset.models\n .filter((model) => isRecommendedModel(provider, model.id))\n .slice(0, limit)\n .map((model) => presetModelToCatalogView(provider, model));\n}\n\nexport function getProviderHint(provider: string): string | undefined {\n return PROVIDER_HINTS[provider];\n}\n\nexport function getOnboardingFeaturedProviders(): string[] {\n const providers = new Set([...getAllProviders(), ...getDomesticProviderPresetIds()]);\n return FEATURED_PROVIDER_ORDER.filter((provider) => providers.has(provider));\n}\n\nexport function sortProvidersForPicker(providers: readonly string[]): string[] {\n const featuredRank = new Map(FEATURED_PROVIDER_ORDER.map((provider, index) => [provider, index] as const));\n return [...providers].sort((a, b) => {\n const aRank = featuredRank.get(a) ?? Number.POSITIVE_INFINITY;\n const bRank = featuredRank.get(b) ?? Number.POSITIVE_INFINITY;\n if (aRank !== bRank) return aRank - bRank;\n const catA = PROVIDER_META[a]?.category ?? 'specialty';\n const catB = PROVIDER_META[b]?.category ?? 'specialty';\n const catOrder: Record<ProviderCategory, number> = {\n common: 0,\n domestic: 1,\n specialty: 2,\n enterprise: 3,\n oauth: 4,\n extension: 5,\n };\n if (catOrder[catA] !== catOrder[catB]) return catOrder[catA] - catOrder[catB];\n return getProviderDisplayName(a).localeCompare(getProviderDisplayName(b), undefined, { sensitivity: 'base' });\n });\n}\n\nexport async function getProviderCatalogViews(): Promise<ProviderCatalogView[]> {\n const pluginRegistry = getProviderRegistry();\n const providers = sortProvidersForPicker([...new Set([...getAllProviders(), ...getDomesticProviderPresetIds()])]);\n const modelCounts = new Map<string, number>();\n for (const model of getAllModels()) {\n modelCounts.set(model.provider, (modelCounts.get(model.provider) ?? 0) + 1);\n }\n\n return Promise.all(\n providers.map(async (provider) => {\n const plugin = pluginRegistry.get(provider);\n const meta = PROVIDER_META[provider];\n const domesticPreset = getDomesticProviderPreset(provider);\n return {\n id: provider,\n name: plugin?.name ?? domesticPreset?.displayName ?? getProviderDisplayName(provider),\n category: plugin ? 'extension' : (meta?.category ?? (domesticPreset ? 'domestic' : 'specialty')),\n supportsOAuth: plugin ? false : (meta?.supportsOAuth ?? false),\n supportsApiKey: plugin ? false : (meta?.supportsApiKey ?? true),\n configured: plugin ? true : await isProviderConfigured(provider),\n onboardingFeatured: FEATURED_PROVIDER_ORDER.includes(provider),\n recommendedModels: getRecommendedModelsForProvider(provider),\n modelCount: modelCounts.get(provider) ?? 0,\n ...(getProviderHint(provider) ? { hint: getProviderHint(provider) } : {}),\n };\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -25,7 +25,7 @@ function resolveSummarizeModel(config, modelRef) {
|
|
|
25
25
|
if (ref) try {
|
|
26
26
|
return resolveModel(ref);
|
|
27
27
|
} catch {}
|
|
28
|
-
for (const candidate of ["openai/gpt-
|
|
28
|
+
for (const candidate of ["openai/gpt-5.6-luna", "google/gemini-3.5-flash"]) try {
|
|
29
29
|
return resolveModel(candidate);
|
|
30
30
|
} catch {}
|
|
31
31
|
return resolveModel(getDefaultModelSync(config));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarize.js","names":[],"sources":["../../../../src/voice/tts/summarize.ts"],"sourcesContent":["import { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\nimport type { Api, Model } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { getDefaultModelSync, resolveModel } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { truncateAtSentenceBoundary } from './sentence-boundary.js';\n\nconst log = createLogger('TTS:Summarize');\n\nconst SUMMARIZE_TIMEOUT_MS = 45_000;\n\nconst SUMMARIZE_SYSTEM_PROMPT = `You are a text summarizer for a text-to-speech system.\nYour task is to condense the given text while preserving the key information and meaning.\n\nRules:\n1. Output ONLY the summarized text, no explanations or meta-commentary\n2. Keep the summary in the same language as the input\n3. Preserve key facts, numbers, names, and conclusions\n4. Use natural, spoken language suitable for audio playback\n5. Remove code blocks, URLs, markdown formatting, and technical syntax where possible\n6. Keep the summary under the specified character limit\n7. End with a complete sentence, never mid-sentence`;\n\nexport interface SummarizeForTtsOptions {\n text: string;\n targetLength: number;\n config?: Config;\n modelRef?: string;\n signal?: AbortSignal;\n}\n\nexport interface SummarizeResult {\n summary: string;\n originalLength: number;\n summaryLength: number;\n wasSummarized: boolean;\n}\n\nfunction resolveSummarizeModel(config: Config | undefined, modelRef?: string): Model<Api> {\n const envRef = process.env.XOPC_TTS_SUMMARIZE_MODEL?.trim();\n const ref = modelRef?.trim() || envRef;\n if (ref) {\n try {\n return resolveModel(ref);\n } catch {\n /* fall through */\n }\n }\n for (const candidate of ['openai/gpt-
|
|
1
|
+
{"version":3,"file":"summarize.js","names":[],"sources":["../../../../src/voice/tts/summarize.ts"],"sourcesContent":["import { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\nimport type { Api, Model } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { getDefaultModelSync, resolveModel } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { truncateAtSentenceBoundary } from './sentence-boundary.js';\n\nconst log = createLogger('TTS:Summarize');\n\nconst SUMMARIZE_TIMEOUT_MS = 45_000;\n\nconst SUMMARIZE_SYSTEM_PROMPT = `You are a text summarizer for a text-to-speech system.\nYour task is to condense the given text while preserving the key information and meaning.\n\nRules:\n1. Output ONLY the summarized text, no explanations or meta-commentary\n2. Keep the summary in the same language as the input\n3. Preserve key facts, numbers, names, and conclusions\n4. Use natural, spoken language suitable for audio playback\n5. Remove code blocks, URLs, markdown formatting, and technical syntax where possible\n6. Keep the summary under the specified character limit\n7. End with a complete sentence, never mid-sentence`;\n\nexport interface SummarizeForTtsOptions {\n text: string;\n targetLength: number;\n config?: Config;\n modelRef?: string;\n signal?: AbortSignal;\n}\n\nexport interface SummarizeResult {\n summary: string;\n originalLength: number;\n summaryLength: number;\n wasSummarized: boolean;\n}\n\nfunction resolveSummarizeModel(config: Config | undefined, modelRef?: string): Model<Api> {\n const envRef = process.env.XOPC_TTS_SUMMARIZE_MODEL?.trim();\n const ref = modelRef?.trim() || envRef;\n if (ref) {\n try {\n return resolveModel(ref);\n } catch {\n /* fall through */\n }\n }\n for (const candidate of ['openai/gpt-5.6-luna', 'google/gemini-3.5-flash']) {\n try {\n return resolveModel(candidate);\n } catch {\n /* next */\n }\n }\n return resolveModel(getDefaultModelSync(config));\n}\n\nfunction fallbackTruncate(text: string, maxLength: number): SummarizeResult {\n const truncated = truncateAtSentenceBoundary(text, maxLength);\n return {\n summary: truncated,\n originalLength: text.length,\n summaryLength: truncated.length,\n wasSummarized: false,\n };\n}\n\n/**\n * Condense text for TTS using a small LLM pass; falls back to sentence-boundary truncation.\n */\nexport async function summarizeForTts(options: SummarizeForTtsOptions): Promise<SummarizeResult> {\n const { text, targetLength, config, modelRef, signal } = options;\n\n if (text.length <= targetLength) {\n return {\n summary: text,\n originalLength: text.length,\n summaryLength: text.length,\n wasSummarized: false,\n };\n }\n\n const startTime = Date.now();\n\n try {\n const model = resolveSummarizeModel(config, modelRef);\n const userPrompt = [\n `Summarize the following text to fit within ${targetLength} characters.`,\n `The summary will be read aloud by a text-to-speech system, so use natural spoken language.`,\n '',\n '---',\n '',\n text,\n ].join('\\n');\n\n const userMsg: UserMessage = {\n role: 'user',\n content: `${SUMMARIZE_SYSTEM_PROMPT}\\n\\n---\\n\\n${userPrompt}`,\n timestamp: Date.now(),\n };\n\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(SUMMARIZE_TIMEOUT_MS)\n : undefined;\n const mergedSignal =\n signal && timeoutSignal && typeof AbortSignal.any === 'function'\n ? AbortSignal.any([signal, timeoutSignal])\n : signal ?? timeoutSignal;\n\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens: Math.min(Math.ceil(targetLength / 2), 4096),\n temperature: 0.3,\n signal: mergedSignal as AbortSignal,\n },\n );\n\n let out = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n out += String((c as { text?: string }).text || '');\n }\n }\n }\n\n const summary = out.trim();\n if (!summary) {\n log.warn('Empty summary returned, falling back to truncation');\n return fallbackTruncate(text, targetLength);\n }\n\n const finalSummary =\n summary.length > targetLength ? truncateAtSentenceBoundary(summary, targetLength) : summary;\n\n log.info(\n {\n originalLength: text.length,\n summaryLength: finalSummary.length,\n durationMs: Date.now() - startTime,\n },\n 'Text summarized for TTS',\n );\n\n return {\n summary: finalSummary,\n originalLength: text.length,\n summaryLength: finalSummary.length,\n wasSummarized: true,\n };\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n log.warn({ errorMessage: em }, 'Summarization failed, falling back to truncation');\n return fallbackTruncate(text, targetLength);\n }\n}\n"],"mappings":";;;;;;gBAI6E;aACxB;AAGrD,MAAM,MAAM,aAAa,gBAAgB;AAEzC,MAAM,uBAAuB;AAE7B,MAAM,0BAA0B;;;;;;;;;;;AA2BhC,SAAS,sBAAsB,QAA4B,UAA+B;CACxF,MAAM,SAAS,QAAQ,IAAI,0BAA0B,MAAM;CAC3D,MAAM,MAAM,UAAU,MAAM,IAAI;AAChC,KAAI,IACF,KAAI;AACF,SAAO,aAAa,IAAI;SAClB;AAIV,MAAK,MAAM,aAAa,CAAC,uBAAuB,0BAA0B,CACxE,KAAI;AACF,SAAO,aAAa,UAAU;SACxB;AAIV,QAAO,aAAa,oBAAoB,OAAO,CAAC;;AAGlD,SAAS,iBAAiB,MAAc,WAAoC;CAC1E,MAAM,YAAY,2BAA2B,MAAM,UAAU;AAC7D,QAAO;EACL,SAAS;EACT,gBAAgB,KAAK;EACrB,eAAe,UAAU;EACzB,eAAe;EAChB;;;;;AAMH,eAAsB,gBAAgB,SAA2D;CAC/F,MAAM,EAAE,MAAM,cAAc,QAAQ,UAAU,WAAW;AAEzD,KAAI,KAAK,UAAU,aACjB,QAAO;EACL,SAAS;EACT,gBAAgB,KAAK;EACrB,eAAe,KAAK;EACpB,eAAe;EAChB;CAGH,MAAM,YAAY,KAAK,KAAK;AAE5B,KAAI;EACF,MAAM,QAAQ,sBAAsB,QAAQ,SAAS;EAUrD,MAAM,UAAuB;GAC3B,MAAM;GACN,SAAS,GAAG,wBAAwB,aAXnB;IACjB,8CAA8C,aAAa;IAC3D;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAIsD;GAC3D,WAAW,KAAK,KAAK;GACtB;EAED,MAAM,gBACJ,OAAO,gBAAgB,eAAe,OAAO,YAAY,YAAY,aACjE,YAAY,QAAQ,qBAAqB,GACzC,KAAA;EACN,MAAM,eACJ,UAAU,iBAAiB,OAAO,YAAY,QAAQ,aAClD,YAAY,IAAI,CAAC,QAAQ,cAAc,CAAC,GACxC,UAAU;EAEhB,MAAM,SAAS,MAAM,SACnB,OACA,EAAE,UAAU,CAAC,QAAQ,EAAE,EACvB;GACE,WAAW,KAAK,IAAI,KAAK,KAAK,eAAe,EAAE,EAAE,KAAK;GACtD,aAAa;GACb,QAAQ;GACT,CACF;EAED,IAAI,MAAM;AACV,MAAI,MAAM,QAAQ,OAAO,QAAQ;QAC1B,MAAM,KAAK,OAAO,QACrB,KAAI,KAAK,OAAO,MAAM,YAAa,EAAwB,SAAS,OAClE,QAAO,OAAQ,EAAwB,QAAQ,GAAG;;EAKxD,MAAM,UAAU,IAAI,MAAM;AAC1B,MAAI,CAAC,SAAS;AACZ,OAAI,KAAK,qDAAqD;AAC9D,UAAO,iBAAiB,MAAM,aAAa;;EAG7C,MAAM,eACJ,QAAQ,SAAS,eAAe,2BAA2B,SAAS,aAAa,GAAG;AAEtF,MAAI,KACF;GACE,gBAAgB,KAAK;GACrB,eAAe,aAAa;GAC5B,YAAY,KAAK,KAAK,GAAG;GAC1B,EACD,0BACD;AAED,SAAO;GACL,SAAS;GACT,gBAAgB,KAAK;GACrB,eAAe,aAAa;GAC5B,eAAe;GAChB;UACM,OAAO;EACd,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,MAAI,KAAK,EAAE,cAAc,IAAI,EAAE,mDAAmD;AAClF,SAAO,iBAAiB,MAAM,aAAa"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.133",
|
|
4
4
|
"description": "Local-first AI system that remembers context, coordinates AI, and sustains long-term progress.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
"node": ">=22.19.0"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@earendil-works/pi-agent-core": "^0.80.
|
|
69
|
-
"@earendil-works/pi-ai": "^0.80.
|
|
70
|
-
"@earendil-works/pi-coding-agent": "^0.80.
|
|
71
|
-
"@earendil-works/pi-tui": "^0.80.
|
|
68
|
+
"@earendil-works/pi-agent-core": "^0.80.7",
|
|
69
|
+
"@earendil-works/pi-ai": "^0.80.7",
|
|
70
|
+
"@earendil-works/pi-coding-agent": "^0.80.7",
|
|
71
|
+
"@earendil-works/pi-tui": "^0.80.7",
|
|
72
72
|
"@grammyjs/runner": "^2.0.3",
|
|
73
73
|
"@hono/node-server": "^2.0.4",
|
|
74
74
|
"@inquirer/prompts": "^8.5.0",
|