From 82132d479a195f82a62de9521b3491c718e0a6f2 Mon Sep 17 00:00:00 2001 From: SuYao Date: Mon, 27 Oct 2025 13:30:23 +0800 Subject: [PATCH] feat: add huggingface provider (#10966) * Refactor code structure for improved readability and maintainability * fix(i18n): Auto update translations for PR #10966 * fix: add empty array for huggingface models in SYSTEM_MODELS * feat: integrate HuggingFace provider and enhance reasoning options * fix: remove debug console logs from provider options functions --------- Co-authored-by: GitHub Action --- ...sdk-huggingface-npm-0.0.4-8080836bc1.patch | 131 ++++++++++++++++++ package.json | 1 + packages/aiCore/src/core/providers/schemas.ts | 10 +- .../src/aiCore/plugins/telemetryPlugin.ts | 22 +-- .../aiCore/provider/providerInitialization.ts | 8 ++ src/renderer/src/aiCore/utils/options.ts | 4 +- src/renderer/src/aiCore/utils/reasoning.ts | 21 ++- .../assets/images/providers/huggingface.webp | Bin 0 -> 27358 bytes src/renderer/src/config/models/default.ts | 3 +- src/renderer/src/config/providers.ts | 25 +++- src/renderer/src/i18n/label.ts | 4 +- src/renderer/src/i18n/locales/en-us.json | 2 + src/renderer/src/i18n/locales/zh-cn.json | 2 + src/renderer/src/i18n/locales/zh-tw.json | 2 + src/renderer/src/i18n/translate/de-de.json | 2 + src/renderer/src/i18n/translate/el-gr.json | 2 + src/renderer/src/i18n/translate/es-es.json | 2 + src/renderer/src/i18n/translate/fr-fr.json | 2 + src/renderer/src/i18n/translate/ja-jp.json | 2 + src/renderer/src/i18n/translate/pt-pt.json | 2 + src/renderer/src/i18n/translate/ru-ru.json | 2 + src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/migrate.ts | 9 ++ src/renderer/src/types/provider.ts | 3 +- yarn.lock | 27 ++++ 25 files changed, 266 insertions(+), 24 deletions(-) create mode 100644 .yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch create mode 100644 src/renderer/src/assets/images/providers/huggingface.webp diff --git a/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch b/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch new file mode 100644 index 0000000000..7aeb4ea9cf --- /dev/null +++ b/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch @@ -0,0 +1,131 @@ +diff --git a/dist/index.mjs b/dist/index.mjs +index b3f018730a93639aad7c203f15fb1aeb766c73f4..ade2a43d66e9184799d072153df61ef7be4ea110 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -296,7 +296,14 @@ var HuggingFaceResponsesLanguageModel = class { + metadata: huggingfaceOptions == null ? void 0 : huggingfaceOptions.metadata, + instructions: huggingfaceOptions == null ? void 0 : huggingfaceOptions.instructions, + ...preparedTools && { tools: preparedTools }, +- ...preparedToolChoice && { tool_choice: preparedToolChoice } ++ ...preparedToolChoice && { tool_choice: preparedToolChoice }, ++ ...(huggingfaceOptions?.reasoningEffort != null && { ++ reasoning: { ++ ...(huggingfaceOptions?.reasoningEffort != null && { ++ effort: huggingfaceOptions.reasoningEffort, ++ }), ++ }, ++ }), + }; + return { args: baseArgs, warnings }; + } +@@ -365,6 +372,20 @@ var HuggingFaceResponsesLanguageModel = class { + } + break; + } ++ case 'reasoning': { ++ for (const contentPart of part.content) { ++ content.push({ ++ type: 'reasoning', ++ text: contentPart.text, ++ providerMetadata: { ++ huggingface: { ++ itemId: part.id, ++ }, ++ }, ++ }); ++ } ++ break; ++ } + case "mcp_call": { + content.push({ + type: "tool-call", +@@ -519,6 +540,11 @@ var HuggingFaceResponsesLanguageModel = class { + id: value.item.call_id, + toolName: value.item.name + }); ++ } else if (value.item.type === 'reasoning') { ++ controller.enqueue({ ++ type: 'reasoning-start', ++ id: value.item.id, ++ }); + } + return; + } +@@ -570,6 +596,22 @@ var HuggingFaceResponsesLanguageModel = class { + }); + return; + } ++ if (isReasoningDeltaChunk(value)) { ++ controller.enqueue({ ++ type: 'reasoning-delta', ++ id: value.item_id, ++ delta: value.delta, ++ }); ++ return; ++ } ++ ++ if (isReasoningEndChunk(value)) { ++ controller.enqueue({ ++ type: 'reasoning-end', ++ id: value.item_id, ++ }); ++ return; ++ } + }, + flush(controller) { + controller.enqueue({ +@@ -593,7 +635,8 @@ var HuggingFaceResponsesLanguageModel = class { + var huggingfaceResponsesProviderOptionsSchema = z2.object({ + metadata: z2.record(z2.string(), z2.string()).optional(), + instructions: z2.string().optional(), +- strictJsonSchema: z2.boolean().optional() ++ strictJsonSchema: z2.boolean().optional(), ++ reasoningEffort: z2.string().optional(), + }); + var huggingfaceResponsesResponseSchema = z2.object({ + id: z2.string(), +@@ -727,12 +770,31 @@ var responseCreatedChunkSchema = z2.object({ + model: z2.string() + }) + }); ++var reasoningTextDeltaChunkSchema = z2.object({ ++ type: z2.literal('response.reasoning_text.delta'), ++ item_id: z2.string(), ++ output_index: z2.number(), ++ content_index: z2.number(), ++ delta: z2.string(), ++ sequence_number: z2.number(), ++}); ++ ++var reasoningTextEndChunkSchema = z2.object({ ++ type: z2.literal('response.reasoning_text.done'), ++ item_id: z2.string(), ++ output_index: z2.number(), ++ content_index: z2.number(), ++ text: z2.string(), ++ sequence_number: z2.number(), ++}); + var huggingfaceResponsesChunkSchema = z2.union([ + responseOutputItemAddedSchema, + responseOutputItemDoneSchema, + textDeltaChunkSchema, + responseCompletedChunkSchema, + responseCreatedChunkSchema, ++ reasoningTextDeltaChunkSchema, ++ reasoningTextEndChunkSchema, + z2.object({ type: z2.string() }).loose() + // fallback for unknown chunks + ]); +@@ -751,6 +813,12 @@ function isResponseCompletedChunk(chunk) { + function isResponseCreatedChunk(chunk) { + return chunk.type === "response.created"; + } ++function isReasoningDeltaChunk(chunk) { ++ return chunk.type === 'response.reasoning_text.delta'; ++} ++function isReasoningEndChunk(chunk) { ++ return chunk.type === 'response.reasoning_text.done'; ++} + + // src/huggingface-provider.ts + function createHuggingFace(options = {}) { diff --git a/package.json b/package.json index d846f69d3a..8da959d182 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "@agentic/tavily": "^7.3.3", "@ai-sdk/amazon-bedrock": "^3.0.35", "@ai-sdk/google-vertex": "^3.0.40", + "@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.4#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch", "@ai-sdk/mistral": "^2.0.19", "@ai-sdk/perplexity": "^2.0.13", "@ant-design/v5-patch-for-react-19": "^1.0.3", diff --git a/packages/aiCore/src/core/providers/schemas.ts b/packages/aiCore/src/core/providers/schemas.ts index f5a8b60a29..0d507d5cc6 100644 --- a/packages/aiCore/src/core/providers/schemas.ts +++ b/packages/aiCore/src/core/providers/schemas.ts @@ -7,6 +7,7 @@ import { createAzure } from '@ai-sdk/azure' import { type AzureOpenAIProviderSettings } from '@ai-sdk/azure' import { createDeepSeek } from '@ai-sdk/deepseek' import { createGoogleGenerativeAI } from '@ai-sdk/google' +import { createHuggingFace } from '@ai-sdk/huggingface' import { createOpenAI, type OpenAIProviderSettings } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { LanguageModelV2 } from '@ai-sdk/provider' @@ -28,7 +29,8 @@ export const baseProviderIds = [ 'azure', 'azure-responses', 'deepseek', - 'openrouter' + 'openrouter', + 'huggingface' ] as const /** @@ -132,6 +134,12 @@ export const baseProviders = [ name: 'OpenRouter', creator: createOpenRouter, supportsImageGeneration: true + }, + { + id: 'huggingface', + name: 'HuggingFace', + creator: createHuggingFace, + supportsImageGeneration: true } ] as const satisfies BaseProvider[] diff --git a/src/renderer/src/aiCore/plugins/telemetryPlugin.ts b/src/renderer/src/aiCore/plugins/telemetryPlugin.ts index 75bf6e116c..0f06091c5a 100644 --- a/src/renderer/src/aiCore/plugins/telemetryPlugin.ts +++ b/src/renderer/src/aiCore/plugins/telemetryPlugin.ts @@ -49,7 +49,7 @@ class AdapterTracer { this.cachedParentContext = undefined } - logger.info('AdapterTracer created with parent context info', { + logger.debug('AdapterTracer created with parent context info', { topicId, modelName, parentTraceId: this.parentSpanContext?.traceId, @@ -62,7 +62,7 @@ class AdapterTracer { startActiveSpan any>(name: string, options: any, fn: F): ReturnType startActiveSpan any>(name: string, options: any, context: any, fn: F): ReturnType startActiveSpan any>(name: string, arg2?: any, arg3?: any, arg4?: any): ReturnType { - logger.info('AdapterTracer.startActiveSpan called', { + logger.debug('AdapterTracer.startActiveSpan called', { spanName: name, topicId: this.topicId, modelName: this.modelName, @@ -88,7 +88,7 @@ class AdapterTracer { // 包装span的end方法 const originalEnd = span.end.bind(span) span.end = (endTime?: any) => { - logger.info('AI SDK span.end() called in startActiveSpan - about to convert span', { + logger.debug('AI SDK span.end() called in startActiveSpan - about to convert span', { spanName: name, spanId: span.spanContext().spanId, traceId: span.spanContext().traceId, @@ -101,14 +101,14 @@ class AdapterTracer { // 转换并保存 span 数据 try { - logger.info('Converting AI SDK span to SpanEntity (from startActiveSpan)', { + logger.debug('Converting AI SDK span to SpanEntity (from startActiveSpan)', { spanName: name, spanId: span.spanContext().spanId, traceId: span.spanContext().traceId, topicId: this.topicId, modelName: this.modelName }) - logger.info('span', span) + logger.silly('span', span) const spanEntity = AiSdkSpanAdapter.convertToSpanEntity({ span, topicId: this.topicId, @@ -118,7 +118,7 @@ class AdapterTracer { // 保存转换后的数据 window.api.trace.saveEntity(spanEntity) - logger.info('AI SDK span converted and saved successfully (from startActiveSpan)', { + logger.debug('AI SDK span converted and saved successfully (from startActiveSpan)', { spanName: name, spanId: span.spanContext().spanId, traceId: span.spanContext().traceId, @@ -151,7 +151,7 @@ class AdapterTracer { if (this.parentSpanContext) { try { const ctx = trace.setSpanContext(otelContext.active(), this.parentSpanContext) - logger.info('Created active context with parent SpanContext for startActiveSpan', { + logger.debug('Created active context with parent SpanContext for startActiveSpan', { spanName: name, parentTraceId: this.parentSpanContext.traceId, parentSpanId: this.parentSpanContext.spanId, @@ -218,7 +218,7 @@ export function createTelemetryPlugin(config: TelemetryPluginConfig) { if (effectiveTopicId) { try { // 从 SpanManagerService 获取当前的 span - logger.info('Attempting to find parent span', { + logger.debug('Attempting to find parent span', { topicId: effectiveTopicId, requestId: context.requestId, modelName: modelName, @@ -230,7 +230,7 @@ export function createTelemetryPlugin(config: TelemetryPluginConfig) { if (parentSpan) { // 直接使用父 span 的 SpanContext,避免手动拼装字段遗漏 parentSpanContext = parentSpan.spanContext() - logger.info('Found active parent span for AI SDK', { + logger.debug('Found active parent span for AI SDK', { parentSpanId: parentSpanContext.spanId, parentTraceId: parentSpanContext.traceId, topicId: effectiveTopicId, @@ -302,7 +302,7 @@ export function createTelemetryPlugin(config: TelemetryPluginConfig) { logger.debug('Updated active context with parent span') }) - logger.info('Set parent context for AI SDK spans', { + logger.debug('Set parent context for AI SDK spans', { parentSpanId: parentSpanContext?.spanId, parentTraceId: parentSpanContext?.traceId, hasActiveContext: !!activeContext, @@ -313,7 +313,7 @@ export function createTelemetryPlugin(config: TelemetryPluginConfig) { } } - logger.info('Injecting AI SDK telemetry config with adapter', { + logger.debug('Injecting AI SDK telemetry config with adapter', { requestId: context.requestId, topicId: effectiveTopicId, modelId: context.modelId, diff --git a/src/renderer/src/aiCore/provider/providerInitialization.ts b/src/renderer/src/aiCore/provider/providerInitialization.ts index 9942ffa405..665f2bd05c 100644 --- a/src/renderer/src/aiCore/provider/providerInitialization.ts +++ b/src/renderer/src/aiCore/provider/providerInitialization.ts @@ -63,6 +63,14 @@ export const NEW_PROVIDER_CONFIGS: ProviderConfig[] = [ creatorFunctionName: 'createMistral', supportsImageGeneration: false, aliases: ['mistral'] + }, + { + id: 'huggingface', + name: 'HuggingFace', + import: () => import('@ai-sdk/huggingface'), + creatorFunctionName: 'createHuggingFace', + supportsImageGeneration: true, + aliases: ['hf', 'hugging-face'] } ] as const diff --git a/src/renderer/src/aiCore/utils/options.ts b/src/renderer/src/aiCore/utils/options.ts index d151b57029..087a9ef157 100644 --- a/src/renderer/src/aiCore/utils/options.ts +++ b/src/renderer/src/aiCore/utils/options.ts @@ -90,7 +90,9 @@ export function buildProviderOptions( serviceTier: serviceTierSetting } break - + case 'huggingface': + providerSpecificOptions = buildOpenAIProviderOptions(assistant, model, capabilities) + break case 'anthropic': providerSpecificOptions = buildAnthropicProviderOptions(assistant, model, capabilities) break diff --git a/src/renderer/src/aiCore/utils/reasoning.ts b/src/renderer/src/aiCore/utils/reasoning.ts index 6e57074980..86a762897f 100644 --- a/src/renderer/src/aiCore/utils/reasoning.ts +++ b/src/renderer/src/aiCore/utils/reasoning.ts @@ -10,6 +10,7 @@ import { isGrok4FastReasoningModel, isGrokReasoningModel, isOpenAIDeepResearchModel, + isOpenAIModel, isOpenAIReasoningModel, isQwenAlwaysThinkModel, isQwenReasoningModel, @@ -319,6 +320,20 @@ export function getOpenAIReasoningParams(assistant: Assistant, model: Model): Re if (!isReasoningModel(model)) { return {} } + + let reasoningEffort = assistant?.settings?.reasoning_effort + + if (!reasoningEffort) { + return {} + } + + // 非OpenAI模型,但是Provider类型是responses/azure openai的情况 + if (!isOpenAIModel(model)) { + return { + reasoningEffort + } + } + const openAI = getStoreSetting('openAI') as SettingsState['openAI'] const summaryText = openAI?.summaryText || 'off' @@ -330,16 +345,10 @@ export function getOpenAIReasoningParams(assistant: Assistant, model: Model): Re reasoningSummary = summaryText } - let reasoningEffort = assistant?.settings?.reasoning_effort - if (isOpenAIDeepResearchModel(model)) { reasoningEffort = 'medium' } - if (!reasoningEffort) { - return {} - } - // OpenAI 推理参数 if (isSupportedReasoningEffortOpenAIModel(model)) { return { diff --git a/src/renderer/src/assets/images/providers/huggingface.webp b/src/renderer/src/assets/images/providers/huggingface.webp new file mode 100644 index 0000000000000000000000000000000000000000..72413f893ecdafd48191e2a0b42dcb1a27a184ca GIT binary patch literal 27358 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 N0000000000007xn=$QZj literal 0 HcmV?d00001 diff --git a/src/renderer/src/config/models/default.ts b/src/renderer/src/config/models/default.ts index c3a40e6ef7..f3e63e631d 100644 --- a/src/renderer/src/config/models/default.ts +++ b/src/renderer/src/config/models/default.ts @@ -1837,5 +1837,6 @@ export const SYSTEM_MODELS: Record = provider: 'longcat', group: 'LongCat' } - ] + ], + huggingface: [] } diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index 7f8d95dcd1..0008013af4 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -22,6 +22,7 @@ import GoogleProviderLogo from '@renderer/assets/images/providers/google.png' import GPUStackProviderLogo from '@renderer/assets/images/providers/gpustack.svg' import GrokProviderLogo from '@renderer/assets/images/providers/grok.png' import GroqProviderLogo from '@renderer/assets/images/providers/groq.png' +import HuggingfaceProviderLogo from '@renderer/assets/images/providers/huggingface.webp' import HyperbolicProviderLogo from '@renderer/assets/images/providers/hyperbolic.png' import InfiniProviderLogo from '@renderer/assets/images/providers/infini.png' import IntelOvmsLogo from '@renderer/assets/images/providers/intel.png' @@ -653,6 +654,16 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = models: SYSTEM_MODELS.longcat, isSystem: true, enabled: false + }, + huggingface: { + id: 'huggingface', + name: 'Hugging Face', + type: 'openai-response', + apiKey: '', + apiHost: 'https://router.huggingface.co/v1/', + models: [], + isSystem: true, + enabled: false } } as const @@ -717,7 +728,8 @@ export const PROVIDER_LOGO_MAP: AtLeast = { 'aws-bedrock': AwsProviderLogo, poe: 'poe', // use svg icon component aionly: AiOnlyProviderLogo, - longcat: LongCatProviderLogo + longcat: LongCatProviderLogo, + huggingface: HuggingfaceProviderLogo } as const export function getProviderLogo(providerId: string) { @@ -1344,6 +1356,17 @@ export const PROVIDER_URLS: Record = { docs: 'https://longcat.chat/platform/docs/zh/', models: 'https://longcat.chat/platform/docs/zh/APIDocs.html' } + }, + huggingface: { + api: { + url: 'https://router.huggingface.co/v1/' + }, + websites: { + official: 'https://huggingface.co/', + apiKey: 'https://huggingface.co/settings/tokens', + docs: 'https://huggingface.co/docs', + models: 'https://huggingface.co/models' + } } } diff --git a/src/renderer/src/i18n/label.ts b/src/renderer/src/i18n/label.ts index 51edc964b6..e679200804 100644 --- a/src/renderer/src/i18n/label.ts +++ b/src/renderer/src/i18n/label.ts @@ -88,7 +88,9 @@ const providerKeyMap = { zhinao: 'provider.zhinao', zhipu: 'provider.zhipu', poe: 'provider.poe', - aionly: 'provider.aionly' + aionly: 'provider.aionly', + longcat: 'provider.longcat', + huggingface: 'provider.huggingface' } as const /** diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index ab3bedef6a..a5a93e4356 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent Hunyuan", "hyperbolic": "Hyperbolic", "infini": "Infini", "jina": "Jina", "lanyun": "LANYUN", "lmstudio": "LM Studio", + "longcat": "LongCat AI", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index ed728ef2c3..44f051be07 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "腾讯混元", "hyperbolic": "Hyperbolic", "infini": "无问芯穹", "jina": "Jina", "lanyun": "蓝耘科技", "lmstudio": "LM Studio", + "longcat": "龙猫", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope 魔搭", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 89b51d5c1b..d933db01d5 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "騰訊混元", "hyperbolic": "Hyperbolic", "infini": "無問芯穹", "jina": "Jina", "lanyun": "藍耘", "lmstudio": "LM Studio", + "longcat": "龍貓", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope 魔搭", diff --git a/src/renderer/src/i18n/translate/de-de.json b/src/renderer/src/i18n/translate/de-de.json index 3d9de52588..3a44387d5d 100644 --- a/src/renderer/src/i18n/translate/de-de.json +++ b/src/renderer/src/i18n/translate/de-de.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent Hunyuan", "hyperbolic": "Hyperbolic", "infini": "Infini-AI", "jina": "Jina", "lanyun": "Lanyun Technologie", "lmstudio": "LM Studio", + "longcat": "Meißner Riesenhamster", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index c740b1b106..59b25aea2d 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent Hunyuan", "hyperbolic": "Υπερβολικός", "infini": "Χωρίς Ερώτημα Xin Qiong", "jina": "Jina", "lanyun": "Λανιούν Τεχνολογία", "lmstudio": "LM Studio", + "longcat": "Τσίρο", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope Magpie", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 654510415e..70defe51da 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent Hùnyuán", "hyperbolic": "Hiperbólico", "infini": "Infini", "jina": "Jina", "lanyun": "Tecnología Lanyun", "lmstudio": "Estudio LM", + "longcat": "Totoro", "minimax": "Minimax", "mistral": "Mistral", "modelscope": "ModelScope Módulo", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 65b7bd6d12..305378447e 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent HunYuan", "hyperbolic": "Hyperbolique", "infini": "Sans Frontières Céleste", "jina": "Jina", "lanyun": "Technologie Lan Yun", "lmstudio": "Studio LM", + "longcat": "Mon voisin Totoro", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope MoDa", diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 1ee2139df7..6e66ace09f 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "ハギングフェイス", "hunyuan": "腾讯混元", "hyperbolic": "Hyperbolic", "infini": "Infini", "jina": "Jina", "lanyun": "LANYUN", "lmstudio": "LM Studio", + "longcat": "トトロ", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index e5231a07e9..4a6dc5b2b6 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Compreender", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent Hún Yuán", "hyperbolic": "Hiperbólico", "infini": "Infinito", "jina": "Jina", "lanyun": "Lanyun Tecnologia", "lmstudio": "Estúdio LM", + "longcat": "Totoro", "minimax": "Minimax", "mistral": "Mistral", "modelscope": "ModelScope MôDá", diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 59df9b25f7..477fcb0a28 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -2345,12 +2345,14 @@ "gpustack": "GPUStack", "grok": "Grok", "groq": "Groq", + "huggingface": "Hugging Face", "hunyuan": "Tencent Hunyuan", "hyperbolic": "Hyperbolic", "infini": "Infini", "jina": "Jina", "lanyun": "LANYUN", "lmstudio": "LM Studio", + "longcat": "Тоторо", "minimax": "MiniMax", "mistral": "Mistral", "modelscope": "ModelScope", diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index c9ec0d5e81..85afd68cb9 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -65,7 +65,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 166, + version: 167, blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index b0b4e36a81..7b7a834838 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2720,6 +2720,15 @@ const migrateConfig = { logger.error('migrate 166 error', error as Error) return state } + }, + '167': (state: RootState) => { + try { + addProvider(state, 'huggingface') + return state + } catch (error) { + logger.error('migrate 167 error', error as Error) + return state + } } } diff --git a/src/renderer/src/types/provider.ts b/src/renderer/src/types/provider.ts index 782d8e98fd..95461d14e9 100644 --- a/src/renderer/src/types/provider.ts +++ b/src/renderer/src/types/provider.ts @@ -162,7 +162,8 @@ export const SystemProviderIds = { 'aws-bedrock': 'aws-bedrock', poe: 'poe', aionly: 'aionly', - longcat: 'longcat' + longcat: 'longcat', + huggingface: 'huggingface' } as const export type SystemProviderId = keyof typeof SystemProviderIds diff --git a/yarn.lock b/yarn.lock index 79909f1781..432a846a47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -180,6 +180,32 @@ __metadata: languageName: node linkType: hard +"@ai-sdk/huggingface@npm:0.0.4": + version: 0.0.4 + resolution: "@ai-sdk/huggingface@npm:0.0.4" + dependencies: + "@ai-sdk/openai-compatible": "npm:1.0.22" + "@ai-sdk/provider": "npm:2.0.0" + "@ai-sdk/provider-utils": "npm:3.0.12" + peerDependencies: + zod: ^3.25.76 || ^4 + checksum: 10c0/756b8f820b89bf9550c9281dfe2a1a813477dec82be5557e236e8b5eaaf0204b65a65925ad486b7576c687f33c709f6d99fd4fc87a46b1add210435b08834986 + languageName: node + linkType: hard + +"@ai-sdk/huggingface@patch:@ai-sdk/huggingface@npm%3A0.0.4#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch": + version: 0.0.4 + resolution: "@ai-sdk/huggingface@patch:@ai-sdk/huggingface@npm%3A0.0.4#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch::version=0.0.4&hash=ceb48e" + dependencies: + "@ai-sdk/openai-compatible": "npm:1.0.22" + "@ai-sdk/provider": "npm:2.0.0" + "@ai-sdk/provider-utils": "npm:3.0.12" + peerDependencies: + zod: ^3.25.76 || ^4 + checksum: 10c0/4726a10de7a6fd554b58d62f79cd6514c2cc5166052e035ba1517e224a310ddb355a5d2922ee8507fb8d928d6d5b2b102d3d221af5a44b181e436e6b64382087 + languageName: node + linkType: hard + "@ai-sdk/mistral@npm:^2.0.19": version: 2.0.19 resolution: "@ai-sdk/mistral@npm:2.0.19" @@ -13853,6 +13879,7 @@ __metadata: "@agentic/tavily": "npm:^7.3.3" "@ai-sdk/amazon-bedrock": "npm:^3.0.35" "@ai-sdk/google-vertex": "npm:^3.0.40" + "@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.4#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.4-8080836bc1.patch" "@ai-sdk/mistral": "npm:^2.0.19" "@ai-sdk/perplexity": "npm:^2.0.13" "@ant-design/v5-patch-for-react-19": "npm:^1.0.3"