diff --git a/app/components/Logo/HuggingFace.vue b/app/components/Logo/HuggingFace.vue new file mode 100644 index 0000000..4737600 --- /dev/null +++ b/app/components/Logo/HuggingFace.vue @@ -0,0 +1,48 @@ + + + diff --git a/app/components/Logo/Poolside.vue b/app/components/Logo/Poolside.vue new file mode 100644 index 0000000..060cde5 --- /dev/null +++ b/app/components/Logo/Poolside.vue @@ -0,0 +1,52 @@ + + + diff --git a/app/components/Logo/Tencent.vue b/app/components/Logo/Tencent.vue new file mode 100644 index 0000000..3b2ce58 --- /dev/null +++ b/app/components/Logo/Tencent.vue @@ -0,0 +1,24 @@ + + + diff --git a/app/utils/model-mapping.ts b/app/utils/model-mapping.ts index 7219c82..436361f 100644 --- a/app/utils/model-mapping.ts +++ b/app/utils/model-mapping.ts @@ -52,6 +52,9 @@ import { LogoInflection, LogoVllm, LogoClosedRouter, + LogoPoolside, + LogoTencent, + LogoHuggingFace, } from '#components'; import { markRaw } from 'vue'; @@ -161,7 +164,7 @@ const MODEL_MAPPINGS: ModelConfig[] = [ }, { Icon: markRaw(LogoZAI), - keywords: [/^glm-4/, /\/glm-4/, /zai-/, /-glm-4/, /glm-\d/] + keywords: [/zai-/, /(^|\/|-)?glm-?\d/] }, { Icon: markRaw(LogoGLMV), @@ -314,6 +317,14 @@ const MODEL_MAPPINGS: ModelConfig[] = [ { Icon: markRaw(LogoInflection), keywords: [/inflection-/, /^inflection\//] + }, + { + Icon: markRaw(LogoPoolside), + keywords: [/poolside\//, /laguna/] + }, + { + Icon: markRaw(LogoTencent), + keywords: [/tencent\//, /hy/], } ]; @@ -324,7 +335,7 @@ export const providerIcons: Record = { azure: null, anthropic: markRaw(LogoAnthropic), cohere: markRaw(LogoCohere), - huggingface: null, + huggingface: markRaw(LogoHuggingFace), openrouter: markRaw(LogoOpenrouter), cerebras: markRaw(LogoCerebras), longcat: markRaw(LogoLongCat), diff --git a/bun.lock b/bun.lock index a22c872..93e0838 100644 --- a/bun.lock +++ b/bun.lock @@ -9,6 +9,7 @@ "@ai-sdk/cerebras": "^2.0.45", "@ai-sdk/cohere": "^3.0.30", "@ai-sdk/google": "^3.0.61", + "@ai-sdk/huggingface": "^1.0.43", "@ai-sdk/mistral": "^3.0.30", "@ai-sdk/openai": "^3.0.52", "@ai-sdk/openai-compatible": "^2.0.41", @@ -30,7 +31,7 @@ "comlink": "^4.4.2", "drizzle-orm": "^1.0.0-beta.21", "glob": "^13.0.6", - "longcat-ai-sdk-provider": "^0.0.4", + "longcat-ai-sdk-provider": "^0.0.5", "nanoid": "^5.1.7", "nuxt": "^4.4.2", "pg": "^8.20.0", @@ -80,6 +81,8 @@ "@ai-sdk/google": ["@ai-sdk/google@3.0.61", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jEKU1Mjcy5CoicejdJQIzM0ntYwyXR8vtYgAZYriKaOuLAiAhiiU538++fGU3CC9HJH/mL1OfsCwMM3gFiCNsw=="], + "@ai-sdk/huggingface": ["@ai-sdk/huggingface@1.0.43", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-bEHZNmQNabbs7zYjRa7e0Hz1RFiVwm/hjfZ2YFwTFsxC0WmPSSvjyG5SqRM8DApbSC2EtGP5K/zO2IKEXZrYyQ=="], + "@ai-sdk/mistral": ["@ai-sdk/mistral@3.0.30", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-+j4IXRSk9E661cFSafmIr+XHOzwjFagawwzMOlSqwL6U4Sq4PCFLDF+oHbX5NUqNjUL7FD1zi/9lBIfa41pUvw=="], "@ai-sdk/openai": ["@ai-sdk/openai@3.0.52", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4Rr8NCGmfWTz6DCUvixn9UmyZcMatiHn0zWoMzI3JCUe9R1P/vsPOpCBALKoSzVYOjyJnhtnVIbfUKujcS39uw=="], @@ -1778,7 +1781,7 @@ "lodash.uniq": ["lodash.uniq@4.5.0", "", {}, "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="], - "longcat-ai-sdk-provider": ["longcat-ai-sdk-provider@0.0.4", "", { "dependencies": { "@ai-sdk/openai-compatible": "^2.0.37", "@ai-sdk/provider": "^3.0.8", "@ai-sdk/provider-utils": "^4.0.21" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-cAyxpYZIO9jRq7UJCt6tPqoBXUpIvKsvWJsJF1pMtRBGxm6SF96rgHDRupxpA5XX4ODqDo7LRE1hs3viEcLxoA=="], + "longcat-ai-sdk-provider": ["longcat-ai-sdk-provider@0.0.5", "", { "dependencies": { "@ai-sdk/openai-compatible": "^2.0.37", "@ai-sdk/provider": "^3.0.8", "@ai-sdk/provider-utils": "^4.0.21" }, "peerDependencies": { "ai": ">=6.0.0" } }, "sha512-VbuhUuJNXnjoRYnpKBi3TSqFsHJaRRlzjFC+Rv4cxbzgHl6KTcohNeVvgbB95p49lMl4kGH6I/VxgdPOSkAi8A=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -2862,10 +2865,6 @@ "listhen/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], - "longcat-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.37", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-+POSFVcgiu47BK64dhsI6OpcDC0/VAE2ZSaXdXGNNhpC/ava++uSRJYks0k2bpfY0wwCTgpAWZsXn/dG2Yppiw=="], - - "longcat-ai-sdk-provider/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], - "magic-regexp/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], "magicast/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], diff --git a/package.json b/package.json index c6134e5..d669c72 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@ai-sdk/cerebras": "^2.0.45", "@ai-sdk/cohere": "^3.0.30", "@ai-sdk/google": "^3.0.61", + "@ai-sdk/huggingface": "^1.0.43", "@ai-sdk/mistral": "^3.0.30", "@ai-sdk/openai": "^3.0.52", "@ai-sdk/openai-compatible": "^2.0.41", @@ -35,7 +36,7 @@ "comlink": "^4.4.2", "drizzle-orm": "^1.0.0-beta.21", "glob": "^13.0.6", - "longcat-ai-sdk-provider": "^0.0.4", + "longcat-ai-sdk-provider": "^0.0.5", "nanoid": "^5.1.7", "nuxt": "^4.4.2", "pg": "^8.20.0", diff --git a/server/utils/providers/huggingface.ts b/server/utils/providers/huggingface.ts new file mode 100644 index 0000000..07e40b9 --- /dev/null +++ b/server/utils/providers/huggingface.ts @@ -0,0 +1,51 @@ +import { createHuggingFace } from '@ai-sdk/huggingface'; +import { GatewayFetchError } from '~~/server/utils/ai-provider'; +import { Err, Ok } from '~~/types/result'; +import type { ProviderModule } from '.'; +import { getModelData } from './helpers'; + +export default { + id: 'huggingface', + baseUrl: 'https://router.huggingface.co/v1', + modelsEndpoint: null, + + createGateway({ apiKey, baseURL }) { + if (apiKey === undefined) { + return Err(GatewayFetchError.NoProviderApiKey); + } + + return Ok({ + gateway: createHuggingFace({ apiKey, baseURL }), + streamTransformer: undefined, + textTransformer: undefined, + }); + }, + + getAuthHeaders(apiKey) { + return apiKey ? { Authorization: `Bearer ${apiKey}` } : {}; + }, + + async fetchModels({ baseURL, apiKey, modelsDevData }) { + const res = await fetch( + 'https://huggingface.co/api/models?pipeline_tag=text-generation&sort=downloads', + { + method: 'GET', + headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {}, + }, + ); + const data = await res.json(); + + if (!res.ok) { + throw createError({ statusCode: res.status, message: JSON.stringify(data) }); + } + + return data + .filter((model: any) => model.tags?.includes('text-generation-inference')) + .map((model: any) => ({ + ...getModelData(model.id, 'huggingface', modelsDevData), + id: model.id, + name: model.id, + releasedAt: model.createdAt, + })); + }, +} satisfies ProviderModule; diff --git a/server/utils/providers/index.ts b/server/utils/providers/index.ts index ab673f7..a1e80b7 100644 --- a/server/utils/providers/index.ts +++ b/server/utils/providers/index.ts @@ -12,6 +12,7 @@ import nvidia from './nvidia'; import xiaomi from './xiaomi'; import openai from './openai'; import anthropic from './anthropic'; +import huggingface from './huggingface'; import type { Result } from '~~/types/result'; import type { Gateway, GatewayFetchError } from '~~/server/utils/ai-provider'; import type { Model as ModelDrizzle } from '~/composables/useModels'; @@ -65,6 +66,7 @@ const providers = [ xiaomi, openai, anthropic, + huggingface, ]; type ProviderId = typeof providers[number]['id'];