@theia/plugin-ext 1.73.0-next.9 → 1.74.0-next.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/common/lm-tool-protocol.d.ts +58 -0
- package/lib/common/lm-tool-protocol.d.ts.map +1 -0
- package/lib/common/lm-tool-protocol.js +38 -0
- package/lib/common/lm-tool-protocol.js.map +1 -0
- package/lib/common/lm-tool-protocol.spec.d.ts +2 -0
- package/lib/common/lm-tool-protocol.spec.d.ts.map +1 -0
- package/lib/common/lm-tool-protocol.spec.js +80 -0
- package/lib/common/lm-tool-protocol.spec.js.map +1 -0
- package/lib/common/plugin-api-rpc-model.d.ts +4 -0
- package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc-model.js.map +1 -1
- package/lib/common/plugin-api-rpc.d.ts +6 -1
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js +4 -2
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/common/plugin-protocol.d.ts +8 -0
- package/lib/common/plugin-protocol.d.ts.map +1 -1
- package/lib/common/plugin-protocol.js.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.d.ts +2 -1
- package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.js +6 -1
- package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
- package/lib/hosted/node/plugin-deployer-handler-impl.js +4 -3
- package/lib/hosted/node/plugin-deployer-handler-impl.js.map +1 -1
- package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
- package/lib/hosted/node/plugin-host-module.js +2 -0
- package/lib/hosted/node/plugin-host-module.js.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.d.ts +2 -0
- package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.js +55 -49
- package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
- package/lib/main/browser/comments/comment-thread-widget.d.ts +3 -3
- package/lib/main/browser/comments/comment-thread-widget.d.ts.map +1 -1
- package/lib/main/browser/comments/comment-thread-widget.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.js +1 -0
- package/lib/main/browser/custom-editors/custom-editor-opener.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-widget.d.ts +1 -0
- package/lib/main/browser/custom-editors/custom-editor-widget.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-widget.js +3 -0
- package/lib/main/browser/custom-editors/custom-editor-widget.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editors-main.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editors-main.js +2 -0
- package/lib/main/browser/custom-editors/custom-editors-main.js.map +1 -1
- package/lib/main/browser/debug/debug-main.d.ts +1 -11
- package/lib/main/browser/debug/debug-main.d.ts.map +1 -1
- package/lib/main/browser/debug/debug-main.js +3 -24
- package/lib/main/browser/debug/debug-main.js.map +1 -1
- package/lib/main/browser/debug/plugin-debug-service.d.ts +2 -1
- package/lib/main/browser/debug/plugin-debug-service.d.ts.map +1 -1
- package/lib/main/browser/debug/plugin-debug-service.js +8 -3
- package/lib/main/browser/debug/plugin-debug-service.js.map +1 -1
- package/lib/main/browser/debug/plugin-debug-session-factory.d.ts +7 -44
- package/lib/main/browser/debug/plugin-debug-session-factory.d.ts.map +1 -1
- package/lib/main/browser/debug/plugin-debug-session-factory.js +29 -35
- package/lib/main/browser/debug/plugin-debug-session-factory.js.map +1 -1
- package/lib/main/browser/languages-main.d.ts +6 -1
- package/lib/main/browser/languages-main.d.ts.map +1 -1
- package/lib/main/browser/languages-main.js +39 -2
- package/lib/main/browser/languages-main.js.map +1 -1
- package/lib/main/browser/lm-main.js +2 -2
- package/lib/main/browser/lm-main.js.map +1 -1
- package/lib/main/browser/lm-tool-main.d.ts +14 -0
- package/lib/main/browser/lm-tool-main.d.ts.map +1 -0
- package/lib/main/browser/lm-tool-main.js +101 -0
- package/lib/main/browser/lm-tool-main.js.map +1 -0
- package/lib/main/browser/lm-tool-main.spec.d.ts +2 -0
- package/lib/main/browser/lm-tool-main.spec.d.ts.map +1 -0
- package/lib/main/browser/lm-tool-main.spec.js +100 -0
- package/lib/main/browser/lm-tool-main.spec.js.map +1 -0
- package/lib/main/browser/main-context.d.ts.map +1 -1
- package/lib/main/browser/main-context.js +3 -0
- package/lib/main/browser/main-context.js.map +1 -1
- package/lib/main/browser/main-file-system-event-service.d.ts +21 -4
- package/lib/main/browser/main-file-system-event-service.d.ts.map +1 -1
- package/lib/main/browser/main-file-system-event-service.js +47 -17
- package/lib/main/browser/main-file-system-event-service.js.map +1 -1
- package/lib/main/browser/main-file-system-event-service.spec.js +61 -26
- package/lib/main/browser/main-file-system-event-service.spec.js.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts +2 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js +11 -5
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.d.ts +2 -0
- package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.js +10 -4
- package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.d.ts +1 -0
- package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.js +28 -3
- package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.spec.js +40 -0
- package/lib/main/browser/view/plugin-view-registry.spec.js.map +1 -1
- package/lib/main/browser/webview/webview-resource-cache.d.ts +2 -0
- package/lib/main/browser/webview/webview-resource-cache.d.ts.map +1 -1
- package/lib/main/browser/webview/webview-resource-cache.js +7 -1
- package/lib/main/browser/webview/webview-resource-cache.js.map +1 -1
- package/lib/main/browser/webview/webview.d.ts +2 -1
- package/lib/main/browser/webview/webview.d.ts.map +1 -1
- package/lib/main/browser/webview/webview.js +7 -1
- package/lib/main/browser/webview/webview.js.map +1 -1
- package/lib/main/node/plugin-deployer-impl.js +2 -1
- package/lib/main/node/plugin-deployer-impl.js.map +1 -1
- package/lib/plugin/languages/diagnostics.d.ts +4 -1
- package/lib/plugin/languages/diagnostics.d.ts.map +1 -1
- package/lib/plugin/languages/diagnostics.js +22 -9
- package/lib/plugin/languages/diagnostics.js.map +1 -1
- package/lib/plugin/languages/multi-document-highlight.d.ts +14 -0
- package/lib/plugin/languages/multi-document-highlight.d.ts.map +1 -0
- package/lib/plugin/languages/multi-document-highlight.js +60 -0
- package/lib/plugin/languages/multi-document-highlight.js.map +1 -0
- package/lib/plugin/languages.d.ts +3 -1
- package/lib/plugin/languages.d.ts.map +1 -1
- package/lib/plugin/languages.js +11 -0
- package/lib/plugin/languages.js.map +1 -1
- package/lib/plugin/lm-tool-ext.d.ts +28 -0
- package/lib/plugin/lm-tool-ext.d.ts.map +1 -0
- package/lib/plugin/lm-tool-ext.js +171 -0
- package/lib/plugin/lm-tool-ext.js.map +1 -0
- package/lib/plugin/lm-tool-ext.spec.d.ts +2 -0
- package/lib/plugin/lm-tool-ext.spec.d.ts.map +1 -0
- package/lib/plugin/lm-tool-ext.spec.js +124 -0
- package/lib/plugin/lm-tool-ext.spec.js.map +1 -0
- package/lib/plugin/plugin-context.d.ts.map +1 -1
- package/lib/plugin/plugin-context.js +10 -11
- package/lib/plugin/plugin-context.js.map +1 -1
- package/lib/plugin/types-impl.d.ts +0 -9
- package/lib/plugin/types-impl.d.ts.map +1 -1
- package/lib/plugin/types-impl.js +22 -15
- package/lib/plugin/types-impl.js.map +1 -1
- package/package.json +32 -31
- package/src/common/lm-tool-protocol.spec.ts +89 -0
- package/src/common/lm-tool-protocol.ts +102 -0
- package/src/common/plugin-api-rpc-model.ts +5 -0
- package/src/common/plugin-api-rpc.ts +9 -2
- package/src/common/plugin-protocol.ts +9 -0
- package/src/hosted/node/hosted-plugin-localization-service.ts +6 -3
- package/src/hosted/node/plugin-deployer-handler-impl.ts +5 -5
- package/src/hosted/node/plugin-host-module.ts +2 -0
- package/src/hosted/node/scanners/scanner-theia.ts +54 -50
- package/src/main/browser/comments/comment-thread-widget.tsx +5 -5
- package/src/main/browser/custom-editors/custom-editor-opener.tsx +1 -0
- package/src/main/browser/custom-editors/custom-editor-widget.ts +4 -0
- package/src/main/browser/custom-editors/custom-editors-main.ts +2 -0
- package/src/main/browser/debug/debug-main.ts +3 -45
- package/src/main/browser/debug/plugin-debug-service.ts +7 -5
- package/src/main/browser/debug/plugin-debug-session-factory.ts +25 -71
- package/src/main/browser/languages-main.ts +49 -4
- package/src/main/browser/lm-main.ts +2 -2
- package/src/main/browser/lm-tool-main.spec.ts +126 -0
- package/src/main/browser/lm-tool-main.ts +116 -0
- package/src/main/browser/main-context.ts +4 -0
- package/src/main/browser/main-file-system-event-service.spec.ts +76 -29
- package/src/main/browser/main-file-system-event-service.ts +46 -16
- package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +11 -7
- package/src/main/browser/plugin-contribution-handler.ts +8 -4
- package/src/main/browser/plugin-ext-widget.tsx +1 -1
- package/src/main/browser/view/plugin-view-registry.spec.ts +69 -1
- package/src/main/browser/view/plugin-view-registry.ts +30 -3
- package/src/main/browser/webview/webview-resource-cache.ts +6 -2
- package/src/main/browser/webview/webview.ts +6 -3
- package/src/main/node/plugin-deployer-impl.ts +2 -2
- package/src/plugin/languages/diagnostics.ts +28 -10
- package/src/plugin/languages/multi-document-highlight.ts +75 -0
- package/src/plugin/languages.ts +20 -0
- package/src/plugin/lm-tool-ext.spec.ts +152 -0
- package/src/plugin/lm-tool-ext.ts +197 -0
- package/src/plugin/plugin-context.ts +11 -11
- package/src/plugin/types-impl.ts +22 -15
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
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 { expect } from 'chai';
|
|
18
|
+
import { LanguageModelToolsExtImpl } from './lm-tool-ext';
|
|
19
|
+
import { ToolResultPartDto, uint8ArrayToBase64 } from '../common/lm-tool-protocol';
|
|
20
|
+
|
|
21
|
+
// Access private methods for testing
|
|
22
|
+
interface LmToolExtInternals {
|
|
23
|
+
convertPartToDto(part: unknown): ToolResultPartDto;
|
|
24
|
+
isTextPart(part: unknown): boolean;
|
|
25
|
+
isDataPart(part: unknown): boolean;
|
|
26
|
+
isPromptTsxPart(part: unknown): boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createInstance(): LmToolExtInternals {
|
|
30
|
+
const impl = Object.create(LanguageModelToolsExtImpl.prototype) as LanguageModelToolsExtImpl;
|
|
31
|
+
return impl as unknown as LmToolExtInternals;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('LanguageModelToolsExtImpl - Part to DTO conversion', () => {
|
|
35
|
+
let instance: LmToolExtInternals;
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
instance = createInstance();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('isTextPart', () => {
|
|
42
|
+
it('should return true for a text part with string value', () => {
|
|
43
|
+
expect(instance.isTextPart({ value: 'hello' })).to.be.true;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return false for a data part (has data and mimeType)', () => {
|
|
47
|
+
expect(instance.isTextPart({ data: new Uint8Array([1, 2]), mimeType: 'text/plain' })).to.be.false;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return false for a non-string value', () => {
|
|
51
|
+
expect(instance.isTextPart({ value: { foo: 'bar' } })).to.be.false;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should return false for undefined', () => {
|
|
55
|
+
expect(instance.isTextPart(undefined)).to.be.false;
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('isDataPart', () => {
|
|
60
|
+
it('should return true for a data part', () => {
|
|
61
|
+
expect(instance.isDataPart({ data: new Uint8Array([1, 2]), mimeType: 'image/png' })).to.be.true;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should return false for a text part', () => {
|
|
65
|
+
expect(instance.isDataPart({ value: 'hello' })).to.be.false;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return false for undefined', () => {
|
|
69
|
+
expect(instance.isDataPart(undefined)).to.be.false;
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('isPromptTsxPart', () => {
|
|
74
|
+
it('should return true for a prompt-tsx part with non-string value', () => {
|
|
75
|
+
expect(instance.isPromptTsxPart({ value: { component: 'div' } })).to.be.true;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return false for a text part with string value', () => {
|
|
79
|
+
expect(instance.isPromptTsxPart({ value: 'hello' })).to.be.false;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should return false for a data part', () => {
|
|
83
|
+
expect(instance.isPromptTsxPart({ data: new Uint8Array(), mimeType: 'text/plain' })).to.be.false;
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('uint8ArrayToBase64', () => {
|
|
88
|
+
it('should encode an empty array', () => {
|
|
89
|
+
const result = uint8ArrayToBase64(new Uint8Array([]));
|
|
90
|
+
expect(result).to.equal('');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should encode a simple string as Uint8Array', () => {
|
|
94
|
+
const data = new Uint8Array(Buffer.from('hello'));
|
|
95
|
+
const result = uint8ArrayToBase64(data);
|
|
96
|
+
expect(result).to.equal(Buffer.from('hello').toString('base64'));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should encode binary data', () => {
|
|
100
|
+
const data = new Uint8Array([0, 1, 2, 255]);
|
|
101
|
+
const result = uint8ArrayToBase64(data);
|
|
102
|
+
expect(result).to.equal(Buffer.from([0, 1, 2, 255]).toString('base64'));
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('convertPartToDto', () => {
|
|
107
|
+
it('should convert a LanguageModelTextPart to text DTO', () => {
|
|
108
|
+
const part = { value: 'some text' };
|
|
109
|
+
const result = instance.convertPartToDto(part);
|
|
110
|
+
expect(result).to.deep.equal({ type: 'text', value: 'some text' });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should convert a LanguageModelDataPart to data DTO', () => {
|
|
114
|
+
const data = new Uint8Array(Buffer.from('binary content'));
|
|
115
|
+
const part = { data, mimeType: 'application/octet-stream' };
|
|
116
|
+
const result = instance.convertPartToDto(part);
|
|
117
|
+
expect(result.type).to.equal('data');
|
|
118
|
+
expect((result as { base64: string }).base64).to.equal(Buffer.from('binary content').toString('base64'));
|
|
119
|
+
expect((result as { mimeType: string }).mimeType).to.equal('application/octet-stream');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should convert a LanguageModelPromptTsxPart to prompt-tsx DTO', () => {
|
|
123
|
+
const part = { value: { component: 'SomeComponent', props: { x: 1 } } };
|
|
124
|
+
const result = instance.convertPartToDto(part);
|
|
125
|
+
expect(result).to.deep.equal({ type: 'prompt-tsx', value: { component: 'SomeComponent', props: { x: 1 } } });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should convert an unknown part to unknown DTO', () => {
|
|
129
|
+
const part = { someUnknownField: 42 };
|
|
130
|
+
const result = instance.convertPartToDto(part);
|
|
131
|
+
expect(result).to.deep.equal({ type: 'unknown', json: '{"someUnknownField":42}' });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should convert a LanguageModelDataPart with image mime type to data DTO', () => {
|
|
135
|
+
const imageData = new Uint8Array([137, 80, 78, 71]); // PNG header bytes
|
|
136
|
+
const part = { data: imageData, mimeType: 'image/png' };
|
|
137
|
+
const result = instance.convertPartToDto(part);
|
|
138
|
+
expect(result.type).to.equal('data');
|
|
139
|
+
expect((result as { base64: string }).base64).to.equal(Buffer.from(imageData).toString('base64'));
|
|
140
|
+
expect((result as { mimeType: string }).mimeType).to.equal('image/png');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should convert a LanguageModelDataPart with text/x-json to data DTO', () => {
|
|
144
|
+
const jsonStr = '{"key":"value"}';
|
|
145
|
+
const data = new Uint8Array(Buffer.from(jsonStr));
|
|
146
|
+
const part = { data, mimeType: 'text/x-json' };
|
|
147
|
+
const result = instance.convertPartToDto(part);
|
|
148
|
+
expect(result.type).to.equal('data');
|
|
149
|
+
expect((result as { base64: string }).base64).to.equal(Buffer.from(jsonStr).toString('base64'));
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource.
|
|
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 type * as theia from '@theia/plugin';
|
|
18
|
+
import { CancellationToken } from '@theia/core/lib/common/cancellation';
|
|
19
|
+
|
|
20
|
+
import { RPCProtocol } from '../common/rpc-protocol';
|
|
21
|
+
import {
|
|
22
|
+
LanguageModelToolsExt,
|
|
23
|
+
LanguageModelToolsMain,
|
|
24
|
+
LanguageModelToolDto,
|
|
25
|
+
ToolInvocationResult,
|
|
26
|
+
ToolResultPartDto,
|
|
27
|
+
isToolInvocationError,
|
|
28
|
+
uint8ArrayToBase64,
|
|
29
|
+
base64ToUint8Array,
|
|
30
|
+
} from '../common/lm-tool-protocol';
|
|
31
|
+
import { PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc';
|
|
32
|
+
import { PluginPackageLanguageModelToolContribution } from '../common';
|
|
33
|
+
import { PluginLogger } from './logger';
|
|
34
|
+
import { Disposable, LanguageModelTextPart, LanguageModelPromptTsxPart, LanguageModelDataPart, LanguageModelToolResult } from './types-impl';
|
|
35
|
+
|
|
36
|
+
export class LanguageModelToolsExtImpl implements LanguageModelToolsExt {
|
|
37
|
+
|
|
38
|
+
private proxy: LanguageModelToolsMain;
|
|
39
|
+
private logger: PluginLogger;
|
|
40
|
+
private handleCounter = 0;
|
|
41
|
+
private readonly tools = new Map<number, theia.LanguageModelTool<unknown>>();
|
|
42
|
+
private readonly toolNameToHandle = new Map<string, number>();
|
|
43
|
+
private readonly toolContributions = new Map<string, PluginPackageLanguageModelToolContribution>();
|
|
44
|
+
private readonly registeredToolMetadata = new Map<number, LanguageModelToolDto>();
|
|
45
|
+
|
|
46
|
+
constructor(protected readonly rpc: RPCProtocol) {
|
|
47
|
+
this.proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.LM_TOOLS_MAIN);
|
|
48
|
+
this.logger = new PluginLogger(this.rpc, 'lm-tools');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
registerToolContributions(contributions: PluginPackageLanguageModelToolContribution[]): void {
|
|
52
|
+
for (const contribution of contributions) {
|
|
53
|
+
this.toolContributions.set(contribution.name, contribution);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
registerTool<T>(name: string, tool: theia.LanguageModelTool<T>, pluginId: string): Disposable {
|
|
58
|
+
const contribution = this.toolContributions.get(name);
|
|
59
|
+
if (!contribution) {
|
|
60
|
+
this.logger.warn(`Tool '${name}' is not declared in package.json contributes.languageModelTools.`);
|
|
61
|
+
}
|
|
62
|
+
if (this.toolNameToHandle.has(name)) {
|
|
63
|
+
this.logger.warn(`Tool '${name}' is already registered. Ignoring duplicate registration.`);
|
|
64
|
+
return Disposable.NULL;
|
|
65
|
+
}
|
|
66
|
+
const handle = this.handleCounter++;
|
|
67
|
+
const metadata: LanguageModelToolDto = {
|
|
68
|
+
name,
|
|
69
|
+
description: contribution?.modelDescription,
|
|
70
|
+
inputSchema: contribution?.inputSchema,
|
|
71
|
+
tags: contribution?.tags,
|
|
72
|
+
};
|
|
73
|
+
this.tools.set(handle, tool);
|
|
74
|
+
this.toolNameToHandle.set(name, handle);
|
|
75
|
+
this.registeredToolMetadata.set(handle, metadata);
|
|
76
|
+
this.proxy.$registerTool(handle, name, metadata, pluginId).catch(err => {
|
|
77
|
+
this.logger.error(`Failed to register tool '${name}': ${err.message || err}`);
|
|
78
|
+
this.tools.delete(handle);
|
|
79
|
+
this.toolNameToHandle.delete(name);
|
|
80
|
+
this.registeredToolMetadata.delete(handle);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return new Disposable(() => {
|
|
84
|
+
this.tools.delete(handle);
|
|
85
|
+
this.toolNameToHandle.delete(name);
|
|
86
|
+
this.registeredToolMetadata.delete(handle);
|
|
87
|
+
this.proxy.$unregisterTool(handle);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async invokeTool(name: string, options: theia.LanguageModelToolInvocationOptions<object>, token?: CancellationToken): Promise<theia.LanguageModelToolResult> {
|
|
92
|
+
const handle = this.toolNameToHandle.get(name);
|
|
93
|
+
if (handle === undefined) {
|
|
94
|
+
throw new Error(`Tool '${name}' is not registered.`);
|
|
95
|
+
}
|
|
96
|
+
const result = await this.$invokeTool(handle, JSON.stringify(options.input), token);
|
|
97
|
+
if (isToolInvocationError(result)) {
|
|
98
|
+
throw new Error(result.error);
|
|
99
|
+
}
|
|
100
|
+
return new LanguageModelToolResult(result.content.map(part => this.convertDtoToApiPart(part)));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getTools(): theia.LanguageModelToolInformation[] {
|
|
104
|
+
const result = new Map<string, theia.LanguageModelToolInformation>();
|
|
105
|
+
// Add declared tool contributions from package.json
|
|
106
|
+
for (const c of this.toolContributions.values()) {
|
|
107
|
+
result.set(c.name, {
|
|
108
|
+
name: c.name,
|
|
109
|
+
description: c.modelDescription ?? '',
|
|
110
|
+
inputSchema: c.inputSchema,
|
|
111
|
+
tags: c.tags ?? [],
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// Add dynamically registered tools not already covered by declarations
|
|
115
|
+
for (const [name, handle] of this.toolNameToHandle.entries()) {
|
|
116
|
+
if (!result.has(name)) {
|
|
117
|
+
const metadata = this.registeredToolMetadata.get(handle);
|
|
118
|
+
if (metadata) {
|
|
119
|
+
result.set(name, {
|
|
120
|
+
name: metadata.name,
|
|
121
|
+
description: metadata.description ?? '',
|
|
122
|
+
inputSchema: metadata.inputSchema,
|
|
123
|
+
tags: metadata.tags ?? [],
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return [...result.values()];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async $invokeTool(handle: number, argsString: string, token?: CancellationToken): Promise<ToolInvocationResult> {
|
|
132
|
+
const tool = this.tools.get(handle);
|
|
133
|
+
if (!tool) {
|
|
134
|
+
return { error: `Tool with handle ${handle} not found` };
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const input = argsString ? JSON.parse(argsString) : {};
|
|
138
|
+
const cancellationToken = token ?? CancellationToken.None;
|
|
139
|
+
const result = await tool.invoke(
|
|
140
|
+
{ input, toolInvocationToken: undefined },
|
|
141
|
+
cancellationToken
|
|
142
|
+
);
|
|
143
|
+
if (!result) {
|
|
144
|
+
return { content: [] };
|
|
145
|
+
}
|
|
146
|
+
return { content: result.content.map(part => this.convertPartToDto(part)) };
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
149
|
+
return { error: errorMessage };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private convertPartToDto(part: unknown): ToolResultPartDto {
|
|
154
|
+
if (this.isTextPart(part)) {
|
|
155
|
+
return { type: 'text', value: part.value };
|
|
156
|
+
}
|
|
157
|
+
if (this.isDataPart(part)) {
|
|
158
|
+
const base64 = uint8ArrayToBase64(part.data);
|
|
159
|
+
return { type: 'data', base64, mimeType: part.mimeType };
|
|
160
|
+
}
|
|
161
|
+
if (this.isPromptTsxPart(part)) {
|
|
162
|
+
return { type: 'prompt-tsx', value: part.value };
|
|
163
|
+
}
|
|
164
|
+
return { type: 'unknown', json: JSON.stringify(part) };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private isTextPart(part: unknown): part is { value: string } {
|
|
168
|
+
return typeof part === 'object' && part !== undefined
|
|
169
|
+
&& 'value' in (part as object) && typeof (part as { value: unknown }).value === 'string'
|
|
170
|
+
&& !('data' in (part as object)) && !('mimeType' in (part as object));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private isDataPart(part: unknown): part is { data: Uint8Array; mimeType: string } {
|
|
174
|
+
return typeof part === 'object' && part !== undefined
|
|
175
|
+
&& 'data' in (part as object) && 'mimeType' in (part as object)
|
|
176
|
+
&& typeof (part as { mimeType: unknown }).mimeType === 'string';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private isPromptTsxPart(part: unknown): part is { value: unknown } {
|
|
180
|
+
return typeof part === 'object' && part !== undefined
|
|
181
|
+
&& 'value' in (part as object) && typeof (part as { value: unknown }).value !== 'string'
|
|
182
|
+
&& !('data' in (part as object)) && !('mimeType' in (part as object));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private convertDtoToApiPart(part: ToolResultPartDto): theia.LanguageModelTextPart | theia.LanguageModelPromptTsxPart | theia.LanguageModelDataPart {
|
|
186
|
+
switch (part.type) {
|
|
187
|
+
case 'text':
|
|
188
|
+
return new LanguageModelTextPart(part.value);
|
|
189
|
+
case 'data':
|
|
190
|
+
return new LanguageModelDataPart(base64ToUint8Array(part.base64), part.mimeType);
|
|
191
|
+
case 'prompt-tsx':
|
|
192
|
+
return new LanguageModelPromptTsxPart(part.value);
|
|
193
|
+
case 'unknown':
|
|
194
|
+
return new LanguageModelTextPart(part.json);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -297,6 +297,7 @@ import { TestingExtImpl } from './tests';
|
|
|
297
297
|
import { UriExtImpl } from './uri-ext';
|
|
298
298
|
import { PluginLogger } from './logger';
|
|
299
299
|
import { LmExtImpl } from './lm-ext';
|
|
300
|
+
import { LanguageModelToolsExtImpl } from './lm-tool-ext';
|
|
300
301
|
|
|
301
302
|
export function createAPIObject<T extends Object>(rawObject: T): T {
|
|
302
303
|
return new Proxy(rawObject, {
|
|
@@ -362,6 +363,7 @@ export function createAPIFactory(
|
|
|
362
363
|
const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry));
|
|
363
364
|
const uriExt = rpc.set(MAIN_RPC_CONTEXT.URI_EXT, new UriExtImpl(rpc));
|
|
364
365
|
const lmExt = rpc.set(MAIN_RPC_CONTEXT.MCP_SERVER_DEFINITION_REGISTRY_EXT, new LmExtImpl(rpc));
|
|
366
|
+
const lmToolExt = rpc.set(MAIN_RPC_CONTEXT.LM_TOOLS_EXT, new LanguageModelToolsExtImpl(rpc));
|
|
365
367
|
rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt);
|
|
366
368
|
|
|
367
369
|
const commandLogger = new PluginLogger(rpc, 'commands-plugin');
|
|
@@ -1061,12 +1063,8 @@ export function createAPIFactory(
|
|
|
1061
1063
|
registerDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.DocumentHighlightProvider): theia.Disposable {
|
|
1062
1064
|
return languagesExt.registerDocumentHighlightProvider(selector, provider, pluginToPluginInfo(plugin));
|
|
1063
1065
|
},
|
|
1064
|
-
/**
|
|
1065
|
-
* @stubbed
|
|
1066
|
-
* @monaco-uplift: wait until API is available in Monaco (1.85.0+)
|
|
1067
|
-
*/
|
|
1068
1066
|
registerMultiDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.MultiDocumentHighlightProvider): theia.Disposable {
|
|
1069
|
-
return
|
|
1067
|
+
return languagesExt.registerMultiDocumentHighlightProvider(selector, provider, pluginToPluginInfo(plugin));
|
|
1070
1068
|
},
|
|
1071
1069
|
registerWorkspaceSymbolProvider(provider: theia.WorkspaceSymbolProvider): theia.Disposable {
|
|
1072
1070
|
return languagesExt.registerWorkspaceSymbolProvider(provider, pluginToPluginInfo(plugin));
|
|
@@ -1374,6 +1372,9 @@ export function createAPIFactory(
|
|
|
1374
1372
|
const mcpContributions = plugin.rawModel.contributes && plugin.rawModel.contributes.mcpServerDefinitionProviders || [];
|
|
1375
1373
|
lmExt.registerMcpContributions(mcpContributions);
|
|
1376
1374
|
|
|
1375
|
+
const toolContributions = plugin.rawModel.contributes && plugin.rawModel.contributes.languageModelTools || [];
|
|
1376
|
+
lmToolExt.registerToolContributions(toolContributions);
|
|
1377
|
+
|
|
1377
1378
|
const lm: typeof theia.lm = {
|
|
1378
1379
|
/** @stubbed LanguageModelChat */
|
|
1379
1380
|
selectChatModels(selector?: theia.LanguageModelChatSelector): Thenable<theia.LanguageModelChat[]> {
|
|
@@ -1381,16 +1382,15 @@ export function createAPIFactory(
|
|
|
1381
1382
|
},
|
|
1382
1383
|
/** @stubbed LanguageModelChat */
|
|
1383
1384
|
onDidChangeChatModels: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables),
|
|
1384
|
-
/** @stubbed LanguageModelTool */
|
|
1385
1385
|
invokeTool(name: string, options: theia.LanguageModelToolInvocationOptions<object>, token?: CancellationToken): Thenable<theia.LanguageModelToolResult> {
|
|
1386
|
-
return
|
|
1386
|
+
return lmToolExt.invokeTool(name, options, token);
|
|
1387
1387
|
},
|
|
1388
|
-
/** @stubbed LanguageModelTool */
|
|
1389
1388
|
registerTool<T>(name: string, tool: theia.LanguageModelTool<T>): Disposable {
|
|
1390
|
-
return
|
|
1389
|
+
return lmToolExt.registerTool(name, tool, plugin.model.id);
|
|
1390
|
+
},
|
|
1391
|
+
get tools(): readonly theia.LanguageModelToolInformation[] {
|
|
1392
|
+
return lmToolExt.getTools();
|
|
1391
1393
|
},
|
|
1392
|
-
/** @stubbed LanguageModelTool */
|
|
1393
|
-
tools: [],
|
|
1394
1394
|
registerMcpServerDefinitionProvider(id: string, provider: any): theia.Disposable {
|
|
1395
1395
|
return lmExt.registerMcpServerDefinitionProvider(id, provider);
|
|
1396
1396
|
},
|
package/src/plugin/types-impl.ts
CHANGED
|
@@ -4126,7 +4126,11 @@ export class LanguageModelToolCallPart {
|
|
|
4126
4126
|
name: string;
|
|
4127
4127
|
input: object;
|
|
4128
4128
|
|
|
4129
|
-
constructor(callId: string, name: string, input: object) {
|
|
4129
|
+
constructor(callId: string, name: string, input: object) {
|
|
4130
|
+
this.callId = callId;
|
|
4131
|
+
this.name = name;
|
|
4132
|
+
this.input = input;
|
|
4133
|
+
}
|
|
4130
4134
|
}
|
|
4131
4135
|
|
|
4132
4136
|
/**
|
|
@@ -4136,24 +4140,25 @@ export class LanguageModelToolResultPart {
|
|
|
4136
4140
|
callId: string;
|
|
4137
4141
|
content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>;
|
|
4138
4142
|
|
|
4139
|
-
constructor(callId: string, content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) {
|
|
4143
|
+
constructor(callId: string, content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) {
|
|
4144
|
+
this.callId = callId;
|
|
4145
|
+
this.content = content;
|
|
4146
|
+
}
|
|
4140
4147
|
}
|
|
4141
4148
|
|
|
4142
|
-
/**
|
|
4143
|
-
* @stubbed
|
|
4144
|
-
*/
|
|
4145
4149
|
export class LanguageModelTextPart {
|
|
4146
4150
|
value: string;
|
|
4147
|
-
constructor(value: string) {
|
|
4151
|
+
constructor(value: string) {
|
|
4152
|
+
this.value = value;
|
|
4153
|
+
}
|
|
4148
4154
|
}
|
|
4149
4155
|
|
|
4150
|
-
/**
|
|
4151
|
-
* @stubbed
|
|
4152
|
-
*/
|
|
4153
4156
|
export class LanguageModelToolResult {
|
|
4154
4157
|
content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>;
|
|
4155
4158
|
|
|
4156
|
-
constructor(content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) {
|
|
4159
|
+
constructor(content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) {
|
|
4160
|
+
this.content = content;
|
|
4161
|
+
}
|
|
4157
4162
|
}
|
|
4158
4163
|
|
|
4159
4164
|
/**
|
|
@@ -4162,12 +4167,11 @@ export class LanguageModelToolResult {
|
|
|
4162
4167
|
export class LanguageModelPromptTsxPart {
|
|
4163
4168
|
value: unknown;
|
|
4164
4169
|
|
|
4165
|
-
constructor(value: unknown) {
|
|
4170
|
+
constructor(value: unknown) {
|
|
4171
|
+
this.value = value;
|
|
4172
|
+
}
|
|
4166
4173
|
}
|
|
4167
4174
|
|
|
4168
|
-
/**
|
|
4169
|
-
* @stubbed
|
|
4170
|
-
*/
|
|
4171
4175
|
export class LanguageModelDataPart {
|
|
4172
4176
|
static image(data: Uint8Array, mime: string): LanguageModelDataPart {
|
|
4173
4177
|
return new LanguageModelDataPart(data, mime);
|
|
@@ -4182,7 +4186,10 @@ export class LanguageModelDataPart {
|
|
|
4182
4186
|
}
|
|
4183
4187
|
mimeType: string;
|
|
4184
4188
|
data: Uint8Array;
|
|
4185
|
-
constructor(data: Uint8Array, mimeType: string) {
|
|
4189
|
+
constructor(data: Uint8Array, mimeType: string) {
|
|
4190
|
+
this.data = data;
|
|
4191
|
+
this.mimeType = mimeType;
|
|
4192
|
+
}
|
|
4186
4193
|
}
|
|
4187
4194
|
|
|
4188
4195
|
/**
|