@theia/ai-vercel-ai 1.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/vercel-ai-frontend-application-contribution.d.ts +21 -0
- package/lib/browser/vercel-ai-frontend-application-contribution.d.ts.map +1 -0
- package/lib/browser/vercel-ai-frontend-application-contribution.js +167 -0
- package/lib/browser/vercel-ai-frontend-application-contribution.js.map +1 -0
- package/lib/browser/vercel-ai-frontend-module.d.ts +4 -0
- package/lib/browser/vercel-ai-frontend-module.d.ts.map +1 -0
- package/lib/browser/vercel-ai-frontend-module.js +32 -0
- package/lib/browser/vercel-ai-frontend-module.js.map +1 -0
- package/lib/browser/vercel-ai-preferences.d.ts +8 -0
- package/lib/browser/vercel-ai-preferences.d.ts.map +1 -0
- package/lib/browser/vercel-ai-preferences.js +132 -0
- package/lib/browser/vercel-ai-preferences.js.map +1 -0
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.d.ts.map +1 -0
- package/lib/common/index.js +20 -0
- package/lib/common/index.js.map +1 -0
- package/lib/common/vercel-ai-language-models-manager.d.ts +45 -0
- package/lib/common/vercel-ai-language-models-manager.d.ts.map +1 -0
- package/lib/common/vercel-ai-language-models-manager.js +21 -0
- package/lib/common/vercel-ai-language-models-manager.js.map +1 -0
- package/lib/node/vercel-ai-backend-module.d.ts +4 -0
- package/lib/node/vercel-ai-backend-module.d.ts.map +1 -0
- package/lib/node/vercel-ai-backend-module.js +33 -0
- package/lib/node/vercel-ai-backend-module.js.map +1 -0
- package/lib/node/vercel-ai-language-model-factory.d.ts +14 -0
- package/lib/node/vercel-ai-language-model-factory.d.ts.map +1 -0
- package/lib/node/vercel-ai-language-model-factory.js +73 -0
- package/lib/node/vercel-ai-language-model-factory.js.map +1 -0
- package/lib/node/vercel-ai-language-model.d.ts +51 -0
- package/lib/node/vercel-ai-language-model.d.ts.map +1 -0
- package/lib/node/vercel-ai-language-model.js +305 -0
- package/lib/node/vercel-ai-language-model.js.map +1 -0
- package/lib/node/vercel-ai-language-models-manager-impl.d.ts +18 -0
- package/lib/node/vercel-ai-language-models-manager-impl.d.ts.map +1 -0
- package/lib/node/vercel-ai-language-models-manager-impl.js +96 -0
- package/lib/node/vercel-ai-language-models-manager-impl.js.map +1 -0
- package/lib/package.spec.d.ts +1 -0
- package/lib/package.spec.d.ts.map +1 -0
- package/lib/package.spec.js +26 -0
- package/lib/package.spec.js.map +1 -0
- package/package.json +56 -0
- package/src/browser/vercel-ai-frontend-application-contribution.ts +196 -0
- package/src/browser/vercel-ai-frontend-module.ts +31 -0
- package/src/browser/vercel-ai-preferences.ts +139 -0
- package/src/common/index.ts +16 -0
- package/src/common/vercel-ai-language-models-manager.ts +63 -0
- package/src/node/vercel-ai-backend-module.ts +35 -0
- package/src/node/vercel-ai-language-model-factory.ts +82 -0
- package/src/node/vercel-ai-language-model.ts +408 -0
- package/src/node/vercel-ai-language-models-manager-impl.ts +101 -0
- package/src/package.spec.ts +27 -0
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theia/ai-vercel-ai",
|
|
3
|
+
"version": "1.62.0",
|
|
4
|
+
"description": "Theia - Vercel AI SDK Integration",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@ai-sdk/anthropic": "^1.2.10",
|
|
7
|
+
"@ai-sdk/openai": "^1.3.21",
|
|
8
|
+
"@ai-sdk/provider": "^1.1.3",
|
|
9
|
+
"@theia/ai-core": "1.62.0",
|
|
10
|
+
"@theia/core": "1.62.0",
|
|
11
|
+
"@theia/filesystem": "1.62.0",
|
|
12
|
+
"@theia/workspace": "1.62.0",
|
|
13
|
+
"ai": "^4.3.13",
|
|
14
|
+
"tslib": "^2.6.2"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"theiaExtensions": [
|
|
20
|
+
{
|
|
21
|
+
"frontend": "lib/browser/vercel-ai-frontend-module",
|
|
22
|
+
"backend": "lib/node/vercel-ai-backend-module"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"theia-extension"
|
|
27
|
+
],
|
|
28
|
+
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/eclipse-theia/theia.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/eclipse-theia/theia/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/eclipse-theia/theia",
|
|
37
|
+
"files": [
|
|
38
|
+
"lib",
|
|
39
|
+
"src"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "theiaext build",
|
|
43
|
+
"clean": "theiaext clean",
|
|
44
|
+
"compile": "theiaext compile",
|
|
45
|
+
"lint": "theiaext lint",
|
|
46
|
+
"test": "theiaext test",
|
|
47
|
+
"watch": "theiaext watch"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@theia/ext-scripts": "1.62.0"
|
|
51
|
+
},
|
|
52
|
+
"nyc": {
|
|
53
|
+
"extends": "../../configs/nyc.json"
|
|
54
|
+
},
|
|
55
|
+
"gitHead": "a4e035ccc26fde24c2769a466cb18e3b54c517c5"
|
|
56
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { FrontendApplicationContribution, PreferenceService, PreferenceChange } from '@theia/core/lib/browser';
|
|
18
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
19
|
+
import { VercelAiLanguageModelsManager, VercelAiModelDescription, VercelAiProvider } from '../common';
|
|
20
|
+
import { ANTHROPIC_API_KEY_PREF, CUSTOM_ENDPOINTS_PREF, MODELS_PREF, OPENAI_API_KEY_PREF, VERCEL_AI_PROVIDER_ID } from './vercel-ai-preferences';
|
|
21
|
+
|
|
22
|
+
interface ModelConfig {
|
|
23
|
+
id: string;
|
|
24
|
+
model: string;
|
|
25
|
+
provider: VercelAiProvider;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@injectable()
|
|
29
|
+
export class VercelAiFrontendApplicationContribution implements FrontendApplicationContribution {
|
|
30
|
+
|
|
31
|
+
@inject(PreferenceService)
|
|
32
|
+
protected preferenceService: PreferenceService;
|
|
33
|
+
|
|
34
|
+
@inject(VercelAiLanguageModelsManager)
|
|
35
|
+
protected manager: VercelAiLanguageModelsManager;
|
|
36
|
+
|
|
37
|
+
onStart(): void {
|
|
38
|
+
this.preferenceService.ready.then(() => {
|
|
39
|
+
// Set up provider-specific API keys
|
|
40
|
+
const openaiApiKey = this.preferenceService.get<string>(OPENAI_API_KEY_PREF, undefined);
|
|
41
|
+
const anthropicApiKey = this.preferenceService.get<string>(ANTHROPIC_API_KEY_PREF, undefined);
|
|
42
|
+
|
|
43
|
+
// Set provider configs
|
|
44
|
+
if (openaiApiKey) {
|
|
45
|
+
this.manager.setProviderConfig('openai', { provider: 'openai', apiKey: openaiApiKey });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (anthropicApiKey) {
|
|
49
|
+
this.manager.setProviderConfig('anthropic', { provider: 'anthropic', apiKey: anthropicApiKey });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Initial setup of models
|
|
53
|
+
const models = this.preferenceService.get<ModelConfig[]>(MODELS_PREF, []);
|
|
54
|
+
this.manager.createOrUpdateLanguageModels(...models.map(model => this.createVercelAiModelDescription(model)));
|
|
55
|
+
|
|
56
|
+
const customModels = this.preferenceService.get<Partial<VercelAiModelDescription>[]>(CUSTOM_ENDPOINTS_PREF, []);
|
|
57
|
+
this.manager.createOrUpdateLanguageModels(...this.createCustomModelDescriptionsFromPreferences(customModels));
|
|
58
|
+
|
|
59
|
+
// Set up listeners for preference changes
|
|
60
|
+
this.preferenceService.onPreferenceChanged(this.handlePreferenceChange.bind(this));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected handlePreferenceChange(event: PreferenceChange): void {
|
|
65
|
+
switch (event.preferenceName) {
|
|
66
|
+
case OPENAI_API_KEY_PREF:
|
|
67
|
+
this.manager.setProviderConfig('openai', { provider: 'openai', apiKey: event.newValue });
|
|
68
|
+
break;
|
|
69
|
+
case ANTHROPIC_API_KEY_PREF:
|
|
70
|
+
this.manager.setProviderConfig('anthropic', { provider: 'anthropic', apiKey: event.newValue });
|
|
71
|
+
break;
|
|
72
|
+
case MODELS_PREF:
|
|
73
|
+
this.handleModelChanges(event);
|
|
74
|
+
break;
|
|
75
|
+
case CUSTOM_ENDPOINTS_PREF:
|
|
76
|
+
this.handleCustomModelChanges(event);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected handleModelChanges(event: PreferenceChange): void {
|
|
82
|
+
const newModels = this.ensureModelConfigArray(event.newValue);
|
|
83
|
+
const oldModels = this.ensureModelConfigArray(event.oldValue);
|
|
84
|
+
|
|
85
|
+
const oldModelIds = new Set(oldModels.map(m => m.id));
|
|
86
|
+
const newModelIds = new Set(newModels.map(m => m.id));
|
|
87
|
+
|
|
88
|
+
const modelsToRemove = [...oldModelIds].filter(modelId => !newModelIds.has(modelId));
|
|
89
|
+
const modelsToAdd = newModels.filter(model => !oldModelIds.has(model.id));
|
|
90
|
+
|
|
91
|
+
this.manager.removeLanguageModels(...modelsToRemove);
|
|
92
|
+
this.manager.createOrUpdateLanguageModels(...modelsToAdd.map(model => this.createVercelAiModelDescription(model)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected handleCustomModelChanges(event: PreferenceChange): void {
|
|
96
|
+
const newCustomModels = this.ensureCustomModelArray(event.newValue);
|
|
97
|
+
const oldCustomModels = this.ensureCustomModelArray(event.oldValue);
|
|
98
|
+
|
|
99
|
+
const oldModels = this.createCustomModelDescriptionsFromPreferences(oldCustomModels);
|
|
100
|
+
const newModels = this.createCustomModelDescriptionsFromPreferences(newCustomModels);
|
|
101
|
+
|
|
102
|
+
const modelsToRemove = oldModels.filter(model => !newModels.some(newModel => newModel.id === model.id));
|
|
103
|
+
const modelsToAddOrUpdate = newModels.filter(newModel =>
|
|
104
|
+
!oldModels.some(model =>
|
|
105
|
+
model.id === newModel.id &&
|
|
106
|
+
model.model === newModel.model &&
|
|
107
|
+
model.url === newModel.url &&
|
|
108
|
+
model.apiKey === newModel.apiKey &&
|
|
109
|
+
model.supportsStructuredOutput === newModel.supportsStructuredOutput &&
|
|
110
|
+
model.enableStreaming === newModel.enableStreaming &&
|
|
111
|
+
model.provider === newModel.provider));
|
|
112
|
+
|
|
113
|
+
this.manager.removeLanguageModels(...modelsToRemove.map(model => model.id));
|
|
114
|
+
this.manager.createOrUpdateLanguageModels(...modelsToAddOrUpdate);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected ensureModelConfigArray(value: unknown): ModelConfig[] {
|
|
118
|
+
if (!value || !Array.isArray(value)) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return value.filter(item =>
|
|
123
|
+
item &&
|
|
124
|
+
typeof item === 'object' &&
|
|
125
|
+
'id' in item &&
|
|
126
|
+
'model' in item &&
|
|
127
|
+
'provider' in item &&
|
|
128
|
+
typeof item.id === 'string' &&
|
|
129
|
+
typeof item.model === 'string' &&
|
|
130
|
+
(typeof item.provider === 'string' || item.provider === undefined)
|
|
131
|
+
) as ModelConfig[];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
protected ensureCustomModelArray(value: unknown): Partial<VercelAiModelDescription>[] {
|
|
135
|
+
if (!value || !Array.isArray(value)) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return value.filter(item =>
|
|
140
|
+
item &&
|
|
141
|
+
typeof item === 'object'
|
|
142
|
+
) as Partial<VercelAiModelDescription>[];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
protected createVercelAiModelDescription(modelInfo: ModelConfig): VercelAiModelDescription {
|
|
146
|
+
// The model ID already includes the 'vercel' prefix from preferences
|
|
147
|
+
return {
|
|
148
|
+
id: modelInfo.id,
|
|
149
|
+
model: modelInfo.model,
|
|
150
|
+
provider: modelInfo.provider,
|
|
151
|
+
apiKey: true,
|
|
152
|
+
enableStreaming: true,
|
|
153
|
+
supportsStructuredOutput: modelsSupportingStructuredOutput.includes(modelInfo.model)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
protected createCustomModelDescriptionsFromPreferences(
|
|
158
|
+
preferences: Partial<VercelAiModelDescription>[]
|
|
159
|
+
): VercelAiModelDescription[] {
|
|
160
|
+
return preferences.reduce((acc, pref) => {
|
|
161
|
+
if (!pref.model || !pref.url || typeof pref.model !== 'string' || typeof pref.url !== 'string') {
|
|
162
|
+
return acc;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Ensure custom model IDs have the 'vercel' prefix
|
|
166
|
+
const modelId = pref.id && typeof pref.id === 'string' ? pref.id : pref.model;
|
|
167
|
+
const prefixedId = modelId.startsWith('vercel/') ? modelId : `${VERCEL_AI_PROVIDER_ID}/${modelId}`;
|
|
168
|
+
|
|
169
|
+
return [
|
|
170
|
+
...acc,
|
|
171
|
+
{
|
|
172
|
+
id: prefixedId,
|
|
173
|
+
model: pref.model,
|
|
174
|
+
url: pref.url,
|
|
175
|
+
provider: pref.provider || 'openai',
|
|
176
|
+
apiKey: typeof pref.apiKey === 'string' || pref.apiKey === true ? pref.apiKey : undefined,
|
|
177
|
+
supportsStructuredOutput: pref.supportsStructuredOutput ?? true,
|
|
178
|
+
enableStreaming: pref.enableStreaming ?? true
|
|
179
|
+
}
|
|
180
|
+
];
|
|
181
|
+
}, []);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// List of models that support structured output via JSON schema
|
|
186
|
+
const modelsSupportingStructuredOutput = [
|
|
187
|
+
'gpt-4.1',
|
|
188
|
+
'gpt-4.1-mini',
|
|
189
|
+
'gpt-4.1-nano',
|
|
190
|
+
'gpt-4o',
|
|
191
|
+
'gpt-4o-mini',
|
|
192
|
+
'gpt-4-turbo',
|
|
193
|
+
'claude-3-7-sonnet-20250219',
|
|
194
|
+
'claude-3-5-haiku-20241022',
|
|
195
|
+
'claude-3-opus-20240229'
|
|
196
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
18
|
+
import { VercelAiPreferencesSchema } from './vercel-ai-preferences';
|
|
19
|
+
import { FrontendApplicationContribution, PreferenceContribution, RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser';
|
|
20
|
+
import { VercelAiFrontendApplicationContribution } from './vercel-ai-frontend-application-contribution';
|
|
21
|
+
import { VERCEL_AI_LANGUAGE_MODELS_MANAGER_PATH, VercelAiLanguageModelsManager } from '../common';
|
|
22
|
+
|
|
23
|
+
export default new ContainerModule(bind => {
|
|
24
|
+
bind(PreferenceContribution).toConstantValue({ schema: VercelAiPreferencesSchema });
|
|
25
|
+
bind(VercelAiFrontendApplicationContribution).toSelf().inSingletonScope();
|
|
26
|
+
bind(FrontendApplicationContribution).toService(VercelAiFrontendApplicationContribution);
|
|
27
|
+
bind(VercelAiLanguageModelsManager).toDynamicValue(ctx => {
|
|
28
|
+
const provider = ctx.container.get<ServiceConnectionProvider>(RemoteConnectionProvider);
|
|
29
|
+
return provider.createProxy<VercelAiLanguageModelsManager>(VERCEL_AI_LANGUAGE_MODELS_MANAGER_PATH);
|
|
30
|
+
}).inSingletonScope();
|
|
31
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution';
|
|
18
|
+
import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences';
|
|
19
|
+
import { nls } from '@theia/core';
|
|
20
|
+
|
|
21
|
+
export const OPENAI_API_KEY_PREF = 'ai-features.vercelAi.openaiApiKey';
|
|
22
|
+
export const ANTHROPIC_API_KEY_PREF = 'ai-features.vercelAi.anthropicApiKey';
|
|
23
|
+
export const MODELS_PREF = 'ai-features.vercelAi.officialModels';
|
|
24
|
+
export const CUSTOM_ENDPOINTS_PREF = 'ai-features.vercelAi.customModels';
|
|
25
|
+
|
|
26
|
+
export const VERCEL_AI_PROVIDER_ID = 'vercel-ai';
|
|
27
|
+
|
|
28
|
+
export const VercelAiPreferencesSchema: PreferenceSchema = {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
[OPENAI_API_KEY_PREF]: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
markdownDescription: nls.localize('theia/ai/vercelai/openaiApiKey/mdDescription',
|
|
34
|
+
'Enter an API Key for OpenAI models used by the Vercel AI SDK. \
|
|
35
|
+
**Please note:** By using this preference the API key will be stored in clear text \
|
|
36
|
+
on the machine running Theia. Use the environment variable `OPENAI_API_KEY` to set the key securely.'),
|
|
37
|
+
title: AI_CORE_PREFERENCES_TITLE,
|
|
38
|
+
},
|
|
39
|
+
[ANTHROPIC_API_KEY_PREF]: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
markdownDescription: nls.localize('theia/ai/vercelai/anthropicApiKey/mdDescription',
|
|
42
|
+
'Enter an API Key for Anthropic models used by the Vercel AI SDK. \
|
|
43
|
+
**Please note:** By using this preference the API key will be stored in clear text \
|
|
44
|
+
on the machine running Theia. Use the environment variable `ANTHROPIC_API_KEY` to set the key securely.'),
|
|
45
|
+
title: AI_CORE_PREFERENCES_TITLE,
|
|
46
|
+
},
|
|
47
|
+
[MODELS_PREF]: {
|
|
48
|
+
type: 'array',
|
|
49
|
+
description: nls.localize('theia/ai/vercelai/models/description', 'Official models to use with Vercel AI SDK'),
|
|
50
|
+
title: AI_CORE_PREFERENCES_TITLE,
|
|
51
|
+
default: [
|
|
52
|
+
{ id: 'vercel/openai/gpt-4.1', model: 'gpt-4.1', provider: 'openai' },
|
|
53
|
+
{ id: 'vercel/openai/gpt-4.1-nano', model: 'gpt-4.1-nano', provider: 'openai' },
|
|
54
|
+
{ id: 'vercel/openai/gpt-4.1-mini', model: 'gpt-4.1-mini', provider: 'openai' },
|
|
55
|
+
{ id: 'vercel/openai/gpt-4-turbo', model: 'gpt-4-turbo', provider: 'openai' },
|
|
56
|
+
{ id: 'vercel/openai/gpt-4o', model: 'gpt-4o', provider: 'openai' },
|
|
57
|
+
{ id: 'vercel/openai/gpt-4o-mini', model: 'gpt-4o-mini', provider: 'openai' },
|
|
58
|
+
{ id: 'vercel/anthropic/claude-3-7-sonnet-20250219', model: 'claude-3-7-sonnet-20250219', provider: 'anthropic' },
|
|
59
|
+
{ id: 'vercel/anthropic/claude-3-5-haiku-20241022', model: 'claude-3-5-haiku-20241022', provider: 'anthropic' },
|
|
60
|
+
{ id: 'vercel/anthropic/claude-3-opus-20240229', model: 'claude-3-opus-20240229', provider: 'anthropic' }
|
|
61
|
+
],
|
|
62
|
+
items: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
id: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
title: nls.localize('theia/ai/vercelai/models/id/title', 'Model ID')
|
|
68
|
+
},
|
|
69
|
+
model: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
title: nls.localize('theia/ai/vercelai/models/model/title', 'Model Name')
|
|
72
|
+
},
|
|
73
|
+
provider: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
enum: ['openai', 'anthropic'],
|
|
76
|
+
title: nls.localize('theia/ai/vercelai/models/provider/title', 'Provider')
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: ['id', 'model', 'provider']
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[CUSTOM_ENDPOINTS_PREF]: {
|
|
83
|
+
type: 'array',
|
|
84
|
+
title: AI_CORE_PREFERENCES_TITLE,
|
|
85
|
+
markdownDescription: nls.localize('theia/ai/vercelai/customEndpoints/mdDescription',
|
|
86
|
+
'Integrate custom models compatible with the Vercel AI SDK. The required attributes are `model` and `url`.\
|
|
87
|
+
\n\
|
|
88
|
+
Optionally, you can\
|
|
89
|
+
\n\
|
|
90
|
+
- specify a unique `id` to identify the custom model in the UI. If none is given `model` will be used as `id`.\
|
|
91
|
+
\n\
|
|
92
|
+
- provide an `apiKey` to access the API served at the given url. Use `true` to indicate the use of the global API key.\
|
|
93
|
+
\n\
|
|
94
|
+
- specify `supportsStructuredOutput: false` to indicate that structured output shall not be used.\
|
|
95
|
+
\n\
|
|
96
|
+
- specify `enableStreaming: false` to indicate that streaming shall not be used.\
|
|
97
|
+
\n\
|
|
98
|
+
- specify `provider` to indicate which provider the model is from (openai, anthropic).'),
|
|
99
|
+
default: [],
|
|
100
|
+
items: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {
|
|
103
|
+
model: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/modelId/title', 'Model ID')
|
|
106
|
+
},
|
|
107
|
+
url: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/url/title', 'The API endpoint where the model is hosted')
|
|
110
|
+
},
|
|
111
|
+
id: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/id/title', 'A unique identifier which is used in the UI to identify the custom model'),
|
|
114
|
+
},
|
|
115
|
+
provider: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
enum: ['openai', 'anthropic'],
|
|
118
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/provider/title', 'Provider')
|
|
119
|
+
},
|
|
120
|
+
apiKey: {
|
|
121
|
+
type: ['string', 'boolean'],
|
|
122
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/apiKey/title',
|
|
123
|
+
'Either the key to access the API served at the given url or `true` to use the global API key'),
|
|
124
|
+
},
|
|
125
|
+
supportsStructuredOutput: {
|
|
126
|
+
type: 'boolean',
|
|
127
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/supportsStructuredOutput/title',
|
|
128
|
+
'Indicates whether the model supports structured output. `true` by default.'),
|
|
129
|
+
},
|
|
130
|
+
enableStreaming: {
|
|
131
|
+
type: 'boolean',
|
|
132
|
+
title: nls.localize('theia/ai/vercelai/customEndpoints/enableStreaming/title',
|
|
133
|
+
'Indicates whether the streaming API shall be used. `true` by default.'),
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
export * from './vercel-ai-language-models-manager';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
export const VERCEL_AI_LANGUAGE_MODELS_MANAGER_PATH = '/services/vercel-ai/language-model-manager';
|
|
17
|
+
|
|
18
|
+
export type VercelAiProvider = 'openai' | 'anthropic';
|
|
19
|
+
|
|
20
|
+
export interface VercelAiProviderConfig {
|
|
21
|
+
provider: VercelAiProvider;
|
|
22
|
+
apiKey?: string;
|
|
23
|
+
baseURL?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface VercelAiModelDescription {
|
|
27
|
+
/**
|
|
28
|
+
* The identifier of the model which will be shown in the UI.
|
|
29
|
+
*/
|
|
30
|
+
id: string;
|
|
31
|
+
/**
|
|
32
|
+
* The model ID as used by the Vercel AI SDK.
|
|
33
|
+
*/
|
|
34
|
+
model: string;
|
|
35
|
+
/**
|
|
36
|
+
* The provider of the model (openai, anthropic, etc.)
|
|
37
|
+
*/
|
|
38
|
+
provider?: VercelAiProvider;
|
|
39
|
+
/**
|
|
40
|
+
* The API base URL where the model is hosted. If not provided the default provider endpoint will be used.
|
|
41
|
+
*/
|
|
42
|
+
url?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The key for the model. If 'true' is provided the global provider API key will be used.
|
|
45
|
+
*/
|
|
46
|
+
apiKey: string | true | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Controls whether streaming is enabled for this model.
|
|
49
|
+
*/
|
|
50
|
+
enableStreaming: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Flag to configure whether the model supports structured output. Default is `true`.
|
|
53
|
+
*/
|
|
54
|
+
supportsStructuredOutput: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const VercelAiLanguageModelsManager = Symbol('VercelAiLanguageModelsManager');
|
|
58
|
+
export interface VercelAiLanguageModelsManager {
|
|
59
|
+
apiKey: string | undefined;
|
|
60
|
+
setProviderConfig(provider: VercelAiProvider, config: Partial<VercelAiProviderConfig>): void;
|
|
61
|
+
createOrUpdateLanguageModels(...models: VercelAiModelDescription[]): Promise<void>;
|
|
62
|
+
removeLanguageModels(...modelIds: string[]): void;
|
|
63
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
18
|
+
import { VERCEL_AI_LANGUAGE_MODELS_MANAGER_PATH, VercelAiLanguageModelsManager } from '../common/vercel-ai-language-models-manager';
|
|
19
|
+
import { ConnectionHandler, RpcConnectionHandler } from '@theia/core';
|
|
20
|
+
import { VercelAiLanguageModelsManagerImpl } from './vercel-ai-language-models-manager-impl';
|
|
21
|
+
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
|
|
22
|
+
import { VercelAiLanguageModelFactory } from './vercel-ai-language-model-factory';
|
|
23
|
+
|
|
24
|
+
const vercelAiConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService, bindFrontendService }) => {
|
|
25
|
+
bind(VercelAiLanguageModelsManagerImpl).toSelf().inSingletonScope();
|
|
26
|
+
bind(VercelAiLanguageModelsManager).toService(VercelAiLanguageModelsManagerImpl);
|
|
27
|
+
bind(VercelAiLanguageModelFactory).toSelf().inSingletonScope();
|
|
28
|
+
bind(ConnectionHandler).toDynamicValue(ctx =>
|
|
29
|
+
new RpcConnectionHandler(VERCEL_AI_LANGUAGE_MODELS_MANAGER_PATH, () => ctx.container.get(VercelAiLanguageModelsManager))
|
|
30
|
+
).inSingletonScope();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default new ContainerModule(bind => {
|
|
34
|
+
bind(ConnectionContainerModule).toConstantValue(vercelAiConnectionModule);
|
|
35
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
18
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
19
|
+
import { LanguageModelV1 } from '@ai-sdk/provider';
|
|
20
|
+
import { injectable } from '@theia/core/shared/inversify';
|
|
21
|
+
import { VercelAiModelDescription } from '../common';
|
|
22
|
+
|
|
23
|
+
export type VercelAiProvider = 'openai' | 'anthropic';
|
|
24
|
+
|
|
25
|
+
export interface VercelAiProviderConfig {
|
|
26
|
+
provider: VercelAiProvider;
|
|
27
|
+
apiKey?: string;
|
|
28
|
+
baseURL?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@injectable()
|
|
32
|
+
export class VercelAiLanguageModelFactory {
|
|
33
|
+
|
|
34
|
+
createLanguageModel(modelDescription: VercelAiModelDescription, providerConfig: VercelAiProviderConfig): LanguageModelV1 {
|
|
35
|
+
const apiKey = this.resolveApiKey(modelDescription, providerConfig);
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
throw new Error(`Please provide an API key for ${providerConfig.provider} in preferences or via environment variable`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const baseURL = modelDescription.url || providerConfig.baseURL;
|
|
41
|
+
|
|
42
|
+
switch (providerConfig.provider) {
|
|
43
|
+
case 'openai':
|
|
44
|
+
return createOpenAI({
|
|
45
|
+
apiKey,
|
|
46
|
+
baseURL,
|
|
47
|
+
compatibility: 'strict'
|
|
48
|
+
}).languageModel(modelDescription.model);
|
|
49
|
+
case 'anthropic':
|
|
50
|
+
return createAnthropic({
|
|
51
|
+
apiKey,
|
|
52
|
+
baseURL
|
|
53
|
+
}).languageModel(modelDescription.model);
|
|
54
|
+
default:
|
|
55
|
+
throw new Error(`Unsupported provider: ${providerConfig.provider}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private resolveApiKey(modelDescription: VercelAiModelDescription, providerConfig: VercelAiProviderConfig): string | undefined {
|
|
60
|
+
if (modelDescription.apiKey === true) {
|
|
61
|
+
return this.getApiKeyBasedOnProvider(providerConfig);
|
|
62
|
+
}
|
|
63
|
+
if (modelDescription.apiKey) {
|
|
64
|
+
return modelDescription.apiKey;
|
|
65
|
+
}
|
|
66
|
+
return this.getApiKeyBasedOnProvider(providerConfig);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private getApiKeyBasedOnProvider(providerConfig: VercelAiProviderConfig): string | undefined {
|
|
70
|
+
if (providerConfig.apiKey) {
|
|
71
|
+
return providerConfig.apiKey;
|
|
72
|
+
}
|
|
73
|
+
switch (providerConfig.provider) {
|
|
74
|
+
case 'openai':
|
|
75
|
+
return process.env.OPENAI_API_KEY;
|
|
76
|
+
case 'anthropic':
|
|
77
|
+
return process.env.ANTHROPIC_API_KEY;
|
|
78
|
+
default:
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|