diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 884337767fe..ea7089a81ea 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -32,6 +32,7 @@ export const providerNames = [ "groq", "chutes", "litellm", + "huggingface", ] as const export const providerNamesSchema = z.enum(providerNames) @@ -219,6 +220,12 @@ const groqSchema = apiModelIdProviderModelSchema.extend({ groqApiKey: z.string().optional(), }) +const huggingFaceSchema = baseProviderSettingsSchema.extend({ + huggingFaceApiKey: z.string().optional(), + huggingFaceModelId: z.string().optional(), + huggingFaceInferenceProvider: z.string().optional(), +}) + const chutesSchema = apiModelIdProviderModelSchema.extend({ chutesApiKey: z.string().optional(), }) @@ -256,6 +263,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })), xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })), groqSchema.merge(z.object({ apiProvider: z.literal("groq") })), + huggingFaceSchema.merge(z.object({ apiProvider: z.literal("huggingface") })), chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })), litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), defaultSchema, @@ -285,6 +293,7 @@ export const providerSettingsSchema = z.object({ ...fakeAiSchema.shape, ...xaiSchema.shape, ...groqSchema.shape, + ...huggingFaceSchema.shape, ...chutesSchema.shape, ...litellmSchema.shape, ...codebaseIndexProviderSchema.shape, @@ -304,6 +313,7 @@ export const MODEL_ID_KEYS: Partial[] = [ "unboundModelId", "requestyModelId", "litellmModelId", + "huggingFaceModelId", ] export const getModelId = (settings: ProviderSettings): string | undefined => { diff --git a/src/api/huggingface-models.ts b/src/api/huggingface-models.ts new file mode 100644 index 00000000000..ec1915d0e3d --- /dev/null +++ b/src/api/huggingface-models.ts @@ -0,0 +1,17 @@ +import { fetchHuggingFaceModels, type HuggingFaceModel } from "../services/huggingface-models" + +export interface HuggingFaceModelsResponse { + models: HuggingFaceModel[] + cached: boolean + timestamp: number +} + +export async function getHuggingFaceModels(): Promise { + const models = await fetchHuggingFaceModels() + + return { + models, + cached: false, // We could enhance this to track if data came from cache + timestamp: Date.now(), + } +} diff --git a/src/api/index.ts b/src/api/index.ts index 4598a711b2a..bda390848cd 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -26,6 +26,7 @@ import { FakeAIHandler, XAIHandler, GroqHandler, + HuggingFaceHandler, ChutesHandler, LiteLLMHandler, ClaudeCodeHandler, @@ -108,6 +109,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { return new XAIHandler(options) case "groq": return new GroqHandler(options) + case "huggingface": + return new HuggingFaceHandler(options) case "chutes": return new ChutesHandler(options) case "litellm": diff --git a/src/api/providers/huggingface.ts b/src/api/providers/huggingface.ts new file mode 100644 index 00000000000..913605bd929 --- /dev/null +++ b/src/api/providers/huggingface.ts @@ -0,0 +1,99 @@ +import OpenAI from "openai" +import { Anthropic } from "@anthropic-ai/sdk" + +import type { ApiHandlerOptions } from "../../shared/api" +import { ApiStream } from "../transform/stream" +import { convertToOpenAiMessages } from "../transform/openai-format" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" +import { DEFAULT_HEADERS } from "./constants" +import { BaseProvider } from "./base-provider" + +export class HuggingFaceHandler extends BaseProvider implements SingleCompletionHandler { + private client: OpenAI + private options: ApiHandlerOptions + + constructor(options: ApiHandlerOptions) { + super() + this.options = options + + if (!this.options.huggingFaceApiKey) { + throw new Error("Hugging Face API key is required") + } + + this.client = new OpenAI({ + baseURL: "https://router.huggingface.co/v1", + apiKey: this.options.huggingFaceApiKey, + defaultHeaders: DEFAULT_HEADERS, + }) + } + + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { + const modelId = this.options.huggingFaceModelId || "meta-llama/Llama-3.3-70B-Instruct" + const temperature = this.options.modelTemperature ?? 0.7 + + const params: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = { + model: modelId, + temperature, + messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)], + stream: true, + stream_options: { include_usage: true }, + } + + const stream = await this.client.chat.completions.create(params) + + for await (const chunk of stream) { + const delta = chunk.choices[0]?.delta + + if (delta?.content) { + yield { + type: "text", + text: delta.content, + } + } + + if (chunk.usage) { + yield { + type: "usage", + inputTokens: chunk.usage.prompt_tokens || 0, + outputTokens: chunk.usage.completion_tokens || 0, + } + } + } + } + + async completePrompt(prompt: string): Promise { + const modelId = this.options.huggingFaceModelId || "meta-llama/Llama-3.3-70B-Instruct" + + try { + const response = await this.client.chat.completions.create({ + model: modelId, + messages: [{ role: "user", content: prompt }], + }) + + return response.choices[0]?.message.content || "" + } catch (error) { + if (error instanceof Error) { + throw new Error(`Hugging Face completion error: ${error.message}`) + } + + throw error + } + } + + override getModel() { + const modelId = this.options.huggingFaceModelId || "meta-llama/Llama-3.3-70B-Instruct" + return { + id: modelId, + info: { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + }, + } + } +} diff --git a/src/api/providers/index.ts b/src/api/providers/index.ts index 89d4c203adf..1cefd0616b4 100644 --- a/src/api/providers/index.ts +++ b/src/api/providers/index.ts @@ -9,6 +9,7 @@ export { FakeAIHandler } from "./fake-ai" export { GeminiHandler } from "./gemini" export { GlamaHandler } from "./glama" export { GroqHandler } from "./groq" +export { HuggingFaceHandler } from "./huggingface" export { HumanRelayHandler } from "./human-relay" export { LiteLLMHandler } from "./lite-llm" export { LmStudioHandler } from "./lm-studio" diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 780d40df891..ebe95530f25 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -674,6 +674,22 @@ export const webviewMessageHandler = async ( // TODO: Cache like we do for OpenRouter, etc? provider.postMessageToWebview({ type: "vsCodeLmModels", vsCodeLmModels }) break + case "requestHuggingFaceModels": + try { + const { getHuggingFaceModels } = await import("../../api/huggingface-models") + const huggingFaceModelsResponse = await getHuggingFaceModels() + provider.postMessageToWebview({ + type: "huggingFaceModels", + huggingFaceModels: huggingFaceModelsResponse.models, + }) + } catch (error) { + console.error("Failed to fetch Hugging Face models:", error) + provider.postMessageToWebview({ + type: "huggingFaceModels", + huggingFaceModels: [], + }) + } + break case "openImage": openImage(message.text!, { values: message.values }) break diff --git a/src/services/huggingface-models.ts b/src/services/huggingface-models.ts new file mode 100644 index 00000000000..9c0bc406f93 --- /dev/null +++ b/src/services/huggingface-models.ts @@ -0,0 +1,171 @@ +export interface HuggingFaceModel { + _id: string + id: string + inferenceProviderMapping: InferenceProviderMapping[] + trendingScore: number + config: ModelConfig + tags: string[] + pipeline_tag: "text-generation" | "image-text-to-text" + library_name?: string +} + +export interface InferenceProviderMapping { + provider: string + providerId: string + status: "live" | "staging" | "error" + task: "conversational" +} + +export interface ModelConfig { + architectures: string[] + model_type: string + tokenizer_config?: { + chat_template?: string | Array<{ name: string; template: string }> + model_max_length?: number + } +} + +interface HuggingFaceApiParams { + pipeline_tag?: "text-generation" | "image-text-to-text" + filter: string + inference_provider: string + limit: number + expand: string[] +} + +const DEFAULT_PARAMS: HuggingFaceApiParams = { + filter: "conversational", + inference_provider: "all", + limit: 100, + expand: [ + "inferenceProviderMapping", + "config", + "library_name", + "pipeline_tag", + "tags", + "mask_token", + "trendingScore", + ], +} + +const BASE_URL = "https://huggingface.co/api/models" +const CACHE_DURATION = 1000 * 60 * 60 // 1 hour + +interface CacheEntry { + data: HuggingFaceModel[] + timestamp: number + status: "success" | "partial" | "error" +} + +let cache: CacheEntry | null = null + +function buildApiUrl(params: HuggingFaceApiParams): string { + const url = new URL(BASE_URL) + + // Add simple params + Object.entries(params).forEach(([key, value]) => { + if (!Array.isArray(value)) { + url.searchParams.append(key, String(value)) + } + }) + + // Handle array params specially + params.expand.forEach((item) => { + url.searchParams.append("expand[]", item) + }) + + return url.toString() +} + +const headers: HeadersInit = { + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + Priority: "u=0, i", + Pragma: "no-cache", + "Cache-Control": "no-cache", +} + +const requestInit: RequestInit = { + credentials: "include", + headers, + method: "GET", + mode: "cors", +} + +export async function fetchHuggingFaceModels(): Promise { + const now = Date.now() + + // Check cache + if (cache && now - cache.timestamp < CACHE_DURATION) { + console.log("Using cached Hugging Face models") + return cache.data + } + + try { + console.log("Fetching Hugging Face models from API...") + + // Fetch both text-generation and image-text-to-text models in parallel + const [textGenResponse, imgTextResponse] = await Promise.allSettled([ + fetch(buildApiUrl({ ...DEFAULT_PARAMS, pipeline_tag: "text-generation" }), requestInit), + fetch(buildApiUrl({ ...DEFAULT_PARAMS, pipeline_tag: "image-text-to-text" }), requestInit), + ]) + + let textGenModels: HuggingFaceModel[] = [] + let imgTextModels: HuggingFaceModel[] = [] + let hasErrors = false + + // Process text-generation models + if (textGenResponse.status === "fulfilled" && textGenResponse.value.ok) { + textGenModels = await textGenResponse.value.json() + } else { + console.error("Failed to fetch text-generation models:", textGenResponse) + hasErrors = true + } + + // Process image-text-to-text models + if (imgTextResponse.status === "fulfilled" && imgTextResponse.value.ok) { + imgTextModels = await imgTextResponse.value.json() + } else { + console.error("Failed to fetch image-text-to-text models:", imgTextResponse) + hasErrors = true + } + + // Combine and filter models + const allModels = [...textGenModels, ...imgTextModels] + .filter((model) => model.inferenceProviderMapping.length > 0) + .sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase())) + + // Update cache + cache = { + data: allModels, + timestamp: now, + status: hasErrors ? "partial" : "success", + } + + console.log(`Fetched ${allModels.length} Hugging Face models (status: ${cache.status})`) + return allModels + } catch (error) { + console.error("Error fetching Hugging Face models:", error) + + // Return cached data if available + if (cache) { + console.log("Using stale cached data due to fetch error") + cache.status = "error" + return cache.data + } + + // No cache available, return empty array + return [] + } +} + +export function getCachedModels(): HuggingFaceModel[] | null { + return cache?.data || null +} + +export function clearCache(): void { + cache = null +} diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 4f2aa2da159..ef45c7ebe30 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -67,6 +67,7 @@ export interface ExtensionMessage { | "ollamaModels" | "lmStudioModels" | "vsCodeLmModels" + | "huggingFaceModels" | "vsCodeLmApiAvailable" | "updatePrompt" | "systemPrompt" @@ -135,6 +136,28 @@ export interface ExtensionMessage { ollamaModels?: string[] lmStudioModels?: string[] vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[] + huggingFaceModels?: Array<{ + _id: string + id: string + inferenceProviderMapping: Array<{ + provider: string + providerId: string + status: "live" | "staging" | "error" + task: "conversational" + }> + trendingScore: number + config: { + architectures: string[] + model_type: string + tokenizer_config?: { + chat_template?: string | Array<{ name: string; template: string }> + model_max_length?: number + } + } + tags: string[] + pipeline_tag: "text-generation" | "image-text-to-text" + library_name?: string + }> mcpServers?: McpServer[] commits?: GitCommit[] listApiConfig?: ProviderSettingsEntry[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 1f56829f7b3..b0529c5a276 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -67,6 +67,7 @@ export interface WebviewMessage { | "requestOllamaModels" | "requestLmStudioModels" | "requestVsCodeLmModels" + | "requestHuggingFaceModels" | "openImage" | "saveImage" | "openFile" diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 6c6c621956c..38d2ceebd37 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -59,6 +59,7 @@ import { Gemini, Glama, Groq, + HuggingFace, LMStudio, LiteLLM, Mistral, @@ -487,6 +488,10 @@ const ApiOptions = ({ )} + {selectedProvider === "huggingface" && ( + + )} + {selectedProvider === "chutes" && ( )} diff --git a/webview-ui/src/components/settings/constants.ts b/webview-ui/src/components/settings/constants.ts index 1140e4c0bcf..995f591034d 100644 --- a/webview-ui/src/components/settings/constants.ts +++ b/webview-ui/src/components/settings/constants.ts @@ -51,6 +51,7 @@ export const PROVIDERS = [ { value: "human-relay", label: "Human Relay" }, { value: "xai", label: "xAI (Grok)" }, { value: "groq", label: "Groq" }, + { value: "huggingface", label: "Hugging Face" }, { value: "chutes", label: "Chutes AI" }, { value: "litellm", label: "LiteLLM" }, ].sort((a, b) => a.label.localeCompare(b.label)) diff --git a/webview-ui/src/components/settings/providers/HuggingFace.tsx b/webview-ui/src/components/settings/providers/HuggingFace.tsx new file mode 100644 index 00000000000..d4195492dd7 --- /dev/null +++ b/webview-ui/src/components/settings/providers/HuggingFace.tsx @@ -0,0 +1,216 @@ +import { useCallback, useState, useEffect, useMemo } from "react" +import { useEvent } from "react-use" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" + +import type { ProviderSettings } from "@roo-code/types" + +import { ExtensionMessage } from "@roo/ExtensionMessage" +import { vscode } from "@src/utils/vscode" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" +import { SearchableSelect, type SearchableSelectOption } from "@src/components/ui" + +import { inputEventTransform } from "../transforms" + +type HuggingFaceModel = { + _id: string + id: string + inferenceProviderMapping: Array<{ + provider: string + providerId: string + status: "live" | "staging" | "error" + task: "conversational" + }> + trendingScore: number + config: { + architectures: string[] + model_type: string + tokenizer_config?: { + chat_template?: string | Array<{ name: string; template: string }> + model_max_length?: number + } + } + tags: string[] + pipeline_tag: "text-generation" | "image-text-to-text" + library_name?: string +} + +type HuggingFaceProps = { + apiConfiguration: ProviderSettings + setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void +} + +export const HuggingFace = ({ apiConfiguration, setApiConfigurationField }: HuggingFaceProps) => { + const { t } = useAppTranslation() + const [models, setModels] = useState([]) + const [loading, setLoading] = useState(false) + const [selectedProvider, setSelectedProvider] = useState( + apiConfiguration?.huggingFaceInferenceProvider || "auto", + ) + + const handleInputChange = useCallback( + ( + field: K, + transform: (event: E) => ProviderSettings[K] = inputEventTransform, + ) => + (event: E | Event) => { + setApiConfigurationField(field, transform(event as E)) + }, + [setApiConfigurationField], + ) + + // Fetch models when component mounts + useEffect(() => { + setLoading(true) + vscode.postMessage({ type: "requestHuggingFaceModels" }) + }, []) + + // Handle messages from extension + const onMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + + switch (message.type) { + case "huggingFaceModels": + setModels(message.huggingFaceModels || []) + setLoading(false) + break + } + }, []) + + useEvent("message", onMessage) + + // Get current model and its providers + const currentModel = models.find((m) => m.id === apiConfiguration?.huggingFaceModelId) + const availableProviders = useMemo( + () => currentModel?.inferenceProviderMapping || [], + [currentModel?.inferenceProviderMapping], + ) + + // Set default provider when model changes + useEffect(() => { + if (currentModel && availableProviders.length > 0) { + const savedProvider = apiConfiguration?.huggingFaceInferenceProvider + if (savedProvider) { + // Use saved provider if it exists + setSelectedProvider(savedProvider) + } else { + const currentProvider = availableProviders.find((p) => p.provider === selectedProvider) + if (!currentProvider) { + // Set to "auto" as default + const defaultProvider = "auto" + setSelectedProvider(defaultProvider) + setApiConfigurationField("huggingFaceInferenceProvider", defaultProvider) + } + } + } + }, [ + currentModel, + availableProviders, + selectedProvider, + apiConfiguration?.huggingFaceInferenceProvider, + setApiConfigurationField, + ]) + + const handleModelSelect = (modelId: string) => { + setApiConfigurationField("huggingFaceModelId", modelId) + // Reset provider selection when model changes + const defaultProvider = "auto" + setSelectedProvider(defaultProvider) + setApiConfigurationField("huggingFaceInferenceProvider", defaultProvider) + } + + const handleProviderSelect = (provider: string) => { + setSelectedProvider(provider) + setApiConfigurationField("huggingFaceInferenceProvider", provider) + } + + // Format provider name for display + const formatProviderName = (provider: string) => { + const nameMap: Record = { + sambanova: "SambaNova", + "fireworks-ai": "Fireworks", + together: "Together AI", + nebius: "Nebius AI Studio", + hyperbolic: "Hyperbolic", + novita: "Novita", + cohere: "Cohere", + "hf-inference": "HF Inference API", + replicate: "Replicate", + } + return nameMap[provider] || provider.charAt(0).toUpperCase() + provider.slice(1) + } + + return ( + <> + + + + +
+ + + ({ + value: model.id, + label: model.id, + }), + )} + placeholder={t("settings:providers.huggingFaceSelectModel")} + searchPlaceholder={t("settings:providers.huggingFaceSearchModels")} + emptyMessage={t("settings:providers.huggingFaceNoModelsFound")} + disabled={loading} + /> +
+ + {currentModel && availableProviders.length > 0 && ( +
+ + ({ + value: mapping.provider, + label: `${formatProviderName(mapping.provider)} (${mapping.status})`, + }), + ), + ]} + placeholder={t("settings:providers.huggingFaceSelectProvider")} + searchPlaceholder={t("settings:providers.huggingFaceSearchProviders")} + emptyMessage={t("settings:providers.huggingFaceNoProvidersFound")} + /> +
+ )} + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ + {!apiConfiguration?.huggingFaceApiKey && ( + + {t("settings:providers.getHuggingFaceApiKey")} + + )} + + ) +} diff --git a/webview-ui/src/components/settings/providers/__tests__/HuggingFace.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/HuggingFace.spec.tsx new file mode 100644 index 00000000000..3fd29e4c722 --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/HuggingFace.spec.tsx @@ -0,0 +1,162 @@ +import React from "react" +import { render, screen } from "@/utils/test-utils" +import { HuggingFace } from "../HuggingFace" +import { ProviderSettings } from "@roo-code/types" + +// Mock the VSCodeTextField component +vi.mock("@vscode/webview-ui-toolkit/react", () => ({ + VSCodeTextField: ({ + children, + value, + onInput, + placeholder, + className, + style, + "data-testid": dataTestId, + ...rest + }: any) => { + return ( +
+ {children} + onInput && onInput(e)} + placeholder={placeholder} + data-testid={dataTestId} + {...rest} + /> +
+ ) + }, + VSCodeLink: ({ children, href, onClick }: any) => ( + + {children} + + ), + VSCodeButton: ({ children, onClick, ...rest }: any) => ( + + ), +})) + +// Mock the translation hook +vi.mock("@src/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => { + // Return the key for testing, but simulate some actual translations + const translations: Record = { + "settings:providers.getHuggingFaceApiKey": "Get Hugging Face API Key", + "settings:providers.huggingFaceApiKey": "Hugging Face API Key", + "settings:providers.huggingFaceModelId": "Model ID", + } + return translations[key] || key + }, + }), +})) + +// Mock the UI components +vi.mock("@src/components/ui", () => ({ + Select: ({ children }: any) =>
{children}
, + SelectContent: ({ children }: any) =>
{children}
, + SelectItem: ({ children }: any) =>
{children}
, + SelectTrigger: ({ children }: any) =>
{children}
, + SelectValue: ({ placeholder }: any) =>
{placeholder}
, + SearchableSelect: ({ value, onValueChange, placeholder, children }: any) => ( +
+ onValueChange && onValueChange(e.target.value)} + placeholder={placeholder} + /> + {children} +
+ ), +})) + +describe("HuggingFace Component", () => { + const mockSetApiConfigurationField = vi.fn() + + beforeEach(() => { + vi.clearAllMocks() + }) + + it("should render with internationalized labels", () => { + const apiConfiguration: Partial = { + huggingFaceApiKey: "", + huggingFaceModelId: "", + } + + render( + , + ) + + // Check that the translated labels are rendered + expect(screen.getByText("Get Hugging Face API Key")).toBeInTheDocument() + expect(screen.getByText("Hugging Face API Key")).toBeInTheDocument() + expect(screen.getByText("Model ID")).toBeInTheDocument() + }) + + it("should render API key input field", () => { + const apiConfiguration: Partial = { + huggingFaceApiKey: "test-api-key", + huggingFaceModelId: "", + } + + render( + , + ) + + // Check that the API key input is rendered with the correct value + const apiKeyInput = screen.getByDisplayValue("test-api-key") + expect(apiKeyInput).toBeInTheDocument() + }) + + it("should render model selection components", () => { + const apiConfiguration: Partial = { + huggingFaceApiKey: "test-api-key", + huggingFaceModelId: "test-model", + } + + render( + , + ) + + // Check that the searchable select component is rendered + expect(screen.getByTestId("searchable-select")).toBeInTheDocument() + expect(screen.getByTestId("searchable-select-input")).toBeInTheDocument() + }) + + it("should display the get API key link", () => { + const apiConfiguration: Partial = { + huggingFaceApiKey: "", + huggingFaceModelId: "", + } + + render( + , + ) + + // Check that the API key button is rendered + const apiKeyButton = screen.getByTestId("vscode-button") + expect(apiKeyButton).toBeInTheDocument() + expect(apiKeyButton).toHaveTextContent("Get Hugging Face API Key") + }) +}) diff --git a/webview-ui/src/components/settings/providers/index.ts b/webview-ui/src/components/settings/providers/index.ts index 54974f7200b..6c6fdddaee0 100644 --- a/webview-ui/src/components/settings/providers/index.ts +++ b/webview-ui/src/components/settings/providers/index.ts @@ -6,6 +6,7 @@ export { DeepSeek } from "./DeepSeek" export { Gemini } from "./Gemini" export { Glama } from "./Glama" export { Groq } from "./Groq" +export { HuggingFace } from "./HuggingFace" export { LMStudio } from "./LMStudio" export { Mistral } from "./Mistral" export { Moonshot } from "./Moonshot" diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 928ebb42f46..8dceb6e1173 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -130,6 +130,16 @@ function getSelectedModel({ const info = groqModels[id as keyof typeof groqModels] return { id, info } } + case "huggingface": { + const id = apiConfiguration.huggingFaceModelId ?? "meta-llama/Llama-3.3-70B-Instruct" + const info = { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + } + return { id, info } + } case "chutes": { const id = apiConfiguration.apiModelId ?? chutesDefaultModelId const info = chutesModels[id as keyof typeof chutesModels] diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index cf17a6f919d..d61aaa92fb9 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Clau API de Gemini", "getGroqApiKey": "Obtenir clau API de Groq", "groqApiKey": "Clau API de Groq", + "getHuggingFaceApiKey": "Obtenir clau API de Hugging Face", + "huggingFaceApiKey": "Clau API de Hugging Face", + "huggingFaceModelId": "ID del model", + "huggingFaceLoading": "Carregant...", + "huggingFaceModelsCount": "({{count}} models)", + "huggingFaceSelectModel": "Selecciona un model...", + "huggingFaceSearchModels": "Cerca models...", + "huggingFaceNoModelsFound": "No s'han trobat models", + "huggingFaceProvider": "Proveïdor", + "huggingFaceProviderAuto": "Automàtic", + "huggingFaceSelectProvider": "Selecciona un proveïdor...", + "huggingFaceSearchProviders": "Cerca proveïdors...", + "huggingFaceNoProvidersFound": "No s'han trobat proveïdors", "getGeminiApiKey": "Obtenir clau API de Gemini", "openAiApiKey": "Clau API d'OpenAI", "apiKey": "Clau API", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 20ffaec5252..1cc9f571ada 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini API-Schlüssel", "getGroqApiKey": "Groq API-Schlüssel erhalten", "groqApiKey": "Groq API-Schlüssel", + "getHuggingFaceApiKey": "Hugging Face API-Schlüssel erhalten", + "huggingFaceApiKey": "Hugging Face API-Schlüssel", + "huggingFaceModelId": "Modell-ID", + "huggingFaceLoading": "Lädt...", + "huggingFaceModelsCount": "({{count}} Modelle)", + "huggingFaceSelectModel": "Modell auswählen...", + "huggingFaceSearchModels": "Modelle durchsuchen...", + "huggingFaceNoModelsFound": "Keine Modelle gefunden", + "huggingFaceProvider": "Anbieter", + "huggingFaceProviderAuto": "Automatisch", + "huggingFaceSelectProvider": "Anbieter auswählen...", + "huggingFaceSearchProviders": "Anbieter durchsuchen...", + "huggingFaceNoProvidersFound": "Keine Anbieter gefunden", "getGeminiApiKey": "Gemini API-Schlüssel erhalten", "openAiApiKey": "OpenAI API-Schlüssel", "apiKey": "API-Schlüssel", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 4a826bddab4..cdf7b2e21c2 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini API Key", "getGroqApiKey": "Get Groq API Key", "groqApiKey": "Groq API Key", + "getHuggingFaceApiKey": "Get Hugging Face API Key", + "huggingFaceApiKey": "Hugging Face API Key", + "huggingFaceModelId": "Model ID", + "huggingFaceLoading": "Loading...", + "huggingFaceModelsCount": "({{count}} models)", + "huggingFaceSelectModel": "Select a model...", + "huggingFaceSearchModels": "Search models...", + "huggingFaceNoModelsFound": "No models found", + "huggingFaceProvider": "Provider", + "huggingFaceProviderAuto": "Auto", + "huggingFaceSelectProvider": "Select a provider...", + "huggingFaceSearchProviders": "Search providers...", + "huggingFaceNoProvidersFound": "No providers found", "getGeminiApiKey": "Get Gemini API Key", "openAiApiKey": "OpenAI API Key", "apiKey": "API Key", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 4c4f24bb0f9..a25395fdf41 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Clave API de Gemini", "getGroqApiKey": "Obtener clave API de Groq", "groqApiKey": "Clave API de Groq", + "getHuggingFaceApiKey": "Obtener clave API de Hugging Face", + "huggingFaceApiKey": "Clave API de Hugging Face", + "huggingFaceModelId": "ID del modelo", + "huggingFaceLoading": "Cargando...", + "huggingFaceModelsCount": "({{count}} modelos)", + "huggingFaceSelectModel": "Seleccionar un modelo...", + "huggingFaceSearchModels": "Buscar modelos...", + "huggingFaceNoModelsFound": "No se encontraron modelos", + "huggingFaceProvider": "Proveedor", + "huggingFaceProviderAuto": "Automático", + "huggingFaceSelectProvider": "Seleccionar un proveedor...", + "huggingFaceSearchProviders": "Buscar proveedores...", + "huggingFaceNoProvidersFound": "No se encontraron proveedores", "getGeminiApiKey": "Obtener clave API de Gemini", "openAiApiKey": "Clave API de OpenAI", "apiKey": "Clave API", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 6e7a964694b..8983b57843c 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Clé API Gemini", "getGroqApiKey": "Obtenir la clé API Groq", "groqApiKey": "Clé API Groq", + "getHuggingFaceApiKey": "Obtenir la clé API Hugging Face", + "huggingFaceApiKey": "Clé API Hugging Face", + "huggingFaceModelId": "ID du modèle", + "huggingFaceLoading": "Chargement...", + "huggingFaceModelsCount": "({{count}} modèles)", + "huggingFaceSelectModel": "Sélectionner un modèle...", + "huggingFaceSearchModels": "Rechercher des modèles...", + "huggingFaceNoModelsFound": "Aucun modèle trouvé", + "huggingFaceProvider": "Fournisseur", + "huggingFaceProviderAuto": "Automatique", + "huggingFaceSelectProvider": "Sélectionner un fournisseur...", + "huggingFaceSearchProviders": "Rechercher des fournisseurs...", + "huggingFaceNoProvidersFound": "Aucun fournisseur trouvé", "getGeminiApiKey": "Obtenir la clé API Gemini", "openAiApiKey": "Clé API OpenAI", "apiKey": "Clé API", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index ca255ca44ee..75b84bbbd89 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini API कुंजी", "getGroqApiKey": "Groq API कुंजी प्राप्त करें", "groqApiKey": "Groq API कुंजी", + "getHuggingFaceApiKey": "Hugging Face API कुंजी प्राप्त करें", + "huggingFaceApiKey": "Hugging Face API कुंजी", + "huggingFaceModelId": "मॉडल ID", + "huggingFaceLoading": "लोड हो रहा है...", + "huggingFaceModelsCount": "({{count}} मॉडल)", + "huggingFaceSelectModel": "एक मॉडल चुनें...", + "huggingFaceSearchModels": "मॉडल खोजें...", + "huggingFaceNoModelsFound": "कोई मॉडल नहीं मिला", + "huggingFaceProvider": "प्रदाता", + "huggingFaceProviderAuto": "स्वचालित", + "huggingFaceSelectProvider": "एक प्रदाता चुनें...", + "huggingFaceSearchProviders": "प्रदाता खोजें...", + "huggingFaceNoProvidersFound": "कोई प्रदाता नहीं मिला", "getGeminiApiKey": "Gemini API कुंजी प्राप्त करें", "openAiApiKey": "OpenAI API कुंजी", "apiKey": "API कुंजी", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 0bff5ef4a17..8e795b9e169 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -263,7 +263,20 @@ "geminiApiKey": "Gemini API Key", "getGroqApiKey": "Dapatkan Groq API Key", "groqApiKey": "Groq API Key", + "getHuggingFaceApiKey": "Dapatkan Kunci API Hugging Face", + "huggingFaceApiKey": "Kunci API Hugging Face", + "huggingFaceModelId": "ID Model", "getGeminiApiKey": "Dapatkan Gemini API Key", + "huggingFaceLoading": "Memuat...", + "huggingFaceModelsCount": "({{count}} model)", + "huggingFaceSelectModel": "Pilih model...", + "huggingFaceSearchModels": "Cari model...", + "huggingFaceNoModelsFound": "Tidak ada model ditemukan", + "huggingFaceProvider": "Penyedia", + "huggingFaceProviderAuto": "Otomatis", + "huggingFaceSelectProvider": "Pilih penyedia...", + "huggingFaceSearchProviders": "Cari penyedia...", + "huggingFaceNoProvidersFound": "Tidak ada penyedia ditemukan", "openAiApiKey": "OpenAI API Key", "apiKey": "API Key", "openAiBaseUrl": "Base URL", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index e5cd37a1106..f097002e447 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Chiave API Gemini", "getGroqApiKey": "Ottieni chiave API Groq", "groqApiKey": "Chiave API Groq", + "getHuggingFaceApiKey": "Ottieni chiave API Hugging Face", + "huggingFaceApiKey": "Chiave API Hugging Face", + "huggingFaceModelId": "ID modello", + "huggingFaceLoading": "Caricamento...", + "huggingFaceModelsCount": "({{count}} modelli)", + "huggingFaceSelectModel": "Seleziona un modello...", + "huggingFaceSearchModels": "Cerca modelli...", + "huggingFaceNoModelsFound": "Nessun modello trovato", + "huggingFaceProvider": "Provider", + "huggingFaceProviderAuto": "Automatico", + "huggingFaceSelectProvider": "Seleziona un provider...", + "huggingFaceSearchProviders": "Cerca provider...", + "huggingFaceNoProvidersFound": "Nessun provider trovato", "getGeminiApiKey": "Ottieni chiave API Gemini", "openAiApiKey": "Chiave API OpenAI", "apiKey": "Chiave API", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index e32fac776a9..41ef0543551 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini APIキー", "getGroqApiKey": "Groq APIキーを取得", "groqApiKey": "Groq APIキー", + "getHuggingFaceApiKey": "Hugging Face APIキーを取得", + "huggingFaceApiKey": "Hugging Face APIキー", + "huggingFaceModelId": "モデルID", + "huggingFaceLoading": "読み込み中...", + "huggingFaceModelsCount": "({{count}}個のモデル)", + "huggingFaceSelectModel": "モデルを選択...", + "huggingFaceSearchModels": "モデルを検索...", + "huggingFaceNoModelsFound": "モデルが見つかりません", + "huggingFaceProvider": "プロバイダー", + "huggingFaceProviderAuto": "自動", + "huggingFaceSelectProvider": "プロバイダーを選択...", + "huggingFaceSearchProviders": "プロバイダーを検索...", + "huggingFaceNoProvidersFound": "プロバイダーが見つかりません", "getGeminiApiKey": "Gemini APIキーを取得", "openAiApiKey": "OpenAI APIキー", "apiKey": "APIキー", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index f48860b0aeb..7ea99d76572 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -260,6 +260,19 @@ "getGroqApiKey": "Groq API 키 받기", "groqApiKey": "Groq API 키", "getGeminiApiKey": "Gemini API 키 받기", + "getHuggingFaceApiKey": "Hugging Face API 키 받기", + "huggingFaceApiKey": "Hugging Face API 키", + "huggingFaceModelId": "모델 ID", + "huggingFaceLoading": "로딩 중...", + "huggingFaceModelsCount": "({{count}}개 모델)", + "huggingFaceSelectModel": "모델 선택...", + "huggingFaceSearchModels": "모델 검색...", + "huggingFaceNoModelsFound": "모델을 찾을 수 없음", + "huggingFaceProvider": "제공자", + "huggingFaceProviderAuto": "자동", + "huggingFaceSelectProvider": "제공자 선택...", + "huggingFaceSearchProviders": "제공자 검색...", + "huggingFaceNoProvidersFound": "제공자를 찾을 수 없음", "apiKey": "API 키", "openAiApiKey": "OpenAI API 키", "openAiBaseUrl": "기본 URL", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index bf6e65c995d..2054e1dc19f 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -260,6 +260,19 @@ "getGroqApiKey": "Groq API-sleutel ophalen", "groqApiKey": "Groq API-sleutel", "getGeminiApiKey": "Gemini API-sleutel ophalen", + "getHuggingFaceApiKey": "Hugging Face API-sleutel ophalen", + "huggingFaceApiKey": "Hugging Face API-sleutel", + "huggingFaceModelId": "Model ID", + "huggingFaceLoading": "Laden...", + "huggingFaceModelsCount": "({{count}} modellen)", + "huggingFaceSelectModel": "Selecteer een model...", + "huggingFaceSearchModels": "Zoek modellen...", + "huggingFaceNoModelsFound": "Geen modellen gevonden", + "huggingFaceProvider": "Provider", + "huggingFaceProviderAuto": "Automatisch", + "huggingFaceSelectProvider": "Selecteer een provider...", + "huggingFaceSearchProviders": "Zoek providers...", + "huggingFaceNoProvidersFound": "Geen providers gevonden", "apiKey": "API-sleutel", "openAiApiKey": "OpenAI API-sleutel", "openAiBaseUrl": "Basis-URL", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index d42e4f51a99..de4ddf40a5a 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -260,6 +260,19 @@ "getGroqApiKey": "Uzyskaj klucz API Groq", "groqApiKey": "Klucz API Groq", "getGeminiApiKey": "Uzyskaj klucz API Gemini", + "getHuggingFaceApiKey": "Uzyskaj klucz API Hugging Face", + "huggingFaceApiKey": "Klucz API Hugging Face", + "huggingFaceModelId": "ID modelu", + "huggingFaceLoading": "Ładowanie...", + "huggingFaceModelsCount": "({{count}} modeli)", + "huggingFaceSelectModel": "Wybierz model...", + "huggingFaceSearchModels": "Szukaj modeli...", + "huggingFaceNoModelsFound": "Nie znaleziono modeli", + "huggingFaceProvider": "Dostawca", + "huggingFaceProviderAuto": "Automatyczny", + "huggingFaceSelectProvider": "Wybierz dostawcę...", + "huggingFaceSearchProviders": "Szukaj dostawców...", + "huggingFaceNoProvidersFound": "Nie znaleziono dostawców", "apiKey": "Klucz API", "openAiApiKey": "Klucz API OpenAI", "openAiBaseUrl": "URL bazowy", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 924542cf096..fbe2119ff54 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -260,6 +260,19 @@ "getGroqApiKey": "Obter chave de API Groq", "groqApiKey": "Chave de API Groq", "getGeminiApiKey": "Obter chave de API Gemini", + "getHuggingFaceApiKey": "Obter chave de API Hugging Face", + "huggingFaceApiKey": "Chave de API Hugging Face", + "huggingFaceModelId": "ID do modelo", + "huggingFaceLoading": "Carregando...", + "huggingFaceModelsCount": "({{count}} modelos)", + "huggingFaceSelectModel": "Selecionar um modelo...", + "huggingFaceSearchModels": "Buscar modelos...", + "huggingFaceNoModelsFound": "Nenhum modelo encontrado", + "huggingFaceProvider": "Provedor", + "huggingFaceProviderAuto": "Automático", + "huggingFaceSelectProvider": "Selecionar um provedor...", + "huggingFaceSearchProviders": "Buscar provedores...", + "huggingFaceNoProvidersFound": "Nenhum provedor encontrado", "apiKey": "Chave de API", "openAiApiKey": "Chave de API OpenAI", "openAiBaseUrl": "URL Base", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index cf719b09769..8ad7213013d 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -260,6 +260,19 @@ "getGroqApiKey": "Получить Groq API-ключ", "groqApiKey": "Groq API-ключ", "getGeminiApiKey": "Получить Gemini API-ключ", + "getHuggingFaceApiKey": "Получить Hugging Face API-ключ", + "huggingFaceApiKey": "Hugging Face API-ключ", + "huggingFaceModelId": "ID модели", + "huggingFaceLoading": "Загрузка...", + "huggingFaceModelsCount": "({{count}} моделей)", + "huggingFaceSelectModel": "Выберите модель...", + "huggingFaceSearchModels": "Поиск моделей...", + "huggingFaceNoModelsFound": "Модели не найдены", + "huggingFaceProvider": "Провайдер", + "huggingFaceProviderAuto": "Автоматически", + "huggingFaceSelectProvider": "Выберите провайдера...", + "huggingFaceSearchProviders": "Поиск провайдеров...", + "huggingFaceNoProvidersFound": "Провайдеры не найдены", "apiKey": "API-ключ", "openAiApiKey": "OpenAI API-ключ", "openAiBaseUrl": "Базовый URL", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index fc8fc9c6775..f1d1a271b66 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini API Anahtarı", "getGroqApiKey": "Groq API Anahtarı Al", "groqApiKey": "Groq API Anahtarı", + "getHuggingFaceApiKey": "Hugging Face API Anahtarı Al", + "huggingFaceApiKey": "Hugging Face API Anahtarı", + "huggingFaceModelId": "Model ID", + "huggingFaceLoading": "Yükleniyor...", + "huggingFaceModelsCount": "({{count}} model)", + "huggingFaceSelectModel": "Bir model seç...", + "huggingFaceSearchModels": "Modelleri ara...", + "huggingFaceNoModelsFound": "Model bulunamadı", + "huggingFaceProvider": "Sağlayıcı", + "huggingFaceProviderAuto": "Otomatik", + "huggingFaceSelectProvider": "Bir sağlayıcı seç...", + "huggingFaceSearchProviders": "Sağlayıcıları ara...", + "huggingFaceNoProvidersFound": "Sağlayıcı bulunamadı", "getGeminiApiKey": "Gemini API Anahtarı Al", "openAiApiKey": "OpenAI API Anahtarı", "apiKey": "API Anahtarı", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 7d3e2803ad7..0a46257bd8d 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Khóa API Gemini", "getGroqApiKey": "Lấy khóa API Groq", "groqApiKey": "Khóa API Groq", + "getHuggingFaceApiKey": "Lấy Khóa API Hugging Face", + "huggingFaceApiKey": "Khóa API Hugging Face", + "huggingFaceModelId": "ID Mô hình", + "huggingFaceLoading": "Đang tải...", + "huggingFaceModelsCount": "({{count}} mô hình)", + "huggingFaceSelectModel": "Chọn một mô hình...", + "huggingFaceSearchModels": "Tìm kiếm mô hình...", + "huggingFaceNoModelsFound": "Không tìm thấy mô hình", + "huggingFaceProvider": "Nhà cung cấp", + "huggingFaceProviderAuto": "Tự động", + "huggingFaceSelectProvider": "Chọn một nhà cung cấp...", + "huggingFaceSearchProviders": "Tìm kiếm nhà cung cấp...", + "huggingFaceNoProvidersFound": "Không tìm thấy nhà cung cấp", "getGeminiApiKey": "Lấy khóa API Gemini", "openAiApiKey": "Khóa API OpenAI", "apiKey": "Khóa API", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index eae71ac706f..152c771545e 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini API 密钥", "getGroqApiKey": "获取 Groq API 密钥", "groqApiKey": "Groq API 密钥", + "getHuggingFaceApiKey": "获取 Hugging Face API 密钥", + "huggingFaceApiKey": "Hugging Face API 密钥", + "huggingFaceModelId": "模型 ID", + "huggingFaceLoading": "加载中...", + "huggingFaceModelsCount": "({{count}} 个模型)", + "huggingFaceSelectModel": "选择模型...", + "huggingFaceSearchModels": "搜索模型...", + "huggingFaceNoModelsFound": "未找到模型", + "huggingFaceProvider": "提供商", + "huggingFaceProviderAuto": "自动", + "huggingFaceSelectProvider": "选择提供商...", + "huggingFaceSearchProviders": "搜索提供商...", + "huggingFaceNoProvidersFound": "未找到提供商", "getGeminiApiKey": "获取 Gemini API 密钥", "openAiApiKey": "OpenAI API 密钥", "apiKey": "API 密钥", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 420ece916e6..da54ad63d60 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -259,6 +259,19 @@ "geminiApiKey": "Gemini API 金鑰", "getGroqApiKey": "取得 Groq API 金鑰", "groqApiKey": "Groq API 金鑰", + "getHuggingFaceApiKey": "取得 Hugging Face API 金鑰", + "huggingFaceApiKey": "Hugging Face API 金鑰", + "huggingFaceModelId": "模型 ID", + "huggingFaceLoading": "載入中...", + "huggingFaceModelsCount": "({{count}} 個模型)", + "huggingFaceSelectModel": "選擇模型...", + "huggingFaceSearchModels": "搜尋模型...", + "huggingFaceNoModelsFound": "找不到模型", + "huggingFaceProvider": "提供者", + "huggingFaceProviderAuto": "自動", + "huggingFaceSelectProvider": "選擇提供者...", + "huggingFaceSearchProviders": "搜尋提供者...", + "huggingFaceNoProvidersFound": "找不到提供者", "getGeminiApiKey": "取得 Gemini API 金鑰", "openAiApiKey": "OpenAI API 金鑰", "apiKey": "API 金鑰",