Files

1 line
24 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "bitan-del/gods-eye", "files": {"apps/desktop/src/renderer/utils/model/modelPlatforms.ts": {"f8e9982f62ba0222b57d1a8ea2ccf45ca2da9e27": {"t": 1785595484.6008902, "n": 9335, "h": "7fa1d387b9144871", "c": "/**\n * @license\n * Copyright 2025 Gods Eye (gods-eye.org)\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * 模型平台配置模块\n * Model Platform Configuration Module\n *\n * 集中管理所有模型平台的配置信息,便于扩展和维护\n * Centralized management of all model platform configurations for extensibility and maintainability\n */\n\n// Provider Logo imports\nimport GeminiLogo from '@/renderer/assets/logos/ai-major/gemini.svg';\nimport OpenAILogo from '@/renderer/assets/logos/ai-major/openai.svg';\nimport AnthropicLogo from '@/renderer/assets/logos/ai-major/anthropic.svg';\nimport BedrockLogo from '@/renderer/assets/logos/ai-cloud/bedrock.svg';\nimport DeepSeekLogo from '@/renderer/assets/logos/ai-major/deepseek.svg';\nimport OpenRouterLogo from '@/renderer/assets/logos/ai-cloud/openrouter.svg';\nimport SiliconFlowLogo from '@/renderer/assets/logos/ai-cloud/siliconflow.png';\nimport QwenLogo from '@/renderer/assets/logos/ai-china/qwen.svg';\nimport KimiLogo from '@/renderer/assets/logos/ai-china/kimi.svg';\nimport ZhipuLogo from '@/renderer/assets/logos/ai-china/zhipu.svg';\nimport XaiLogo from '@/renderer/assets/logos/ai-major/xai.svg';\nimport VolcengineLogo from '@/renderer/assets/logos/ai-china/volcengine.svg';\nimport BaiduLogo from '@/renderer/assets/logos/ai-china/baidu.svg';\nimport TencentLogo from '@/renderer/assets/logos/ai-china/tencent.svg';\nimport LingyiLogo from '@/renderer/assets/logos/ai-china/lingyiwanwu.svg';\nimport PoeLogo from '@/renderer/assets/logos/ai-cloud/poe.svg';\nimport ModelScopeLogo from '@/renderer/assets/logos/ai-cloud/modelscope.svg';\nimport InfiniAILogo from '@/renderer/assets/logos/ai-cloud/infiniai.svg';\nimport CtyunLogo from '@/renderer/assets/logos/ai-cloud/ctyun.svg';\nimport StepFunLogo from '@/renderer/assets/logos/ai-china/stepfun.svg';\nimport MiniMaxLogo from '@/renderer/assets/logos/ai-china/minimax.png';\nimport NewApiLogo from '@/renderer/assets/logos/ai-cloud/newapi.svg';\nimport NovitaLogo from '@/renderer/assets/logos/ai-cloud/novita.svg';\nimport PPIOLogo from '@/renderer/assets/logos/ai-cloud/ppio.svg';\n\n/**\n * 平台类型\n * Platform type\n */\nexport type PlatformType = 'gemini' | 'gemini-vertex-ai' | 'anthropic' | 'custom' | 'new-api' | 'bedrock';\n\n/**\n * 模型平台配置接口\n * Model Platform Configuration Interface\n */\nexport interface PlatformConfig {\n /** 平台名称 / Platform name */\n name: string;\n /** 平台值(用于表单) / Platform value (for form) */\n value: string;\n /** Logo 路径 / Logo path */\n logo: string | null;\n /** 平台标识 / Platform identifier */\n platform: PlatformType;\n /** Base URL(预设供应商使用) / Base URL (for preset providers) */\n baseUrl?: string;\n /** 国际化 key(可选,用于需要翻译的平台名称) / i18n key (optional, for platform names that need translation) */\n i18nKey?: string;\n}\n\n/**\n * 模型平台选项列表\n * Model Platform options list\n *\n * 顺序:\n * 1. Gemini (官方)\n * 2. Gemini Vertex AI\n * 3. 自定义(需要用户输入 base url)\n * 4+ 预设供应商\n */\nexport const MODEL_PLATFORMS: PlatformConfig[] = [\n // 自定义选项(需要用户输入 base url)/ Custom option (requires user to input base url)\n { name: 'Custom', value: 'custom', logo: null, platform: 'custom', i18nKey: 'settings.platformCustom' },\n\n // New API 多模型网关 / New API multi-model gateway\n { name: 'New API', value: 'new-api', logo: NewApiLogo, platform: 'new-api', i18nKey: 'settings.platformNewApi' },\n\n // 官方 Gemini 平台\n {\n name: 'Gemini',\n value: 'gemini',\n logo: GeminiLogo,\n platform: 'gemini',\n baseUrl: 'https://generativelanguage.googleapis.com',\n },\n { name: 'Gemini (Vertex AI)', value: 'gemini-vertex-ai', logo: GeminiLogo, platform: 'gemini-vertex-ai' },\n\n // 预设供应商(按字母顺序排列)\n { name: 'OpenAI', value: 'OpenAI', logo: OpenAILogo, platform: 'custom', baseUrl: 'https://api.openai.com/v1' },\n {\n name: 'Anthropic',\n value: 'Anthropic',\n logo: AnthropicLogo,\n platform: 'anthropic',\n baseUrl: 'https://api.anthropic.com',\n },\n {\n name: 'AWS Bedrock',\n value: 'AWS-Bedrock',\n logo: BedrockLogo,\n platform: 'bedrock',\n i18nKey: 'settings.platformBedrock',\n },\n {\n name: 'DeepSeek',\n value: 'DeepSeek',\n logo: DeepSeekLogo,\n platform: 'custom',\n baseUrl: 'https://api.deepseek.com/v1',\n },\n { name: 'MiniMax', value: 'MiniMax', logo: MiniMaxLogo, platform: 'custom', baseUrl: 'https://api.minimaxi.com/v1' },\n {\n name: 'Novita',\n value: 'Novita',\n logo: NovitaLogo,\n platform: 'custom',\n baseUrl: 'https://api.novita.ai/openai/v1',\n },\n {\n name: 'OpenRouter',\n value: 'OpenRouter',\n logo: OpenRouterLogo,\n platform: 'custom',\n baseUrl: 'https://openrouter.ai/api/v1',\n },\n {\n name: 'Dashscope',\n value: 'Dashscope',\n logo: QwenLogo,\n platform: 'custom',\n baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',\n },\n {\n name: 'Dashscope Coding Plan',\n value: 'Dashscope-Coding',\n logo: QwenLogo,\n platform: 'custom',\n baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',\n },\n {\n name: 'SiliconFlow-CN',\n value: 'SiliconFlow-CN',\n logo: SiliconFlowLogo,\n platform: 'custom',\n baseUrl: 'https://api.siliconflow.cn/v1',\n },\n {\n name: 'SiliconFlow',\n value: 'SiliconFlow',\n logo: SiliconFlowLogo,\n platform: 'custom',\n baseUrl: 'https://api.siliconflow.com/v1',\n },\n {\n name: 'Zhipu',\n value: 'Zhipu',\n logo: ZhipuLogo,\n platform: 'custom',\n baseUrl: 'https://open.bigmodel.cn/api/paas/v4',\n },\n {\n name: 'Moonshot (China)',\n value: 'Moonshot',\n logo: KimiLogo,\n platform: 'custom',\n baseUrl: 'https://api.moonshot.cn/v1',\n },\n {\n name: 'Moonshot (Global)',\n value: 'Moonshot-Global',\n logo: KimiLogo,\n platform: 'custom',\n baseUrl: 'https://api.moonshot.ai/v1',\n },\n { name: 'xAI', value: 'xAI', logo: XaiLogo, platform: 'custom', baseUrl: 'https://api.x.ai/v1' },\n {\n name: 'Ark',\n value: 'Ark',\n logo: VolcengineLogo,\n platform: 'custom',\n baseUrl: 'https://ark.cn-beijing.volces.com/api/v3',\n },\n {\n name: 'Qianfan',\n value: 'Qianfan',\n logo: BaiduLogo,\n platform: 'custom',\n baseUrl: 'https://qianfan.baidubce.com/v2',\n },\n {\n name: 'Hunyuan',\n value: 'Hunyuan',\n logo: TencentLogo,\n platform: 'custom',\n baseUrl: 'https://api.hunyuan.cloud.tencent.com/v1',\n },\n { name: 'Lingyi', value: 'Lingyi', logo: LingyiLogo, platform: 'custom', baseUrl: 'https://api.lingyiwanwu.com/v1' },\n { name: 'Poe', value: 'Poe', logo: PoeLogo, platform: 'custom', baseUrl: 'https://api.poe.com/v1' },\n { name: 'PPIO', value: 'PPIO', logo: PPIOLogo, platform: 'custom', baseUrl: 'https://api.ppinfra.com/v3/openai' },\n {\n name: 'ModelScope',\n value: 'ModelScope',\n logo: ModelScopeLogo,\n platform: 'custom',\n baseUrl: 'https://api-inference.modelscope.cn/v1',\n },\n {\n name: 'InfiniAI',\n value: 'InfiniAI',\n logo: InfiniAILogo,\n platform: 'custom',\n baseUrl: 'https://cloud.infini-ai.com/maas/v1',\n },\n { name: 'Ctyun', value: 'Ctyun', logo: CtyunLogo, platform: 'custom', baseUrl: 'https://wishub-x1.ctyun.cn/v1' },\n { name: 'StepFun', value: 'StepFun', logo: StepFunLogo, platform: 'custom', baseUrl: 'https://api.stepfun.com/v1' },\n];\n\n/**\n * New API 协议选项\n * New API protocol options for per-model protocol configuration\n */\nexport const NEW_API_PROTOCOL_OPTIONS = [\n { label: 'OpenAI', value: 'openai' },\n { label: 'Gemini', value: 'gemini' },\n { label: 'Anthropic', value: 'anthropic' },\n];\n\n/**\n * 根据模型名称自动推断 New API 协议类型\n * Auto-detect New API protocol type based on model name\n */\nexport const detectNewApiProtocol = (modelName: string): string => {\n const name = modelName.toLowerCase();\n if (name.startsWith('claude') || name.startsWith('anthropic')) return 'anthropic';\n if (name.startsWith('gemini') || name.startsWith('models/gemini')) return 'gemini';\n // Default to openai (covers gpt, deepseek, qwen, o1, o3, etc.)\n return 'openai';\n};\n\n// ============ 工具函数 / Utility Functions ============\n\n/**\n * 根据 value 获取平台配置\n * Get platform config by value\n */\nexport const getPlatformByValue = (value: string): PlatformConfig | undefined => {\n return MODEL_PLATFORMS.find((p) => p.value === value);\n};\n\n/**\n * 获取所有预设供应商(有 baseUrl 的)\n * Get all preset providers (with baseUrl)\n */\nexport const getPresetProviders = (): PlatformConfig[] => {\n return MODEL_PLATFORMS.filter((p) => p.baseUrl);\n};\n\n/**\n * 获取官方 Gemini 平台\n * Get official Gemini platforms\n */\nexport const getGeminiPlatforms = (): PlatformConfig[] => {\n return MODEL_PLATFORMS.filter((p) => p.platform === 'gemini' || p.platform === 'gemini-vertex-ai');\n};\n\n/**\n * 检查平台是否为 Gemini 类型\n * Check if platform is Gemini type\n */\nexport const isGeminiPlatform = (platform: PlatformType): boolean => {\n return platform === 'gemini' || platform === 'gemini-vertex-ai';\n};\n\n/**\n * 检查是否为自定义选项(无预设 baseUrl)\n * Check if it's custom option (no preset baseUrl)\n */\nexport const isCustomOption = (value: string): boolean => {\n const platform = getPlatformByValue(value);\n return value === 'custom' && !platform?.baseUrl;\n};\n\n// Re-export from common for renderer convenience\nexport { isNewApiPlatform } from '@/common/utils/platformConstants';\n\n/**\n * 根据名称搜索平台(不区分大小写)\n * Search platforms by name (case-insensitive)\n */\nexport const searchPlatformsByName = (keyword: string): PlatformConfig[] => {\n const lowerKeyword = keyword.toLowerCase();\n return MODEL_PLATFORMS.filter((p) => p.name.toLowerCase().includes(lowerKeyword));\n};\n"}}, "extensions/minimax/web-search-contract-api.ts": {"f8e9982f62ba0222b57d1a8ea2ccf45ca2da9e27": {"t": 1785674021.1499662, "n": 1062, "h": "992bd1bf217d34c2", "c": "import {\n createWebSearchProviderContractFields,\n type WebSearchProviderPlugin,\n} from \"godseye/plugin-sdk/provider-web-search-config-contract\";\n\nconst MINIMAX_CODING_PLAN_ENV_VARS = [\"MINIMAX_CODE_PLAN_KEY\", \"MINIMAX_CODING_API_KEY\"] as const;\n\nexport function createMiniMaxWebSearchProvider(): WebSearchProviderPlugin {\n const credentialPath = \"plugins.entries.minimax.config.webSearch.apiKey\";\n\n return {\n id: \"minimax\",\n label: \"MiniMax Search\",\n hint: \"Structured results via MiniMax Coding Plan search API\",\n credentialLabel: \"MiniMax Coding Plan key\",\n envVars: [...MINIMAX_CODING_PLAN_ENV_VARS],\n placeholder: \"sk-cp-...\",\n signupUrl: \"https://platform.minimax.io/user-center/basic-information/interface-key\",\n docsUrl: \"https://docs.openclaw.ai/tools/minimax-search\",\n autoDetectOrder: 15,\n credentialPath,\n ...createWebSearchProviderContractFields({\n credentialPath,\n searchCredential: { type: \"top-level\" },\n configuredCredential: { pluginId: \"minimax\" },\n }),\n createTool: () => null,\n };\n}\n"}}, "extensions/minimax/src/minimax-web-search-provider.ts": {"f8e9982f62ba0222b57d1a8ea2ccf45ca2da9e27": {"t": 1785674023.276232, "n": 9972, "h": "f57772513fb75838", "c": "import { Type } from \"@sinclair/typebox\";\nimport {\n DEFAULT_SEARCH_COUNT,\n MAX_SEARCH_COUNT,\n buildSearchCacheKey,\n formatCliCommand,\n mergeScopedSearchConfig,\n readCachedSearchPayload,\n readConfiguredSecretString,\n readNumberParam,\n readProviderEnvValue,\n readStringParam,\n resolveProviderWebSearchPluginConfig,\n resolveSearchCacheTtlMs,\n resolveSearchCount,\n resolveSearchTimeoutSeconds,\n resolveSiteName,\n setProviderWebSearchPluginConfigValue,\n setTopLevelCredentialValue,\n withTrustedWebSearchEndpoint,\n wrapWebContent,\n writeCachedSearchPayload,\n type SearchConfigRecord,\n type WebSearchProviderPlugin,\n type WebSearchProviderToolDefinition,\n} from \"godseye/plugin-sdk/provider-web-search\";\nimport { normalizeOptionalString } from \"godseye/plugin-sdk/text-runtime\";\n\nconst MINIMAX_SEARCH_ENDPOINT_GLOBAL = \"https://api.minimax.io/v1/coding_plan/search\";\nconst MINIMAX_SEARCH_ENDPOINT_CN = \"https://api.minimaxi.com/v1/coding_plan/search\";\nconst MINIMAX_CODING_PLAN_ENV_VARS = [\"MINIMAX_CODE_PLAN_KEY\", \"MINIMAX_CODING_API_KEY\"] as const;\n\ntype MiniMaxSearchResult = {\n title?: string;\n link?: string;\n snippet?: string;\n date?: string;\n};\n\ntype MiniMaxRelatedSearch = {\n query?: string;\n};\n\ntype MiniMaxSearchResponse = {\n organic?: MiniMaxSearchResult[];\n related_searches?: MiniMaxRelatedSearch[];\n base_resp?: {\n status_code?: number;\n status_msg?: string;\n };\n};\n\nfunction resolveMiniMaxApiKey(searchConfig?: SearchConfigRecord): string | undefined {\n return (\n readConfiguredSecretString(searchConfig?.apiKey, \"tools.web.search.apiKey\") ??\n readProviderEnvValue([...MINIMAX_CODING_PLAN_ENV_VARS, \"MINIMAX_API_KEY\"])\n );\n}\n\nfunction isMiniMaxCnHost(value: string | undefined): boolean {\n const trimmed = normalizeOptionalString(value);\n if (!trimmed) {\n return false;\n }\n try {\n return new URL(trimmed).hostname.endsWith(\"minimaxi.com\");\n } catch {\n return trimmed.includes(\"minimaxi.com\");\n }\n}\n\nfunction resolveMiniMaxRegion(\n searchConfig?: SearchConfigRecord,\n config?: Record<string, unknown>,\n): \"cn\" | \"global\" {\n // 1. Explicit region in search config takes priority\n const minimax =\n typeof searchConfig?.minimax === \"object\" &&\n searchConfig.minimax !== null &&\n !Array.isArray(searchConfig.minimax)\n ? (searchConfig.minimax as Record<string, unknown>)\n : undefined;\n const configuredRegion =\n typeof minimax?.region === \"string\" ? normalizeOptionalString(minimax.region) : undefined;\n if (configuredRegion) {\n return configuredRegion === \"cn\" ? \"cn\" : \"global\";\n }\n\n // 2. Infer from the shared MiniMax host override.\n if (isMiniMaxCnHost(process.env.MINIMAX_API_HOST)) {\n return \"cn\";\n }\n\n // 3. Infer from model provider base URL (set by CN onboarding)\n const models = config?.models as Record<string, unknown> | undefined;\n const providers = models?.providers as Record<string, unknown> | undefined;\n const minimaxProvider = providers?.minimax as Record<string, unknown> | undefined;\n const portalProvider = providers?.[\"minimax-portal\"] as Record<string, unknown> | undefined;\n const baseUrl = typeof minimaxProvider?.baseUrl === \"string\" ? minimaxProvider.baseUrl : \"\";\n const portalBaseUrl = typeof portalProvider?.baseUrl === \"string\" ? portalProvider.baseUrl : \"\";\n if (isMiniMaxCnHost(baseUrl) || isMiniMaxCnHost(portalBaseUrl)) {\n return \"cn\";\n }\n\n return \"global\";\n}\n\nfunction resolveMiniMaxEndpoint(\n searchConfig?: SearchConfigRecord,\n config?: Record<string, unknown>,\n): string {\n return resolveMiniMaxRegion(searchConfig, config) === \"cn\"\n ? MINIMAX_SEARCH_ENDPOINT_CN\n : MINIMAX_SEARCH_ENDPOINT_GLOBAL;\n}\n\nasync function runMiniMaxSearch(params: {\n query: string;\n count: number;\n apiKey: string;\n endpoint: string;\n timeoutSeconds: number;\n}): Promise<{\n results: Array<Record<string, unknown>>;\n relatedSearches?: string[];\n}> {\n return withTrustedWebSearchEndpoint(\n {\n url: params.endpoint,\n timeoutSeconds: params.timeoutSeconds,\n init: {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${params.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify({ q: params.query }),\n },\n },\n async (res) => {\n if (!res.ok) {\n const detail = await res.text();\n throw new Error(`MiniMax Search API error (${res.status}): ${detail || res.statusText}`);\n }\n\n const data = (await res.json()) as MiniMaxSearchResponse;\n\n if (data.base_resp?.status_code && data.base_resp.status_code !== 0) {\n throw new Error(\n `MiniMax Search API error (${data.base_resp.status_code}): ${data.base_resp.status_msg || \"unknown error\"}`,\n );\n }\n\n const organic = Array.isArray(data.organic) ? data.organic : [];\n const results = organic.slice(0, params.count).map((entry) => {\n const title = entry.title ?? \"\";\n const url = entry.link ?? \"\";\n const snippet = entry.snippet ?? \"\";\n return {\n title: title ? wrapWebContent(title, \"web_search\") : \"\",\n url,\n description: snippet ? wrapWebContent(snippet, \"web_search\") : \"\",\n published: entry.date || undefined,\n siteName: resolveSiteName(url) || undefined,\n };\n });\n\n const relatedSearches = Array.isArray(data.related_searches)\n ? data.related_searches\n .map((r) => r.query)\n .filter((q): q is string => typeof q === \"string\" && q.length > 0)\n .map((q) => wrapWebContent(q, \"web_search\"))\n : undefined;\n\n return { results, relatedSearches };\n },\n );\n}\n\nconst MiniMaxSearchSchema = Type.Object({\n query: Type.String({ description: \"Search query string.\" }),\n count: Type.Optional(\n Type.Number({\n description: \"Number of results to return (1-10).\",\n minimum: 1,\n maximum: MAX_SEARCH_COUNT,\n }),\n ),\n});\n\nfunction missingMiniMaxKeyPayload() {\n return {\n error: \"missing_minimax_api_key\",\n message: `web_search (minimax) needs a MiniMax Coding Plan key. Run \\`${formatCliCommand(\"openclaw configure --section web\")}\\` to store it, or set MINIMAX_CODE_PLAN_KEY, MINIMAX_CODING_API_KEY, or MINIMAX_API_KEY in the Gateway environment.`,\n docs: \"https://docs.openclaw.ai/tools/web\",\n };\n}\n\nfunction createMiniMaxToolDefinition(\n searchConfig?: SearchConfigRecord,\n config?: Record<string, unknown>,\n): WebSearchProviderToolDefinition {\n return {\n description:\n \"Search the web using MiniMax Search API. Returns titles, URLs, snippets, and related search suggestions.\",\n parameters: MiniMaxSearchSchema,\n execute: async (args) => {\n const apiKey = resolveMiniMaxApiKey(searchConfig);\n if (!apiKey) {\n return missingMiniMaxKeyPayload();\n }\n\n const params = args;\n const query = readStringParam(params, \"query\", { required: true });\n const count =\n readNumberParam(params, \"count\", { integer: true }) ??\n searchConfig?.maxResults ??\n undefined;\n\n const resolvedCount = resolveSearchCount(count, DEFAULT_SEARCH_COUNT);\n const endpoint = resolveMiniMaxEndpoint(searchConfig, config);\n\n const cacheKey = buildSearchCacheKey([\"minimax\", endpoint, query, resolvedCount]);\n const cached = readCachedSearchPayload(cacheKey);\n if (cached) {\n return cached;\n }\n\n const start = Date.now();\n const timeoutSeconds = resolveSearchTimeoutSeconds(searchConfig);\n const cacheTtlMs = resolveSearchCacheTtlMs(searchConfig);\n\n const { results, relatedSearches } = await runMiniMaxSearch({\n query,\n count: resolvedCount,\n apiKey,\n endpoint,\n timeoutSeconds,\n });\n\n const payload: Record<string, unknown> = {\n query,\n provider: \"minimax\",\n count: results.length,\n tookMs: Date.now() - start,\n externalContent: {\n untrusted: true,\n source: \"web_search\",\n provider: \"minimax\",\n wrapped: true,\n },\n results,\n };\n\n if (relatedSearches && relatedSearches.length > 0) {\n payload.relatedSearches = relatedSearches;\n }\n\n writeCachedSearchPayload(cacheKey, payload, cacheTtlMs);\n return payload;\n },\n };\n}\n\nexport const __testing = {\n MINIMAX_SEARCH_ENDPOINT_GLOBAL,\n MINIMAX_SEARCH_ENDPOINT_CN,\n resolveMiniMaxApiKey,\n resolveMiniMaxEndpoint,\n resolveMiniMaxRegion,\n} as const;\n\nexport function createMiniMaxWebSearchProvider(): WebSearchProviderPlugin {\n return {\n id: \"minimax\",\n label: \"MiniMax Search\",\n hint: \"Structured results via MiniMax Coding Plan search API\",\n credentialLabel: \"MiniMax Coding Plan key\",\n envVars: [...MINIMAX_CODING_PLAN_ENV_VARS],\n placeholder: \"sk-cp-...\",\n signupUrl: \"https://platform.minimax.io/user-center/basic-information/interface-key\",\n docsUrl: \"https://docs.openclaw.ai/tools/minimax-search\",\n autoDetectOrder: 15,\n credentialPath: \"plugins.entries.minimax.config.webSearch.apiKey\",\n inactiveSecretPaths: [\"plugins.entries.minimax.config.webSearch.apiKey\"],\n getCredentialValue: (searchConfig) => searchConfig?.apiKey,\n setCredentialValue: setTopLevelCredentialValue,\n getConfiguredCredentialValue: (config) =>\n resolveProviderWebSearchPluginConfig(config, \"minimax\")?.apiKey,\n setConfiguredCredentialValue: (configTarget, value) => {\n setProviderWebSearchPluginConfigValue(configTarget, \"minimax\", \"apiKey\", value);\n },\n createTool: (ctx) =>\n createMiniMaxToolDefinition(\n mergeScopedSearchConfig(\n ctx.searchConfig as SearchConfigRecord | undefined,\n \"minimax\",\n resolveProviderWebSearchPluginConfig(ctx.config, \"minimax\"),\n { mirrorApiKeyToTopLevel: true },\n ) as SearchConfigRecord | undefined,\n ctx.config as Record<string, unknown> | undefined,\n ),\n };\n}\n"}}, "extensions/together/godseye.plugin.json": {"f8e9982f62ba0222b57d1a8ea2ccf45ca2da9e27": {"t": 1785674487.6677961, "n": 765, "h": "42a527b6f7207d64", "c": "{\n \"id\": \"together\",\n \"enabledByDefault\": true,\n \"providers\": [\"together\"],\n \"providerAuthEnvVars\": {\n \"together\": [\"TOGETHER_API_KEY\"]\n },\n \"providerAuthChoices\": [\n {\n \"provider\": \"together\",\n \"method\": \"api-key\",\n \"choiceId\": \"together-api-key\",\n \"choiceLabel\": \"Together AI API key\",\n \"groupId\": \"together\",\n \"groupLabel\": \"Together AI\",\n \"groupHint\": \"API key\",\n \"optionKey\": \"togetherApiKey\",\n \"cliFlag\": \"--together-api-key\",\n \"cliOption\": \"--together-api-key <key>\",\n \"cliDescription\": \"Together AI API key\"\n }\n ],\n \"contracts\": {\n \"videoGenerationProviders\": [\"together\"]\n },\n \"configSchema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {}\n }\n}\n"}}, "docs/providers/mistral.md": {"f8e9982f62ba0222b57d1a8ea2ccf45ca2da9e27": {"t": 1785674507.6019387, "n": 1381, "h": "940a53cfa0858788", "c": "---\nsummary: \"Use Mistral models and Voxtral transcription with Gods Eye\"\nread_when:\n - You want to use Mistral models in Gods Eye\n - You need Mistral API key onboarding and model refs\ntitle: \"Mistral\"\n---\n\n# Mistral\n\nGods Eye supports Mistral for both text/image model routing (`mistral/...`) and\naudio transcription via Voxtral in media understanding.\nMistral can also be used for memory embeddings (`memorySearch.provider = \"mistral\"`).\n\n## CLI setup\n\n```bash\ngodseye onboard --auth-choice mistral-api-key\n# or non-interactive\ngodseye onboard --mistral-api-key \"$MISTRAL_API_KEY\"\n```\n\n## Config snippet (LLM provider)\n\n```json5\n{\n env: { MISTRAL_API_KEY: \"sk-...\" },\n agents: { defaults: { model: { primary: \"mistral/mistral-large-latest\" } } },\n}\n```\n\n## Config snippet (audio transcription with Voxtral)\n\n```json5\n{\n tools: {\n media: {\n audio: {\n enabled: true,\n models: [{ provider: \"mistral\", model: \"voxtral-mini-latest\" }],\n },\n },\n },\n}\n```\n\n## Notes\n\n- Mistral auth uses `MISTRAL_API_KEY`.\n- Provider base URL defaults to `https://api.mistral.ai/v1`.\n- Onboarding default model is `mistral/mistral-large-latest`.\n- Media-understanding default audio model for Mistral is `voxtral-mini-latest`.\n- Media transcription path uses `/v1/audio/transcriptions`.\n- Memory embeddings path uses `/v1/embeddings` (default model: `mistral-embed`).\n"}}}}