mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
Alibaba Cloud support, incl Qwen Max, Plus, Turbo. Fixes #759
This commit is contained in:
@@ -174,14 +174,14 @@ For full details and former releases, check out the [changelog](docs/changelog.m
|
||||
|
||||
You can easily configure 100s of AI models in big-AGI:
|
||||
|
||||
| **AI models** | _supported vendors_ |
|
||||
|:--------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Opensource Servers | [LocalAI](https://localai.io/) (multimodal) · [Ollama](https://ollama.com/) |
|
||||
| Local Servers | [LM Studio](https://lmstudio.ai/) |
|
||||
| Multimodal services | [Azure](https://azure.microsoft.com/en-us/products/ai-services/openai-service) · [Google Gemini](https://ai.google.dev/) · [OpenAI](https://platform.openai.com/docs/overview) |
|
||||
| Language services | [Anthropic](https://anthropic.com) · [Groq](https://wow.groq.com/) · [Mistral](https://mistral.ai/) · [OpenRouter](https://openrouter.ai/) · [Perplexity](https://www.perplexity.ai/) · [Together AI](https://www.together.ai/) |
|
||||
| Image services | [Prodia](https://prodia.com/) (SDXL) |
|
||||
| Speech services | [ElevenLabs](https://elevenlabs.io) (Voice synthesis / cloning) |
|
||||
| **AI models** | _supported vendors_ |
|
||||
|:--------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Opensource Servers | [LocalAI](https://localai.io/) (multimodal) · [Ollama](https://ollama.com/) |
|
||||
| Local Servers | [LM Studio](https://lmstudio.ai/) |
|
||||
| Multimodal services | [Azure](https://azure.microsoft.com/en-us/products/ai-services/openai-service) · [Anthropic](https://anthropic.com) · [Google Gemini](https://ai.google.dev/) · [OpenAI](https://platform.openai.com/docs/overview) |
|
||||
| Language services | [Alibaba](https://www.alibabacloud.com/en/product/modelstudio) · [DeepSeek](https://deepseek.com) · [Groq](https://wow.groq.com/) · [Mistral](https://mistral.ai/) · [OpenRouter](https://openrouter.ai/) · [Perplexity](https://www.perplexity.ai/) · [Together AI](https://www.together.ai/) |
|
||||
| Image services | [Prodia](https://prodia.com/) (SDXL) |
|
||||
| Speech services | [ElevenLabs](https://elevenlabs.io) (Voice synthesis / cloning) |
|
||||
|
||||
Add extra functionality with these integrations:
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ How to set up AI models and features in big-AGI.
|
||||
|
||||
- **Cloud AI Services**:
|
||||
- Easy API key configuration:
|
||||
[Alibaba](https://bailian.console.alibabacloud.com/?apiKey=1#/api-key),
|
||||
[Anthropic](https://console.anthropic.com/settings/keys),
|
||||
[Deepseek](https://platform.deepseek.com/api_keys),
|
||||
[Google Gemini](https://aistudio.google.com/app/apikey),
|
||||
|
||||
@@ -23,6 +23,8 @@ MDB_URI=
|
||||
OPENAI_API_KEY=
|
||||
OPENAI_API_HOST=
|
||||
OPENAI_API_ORG_ID=
|
||||
ALIBABA_API_HOST=
|
||||
ALIBABA_API_KEY=
|
||||
AZURE_OPENAI_API_ENDPOINT=
|
||||
AZURE_OPENAI_API_KEY=
|
||||
ANTHROPIC_API_KEY=
|
||||
@@ -88,6 +90,8 @@ requiring the user to enter an API key
|
||||
| `OPENAI_API_KEY` | API key for OpenAI | Recommended |
|
||||
| `OPENAI_API_HOST` | Changes the backend host for the OpenAI vendor, to enable platforms such as Helicone and CloudFlare AI Gateway | Optional |
|
||||
| `OPENAI_API_ORG_ID` | Sets the "OpenAI-Organization" header field to support organization users | Optional |
|
||||
| `ALIBABA_API_HOST` | The Alibaba AI OpenAI-compatible endpoint | Optional |
|
||||
| `ALIBABA_API_KEY` | The API key for Alibaba AI | Optional |
|
||||
| `AZURE_OPENAI_API_ENDPOINT` | Azure OpenAI endpoint - host only, without the path | Optional, but if set `AZURE_OPENAI_API_KEY` must also be set |
|
||||
| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key, see [config-azure-openai.md](config-azure-openai.md) | Optional, but if set `AZURE_OPENAI_API_ENDPOINT` must also be set |
|
||||
| `ANTHROPIC_API_KEY` | The API key for Anthropic | Optional |
|
||||
|
||||
@@ -16,6 +16,8 @@ stringData:
|
||||
OPENAI_API_KEY: ""
|
||||
OPENAI_API_HOST: ""
|
||||
OPENAI_API_ORG_ID: ""
|
||||
ALIBABA_API_HOST: ""
|
||||
ALIBABA_API_KEY: ""
|
||||
AZURE_OPENAI_API_ENDPOINT: ""
|
||||
AZURE_OPENAI_API_KEY: ""
|
||||
ANTHROPIC_API_KEY: ""
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { SvgIcon, SvgIconProps } from '@mui/joy';
|
||||
|
||||
// This icon has been converted from the official SVG of the Alibaba Cloud logo
|
||||
export function AlibabaCloudIcon(props: SvgIconProps) {
|
||||
return <SvgIcon viewBox='0 0 24 24' width='24' height='24' {...props}>
|
||||
<path d='m 8.025,12.9 h 7.95 v -1.8 h -7.95 z' />
|
||||
<path d='M 19.9875,4.5 H 14.7 l 1.275,1.8 3.8625,1.2 C 20.55,7.725 21,8.3625 21,9.1125 v 5.775 c 0,0.75 -0.45,1.3875 -1.1625,1.6125 L 15.975,17.7 14.7,19.5 h 5.2875 C 22.2375,19.5 24,17.7 24,15.4875 V 8.5125 C 24,6.2625 22.2,4.5 19.9875,4.5 m -15.975,0 H 9.3 L 8.025,6.3 4.1625,7.5 A 1.6875,1.6875 0 0 0 3,9.1125 v 5.775 c 0,0.75 0.45,1.3875 1.1625,1.6125 L 8.025,17.7 9.3,19.5 H 4.0125 C 1.7625,19.5 0,17.7 0,15.4875 V 8.5125 C 0,6.2625 1.8,4.5 4.0125,4.5' />
|
||||
</SvgIcon>;
|
||||
}
|
||||
@@ -345,6 +345,16 @@ export function prettyShortChatModelName(model: string | undefined): string {
|
||||
}
|
||||
// [LocalAI?]
|
||||
if (model.endsWith('.bin')) return model.slice(0, -4);
|
||||
// [Alibaba]
|
||||
if (model.startsWith('alibaba-qwen-') || model.startsWith('qwen-')) {
|
||||
return model
|
||||
.replace('alibaba-', ' ')
|
||||
.replace('qwen', 'Qwen')
|
||||
.replace('max', 'Max')
|
||||
.replace('plus', 'Plus')
|
||||
.replace('turbo', 'Turbo')
|
||||
.replaceAll('-', ' ');
|
||||
}
|
||||
// [Anthropic]
|
||||
const prettyAnthropic = _prettyAnthropicModelName(model);
|
||||
if (prettyAnthropic) return prettyAnthropic;
|
||||
|
||||
@@ -22,6 +22,7 @@ Built with tRPC, it manages the lifecycle of AI-generated content from request t
|
||||
|
||||
| Service | Chat | Function Calling | Multi-Modal Input | Cont. (1) | Streaming | Idiosyncratic |
|
||||
|------------|------------|------------------|-------------------|-----------|-----------|---------------|
|
||||
| Alibaba | ✅ | ✅ | | ✅ | Yes + 📦 | |
|
||||
| Anthropic | ✅ | ✅ + Parallel | Img: ✅ | ✅ | Yes + 📦 | |
|
||||
| Azure | ✅ | ✅ | | ✅ | Yes + 📦 | |
|
||||
| Deepseek | ✅ | ❌ (rejected) | | ✅ | Yes + 📦 | |
|
||||
|
||||
@@ -74,6 +74,7 @@ export function createChatGenerateDispatch(access: AixAPI_Access, model: AixAPI_
|
||||
chatGenerateParse: streaming ? createOpenAIChatCompletionsChunkParser() : createOpenAIChatCompletionsParserNS(),
|
||||
};
|
||||
|
||||
case 'alibaba':
|
||||
case 'azure':
|
||||
case 'deepseek':
|
||||
case 'groq':
|
||||
|
||||
@@ -49,6 +49,7 @@ export const backendRouter = createTRPCRouter({
|
||||
.query(async ({ ctx: _unused }): Promise<BackendCapabilities> => {
|
||||
return {
|
||||
// llms
|
||||
hasLlmAlibaba: !!env.ALIBABA_API_KEY || !!env.ALIBABA_API_HOST,
|
||||
hasLlmAnthropic: !!env.ANTHROPIC_API_KEY,
|
||||
hasLlmAzureOpenAI: !!env.AZURE_OPENAI_API_KEY && !!env.AZURE_OPENAI_API_ENDPOINT,
|
||||
hasLlmDeepseek: !!env.DEEPSEEK_API_KEY,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
export interface BackendCapabilities {
|
||||
// llms
|
||||
hasLlmAlibaba: boolean;
|
||||
hasLlmAnthropic: boolean;
|
||||
hasLlmAzureOpenAI: boolean;
|
||||
hasLlmDeepseek: boolean;
|
||||
@@ -48,6 +49,7 @@ const useBackendCapabilitiesStore = create<BackendStore>()(
|
||||
(set) => ({
|
||||
|
||||
// initial values
|
||||
hasLlmAlibaba: false,
|
||||
hasLlmAnthropic: false,
|
||||
hasLlmAzureOpenAI: false,
|
||||
hasLlmDeepseek: false,
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Vision } from '~/common/stores/llms/llms.types';
|
||||
|
||||
import type { ModelDescriptionSchema } from '../../llm.server.types';
|
||||
|
||||
import { fromManualMapping, ManualMappings } from './models.data';
|
||||
|
||||
// - Models: https://www.alibabacloud.com/help/en/model-studio/getting-started/models
|
||||
// - Pricing: https://www.alibabacloud.com/en/product/modelstudio?_p_lc=1&spm=a3c0i.11852017.6791778070.50.46f07ac9erixlG#J_9325669630
|
||||
|
||||
const _knownAlibabaChatModels: ManualMappings = [
|
||||
// Commercial Models
|
||||
{
|
||||
idPrefix: 'qwen-max',
|
||||
label: 'Qwen-Max',
|
||||
description: 'Best inference performance among Qwen models, especially for complex tasks. 32K context.',
|
||||
contextWindow: 32768,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 8192,
|
||||
chatPrice: { input: 0.0016, output: 0.0064 },
|
||||
benchmark: { cbaElo: 1332 },
|
||||
},
|
||||
{
|
||||
idPrefix: 'qwen-plus',
|
||||
label: 'Qwen-Plus',
|
||||
description: 'Balanced performance, speed, and cost. 131K context.',
|
||||
contextWindow: 131072,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 8192,
|
||||
chatPrice: { input: 0.0004, output: 0.0012 },
|
||||
benchmark: { cbaElo: 1282 },
|
||||
},
|
||||
{
|
||||
idPrefix: 'qwen-turbo',
|
||||
label: 'Qwen-Turbo',
|
||||
description: 'Fast speed and low cost, suitable for simple tasks. 1M context.',
|
||||
contextWindow: 1000000,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 8192,
|
||||
chatPrice: { input: 0.00005, output: 0.0002 },
|
||||
},
|
||||
|
||||
// Vision Models
|
||||
{
|
||||
idPrefix: 'qwen-vl-max',
|
||||
label: 'Qwen-VL Max',
|
||||
description: 'Enhanced visual reasoning and instruction-following capabilities.',
|
||||
contextWindow: 7500,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision],
|
||||
maxCompletionTokens: 1500,
|
||||
chatPrice: { input: 'free', output: 'free' }, // Time-limited free trial
|
||||
},
|
||||
{
|
||||
idPrefix: 'qwen-vl-plus',
|
||||
label: 'Qwen-VL Plus',
|
||||
description: 'Enhanced detail and text recognition for visual tasks.',
|
||||
contextWindow: 7500,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision],
|
||||
maxCompletionTokens: 1500,
|
||||
chatPrice: { input: 'free', output: 'free' }, // Time-limited free trial
|
||||
},
|
||||
|
||||
// Open Source Models - Qwen2.5
|
||||
{
|
||||
idPrefix: 'qwen2.5-72b-instruct',
|
||||
label: 'Qwen 2.5 72B',
|
||||
description: 'Latest Qwen series, 131K context.',
|
||||
contextWindow: 131072,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 8192,
|
||||
chatPrice: { input: 'free', output: 'free' }, // Time-limited free trial
|
||||
},
|
||||
{
|
||||
idPrefix: 'qwen2.5-14b-instruct-1m',
|
||||
label: 'Qwen 2.5 14B (1M)',
|
||||
description: 'Latest Qwen series with 1M context.',
|
||||
contextWindow: 1000000,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 8192,
|
||||
chatPrice: { input: 'free', output: 'free' }, // Time-limited free trial
|
||||
},
|
||||
{
|
||||
idPrefix: 'qwen2.5-7b-instruct-1m',
|
||||
label: 'Qwen 2.5 7B (1M)',
|
||||
description: 'Latest Qwen series with 1M context.',
|
||||
contextWindow: 1000000,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 8192,
|
||||
chatPrice: { input: 'free', output: 'free' }, // Time-limited free trial
|
||||
},
|
||||
|
||||
// Open Source Models - Qwen2
|
||||
{
|
||||
idPrefix: 'qwen2-7b-instruct',
|
||||
label: 'Qwen 2 7B',
|
||||
description: 'Open source Qwen2 model.',
|
||||
contextWindow: 131072,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn],
|
||||
maxCompletionTokens: 6144,
|
||||
chatPrice: { input: 'free', output: 'free' }, // Time-limited free trial
|
||||
},
|
||||
] as const;
|
||||
|
||||
|
||||
export function alibabaModelToModelDescription(alibabaModelId: string, created?: number): ModelDescriptionSchema {
|
||||
// create is a number like '1728632029' - convert to Month/Year
|
||||
// const createdDate = created ? new Date(created * 1000) : undefined;
|
||||
// const createdStr = createdDate?.toLocaleString('en-US', { month: 'short', year: 'numeric' });
|
||||
// NOTE: as of Feb 2025, reports that the 4 Qwen models were created in Oct 2024.
|
||||
// So we're not using the created date for now, as to not confuse Users.
|
||||
return fromManualMapping(_knownAlibabaChatModels, alibabaModelId, created, undefined, {
|
||||
idPrefix: alibabaModelId,
|
||||
label: alibabaModelId.replaceAll(/[_-]/g, ' '),
|
||||
description: 'New Alibaba Model',
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Vision], // assume..
|
||||
});
|
||||
}
|
||||
|
||||
export function alibabaModelSort(a: ModelDescriptionSchema, b: ModelDescriptionSchema) {
|
||||
// sort by the order in the known models list
|
||||
const aIndex = _knownAlibabaChatModels.findIndex(m => a.id.startsWith(m.idPrefix));
|
||||
const bIndex = _knownAlibabaChatModels.findIndex(m => b.id.startsWith(m.idPrefix));
|
||||
if (aIndex !== -1 && bIndex !== -1)
|
||||
return aIndex - bIndex;
|
||||
return a.id.localeCompare(b.id);
|
||||
}
|
||||
@@ -14,11 +14,12 @@ import { fixupHost } from '~/common/util/urlUtils';
|
||||
import { OpenAIWire_API_Images_Generations, OpenAIWire_API_Models_List, OpenAIWire_API_Moderations_Create } from '~/modules/aix/server/dispatch/wiretypes/openai.wiretypes';
|
||||
|
||||
import { ListModelsResponse_schema, ModelDescriptionSchema } from '../llm.server.types';
|
||||
import { alibabaModelSort, alibabaModelToModelDescription } from './models/alibaba.models';
|
||||
import { azureModelToModelDescription, openAIModelFilter, openAIModelToModelDescription, openAISortModels } from './models/openai.models';
|
||||
import { deepseekModelFilter, deepseekModelSort, deepseekModelToModelDescription } from './models/deepseek.models';
|
||||
import { fireworksAIHeuristic, fireworksAIModelsToModelDescriptions } from './models/fireworksai.models';
|
||||
import { groqModelFilter, groqModelSortFn, groqModelToModelDescription } from './models/groq.models';
|
||||
import { lmStudioModelToModelDescription, localAIModelToModelDescription, localAIModelSortFn } from './models/models.data';
|
||||
import { lmStudioModelToModelDescription, localAIModelSortFn, localAIModelToModelDescription } from './models/models.data';
|
||||
import { mistralModelsSort, mistralModelToModelDescription } from './models/mistral.models';
|
||||
import { openPipeModelDescriptions, openPipeModelSort, openPipeModelToModelDescriptions } from './models/openpipe.models';
|
||||
import { openRouterModelFamilySortFn, openRouterModelToModelDescription } from './models/openrouter.models';
|
||||
@@ -29,7 +30,7 @@ import { xaiModelDescriptions, xaiModelSort } from './models/xai.models';
|
||||
|
||||
|
||||
const openAIDialects = z.enum([
|
||||
'azure', 'deepseek', 'groq', 'lmstudio', 'localai', 'mistral', 'openai', 'openpipe', 'openrouter', 'perplexity', 'togetherai', 'xai',
|
||||
'alibaba', 'azure', 'deepseek', 'groq', 'lmstudio', 'localai', 'mistral', 'openai', 'openpipe', 'openrouter', 'perplexity', 'togetherai', 'xai',
|
||||
]);
|
||||
export type OpenAIDialects = z.infer<typeof openAIDialects>;
|
||||
|
||||
@@ -157,6 +158,12 @@ export const llmOpenAIRouter = createTRPCRouter({
|
||||
// every dialect has a different way to enumerate models - we execute the mapping on the server side
|
||||
switch (access.dialect) {
|
||||
|
||||
case 'alibaba':
|
||||
models = openAIModels
|
||||
.map(({ id, created }) => alibabaModelToModelDescription(id, created))
|
||||
.sort(alibabaModelSort);
|
||||
break;
|
||||
|
||||
case 'deepseek':
|
||||
models = openAIModels
|
||||
.filter(({ id }) => deepseekModelFilter(id))
|
||||
@@ -343,6 +350,7 @@ export const llmOpenAIRouter = createTRPCRouter({
|
||||
});
|
||||
|
||||
|
||||
const DEFAULT_ALIBABA_HOST = 'https://dashscope-intl.aliyuncs.com/compatible-mode';
|
||||
const DEFAULT_HELICONE_OPENAI_HOST = 'oai.hconeai.com';
|
||||
const DEFAULT_DEEPSEEK_HOST = 'https://api.deepseek.com';
|
||||
const DEFAULT_GROQ_HOST = 'https://api.groq.com/openai';
|
||||
@@ -358,6 +366,21 @@ const DEFAULT_XAI_HOST = 'https://api.x.ai';
|
||||
export function openAIAccess(access: OpenAIAccessSchema, modelRefId: string | null, apiPath: string): { headers: HeadersInit, url: string } {
|
||||
switch (access.dialect) {
|
||||
|
||||
case 'alibaba':
|
||||
const alibabaOaiKey = access.oaiKey || env.ALIBABA_API_KEY || '';
|
||||
const alibabaOaiHost = fixupHost(access.oaiHost || env.ALIBABA_API_HOST || DEFAULT_ALIBABA_HOST, apiPath);
|
||||
if (!alibabaOaiKey || !alibabaOaiHost)
|
||||
throw new Error('Missing Alibaba API Key. Add it on the UI or server side (your deployment).');
|
||||
|
||||
return {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${alibabaOaiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
url: alibabaOaiHost + apiPath,
|
||||
};
|
||||
|
||||
case 'azure':
|
||||
const azureKey = access.oaiKey || env.AZURE_OPENAI_API_KEY || '';
|
||||
const azureHost = fixupHost(access.oaiHost || env.AZURE_OPENAI_API_ENDPOINT || '', apiPath);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Typography } from '@mui/joy';
|
||||
|
||||
import type { DModelsServiceId } from '~/common/stores/llms/llms.service.types';
|
||||
import { AlreadySet } from '~/common/components/AlreadySet';
|
||||
import { ExternalLink } from '~/common/components/ExternalLink';
|
||||
import { FormInputKey } from '~/common/components/forms/FormInputKey';
|
||||
import { FormTextField } from '~/common/components/forms/FormTextField';
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
import { Link } from '~/common/components/Link';
|
||||
import { SetupFormRefetchButton } from '~/common/components/forms/SetupFormRefetchButton';
|
||||
import { useToggleableBoolean } from '~/common/util/hooks/useToggleableBoolean';
|
||||
|
||||
import { ApproximateCosts } from '../ApproximateCosts';
|
||||
import { useLlmUpdateModels } from '../../llm.client.hooks';
|
||||
import { useServiceSetup } from '../useServiceSetup';
|
||||
|
||||
import { ModelVendorAlibaba } from './alibaba.vendor';
|
||||
|
||||
|
||||
const CLIENT_ALIBABA_DEFAULT_HOST = 'https://dashscope-intl.aliyuncs.com/compatible-mode';
|
||||
const ALIBABA_REG_LINK = 'https://bailian.console.alibabacloud.com/?apiKey=1#/api-key';
|
||||
const ALIBABA_MODELS = 'https://www.alibabacloud.com/help/en/model-studio/getting-started/models';
|
||||
|
||||
|
||||
export function AlibabaServiceSetup(props: { serviceId: DModelsServiceId }) {
|
||||
|
||||
// state
|
||||
const advanced = useToggleableBoolean();
|
||||
|
||||
// external state
|
||||
const {
|
||||
service, serviceAccess, serviceHasBackendCap, serviceHasLLMs,
|
||||
serviceSetupValid, updateSettings,
|
||||
} = useServiceSetup(props.serviceId, ModelVendorAlibaba);
|
||||
|
||||
// derived state
|
||||
const { oaiKey: alibabaOaiKey, oaiHost: alibabaOaiHost } = serviceAccess;
|
||||
const needsUserKey = !serviceHasBackendCap;
|
||||
const shallFetchSucceed = !needsUserKey || (!!alibabaOaiKey && serviceSetupValid);
|
||||
const showKeyError = !!alibabaOaiKey && !serviceSetupValid;
|
||||
|
||||
// fetch models
|
||||
const { isFetching, refetch, isError, error } =
|
||||
useLlmUpdateModels(!serviceHasLLMs && shallFetchSucceed, service);
|
||||
|
||||
return <>
|
||||
|
||||
<ApproximateCosts serviceId={service?.id}>
|
||||
<div>
|
||||
<Typography level='body-sm'>
|
||||
Alibaba Cloud supports the <ExternalLink href={ALIBABA_MODELS}>following models</ExternalLink> via
|
||||
the OpenAI-compatible endpoint.
|
||||
</Typography>
|
||||
</div>
|
||||
</ApproximateCosts>
|
||||
|
||||
<FormInputKey
|
||||
autoCompleteId='alibaba-key' label='Alibaba Cloud API Key'
|
||||
rightLabel={needsUserKey
|
||||
? alibabaOaiKey && <Link level='body-sm' href={ALIBABA_REG_LINK} target='_blank'>get API key</Link>
|
||||
: <AlreadySet />
|
||||
}
|
||||
value={alibabaOaiKey}
|
||||
onChange={value => updateSettings({ alibabaOaiKey: value })}
|
||||
required={needsUserKey} isError={showKeyError}
|
||||
placeholder={needsUserKey ? 'sk-...' : ''}
|
||||
/>
|
||||
|
||||
{/*<Typography level='body-sm'>*/}
|
||||
{/* Alibaba Cloud Qwen models provide high-quality language model capabilities.*/}
|
||||
{/* See the <ExternalLink href={ALIBABA_REG_LINK}>Alibaba Cloud Model Studio</ExternalLink> for more information.*/}
|
||||
{/*</Typography>*/}
|
||||
|
||||
{advanced.on && <FormTextField
|
||||
autoCompleteId='alibaba-host'
|
||||
title='API Endpoint'
|
||||
tooltip={`The API endpoint for the Alibaba Cloud OpenAI service, to be used instead of the default endpoint.`}
|
||||
placeholder={`e.g., ${CLIENT_ALIBABA_DEFAULT_HOST}`}
|
||||
value={alibabaOaiHost}
|
||||
onChange={text => updateSettings({ alibabaOaiHost: text })}
|
||||
/>}
|
||||
|
||||
<SetupFormRefetchButton refetch={refetch} disabled={/*!shallFetchSucceed ||*/ isFetching} loading={isFetching} error={isError} advanced={advanced} />
|
||||
|
||||
{isError && <InlineError error={error} />}
|
||||
|
||||
</>;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { AlibabaCloudIcon } from '~/common/components/icons/vendors/AlibabaCloudIcon';
|
||||
|
||||
import type { IModelVendor } from '../IModelVendor';
|
||||
import type { OpenAIAccessSchema } from '../../server/openai/openai.router';
|
||||
import { ModelVendorOpenAI } from '../openai/openai.vendor';
|
||||
|
||||
import { AlibabaServiceSetup } from './AlibabaServiceSetup';
|
||||
|
||||
|
||||
interface DAlibabaServiceSettings {
|
||||
alibabaOaiKey: string;
|
||||
alibabaOaiHost: string;
|
||||
}
|
||||
|
||||
export const ModelVendorAlibaba: IModelVendor<DAlibabaServiceSettings, OpenAIAccessSchema> = {
|
||||
id: 'alibaba',
|
||||
name: 'Alibaba Cloud',
|
||||
displayRank: 35,
|
||||
location: 'cloud',
|
||||
instanceLimit: 1,
|
||||
hasBackendCapKey: 'hasLlmAlibaba',
|
||||
|
||||
// components
|
||||
Icon: AlibabaCloudIcon,
|
||||
ServiceSetupComponent: AlibabaServiceSetup,
|
||||
|
||||
// functions
|
||||
initializeSetup: () => ({
|
||||
alibabaOaiKey: '',
|
||||
alibabaOaiHost: '',
|
||||
}),
|
||||
validateSetup: (setup) => {
|
||||
return setup.alibabaOaiKey?.length >= 32;
|
||||
},
|
||||
getTransportAccess: (partialSetup) => ({
|
||||
dialect: 'alibaba',
|
||||
oaiKey: partialSetup?.alibabaOaiKey || '',
|
||||
oaiOrg: '',
|
||||
oaiHost: partialSetup?.alibabaOaiHost || '',
|
||||
heliKey: '',
|
||||
moderationCheck: false,
|
||||
}),
|
||||
|
||||
// OpenAI transport ('alibaba' dialect in 'access')
|
||||
rpcUpdateModelsOrThrow: ModelVendorOpenAI.rpcUpdateModelsOrThrow,
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ModelVendorAlibaba } from './alibaba/alibaba.vendor';
|
||||
import { ModelVendorAnthropic } from './anthropic/anthropic.vendor';
|
||||
import { ModelVendorAzure } from './azure/azure.vendor';
|
||||
import { ModelVendorDeepseek } from './deepseek/deepseekai.vendor';
|
||||
@@ -18,6 +19,7 @@ import type { IModelVendor } from './IModelVendor';
|
||||
|
||||
|
||||
export type ModelVendorId =
|
||||
| 'alibaba'
|
||||
| 'anthropic'
|
||||
| 'azure'
|
||||
| 'deepseek'
|
||||
@@ -37,6 +39,7 @@ export type ModelVendorId =
|
||||
|
||||
/** Global: Vendor Instances Registry **/
|
||||
const MODEL_VENDOR_REGISTRY: Record<ModelVendorId, IModelVendor> = {
|
||||
alibaba: ModelVendorAlibaba,
|
||||
anthropic: ModelVendorAnthropic,
|
||||
azure: ModelVendorAzure,
|
||||
deepseek: ModelVendorDeepseek,
|
||||
|
||||
@@ -21,6 +21,10 @@ export const env = createEnv({
|
||||
OPENAI_API_HOST: z.string().url().optional(),
|
||||
OPENAI_API_ORG_ID: z.string().optional(),
|
||||
|
||||
// LLM: Alibaba (OpenAI)
|
||||
ALIBABA_API_HOST: z.string().url().optional(),
|
||||
ALIBABA_API_KEY: z.string().optional(),
|
||||
|
||||
// LLM: Azure OpenAI
|
||||
AZURE_OPENAI_API_ENDPOINT: z.string().url().optional(),
|
||||
AZURE_OPENAI_API_KEY: z.string().optional(),
|
||||
|
||||
Reference in New Issue
Block a user