@theia/ai-openai 1.58.3 → 1.59.0-next.72

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.
Files changed (47) hide show
  1. package/README.md +11 -9
  2. package/lib/browser/openai-frontend-application-contribution.d.ts.map +1 -1
  3. package/lib/browser/openai-frontend-application-contribution.js +11 -7
  4. package/lib/browser/openai-frontend-application-contribution.js.map +1 -1
  5. package/lib/browser/openai-preferences.d.ts.map +1 -1
  6. package/lib/browser/openai-preferences.js +29 -15
  7. package/lib/browser/openai-preferences.js.map +1 -1
  8. package/lib/common/openai-language-models-manager.d.ts +9 -2
  9. package/lib/common/openai-language-models-manager.d.ts.map +1 -1
  10. package/lib/node/openai-backend-module.d.ts.map +1 -1
  11. package/lib/node/openai-backend-module.js +2 -0
  12. package/lib/node/openai-backend-module.js.map +1 -1
  13. package/lib/node/openai-language-model.d.ts +29 -7
  14. package/lib/node/openai-language-model.d.ts.map +1 -1
  15. package/lib/node/openai-language-model.js +93 -103
  16. package/lib/node/openai-language-model.js.map +1 -1
  17. package/lib/node/openai-language-models-manager-impl.d.ts +2 -0
  18. package/lib/node/openai-language-models-manager-impl.d.ts.map +1 -1
  19. package/lib/node/openai-language-models-manager-impl.js +7 -2
  20. package/lib/node/openai-language-models-manager-impl.js.map +1 -1
  21. package/lib/node/openai-model-utils.spec.d.ts +4 -0
  22. package/lib/node/openai-model-utils.spec.d.ts.map +1 -0
  23. package/lib/node/openai-model-utils.spec.js +155 -0
  24. package/lib/node/openai-model-utils.spec.js.map +1 -0
  25. package/lib/node/openai-streaming-iterator.d.ts +21 -0
  26. package/lib/node/openai-streaming-iterator.d.ts.map +1 -0
  27. package/lib/node/openai-streaming-iterator.js +126 -0
  28. package/lib/node/openai-streaming-iterator.js.map +1 -0
  29. package/lib/node/openai-streaming-iterator.spec.d.ts +2 -0
  30. package/lib/node/openai-streaming-iterator.spec.d.ts.map +1 -0
  31. package/lib/node/openai-streaming-iterator.spec.js +208 -0
  32. package/lib/node/openai-streaming-iterator.spec.js.map +1 -0
  33. package/package.json +7 -7
  34. package/src/browser/openai-frontend-application-contribution.ts +9 -5
  35. package/src/browser/openai-preferences.ts +36 -15
  36. package/src/common/openai-language-models-manager.ts +10 -2
  37. package/src/node/openai-backend-module.ts +2 -0
  38. package/src/node/openai-language-model.ts +106 -108
  39. package/src/node/openai-language-models-manager-impl.ts +9 -3
  40. package/src/node/openai-model-utils.spec.ts +164 -0
  41. package/src/node/openai-streaming-iterator.spec.ts +254 -0
  42. package/src/node/openai-streaming-iterator.ts +124 -0
  43. package/lib/package.spec.d.ts +0 -1
  44. package/lib/package.spec.d.ts.map +0 -1
  45. package/lib/package.spec.js +0 -26
  46. package/lib/package.spec.js.map +0 -1
  47. package/src/package.spec.ts +0 -28
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2025 EclipseSource GmbH.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.StreamingAsyncIterator = void 0;
19
+ const core_1 = require("@theia/core");
20
+ const promise_util_1 = require("@theia/core/lib/common/promise-util");
21
+ class StreamingAsyncIterator {
22
+ constructor(stream, cancellationToken) {
23
+ this.stream = stream;
24
+ this.requestQueue = new Array();
25
+ this.messageCache = new Array();
26
+ this.done = false;
27
+ this.terminalError = undefined;
28
+ this.toDispose = new core_1.DisposableCollection();
29
+ this.registerStreamListener('error', error => {
30
+ console.error('Error in OpenAI chat completion stream:', error);
31
+ this.terminalError = error;
32
+ this.dispose();
33
+ });
34
+ this.registerStreamListener('abort', () => {
35
+ this.terminalError = new core_1.CancellationError();
36
+ this.dispose();
37
+ }, true);
38
+ this.registerStreamListener('message', message => {
39
+ if (message.role === 'tool') {
40
+ this.handleIncoming({
41
+ tool_calls: [{
42
+ id: message.tool_call_id,
43
+ finished: true,
44
+ result: Array.isArray(message.content) ? message.content.join('') : message.content
45
+ }]
46
+ });
47
+ }
48
+ console.debug('Received Open AI message', JSON.stringify(message));
49
+ });
50
+ this.registerStreamListener('end', () => {
51
+ this.dispose();
52
+ }, true);
53
+ this.registerStreamListener('chunk', chunk => {
54
+ var _a;
55
+ this.handleIncoming({ ...(_a = chunk.choices[0]) === null || _a === void 0 ? void 0 : _a.delta });
56
+ });
57
+ if (cancellationToken) {
58
+ this.toDispose.push(cancellationToken.onCancellationRequested(() => stream.abort()));
59
+ }
60
+ }
61
+ [Symbol.asyncIterator]() { return this; }
62
+ next() {
63
+ if (this.messageCache.length && this.requestQueue.length) {
64
+ throw new Error('Assertion error: cache and queue should not both be populated.');
65
+ }
66
+ // Deliver all the messages we got, even if we've since terminated.
67
+ if (this.messageCache.length) {
68
+ return Promise.resolve({
69
+ done: false,
70
+ value: this.messageCache.shift()
71
+ });
72
+ }
73
+ else if (this.terminalError) {
74
+ return Promise.reject(this.terminalError);
75
+ }
76
+ else if (this.done) {
77
+ return Promise.resolve({
78
+ done: true,
79
+ value: undefined
80
+ });
81
+ }
82
+ else {
83
+ const toQueue = new promise_util_1.Deferred();
84
+ this.requestQueue.push(toQueue);
85
+ return toQueue.promise;
86
+ }
87
+ }
88
+ handleIncoming(message) {
89
+ if (this.messageCache.length && this.requestQueue.length) {
90
+ throw new Error('Assertion error: cache and queue should not both be populated.');
91
+ }
92
+ if (this.requestQueue.length) {
93
+ this.requestQueue.shift().resolve({
94
+ done: false,
95
+ value: message
96
+ });
97
+ }
98
+ else {
99
+ this.messageCache.push(message);
100
+ }
101
+ }
102
+ registerStreamListener(eventType, handler, once) {
103
+ if (once) {
104
+ this.stream.once(eventType, handler);
105
+ }
106
+ else {
107
+ this.stream.on(eventType, handler);
108
+ }
109
+ this.toDispose.push({ dispose: () => this.stream.off(eventType, handler) });
110
+ }
111
+ dispose() {
112
+ this.done = true;
113
+ this.toDispose.dispose();
114
+ // We will be receiving no more messages. Any outstanding requests have to be handled.
115
+ if (this.terminalError) {
116
+ this.requestQueue.forEach(request => request.reject(this.terminalError));
117
+ }
118
+ else {
119
+ this.requestQueue.forEach(request => request.resolve({ done: true, value: undefined }));
120
+ }
121
+ // Leave the message cache alone - if it was populated, then the request queue was empty, but we'll still try to deliver the messages if asked.
122
+ this.requestQueue.length = 0;
123
+ }
124
+ }
125
+ exports.StreamingAsyncIterator = StreamingAsyncIterator;
126
+ //# sourceMappingURL=openai-streaming-iterator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-streaming-iterator.js","sourceRoot":"","sources":["../../src/node/openai-streaming-iterator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=openai-streaming-iterator.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-streaming-iterator.spec.d.ts","sourceRoot":"","sources":["../../src/node/openai-streaming-iterator.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ // *****************************************************************************
3
+ // Copyright (C) 2025 EclipseSource GmbH.
4
+ //
5
+ // This program and the accompanying materials are made available under the
6
+ // terms of the Eclipse Public License v. 2.0 which is available at
7
+ // http://www.eclipse.org/legal/epl-2.0.
8
+ //
9
+ // This Source Code may also be made available under the following Secondary
10
+ // Licenses when the conditions for such availability set forth in the Eclipse
11
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
12
+ // with the GNU Classpath Exception which is available at
13
+ // https://www.gnu.org/software/classpath/license.html.
14
+ //
15
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16
+ // *****************************************************************************
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const chai_1 = require("chai");
19
+ const sinon = require("sinon");
20
+ const openai_streaming_iterator_1 = require("./openai-streaming-iterator");
21
+ const core_1 = require("@theia/core");
22
+ const events_1 = require("events");
23
+ describe('StreamingAsyncIterator', () => {
24
+ let mockStream;
25
+ let iterator;
26
+ let cts;
27
+ const consoleError = console.error;
28
+ beforeEach(() => {
29
+ mockStream = new events_1.EventEmitter();
30
+ mockStream.abort = sinon.stub();
31
+ cts = new core_1.CancellationTokenSource();
32
+ });
33
+ afterEach(() => {
34
+ if (iterator) {
35
+ iterator.dispose();
36
+ }
37
+ cts.dispose();
38
+ console.error = consoleError;
39
+ });
40
+ function createIterator(withCancellationToken = false) {
41
+ return new openai_streaming_iterator_1.StreamingAsyncIterator(mockStream, withCancellationToken ? cts.token : undefined);
42
+ }
43
+ it('should yield messages in the correct order when consumed immediately', async () => {
44
+ iterator = createIterator();
45
+ setTimeout(() => {
46
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'Hello' } }] });
47
+ mockStream.emit('chunk', { choices: [{ delta: { content: ' ' } }] });
48
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'World' } }] });
49
+ mockStream.emit('end');
50
+ }, 10);
51
+ const results = [];
52
+ while (true) {
53
+ const { value, done } = await iterator.next();
54
+ if (done) {
55
+ break;
56
+ }
57
+ results.push(value);
58
+ }
59
+ (0, chai_1.expect)(results).to.deep.equal([
60
+ { content: 'Hello' },
61
+ { content: ' ' },
62
+ { content: 'World' }
63
+ ]);
64
+ });
65
+ it('should buffer messages if consumer is slower (messages arrive before .next() is called)', async () => {
66
+ var _a;
67
+ iterator = createIterator();
68
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'A' } }] });
69
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'B' } }] });
70
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'C' } }] });
71
+ mockStream.emit('end');
72
+ const results = [];
73
+ while (true) {
74
+ const { value, done } = await iterator.next();
75
+ if (done) {
76
+ break;
77
+ }
78
+ results.push((_a = value.content) !== null && _a !== void 0 ? _a : '');
79
+ }
80
+ (0, chai_1.expect)(results).to.deep.equal(['A', 'B', 'C']);
81
+ });
82
+ it('should resolve queued next() call when a message arrives (consumer is waiting first)', async () => {
83
+ iterator = createIterator();
84
+ const nextPromise = iterator.next();
85
+ setTimeout(() => {
86
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'Hello from queue' } }] });
87
+ mockStream.emit('end');
88
+ }, 10);
89
+ const first = await nextPromise;
90
+ (0, chai_1.expect)(first.done).to.be.false;
91
+ (0, chai_1.expect)(first.value.content).to.equal('Hello from queue');
92
+ const second = await iterator.next();
93
+ (0, chai_1.expect)(second.done).to.be.true;
94
+ (0, chai_1.expect)(second.value).to.be.undefined;
95
+ });
96
+ it('should handle the end event correctly', async () => {
97
+ var _a;
98
+ iterator = createIterator();
99
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'EndTest1' } }] });
100
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'EndTest2' } }] });
101
+ mockStream.emit('end');
102
+ const results = [];
103
+ while (true) {
104
+ const { value, done } = await iterator.next();
105
+ if (done) {
106
+ break;
107
+ }
108
+ results.push((_a = value.content) !== null && _a !== void 0 ? _a : '');
109
+ }
110
+ (0, chai_1.expect)(results).to.deep.equal(['EndTest1', 'EndTest2']);
111
+ });
112
+ it('should reject pending .next() call with an error if error event occurs', async () => {
113
+ iterator = createIterator();
114
+ const pendingNext = iterator.next();
115
+ // Suppress console.error output
116
+ console.error = () => { };
117
+ const error = new Error('Stream error occurred');
118
+ mockStream.emit('error', error);
119
+ try {
120
+ await pendingNext;
121
+ chai_1.expect.fail('The promise should have been rejected with an error.');
122
+ }
123
+ catch (err) {
124
+ (0, chai_1.expect)(err).to.equal(error);
125
+ }
126
+ });
127
+ it('should reject pending .next() call with a CancellationError if "abort" event occurs', async () => {
128
+ iterator = createIterator();
129
+ const pendingNext = iterator.next();
130
+ // Suppress console.error output
131
+ console.error = () => { };
132
+ mockStream.emit('abort');
133
+ try {
134
+ await pendingNext;
135
+ chai_1.expect.fail('The promise should have been rejected with a CancellationError.');
136
+ }
137
+ catch (err) {
138
+ (0, chai_1.expect)(err).to.be.instanceOf(core_1.CancellationError);
139
+ }
140
+ });
141
+ it('should call stream.abort() when cancellation token is triggered', async () => {
142
+ iterator = createIterator(true);
143
+ cts.cancel();
144
+ sinon.assert.calledOnce(mockStream.abort);
145
+ });
146
+ it('should not lose unconsumed messages after disposal, but no new ones arrive', async () => {
147
+ iterator = createIterator();
148
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'Msg1' } }] });
149
+ mockStream.emit('chunk', { choices: [{ delta: { content: 'Msg2' } }] });
150
+ iterator.dispose();
151
+ let result = await iterator.next();
152
+ (0, chai_1.expect)(result.done).to.be.false;
153
+ (0, chai_1.expect)(result.value.content).to.equal('Msg1');
154
+ result = await iterator.next();
155
+ (0, chai_1.expect)(result.done).to.be.false;
156
+ (0, chai_1.expect)(result.value.content).to.equal('Msg2');
157
+ result = await iterator.next();
158
+ (0, chai_1.expect)(result.done).to.be.true;
159
+ (0, chai_1.expect)(result.value).to.be.undefined;
160
+ });
161
+ it('should reject all pending requests with an error if disposal occurs after stream error', async () => {
162
+ iterator = createIterator();
163
+ const pendingNext1 = iterator.next();
164
+ const pendingNext2 = iterator.next();
165
+ // Suppress console.error output
166
+ console.error = () => { };
167
+ const error = new Error('Critical error');
168
+ mockStream.emit('error', error);
169
+ try {
170
+ await pendingNext1;
171
+ chai_1.expect.fail('expected to be rejected');
172
+ }
173
+ catch (err) {
174
+ (0, chai_1.expect)(err).to.equal(error);
175
+ }
176
+ try {
177
+ await pendingNext2;
178
+ chai_1.expect.fail('expected to be rejected');
179
+ }
180
+ catch (err) {
181
+ (0, chai_1.expect)(err).to.equal(error);
182
+ }
183
+ });
184
+ it('should handle receiving a "message" event with role="tool"', async () => {
185
+ iterator = createIterator();
186
+ setTimeout(() => {
187
+ mockStream.emit('message', {
188
+ role: 'tool',
189
+ tool_call_id: 'tool-123',
190
+ content: ['Part1', 'Part2']
191
+ });
192
+ mockStream.emit('end');
193
+ }, 10);
194
+ const results = [];
195
+ for await (const part of iterator) {
196
+ results.push(part);
197
+ }
198
+ (0, chai_1.expect)(results).to.have.lengthOf(1);
199
+ (0, chai_1.expect)(results[0].tool_calls).to.deep.equal([
200
+ {
201
+ id: 'tool-123',
202
+ finished: true,
203
+ result: 'Part1Part2'
204
+ }
205
+ ]);
206
+ });
207
+ });
208
+ //# sourceMappingURL=openai-streaming-iterator.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-streaming-iterator.spec.js","sourceRoot":"","sources":["../../src/node/openai-streaming-iterator.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@theia/ai-openai",
3
- "version": "1.58.3",
3
+ "version": "1.59.0-next.72+f41d8efcd",
4
4
  "description": "Theia - OpenAI Integration",
5
5
  "dependencies": {
6
- "@theia/ai-core": "1.58.3",
7
- "@theia/core": "1.58.3",
8
- "@theia/filesystem": "1.58.3",
9
- "@theia/workspace": "1.58.3",
6
+ "@theia/ai-core": "1.59.0-next.72+f41d8efcd",
7
+ "@theia/core": "1.59.0-next.72+f41d8efcd",
8
+ "@theia/filesystem": "1.59.0-next.72+f41d8efcd",
9
+ "@theia/workspace": "1.59.0-next.72+f41d8efcd",
10
10
  "minimatch": "^5.1.0",
11
11
  "openai": "^4.77.0",
12
12
  "tslib": "^2.6.2"
@@ -45,10 +45,10 @@
45
45
  "watch": "theiaext watch"
46
46
  },
47
47
  "devDependencies": {
48
- "@theia/ext-scripts": "1.58.3"
48
+ "@theia/ext-scripts": "1.58.0"
49
49
  },
50
50
  "nyc": {
51
51
  "extends": "../../configs/nyc.json"
52
52
  },
53
- "gitHead": "ca70c15332f91e0f61b12cd147b5ff8326e2e6d2"
53
+ "gitHead": "f41d8efcd4abb79167b74bf476eafc7857e97306"
54
54
  }
@@ -92,7 +92,8 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio
92
92
  model.url === newModel.url &&
93
93
  model.apiKey === newModel.apiKey &&
94
94
  model.apiVersion === newModel.apiVersion &&
95
- model.supportsDeveloperMessage === newModel.supportsDeveloperMessage &&
95
+ model.developerMessageSettings === newModel.developerMessageSettings &&
96
+ model.supportsStructuredOutput === newModel.supportsStructuredOutput &&
96
97
  model.enableStreaming === newModel.enableStreaming));
97
98
 
98
99
  this.manager.removeLanguageModels(...modelsToRemove.map(model => model.id));
@@ -116,8 +117,9 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio
116
117
  model: modelId,
117
118
  apiKey: true,
118
119
  apiVersion: true,
119
- supportsDeveloperMessage: !openAIModelsSupportingDeveloperMessages.includes(modelId),
120
+ developerMessageSettings: openAIModelsNotSupportingDeveloperMessages.includes(modelId) ? 'user' : 'developer',
120
121
  enableStreaming: !openAIModelsWithDisabledStreaming.includes(modelId),
122
+ supportsStructuredOutput: !openAIModelsWithoutStructuredOutput.includes(modelId),
121
123
  defaultRequestSettings: modelRequestSetting?.requestSettings
122
124
  };
123
125
  }
@@ -141,7 +143,8 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio
141
143
  url: pref.url,
142
144
  apiKey: typeof pref.apiKey === 'string' || pref.apiKey === true ? pref.apiKey : undefined,
143
145
  apiVersion: typeof pref.apiVersion === 'string' || pref.apiVersion === true ? pref.apiVersion : undefined,
144
- supportsDeveloperMessage: pref.supportsDeveloperMessage ?? true,
146
+ developerMessageSettings: pref.developerMessageSettings ?? 'developer',
147
+ supportsStructuredOutput: pref.supportsStructuredOutput ?? true,
145
148
  enableStreaming: pref.enableStreaming ?? true,
146
149
  defaultRequestSettings: modelRequestSetting?.requestSettings
147
150
  }
@@ -165,5 +168,6 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio
165
168
  }
166
169
  }
167
170
 
168
- const openAIModelsWithDisabledStreaming = ['o1-preview', 'o1-mini'];
169
- const openAIModelsSupportingDeveloperMessages = ['o1-preview', 'o1-mini'];
171
+ const openAIModelsWithDisabledStreaming: string[] = [];
172
+ const openAIModelsNotSupportingDeveloperMessages = ['o1-preview', 'o1-mini'];
173
+ const openAIModelsWithoutStructuredOutput = ['o1-preview', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo', 'o1-mini', 'gpt-4o-2024-05-13'];
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution';
18
18
  import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences';
19
+ import { nls } from '@theia/core';
19
20
 
20
21
  export const API_KEY_PREF = 'ai-features.openAiOfficial.openAiApiKey';
21
22
  export const MODELS_PREF = 'ai-features.openAiOfficial.officialOpenAiModels';
@@ -26,15 +27,16 @@ export const OpenAiPreferencesSchema: PreferenceSchema = {
26
27
  properties: {
27
28
  [API_KEY_PREF]: {
28
29
  type: 'string',
29
- markdownDescription: 'Enter an API Key of your official OpenAI Account. **Please note:** By using this preference the Open AI API key will be stored in clear text\
30
- on the machine running Theia. Use the environment variable `OPENAI_API_KEY` to set the key securely.',
30
+ markdownDescription: nls.localize('theia/ai/openai/apiKey/mdDescription',
31
+ 'Enter an API Key of your official OpenAI Account. **Please note:** By using this preference the Open AI API key will be stored in clear text \
32
+ on the machine running Theia. Use the environment variable `OPENAI_API_KEY` to set the key securely.'),
31
33
  title: AI_CORE_PREFERENCES_TITLE,
32
34
  },
33
35
  [MODELS_PREF]: {
34
36
  type: 'array',
35
- description: 'Official OpenAI models to use',
37
+ description: nls.localize('theia/ai/openai/models/description', 'Official OpenAI models to use'),
36
38
  title: AI_CORE_PREFERENCES_TITLE,
37
- default: ['gpt-4o', 'gpt-4o-2024-08-06', 'gpt-4o-2024-05-13', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
39
+ default: ['gpt-4o', 'gpt-4o-2024-11-20', 'gpt-4o-2024-08-06', 'gpt-4o-mini', 'o1', 'o1-mini', 'o3-mini'],
38
40
  items: {
39
41
  type: 'string'
40
42
  }
@@ -42,7 +44,8 @@ export const OpenAiPreferencesSchema: PreferenceSchema = {
42
44
  [CUSTOM_ENDPOINTS_PREF]: {
43
45
  type: 'array',
44
46
  title: AI_CORE_PREFERENCES_TITLE,
45
- markdownDescription: 'Integrate custom models compatible with the OpenAI API, for example via `vllm`. The required attributes are `model` and `url`.\
47
+ markdownDescription: nls.localize('theia/ai/openai/customEndpoints/mdDescription',
48
+ 'Integrate custom models compatible with the OpenAI API, for example via `vllm`. The required attributes are `model` and `url`.\
46
49
  \n\
47
50
  Optionally, you can\
48
51
  \n\
@@ -52,42 +55,60 @@ export const OpenAiPreferencesSchema: PreferenceSchema = {
52
55
  \n\
53
56
  - provide an `apiVersion` to access the API served at the given url in Azure. Use `true` to indicate the use of the global OpenAI API version.\
54
57
  \n\
55
- - specify `supportsDeveloperMessage: false` to indicate that the developer role shall not be used.\
58
+ - set `developerMessageSettings` to one of `user`, `system`, `developer`, `mergeWithFollowingUserMessage`, or `skip` to control how the developer message is\
59
+ included (where `user`, `system`, and `developer` will be used as a role, `mergeWithFollowingUserMessage` will prefix the following user message with the system\
60
+ message or convert the system message to user message if the next message is not a user message. `skip` will just remove the system message).\
61
+ Defaulting to `developer`.\
62
+ \n\
63
+ - specify `supportsStructuredOutput: false` to indicate that structured output shall not be used.\
56
64
  \n\
57
65
  - specify `enableStreaming: false` to indicate that streaming shall not be used.\
58
66
  \n\
59
- Refer to [our documentation](https://theia-ide.org/docs/user_ai/#openai-compatible-models-eg-via-vllm) for more information.',
67
+ Refer to [our documentation](https://theia-ide.org/docs/user_ai/#openai-compatible-models-eg-via-vllm) for more information.'),
60
68
  default: [],
61
69
  items: {
62
70
  type: 'object',
63
71
  properties: {
64
72
  model: {
65
73
  type: 'string',
66
- title: 'Model ID'
74
+ title: nls.localize('theia/ai/openai/customEndpoints/modelId/title', 'Model ID')
67
75
  },
68
76
  url: {
69
77
  type: 'string',
70
- title: 'The Open AI API compatible endpoint where the model is hosted'
78
+ title: nls.localize('theia/ai/openai/customEndpoints/url/title', 'The Open AI API compatible endpoint where the model is hosted')
71
79
  },
72
80
  id: {
73
81
  type: 'string',
74
- title: 'A unique identifier which is used in the UI to identify the custom model',
82
+ title: nls.localize('theia/ai/openai/customEndpoints/id/title', 'A unique identifier which is used in the UI to identify the custom model'),
75
83
  },
76
84
  apiKey: {
77
85
  type: ['string', 'boolean'],
78
- title: 'Either the key to access the API served at the given url or `true` to use the global OpenAI API key',
86
+ title: nls.localize('theia/ai/openai/customEndpoints/apiKey/title',
87
+ 'Either the key to access the API served at the given url or `true` to use the global OpenAI API key'),
79
88
  },
80
89
  apiVersion: {
81
90
  type: ['string', 'boolean'],
82
- title: 'Either the version to access the API served at the given url in Azure or `true` to use the global OpenAI API version',
91
+ title: nls.localize('theia/ai/openai/customEndpoints/apiVersion/title',
92
+ 'Either the version to access the API served at the given url in Azure or `true` to use the global OpenAI API version'),
93
+ },
94
+ developerMessageSettings: {
95
+ type: 'string',
96
+ enum: ['user', 'system', 'developer', 'mergeWithFollowingUserMessage', 'skip'],
97
+ default: 'developer',
98
+ title: nls.localize('theia/ai/openai/customEndpoints/developerMessageSettings/title',
99
+ 'Controls the handling of system messages: `user`, `system`, and `developer` will be used as a role, `mergeWithFollowingUserMessage` will prefix\
100
+ the following user message with the system message or convert the system message to user message if the next message is not a user message.\
101
+ `skip` will just remove the system message), defaulting to `developer`.')
83
102
  },
84
- supportsDeveloperMessage: {
103
+ supportsStructuredOutput: {
85
104
  type: 'boolean',
86
- title: 'Indicates whether the model supports the `developer` role. `true` by default.',
105
+ title: nls.localize('theia/ai/openai/customEndpoints/supportsStructuredOutput/title',
106
+ 'Indicates whether the model supports structured output. `true` by default.'),
87
107
  },
88
108
  enableStreaming: {
89
109
  type: 'boolean',
90
- title: 'Indicates whether the streaming API shall be used. `true` by default.',
110
+ title: nls.localize('theia/ai/openai/customEndpoints/enableStreaming/title',
111
+ 'Indicates whether the streaming API shall be used. `true` by default.'),
91
112
  }
92
113
  }
93
114
  }
@@ -15,6 +15,7 @@
15
15
  // *****************************************************************************
16
16
  export const OPENAI_LANGUAGE_MODELS_MANAGER_PATH = '/services/open-ai/language-model-manager';
17
17
  export const OpenAiLanguageModelsManager = Symbol('OpenAiLanguageModelsManager');
18
+
18
19
  export interface OpenAiModelDescription {
19
20
  /**
20
21
  * The identifier of the model which will be shown in the UI.
@@ -41,9 +42,16 @@ export interface OpenAiModelDescription {
41
42
  */
42
43
  enableStreaming: boolean;
43
44
  /**
44
- * Flag to configure whether the OpenAPI model supports the `developer` role. Default is `true`.
45
+ * Property to configure the developer message of the model. Setting this property to 'user', 'system', or 'developer' will use that string as the role for the system message.
46
+ * Setting it to 'mergeWithFollowingUserMessage' will prefix the following user message with the system message or convert the system message to user if the following message
47
+ * is not a user message. 'skip' will remove the system message altogether.
48
+ * Defaults to 'developer'.
45
49
  */
46
- supportsDeveloperMessage: boolean;
50
+ developerMessageSettings?: 'user' | 'system' | 'developer' | 'mergeWithFollowingUserMessage' | 'skip';
51
+ /**
52
+ * Flag to configure whether the OpenAPI model supports structured output. Default is `true`.
53
+ */
54
+ supportsStructuredOutput: boolean;
47
55
  /**
48
56
  * Default request settings for the OpenAI model.
49
57
  */
@@ -19,6 +19,7 @@ import { OPENAI_LANGUAGE_MODELS_MANAGER_PATH, OpenAiLanguageModelsManager } from
19
19
  import { ConnectionHandler, RpcConnectionHandler } from '@theia/core';
20
20
  import { OpenAiLanguageModelsManagerImpl } from './openai-language-models-manager-impl';
21
21
  import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
22
+ import { OpenAiModelUtils } from './openai-language-model';
22
23
 
23
24
  export const OpenAiModelFactory = Symbol('OpenAiModelFactory');
24
25
 
@@ -32,5 +33,6 @@ const openAiConnectionModule = ConnectionContainerModule.create(({ bind, bindBac
32
33
  });
33
34
 
34
35
  export default new ContainerModule(bind => {
36
+ bind(OpenAiModelUtils).toSelf().inSingletonScope();
35
37
  bind(ConnectionContainerModule).toConstantValue(openAiConnectionModule);
36
38
  });