From e3e8df3d530aa8f0f07ad9ff89e9e9477ec3b27c Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 11 Jun 2025 20:07:19 +0200 Subject: [PATCH 1/2] togetherAI added togetherAI provider : TogetherAI provider --- package.json | 1 + .../presenter/configPresenter/providers.ts | 33 ++++---- src/main/presenter/index.ts | 4 +- .../presenter/llmProviderPresenter/index.ts | 3 + .../providers/openAICompatibleProvider.ts | 2 +- .../providers/togetherProvider.ts | 82 +++++++++++++++++++ 6 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 src/main/presenter/llmProviderPresenter/providers/togetherProvider.ts diff --git a/package.json b/package.json index 7e83a7b88..d6733ff33 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "pdf-parse-new": "^1.3.9", "pyodide": "^0.27.5", "sharp": "^0.33.5", + "together-ai": "^0.16.0", "tokenx": "^0.4.1", "turndown": "^7.2.0", "undici": "^7.8.0", diff --git a/src/main/presenter/configPresenter/providers.ts b/src/main/presenter/configPresenter/providers.ts index aae676e0d..4ee1e27f9 100644 --- a/src/main/presenter/configPresenter/providers.ts +++ b/src/main/presenter/configPresenter/providers.ts @@ -197,21 +197,21 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [ // defaultBaseUrl: 'https://api.ocoolai.com' // } // }, - // { - // id: 'together', - // name: 'Together', - // apiType: 'together', - // apiKey: '', - // baseUrl: 'https://api.tohgether.xyz', - // enable: false, - // websites: { - // official: 'https://www.together.ai/', - // apiKey: 'https://api.together.ai/settings/api-keys', - // docs: 'https://docs.together.ai/docs/introduction', - // models: 'https://docs.together.ai/docs/chat-models', - // defaultBaseUrl: 'https://api.tohgether.xyz' - // } - // }, + { + id: 'together', + name: 'Together', + apiType: 'openai', + apiKey: '', + baseUrl: 'https://api.together.xyz/v1', + enable: false, + websites: { + official: 'https://www.together.ai/', + apiKey: 'https://api.together.ai/settings/api-keys', + docs: 'https://docs.together.ai/docs/introduction', + models: 'https://docs.together.ai/docs/chat-models', + defaultBaseUrl: 'https://api.tohgether.xyz/v1' + } + }, { id: 'github', name: 'GitHub Models', @@ -238,7 +238,8 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [ official: 'https://github.com/features/copilot', apiKey: 'https://github.com/settings/tokens', docs: 'https://docs.github.com/en/copilot', - models: 'https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-chat-in-your-ide', + models: + 'https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-chat-in-your-ide', defaultBaseUrl: 'https://api.githubcopilot.com' } }, diff --git a/src/main/presenter/index.ts b/src/main/presenter/index.ts index f62b9cb10..93940a2ea 100644 --- a/src/main/presenter/index.ts +++ b/src/main/presenter/index.ts @@ -256,8 +256,8 @@ ipcMain.handle( return { error: `Method "${method}" not found or not a function on "${name}"` } } } catch ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - e: any + // eslint-disable-next-line @typescript-eslint/no-explicit-any + e: any ) { console.error('error on presenter handle', e) // 保留错误日志 return { error: e.message || String(e) } diff --git a/src/main/presenter/llmProviderPresenter/index.ts b/src/main/presenter/llmProviderPresenter/index.ts index d7d726ea6..6cd075537 100644 --- a/src/main/presenter/llmProviderPresenter/index.ts +++ b/src/main/presenter/llmProviderPresenter/index.ts @@ -25,6 +25,7 @@ import { AnthropicProvider } from './providers/anthropicProvider' import { DoubaoProvider } from './providers/doubaoProvider' import { ShowResponse } from 'ollama' import { CONFIG_EVENTS } from '@/events' +import { TogetherProvider } from './providers/togetherProvider' import { GrokProvider } from './providers/grokProvider' import { presenter } from '@/presenter' import { ZhipuProvider } from './providers/zhipuProvider' @@ -126,6 +127,8 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { return new OpenAIResponsesProvider(provider, this.configPresenter) case 'lmstudio': return new LMStudioProvider(provider, this.configPresenter) + case 'together': + return new TogetherProvider(provider, this.configPresenter) default: console.warn(`Unknown provider type: ${provider.apiType}`) return undefined diff --git a/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts b/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts index 6641507a7..cbff5b9a5 100644 --- a/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts +++ b/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts @@ -50,7 +50,7 @@ const SIZE_CONFIGURABLE_MODELS = ['gpt-image-1', 'gpt-4o-image', 'gpt-4o-all'] export class OpenAICompatibleProvider extends BaseLLMProvider { protected openai!: OpenAI - private isNoModelsApi: boolean = false + protected isNoModelsApi: boolean = false // 添加不支持 OpenAI 标准接口的供应商黑名单 private static readonly NO_MODELS_API_LIST: string[] = [] diff --git a/src/main/presenter/llmProviderPresenter/providers/togetherProvider.ts b/src/main/presenter/llmProviderPresenter/providers/togetherProvider.ts new file mode 100644 index 000000000..10aad0377 --- /dev/null +++ b/src/main/presenter/llmProviderPresenter/providers/togetherProvider.ts @@ -0,0 +1,82 @@ +import { LLM_PROVIDER, LLMResponse, MODEL_META } from '@shared/presenter' +import { OpenAICompatibleProvider } from './openAICompatibleProvider' +import { ConfigPresenter } from '../../configPresenter' +import Together from 'together-ai' +export class TogetherProvider extends OpenAICompatibleProvider { + constructor(provider: LLM_PROVIDER, configPresenter: ConfigPresenter) { + super(provider, configPresenter) + } + + async completions( + messages: { role: 'system' | 'user' | 'assistant'; content: string }[], + modelId: string, + temperature?: number, + maxTokens?: number + ): Promise { + return this.openAICompletion(messages, modelId, temperature, maxTokens) + } + + async summaries( + text: string, + modelId: string, + temperature?: number, + maxTokens?: number + ): Promise { + return this.openAICompletion( + [ + { + role: 'user', + content: `请总结以下内容,使用简洁的语言,突出重点:\n${text}` + } + ], + modelId, + temperature, + maxTokens + ) + } + + async generateText( + prompt: string, + modelId: string, + temperature?: number, + maxTokens?: number + ): Promise { + return this.openAICompletion( + [ + { + role: 'user', + content: prompt + } + ], + modelId, + temperature, + maxTokens + ) + } + protected async fetchProviderModels(options?: { timeout: number }): Promise { + // 检查供应商是否在黑名单中 + if (this.isNoModelsApi) { + // console.log(`Provider ${this.provider.name} does not support OpenAI models API`) + return this.models + } + return this.fetchTogetherAIModels(options) + } + + protected async fetchTogetherAIModels(options?: { timeout: number }): Promise { + const togetherai = new Together({ + apiKey: this.provider.apiKey + }) + const response = await togetherai.models.list(options) + return response + .filter((model) => model.type === 'chat' || model.type === 'language') + .map((model) => ({ + id: model.id, + name: model.id, + group: 'default', + providerId: this.provider.id, + isCustom: false, + contextLength: 4096, + maxTokens: 2048 + })) + } +} From a97279a1893791038e2010c7f5cefd5174c6bd9a Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 12 Jun 2025 10:38:55 +0200 Subject: [PATCH 2/2] apitype --- src/main/presenter/configPresenter/providers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/presenter/configPresenter/providers.ts b/src/main/presenter/configPresenter/providers.ts index 4ee1e27f9..e0b587112 100644 --- a/src/main/presenter/configPresenter/providers.ts +++ b/src/main/presenter/configPresenter/providers.ts @@ -200,7 +200,7 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [ { id: 'together', name: 'Together', - apiType: 'openai', + apiType: 'together', apiKey: '', baseUrl: 'https://api.together.xyz/v1', enable: false,