mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-15 20:16:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e64ee659e | |||
| f653ce1737 | |||
| eeabb69088 | |||
| 356cf029dd | |||
| 6e7b420347 | |||
| ee464838ac | |||
| ec5af1b4c7 |
@@ -2,6 +2,56 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
### [Version 1.132.17](https://github.com/lobehub/lobe-chat/compare/v1.132.16...v1.132.17)
|
||||
|
||||
<sup>Released on **2025-09-27**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fix input empty group name.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fix input empty group name, closes [#9441](https://github.com/lobehub/lobe-chat/issues/9441) ([f653ce1](https://github.com/lobehub/lobe-chat/commit/f653ce1))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 1.132.16](https://github.com/lobehub/lobe-chat/compare/v1.132.15...v1.132.16)
|
||||
|
||||
<sup>Released on **2025-09-26**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Resolve qwen-image-edit imageUrls conversion issue.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Resolve qwen-image-edit imageUrls conversion issue, closes [#9414](https://github.com/lobehub/lobe-chat/issues/9414) ([ec5af1b](https://github.com/lobehub/lobe-chat/commit/ec5af1b))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 1.132.15](https://github.com/lobehub/lobe-chat/compare/v1.132.14...v1.132.15)
|
||||
|
||||
<sup>Released on **2025-09-25**</sup>
|
||||
|
||||
@@ -336,7 +336,6 @@ export default class Browser {
|
||||
vibrancy: 'sidebar',
|
||||
visualEffectState: 'active',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
contextIsolation: true,
|
||||
preload: join(preloadDir, 'index.js'),
|
||||
},
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
[
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Resolve qwen-image-edit imageUrls conversion issue."]
|
||||
},
|
||||
"date": "2025-09-26",
|
||||
"version": "1.132.16"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Add proxyUrl configuration for NEW API provider."]
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lobehub/chat",
|
||||
"version": "1.132.15",
|
||||
"version": "1.132.17",
|
||||
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"./novita": "./src/aiModels/novita.ts",
|
||||
"./nvidia": "./src/aiModels/nvidia.ts",
|
||||
"./ollama": "./src/aiModels/ollama.ts",
|
||||
"./ollamacloud": "./src/aiModels/ollamacloud.ts",
|
||||
"./openai": "./src/aiModels/openai.ts",
|
||||
"./openrouter": "./src/aiModels/openrouter.ts",
|
||||
"./perplexity": "./src/aiModels/perplexity.ts",
|
||||
|
||||
@@ -36,6 +36,7 @@ import { default as newapi } from './newapi';
|
||||
import { default as novita } from './novita';
|
||||
import { default as nvidia } from './nvidia';
|
||||
import { default as ollama } from './ollama';
|
||||
import { default as ollamacloud } from './ollamacloud';
|
||||
import { default as openai } from './openai';
|
||||
import { default as openrouter } from './openrouter';
|
||||
import { default as perplexity } from './perplexity';
|
||||
@@ -120,6 +121,7 @@ export const LOBE_DEFAULT_MODEL_LIST = buildDefaultModelList({
|
||||
novita,
|
||||
nvidia,
|
||||
ollama,
|
||||
ollamacloud,
|
||||
openai,
|
||||
openrouter,
|
||||
perplexity,
|
||||
@@ -186,6 +188,7 @@ export { default as newapi } from './newapi';
|
||||
export { default as novita } from './novita';
|
||||
export { default as nvidia } from './nvidia';
|
||||
export { default as ollama } from './ollama';
|
||||
export { default as ollamacloud } from './ollamacloud';
|
||||
export { gptImage1ParamsSchema, default as openai, openaiChatModels } from './openai';
|
||||
export { default as openrouter } from './openrouter';
|
||||
export { default as perplexity } from './perplexity';
|
||||
|
||||
@@ -10,7 +10,6 @@ const ollamaChatModels: AIChatModelCard[] = [
|
||||
description:
|
||||
'DeepSeek V3.1:下一代推理模型,提升了复杂推理与链路思考能力,适合需要深入分析的任务。',
|
||||
displayName: 'DeepSeek V3.1',
|
||||
enabled: true,
|
||||
id: 'deepseek-v3.1:671b',
|
||||
type: 'chat',
|
||||
},
|
||||
@@ -23,7 +22,6 @@ const ollamaChatModels: AIChatModelCard[] = [
|
||||
description:
|
||||
'GPT-OSS 20B 是 OpenAI 发布的开源大语言模型,采用 MXFP4 量化技术,适合在高端消费级GPU或Apple Silicon Mac上运行。该模型在对话生成、代码编写和推理任务方面表现出色,支持函数调用和工具使用。',
|
||||
displayName: 'GPT-OSS 20B',
|
||||
enabled: true,
|
||||
id: 'gpt-oss:20b',
|
||||
releasedAt: '2025-08-05',
|
||||
type: 'chat',
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { AIChatModelCard } from '../types/aiModel';
|
||||
|
||||
const ollamaCloudModels: AIChatModelCard[] = [
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
reasoning: true,
|
||||
},
|
||||
contextWindowTokens: 163_840,
|
||||
description:
|
||||
'DeepSeek V3.1:下一代推理模型,提升了复杂推理与链路思考能力,适合需要深入分析的任务。',
|
||||
displayName: 'DeepSeek V3.1',
|
||||
enabled: true,
|
||||
id: 'deepseek-v3.1:671b',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
reasoning: true,
|
||||
},
|
||||
contextWindowTokens: 131_072,
|
||||
description:
|
||||
'GPT-OSS 20B 是 OpenAI 发布的开源大语言模型,采用 MXFP4 量化技术,适合在高端消费级GPU或Apple Silicon Mac上运行。该模型在对话生成、代码编写和推理任务方面表现出色,支持函数调用和工具使用。',
|
||||
displayName: 'GPT-OSS 20B',
|
||||
id: 'gpt-oss:20b',
|
||||
releasedAt: '2025-08-05',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
reasoning: true,
|
||||
},
|
||||
contextWindowTokens: 131_072,
|
||||
description:
|
||||
'GPT-OSS 120B 是 OpenAI 发布的大型开源语言模型,采用 MXFP4 量化技术,为旗舰级模型。需要多GPU或高性能工作站环境运行,在复杂推理、代码生成和多语言处理方面具备卓越性能,支持高级函数调用和工具集成。',
|
||||
displayName: 'GPT-OSS 120B',
|
||||
id: 'gpt-oss:120b',
|
||||
releasedAt: '2025-08-05',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
reasoning: true,
|
||||
},
|
||||
contextWindowTokens: 131_072,
|
||||
description:
|
||||
'Kimi K2 是由月之暗面 AI 开发的大规模混合专家 (MoE) 语言模型,具有 1 万亿总参数和每次前向传递 320 亿激活参数。它针对代理能力进行了优化,包括高级工具使用、推理和代码合成。',
|
||||
displayName: 'Kimi K2',
|
||||
id: 'kimi-k2:1t',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
},
|
||||
contextWindowTokens: 262_144,
|
||||
description:
|
||||
'阿里巴巴针对代理和编码任务的高性能长上下文模型。',
|
||||
displayName: 'Qwen3 Coder 480B',
|
||||
id: 'qwen3-coder:480b',
|
||||
type: 'chat',
|
||||
},
|
||||
];
|
||||
|
||||
export const allModels = [...ollamaCloudModels];
|
||||
|
||||
export default allModels;
|
||||
@@ -37,6 +37,7 @@ export enum ModelProvider {
|
||||
Novita = 'novita',
|
||||
Nvidia = 'nvidia',
|
||||
Ollama = 'ollama',
|
||||
OllamaCloud = 'ollamacloud',
|
||||
OpenAI = 'openai',
|
||||
OpenRouter = 'openrouter',
|
||||
PPIO = 'ppio',
|
||||
|
||||
@@ -19,6 +19,7 @@ export { LobeMoonshotAI } from './providers/moonshot';
|
||||
export { LobeNebiusAI } from './providers/nebius';
|
||||
export { LobeNewAPIAI } from './providers/newapi';
|
||||
export { LobeOllamaAI } from './providers/ollama';
|
||||
export { LobeOllamaCloudAI } from './providers/ollamacloud';
|
||||
export { LobeOpenAI } from './providers/openai';
|
||||
export { LobeOpenRouterAI } from './providers/openrouter';
|
||||
export { LobePerplexityAI } from './providers/perplexity';
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ModelProvider } from 'model-bank';
|
||||
|
||||
import { createOpenAICompatibleRuntime } from '../../core/openaiCompatibleFactory';
|
||||
import { processMultiProviderModelList } from '../../utils/modelParse';
|
||||
|
||||
export const LobeOllamaCloudAI = createOpenAICompatibleRuntime({
|
||||
baseURL: 'https://api.ollama.com/v1',
|
||||
chatCompletion: {
|
||||
handlePayload: (payload) => {
|
||||
const { model, ...rest } = payload;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
model,
|
||||
} as any;
|
||||
},
|
||||
},
|
||||
debug: {
|
||||
chatCompletion: () => process.env.DEBUG_OLLAMA_CLOUD_CHAT_COMPLETION === '1',
|
||||
},
|
||||
models: async ({ client }) => {
|
||||
try {
|
||||
const modelsPage = (await client.models.list()) as any;
|
||||
const modelList = Array.isArray(modelsPage?.data)
|
||||
? modelsPage.data
|
||||
: Array.isArray(modelsPage)
|
||||
? modelsPage
|
||||
: [];
|
||||
|
||||
return await processMultiProviderModelList(modelList, 'ollamacloud');
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'Failed to fetch Ollama Cloud models. Please ensure your Ollama Cloud API key is valid:',
|
||||
error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
provider: ModelProvider.OllamaCloud,
|
||||
});
|
||||
@@ -700,5 +700,167 @@ describe('createQwenImage', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should convert imageUrls array to imageUrl for qwen-image-edit', async () => {
|
||||
const mockImageUrl =
|
||||
'https://dashscope.oss-cn-beijing.aliyuncs.com/aigc/imageUrls-converted.jpg';
|
||||
|
||||
global.fetch = vi.fn().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
output: {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: [{ image: mockImageUrl }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request_id: 'req-imageUrls-123',
|
||||
}),
|
||||
});
|
||||
|
||||
const payload: CreateImagePayload = {
|
||||
model: 'qwen-image-edit',
|
||||
params: {
|
||||
prompt: 'Edit this image to add a dog',
|
||||
imageUrls: [
|
||||
'https://example.com/source-image-1.jpg',
|
||||
'https://example.com/source-image-2.jpg',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = await createQwenImage(payload, mockOptions);
|
||||
|
||||
expect(result).toEqual({
|
||||
imageUrl: mockImageUrl,
|
||||
});
|
||||
|
||||
const [url, options] = (fetch as any).mock.calls[0];
|
||||
const body = JSON.parse(options.body);
|
||||
|
||||
// Verify that the first imageUrl from imageUrls array was used
|
||||
expect(body.input.messages[0].content[0].image).toBe(
|
||||
'https://example.com/source-image-1.jpg',
|
||||
);
|
||||
});
|
||||
|
||||
it('should use first imageUrl when imageUrls has multiple elements', async () => {
|
||||
const mockImageUrl = 'https://dashscope.oss-cn-beijing.aliyuncs.com/aigc/first-element.jpg';
|
||||
|
||||
global.fetch = vi.fn().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
output: {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: [{ image: mockImageUrl }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request_id: 'req-first-element',
|
||||
}),
|
||||
});
|
||||
|
||||
const payload: CreateImagePayload = {
|
||||
model: 'qwen-image-edit',
|
||||
params: {
|
||||
prompt: 'Use the first image only',
|
||||
imageUrls: [
|
||||
'https://example.com/first-image.jpg',
|
||||
'https://example.com/second-image.jpg',
|
||||
'https://example.com/third-image.jpg',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await createQwenImage(payload, mockOptions);
|
||||
|
||||
const [url, options] = (fetch as any).mock.calls[0];
|
||||
const body = JSON.parse(options.body);
|
||||
|
||||
// Should use only the first image from the array
|
||||
expect(body.input.messages[0].content[0].image).toBe('https://example.com/first-image.jpg');
|
||||
});
|
||||
|
||||
it('should throw error when imageUrls is empty array', async () => {
|
||||
const payload: CreateImagePayload = {
|
||||
model: 'qwen-image-edit',
|
||||
params: {
|
||||
prompt: 'Edit this image',
|
||||
imageUrls: [], // Empty array
|
||||
},
|
||||
};
|
||||
|
||||
await expect(createQwenImage(payload, mockOptions)).rejects.toEqual(
|
||||
expect.objectContaining({
|
||||
errorType: 'ProviderBizError',
|
||||
provider: 'qwen',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should prioritize imageUrl over imageUrls when both are provided', async () => {
|
||||
const mockImageUrl = 'https://dashscope.oss-cn-beijing.aliyuncs.com/aigc/priority-test.jpg';
|
||||
|
||||
global.fetch = vi.fn().mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
output: {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: [{ image: mockImageUrl }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request_id: 'req-priority-test',
|
||||
}),
|
||||
});
|
||||
|
||||
const payload: CreateImagePayload = {
|
||||
model: 'qwen-image-edit',
|
||||
params: {
|
||||
prompt: 'Test priority between imageUrl and imageUrls',
|
||||
imageUrl: 'https://example.com/priority-image.jpg',
|
||||
imageUrls: [
|
||||
'https://example.com/should-not-use-1.jpg',
|
||||
'https://example.com/should-not-use-2.jpg',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await createQwenImage(payload, mockOptions);
|
||||
|
||||
const [url, options] = (fetch as any).mock.calls[0];
|
||||
const body = JSON.parse(options.body);
|
||||
|
||||
// Should use imageUrl, not imageUrls
|
||||
expect(body.input.messages[0].content[0].image).toBe(
|
||||
'https://example.com/priority-image.jpg',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when neither imageUrl nor imageUrls are provided', async () => {
|
||||
const payload: CreateImagePayload = {
|
||||
model: 'qwen-image-edit',
|
||||
params: {
|
||||
prompt: 'Edit this image',
|
||||
// Neither imageUrl nor imageUrls provided
|
||||
},
|
||||
};
|
||||
|
||||
await expect(createQwenImage(payload, mockOptions)).rejects.toEqual(
|
||||
expect.objectContaining({
|
||||
errorType: 'ProviderBizError',
|
||||
provider: 'qwen',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -98,8 +98,15 @@ async function createImageEdit(
|
||||
const endpoint = `https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation`;
|
||||
log('Creating image edit with model: %s, endpoint: %s', model, endpoint);
|
||||
|
||||
if (!params.imageUrl) {
|
||||
throw new Error('imageUrl is required for qwen-image-edit model');
|
||||
// Handle imageUrls to imageUrl conversion
|
||||
let imageUrl = params.imageUrl;
|
||||
if (!imageUrl && params.imageUrls && params.imageUrls.length > 0) {
|
||||
imageUrl = params.imageUrls[0];
|
||||
log('Converting imageUrls to imageUrl: using first image %s', imageUrl);
|
||||
}
|
||||
|
||||
if (!imageUrl) {
|
||||
throw new Error('imageUrl or imageUrls is required for qwen-image-edit model');
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
@@ -107,7 +114,7 @@ async function createImageEdit(
|
||||
input: {
|
||||
messages: [
|
||||
{
|
||||
content: [{ image: params.imageUrl }, { text: params.prompt }],
|
||||
content: [{ image: imageUrl }, { text: params.prompt }],
|
||||
role: 'user',
|
||||
},
|
||||
],
|
||||
@@ -152,10 +159,10 @@ async function createImageEdit(
|
||||
throw new Error('No image found in response content');
|
||||
}
|
||||
|
||||
const imageUrl = imageContent.image;
|
||||
log('Image edit generated successfully: %s', imageUrl);
|
||||
const resultImageUrl = imageContent.image;
|
||||
log('Image edit generated successfully: %s', resultImageUrl);
|
||||
|
||||
return { imageUrl };
|
||||
return { imageUrl: resultImageUrl };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,11 +231,11 @@ export async function createQwenImage(
|
||||
};
|
||||
}
|
||||
|
||||
const imageUrl = taskStatus.output.results[0].url;
|
||||
log('Image generated successfully: %s', imageUrl);
|
||||
const generatedImageUrl = taskStatus.output.results[0].url;
|
||||
log('Image generated successfully: %s', generatedImageUrl);
|
||||
|
||||
return {
|
||||
data: { imageUrl },
|
||||
data: { imageUrl: generatedImageUrl },
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import { LobeNewAPIAI } from './providers/newapi';
|
||||
import { LobeNovitaAI } from './providers/novita';
|
||||
import { LobeNvidiaAI } from './providers/nvidia';
|
||||
import { LobeOllamaAI } from './providers/ollama';
|
||||
import { LobeOllamaCloudAI } from './providers/ollamacloud';
|
||||
import { LobeOpenAI } from './providers/openai';
|
||||
import { LobeOpenRouterAI } from './providers/openrouter';
|
||||
import { LobePerplexityAI } from './providers/perplexity';
|
||||
@@ -99,6 +100,7 @@ export const providerRuntimeMap = {
|
||||
novita: LobeNovitaAI,
|
||||
nvidia: LobeNvidiaAI,
|
||||
ollama: LobeOllamaAI,
|
||||
ollamacloud: LobeOllamaCloudAI,
|
||||
openai: LobeOpenAI,
|
||||
openrouter: LobeOpenRouterAI,
|
||||
perplexity: LobePerplexityAI,
|
||||
|
||||
@@ -75,6 +75,7 @@ export interface UserKeyVaults extends SearchEngineKeyVaults {
|
||||
novita?: OpenAICompatibleKeyVault;
|
||||
nvidia?: OpenAICompatibleKeyVault;
|
||||
ollama?: OpenAICompatibleKeyVault;
|
||||
ollamacloud?: OpenAICompatibleKeyVault;
|
||||
openai?: OpenAICompatibleKeyVault;
|
||||
openrouter?: OpenAICompatibleKeyVault;
|
||||
password?: string;
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ const GroupItem = memo<SessionGroupItem>(({ id, name }) => {
|
||||
onChangeEnd={async (input) => {
|
||||
if (name !== input) {
|
||||
if (!input) return;
|
||||
if (input.length === 0 || input.length > 20)
|
||||
if (input.length === 0 || input.length > 20 || input.trim() === '')
|
||||
return message.warning(t('sessionGroup.tooLong'));
|
||||
|
||||
await updateSessionGroupName(id, input);
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ const CreateGroupModal = memo<CreateGroupModalProps>(
|
||||
onCancel?.(e);
|
||||
}}
|
||||
onOk={async (e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (input.length === 0 || input.length > 20)
|
||||
if (input.length === 0 || input.length > 20 || input.trim() === '')
|
||||
return message.warning(t('sessionGroup.tooLong'));
|
||||
|
||||
setLoading(true);
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
MoonshotProviderCard,
|
||||
NovitaProviderCard,
|
||||
NvidiaProviderCard,
|
||||
OllamaCloudProviderCard,
|
||||
OpenRouterProviderCard,
|
||||
PPIOProviderCard,
|
||||
PerplexityProviderCard,
|
||||
@@ -116,6 +117,7 @@ export const useProviderList = (): ProviderItem[] => {
|
||||
InfiniAIProviderCard,
|
||||
AkashChatProviderCard,
|
||||
Ai302ProviderCard,
|
||||
OllamaCloudProviderCard,
|
||||
],
|
||||
[
|
||||
AzureProvider,
|
||||
|
||||
@@ -37,6 +37,7 @@ import NewAPIProvider from './newapi';
|
||||
import NovitaProvider from './novita';
|
||||
import NvidiaProvider from './nvidia';
|
||||
import OllamaProvider from './ollama';
|
||||
import OllamaCloudProvider from './ollamacloud';
|
||||
import OpenAIProvider from './openai';
|
||||
import OpenRouterProvider from './openrouter';
|
||||
import PerplexityProvider from './perplexity';
|
||||
@@ -125,6 +126,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
|
||||
{ ...AzureProvider, chatModels: [] },
|
||||
AzureAIProvider,
|
||||
OllamaProvider,
|
||||
OllamaCloudProvider,
|
||||
VLLMProvider,
|
||||
XinferenceProvider,
|
||||
AnthropicProvider,
|
||||
@@ -233,6 +235,7 @@ export { default as NewAPIProviderCard } from './newapi';
|
||||
export { default as NovitaProviderCard } from './novita';
|
||||
export { default as NvidiaProviderCard } from './nvidia';
|
||||
export { default as OllamaProviderCard } from './ollama';
|
||||
export { default as OllamaCloudProviderCard } from './ollamacloud';
|
||||
export { default as OpenAIProviderCard } from './openai';
|
||||
export { default as OpenRouterProviderCard } from './openrouter';
|
||||
export { default as PerplexityProviderCard } from './perplexity';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ModelProviderCard } from '@/types/llm';
|
||||
|
||||
const OllamaCloud: ModelProviderCard = {
|
||||
chatModels: [],
|
||||
checkModel: 'gpt-oss:20b',
|
||||
description:
|
||||
'Ollama Cloud 提供官方托管的推理服务,开箱即用地访问 Ollama 模型库,并支持 OpenAI 兼容接口。',
|
||||
id: 'ollamacloud',
|
||||
modelsUrl: 'https://ollama.com/library',
|
||||
name: 'Ollama Cloud',
|
||||
settings: {
|
||||
sdkType: 'openai',
|
||||
showModelFetcher: true,
|
||||
},
|
||||
url: 'https://ollama.com/cloud',
|
||||
};
|
||||
|
||||
export default OllamaCloud;
|
||||
@@ -71,6 +71,8 @@ export const getLLMConfig = () => {
|
||||
WENXIN_API_KEY: z.string().optional(),
|
||||
|
||||
ENABLED_OLLAMA: z.boolean(),
|
||||
ENABLED_OLLAMA_CLOUD: z.boolean(),
|
||||
OLLAMA_CLOUD_API_KEY: z.string().optional(),
|
||||
|
||||
ENABLED_VLLM: z.boolean(),
|
||||
VLLM_API_KEY: z.string().optional(),
|
||||
@@ -267,6 +269,8 @@ export const getLLMConfig = () => {
|
||||
WENXIN_API_KEY: process.env.WENXIN_API_KEY,
|
||||
|
||||
ENABLED_OLLAMA: process.env.ENABLED_OLLAMA !== '0',
|
||||
ENABLED_OLLAMA_CLOUD: !!process.env.OLLAMA_CLOUD_API_KEY,
|
||||
OLLAMA_CLOUD_API_KEY: process.env.OLLAMA_CLOUD_API_KEY,
|
||||
|
||||
ENABLED_VLLM: !!process.env.VLLM_API_KEY,
|
||||
VLLM_API_KEY: process.env.VLLM_API_KEY,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { genServerLLMConfig } from './_deprecated';
|
||||
import { genServerAiProvidersConfig } from './genServerAiProviderConfig';
|
||||
import { parseAgentConfig } from './parseDefaultAgent';
|
||||
import { parseFilesConfig } from './parseFilesConfig';
|
||||
import { ollamacloud } from 'packages/model-bank/src';
|
||||
|
||||
export const getServerGlobalConfig = async () => {
|
||||
const { ACCESS_CODES, DEFAULT_AGENT_CONFIG } = getAppConfig();
|
||||
@@ -40,6 +41,9 @@ export const getServerGlobalConfig = async () => {
|
||||
enabled: isDesktop ? true : undefined,
|
||||
fetchOnClient: isDesktop ? false : !process.env.OLLAMA_PROXY_URL,
|
||||
},
|
||||
ollamacloud: {
|
||||
enabledKey: 'ENABLED_OLLAMA_CLOUD',
|
||||
},
|
||||
qwen: {
|
||||
withDeploymentName: true,
|
||||
},
|
||||
|
||||
@@ -98,6 +98,14 @@ const getParamsFromPayload = (provider: string, payload: ClientSecretPayload) =>
|
||||
return { apiKey };
|
||||
}
|
||||
|
||||
case ModelProvider.OllamaCloud: {
|
||||
const { OLLAMA_CLOUD_API_KEY } = llmConfig;
|
||||
|
||||
const apiKey = apiKeyManager.pick(payload?.apiKey || OLLAMA_CLOUD_API_KEY);
|
||||
|
||||
return { apiKey };
|
||||
}
|
||||
|
||||
case ModelProvider.TencentCloud: {
|
||||
const { TENCENT_CLOUD_API_KEY } = llmConfig;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user