@theia/ai-chat-ui 1.58.2 → 1.59.0-next.62
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/ai-chat-ui-contribution.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-contribution.js +7 -7
- package/lib/browser/ai-chat-ui-contribution.js.map +1 -1
- package/lib/browser/ai-chat-ui-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-frontend-module.js +7 -4
- package/lib/browser/ai-chat-ui-frontend-module.js.map +1 -1
- package/lib/browser/chat-input-widget.d.ts +21 -4
- package/lib/browser/chat-input-widget.d.ts.map +1 -1
- package/lib/browser/chat-input-widget.js +177 -37
- package/lib/browser/chat-input-widget.js.map +1 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.js +4 -3
- package/lib/browser/chat-response-renderer/code-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/text-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/text-part-renderer.js +3 -1
- package/lib/browser/chat-response-renderer/text-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js +7 -3
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js +25 -16
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
- package/lib/browser/chat-view-contribution.d.ts +2 -2
- package/lib/browser/chat-view-contribution.d.ts.map +1 -1
- package/lib/browser/chat-view-contribution.js +6 -6
- package/lib/browser/chat-view-contribution.js.map +1 -1
- package/lib/browser/chat-view-language-contribution.d.ts +10 -5
- package/lib/browser/chat-view-language-contribution.d.ts.map +1 -1
- package/lib/browser/chat-view-language-contribution.js +94 -14
- package/lib/browser/chat-view-language-contribution.js.map +1 -1
- package/lib/browser/chat-view-widget.d.ts +4 -1
- package/lib/browser/chat-view-widget.d.ts.map +1 -1
- package/lib/browser/chat-view-widget.js +18 -7
- package/lib/browser/chat-view-widget.js.map +1 -1
- package/lib/browser/context-variable-picker.d.ts +9 -0
- package/lib/browser/context-variable-picker.d.ts.map +1 -0
- package/lib/browser/context-variable-picker.js +86 -0
- package/lib/browser/context-variable-picker.js.map +1 -0
- package/package.json +11 -11
- package/src/browser/ai-chat-ui-contribution.ts +8 -8
- package/src/browser/ai-chat-ui-frontend-module.ts +8 -4
- package/src/browser/chat-input-widget.tsx +255 -49
- package/src/browser/chat-response-renderer/code-part-renderer.tsx +4 -3
- package/src/browser/chat-response-renderer/text-part-renderer.tsx +4 -1
- package/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +9 -3
- package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +26 -16
- package/src/browser/chat-view-contribution.ts +6 -6
- package/src/browser/chat-view-language-contribution.ts +103 -19
- package/src/browser/chat-view-widget.tsx +22 -8
- package/src/browser/context-variable-picker.ts +85 -0
- package/src/browser/style/index.css +95 -9
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
postConstruct
|
|
48
48
|
} from '@theia/core/shared/inversify';
|
|
49
49
|
import * as React from '@theia/core/shared/react';
|
|
50
|
+
import { nls } from '@theia/core/lib/common/nls';
|
|
50
51
|
|
|
51
52
|
import { ChatNodeToolbarActionContribution } from '../chat-node-toolbar-action-contribution';
|
|
52
53
|
import { ChatResponsePartRenderer } from '../chat-response-part-renderer';
|
|
@@ -151,43 +152,51 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
151
152
|
return <div className={'theia-ResponseNode'}>
|
|
152
153
|
<div className='theia-ResponseNode-Content' key={'disabled-message'}>
|
|
153
154
|
<div className="disable-message">
|
|
154
|
-
<span className="section-header">
|
|
155
|
+
<span className="section-header">{
|
|
156
|
+
nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiFeatureHeader', '🚀 AI Features Available (Alpha Version)!')}
|
|
157
|
+
</span>
|
|
155
158
|
<div className="section-title">
|
|
156
|
-
<p><code>Currently, all AI Features are disabled
|
|
159
|
+
<p><code>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/featuresDisabled', 'Currently, all AI Features are disabled!')}</code></p>
|
|
157
160
|
</div>
|
|
158
161
|
<div className="section-title">
|
|
159
|
-
<p>How to Enable
|
|
162
|
+
<p>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/howToEnable', 'How to Enable the AI Features:')}</p>
|
|
160
163
|
</div>
|
|
161
164
|
<div className="section-content">
|
|
162
|
-
<p>To enable the
|
|
163
|
-
{this.renderLinkButton('the settings menu', CommonCommands.OPEN_PREFERENCES.id)}
|
|
165
|
+
<p>To enable the AI features, please go to
|
|
166
|
+
{this.renderLinkButton(nls.localize('theia/ai/chat-ui/chat-view-tree-widget/settingsMenu', 'the settings menu'), CommonCommands.OPEN_PREFERENCES.id)}
|
|
164
167
|
and locate the <strong>AI Features</strong> section.</p>
|
|
165
168
|
<ol>
|
|
166
|
-
<li>Toggle the switch for <strong>'Ai-features: Enable'</strong>.</li>
|
|
169
|
+
<li>Toggle the switch for <strong>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiFeaturesEnable', 'Ai-features: Enable')}</strong>.</li>
|
|
167
170
|
<li>Provide at least one LLM provider (e.g. OpenAI), also see <a href="https://theia-ide.org/docs/user_ai/" target="_blank">the documentation</a>
|
|
168
171
|
for more information.</li>
|
|
169
172
|
</ol>
|
|
170
|
-
<p>This will activate the
|
|
173
|
+
<p>This will activate the AI capabilities in the app. Please remember, these features are <strong>in an alpha state</strong>,
|
|
174
|
+
so they may change and we are working on improving them 🚧.<br></br>
|
|
175
|
+
Please support us by <a href="https://github.com/eclipse-theia/theia">providing feedback
|
|
176
|
+
</a>!</p>
|
|
171
177
|
</div>
|
|
172
178
|
|
|
173
179
|
<div className="section-title">
|
|
174
180
|
<p>Currently Supported Views and Features:</p>
|
|
175
181
|
</div>
|
|
176
182
|
<div className="section-content">
|
|
177
|
-
<p>Once the
|
|
183
|
+
<p>Once the AI features are enabled, you can access the following views and features:</p>
|
|
178
184
|
<ul>
|
|
179
185
|
<li>Code Completion</li>
|
|
180
186
|
<li>Terminal Assistance (via CTRL+I in a terminal)</li>
|
|
181
187
|
<li>This Chat View (features the following agents):
|
|
182
188
|
<ul>
|
|
183
189
|
<li>Universal Chat Agent</li>
|
|
184
|
-
<li>
|
|
190
|
+
<li>Coder Chat Agent</li>
|
|
191
|
+
<li>Architect Chat Agent</li>
|
|
185
192
|
<li>Command Chat Agent</li>
|
|
186
193
|
<li>Orchestrator Chat Agent</li>
|
|
187
194
|
</ul>
|
|
188
195
|
</li>
|
|
189
|
-
<li>{this.renderLinkButton('AI History View', 'aiHistory:open')}</li>
|
|
190
|
-
<li>{this.renderLinkButton(
|
|
196
|
+
<li>{this.renderLinkButton(nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiHistoryView', 'AI History View'), 'aiHistory:open')}</li>
|
|
197
|
+
<li>{this.renderLinkButton(
|
|
198
|
+
nls.localize('theia/ai/chat-ui/chat-view-tree-widget/aiConfigurationView', 'AI Configuration View'), 'aiConfiguration:open')}
|
|
199
|
+
</li>
|
|
191
200
|
</ul>
|
|
192
201
|
<p>See <a href="https://theia-ide.org/docs/user_ai/" target="_blank">the documentation</a> for more information.</p>
|
|
193
202
|
</div>
|
|
@@ -307,8 +316,9 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
307
316
|
}}>
|
|
308
317
|
{this.getAgentLabel(node)}
|
|
309
318
|
</h3>
|
|
310
|
-
{inProgress && !waitingForInput && <span className='theia-ChatContentInProgress'>Generating</span>}
|
|
311
|
-
{inProgress && waitingForInput && <span className='theia-ChatContentInProgress'>
|
|
319
|
+
{inProgress && !waitingForInput && <span className='theia-ChatContentInProgress'>{nls.localizeByDefault('Generating')}</span>}
|
|
320
|
+
{inProgress && waitingForInput && <span className='theia-ChatContentInProgress'>{
|
|
321
|
+
nls.localize('theia/ai/chat-ui/chat-view-tree-widget/waitingForInput', 'Waiting for input')}</span>}
|
|
312
322
|
<div className='theia-ChatNodeToolbar'>
|
|
313
323
|
{!inProgress &&
|
|
314
324
|
toolbarContributions.length > 0 &&
|
|
@@ -338,9 +348,9 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
338
348
|
private getAgentLabel(node: RequestNode | ResponseNode): string {
|
|
339
349
|
if (isRequestNode(node)) {
|
|
340
350
|
// TODO find user name
|
|
341
|
-
return 'You';
|
|
351
|
+
return nls.localize('theia/ai/chat-ui/chat-view-tree-widget/you', 'You');
|
|
342
352
|
}
|
|
343
|
-
return this.getAgent(node)?.name ?? 'AI';
|
|
353
|
+
return this.getAgent(node)?.name ?? nls.localize('theia/ai/chat-ui/chat-view-tree-widget/ai', 'AI');
|
|
344
354
|
}
|
|
345
355
|
|
|
346
356
|
private getAgent(node: RequestNode | ResponseNode): ChatAgent | undefined {
|
|
@@ -420,7 +430,7 @@ export class ChatViewTreeWidget extends TreeWidget {
|
|
|
420
430
|
[-1, undefined])[1];
|
|
421
431
|
if (!renderer) {
|
|
422
432
|
console.error('No renderer found for content', content);
|
|
423
|
-
return <div>Error: No renderer found</div>;
|
|
433
|
+
return <div>{nls.localize('theia/ai/chat-ui/chat-view-tree-widget/noRenderer', 'Error: No renderer found')}</div>;
|
|
424
434
|
}
|
|
425
435
|
return renderer.render(content, node);
|
|
426
436
|
}
|
|
@@ -69,7 +69,7 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
|
|
|
69
69
|
const parent = extractRequestOrResponseNodes(args).find(arg => arg.parent)?.parent;
|
|
70
70
|
const text = parent?.children
|
|
71
71
|
.filter(isRequestOrResponseNode)
|
|
72
|
-
.map(child => this.
|
|
72
|
+
.map(child => this.getCopyText(child))
|
|
73
73
|
.join('\n\n---\n\n');
|
|
74
74
|
if (text) {
|
|
75
75
|
this.clipboardService.writeText(text);
|
|
@@ -93,19 +93,19 @@ export class ChatViewMenuContribution implements MenuContribution, CommandContri
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
protected copyMessage(args: (RequestNode | ResponseNode)[]): void {
|
|
96
|
-
const text = this.
|
|
96
|
+
const text = this.getCopyTextAndJoin(args);
|
|
97
97
|
this.clipboardService.writeText(text);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
protected
|
|
101
|
-
return args !== undefined ? args.map(arg => this.
|
|
100
|
+
protected getCopyTextAndJoin(args: (RequestNode | ResponseNode)[] | undefined): string {
|
|
101
|
+
return args !== undefined ? args.map(arg => this.getCopyText(arg)).join() : '';
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
protected
|
|
104
|
+
protected getCopyText(arg: RequestNode | ResponseNode): string {
|
|
105
105
|
if (isRequestNode(arg)) {
|
|
106
106
|
return arg.request.request.text;
|
|
107
107
|
} else if (isResponseNode(arg)) {
|
|
108
|
-
return arg.response.response.
|
|
108
|
+
return arg.response.response.asDisplayString();
|
|
109
109
|
}
|
|
110
110
|
return '';
|
|
111
111
|
}
|
|
@@ -13,18 +13,24 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { ChatAgentService } from '@theia/ai-chat';
|
|
17
|
+
import { AIContextVariable, AIVariableService } from '@theia/ai-core/lib/common';
|
|
18
|
+
import { PromptText } from '@theia/ai-core/lib/common/prompt-text';
|
|
19
|
+
import { ToolInvocationRegistry } from '@theia/ai-core/lib/common/tool-invocation-registry';
|
|
20
|
+
import { MaybePromise, nls } from '@theia/core';
|
|
21
|
+
import { ApplicationShell, FrontendApplication, FrontendApplicationContribution } from '@theia/core/lib/browser';
|
|
22
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
23
|
import * as monaco from '@theia/monaco-editor-core';
|
|
19
|
-
import { ContributionProvider, MaybePromise } from '@theia/core';
|
|
20
24
|
import { ProviderResult } from '@theia/monaco-editor-core/esm/vs/editor/common/languages';
|
|
21
|
-
import {
|
|
22
|
-
import { AIVariableService } from '@theia/ai-core/lib/common';
|
|
23
|
-
import { ToolProvider } from '@theia/ai-core/lib/common/tool-invocation-registry';
|
|
25
|
+
import { ChatViewWidget } from './chat-view-widget';
|
|
24
26
|
|
|
25
27
|
export const CHAT_VIEW_LANGUAGE_ID = 'theia-ai-chat-view-language';
|
|
26
28
|
export const CHAT_VIEW_LANGUAGE_EXTENSION = 'aichatviewlanguage';
|
|
27
29
|
|
|
30
|
+
const VARIABLE_RESOLUTION_CONTEXT = { context: 'chat-input-autocomplete' };
|
|
31
|
+
const VARIABLE_ARGUMENT_PICKER_COMMAND = 'trigger-variable-argument-picker';
|
|
32
|
+
const VARIABLE_ADD_CONTEXT_COMMAND = 'add-context-variable';
|
|
33
|
+
|
|
28
34
|
@injectable()
|
|
29
35
|
export class ChatViewLanguageContribution implements FrontendApplicationContribution {
|
|
30
36
|
|
|
@@ -34,25 +40,34 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
34
40
|
@inject(AIVariableService)
|
|
35
41
|
protected readonly variableService: AIVariableService;
|
|
36
42
|
|
|
37
|
-
@inject(
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
@inject(ToolInvocationRegistry)
|
|
44
|
+
protected readonly toolInvocationRegistry: ToolInvocationRegistry;
|
|
45
|
+
|
|
46
|
+
@inject(ApplicationShell)
|
|
47
|
+
protected readonly shell: ApplicationShell;
|
|
40
48
|
|
|
41
49
|
onStart(_app: FrontendApplication): MaybePromise<void> {
|
|
42
50
|
monaco.languages.register({ id: CHAT_VIEW_LANGUAGE_ID, extensions: [CHAT_VIEW_LANGUAGE_EXTENSION] });
|
|
43
51
|
|
|
44
52
|
monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
|
|
45
|
-
triggerCharacters: [
|
|
53
|
+
triggerCharacters: [PromptText.AGENT_CHAR],
|
|
46
54
|
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> => this.provideAgentCompletions(model, position),
|
|
47
55
|
});
|
|
48
56
|
monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
|
|
49
|
-
triggerCharacters: [
|
|
57
|
+
triggerCharacters: [PromptText.VARIABLE_CHAR],
|
|
50
58
|
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> => this.provideVariableCompletions(model, position),
|
|
51
59
|
});
|
|
52
60
|
monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
|
|
53
|
-
triggerCharacters: [
|
|
61
|
+
triggerCharacters: [PromptText.VARIABLE_CHAR, PromptText.VARIABLE_SEPARATOR_CHAR],
|
|
62
|
+
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> => this.provideVariableWithArgCompletions(model, position),
|
|
63
|
+
});
|
|
64
|
+
monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
|
|
65
|
+
triggerCharacters: [PromptText.FUNCTION_CHAR],
|
|
54
66
|
provideCompletionItems: (model, position, _context, _token): ProviderResult<monaco.languages.CompletionList> => this.provideToolCompletions(model, position),
|
|
55
67
|
});
|
|
68
|
+
|
|
69
|
+
monaco.editor.registerCommand(VARIABLE_ARGUMENT_PICKER_COMMAND, this.triggerVariableArgumentPicker.bind(this));
|
|
70
|
+
monaco.editor.registerCommand(VARIABLE_ADD_CONTEXT_COMMAND, (_, ...args) => args.length > 1 ? this.addContextVariable(args[0], args[1]) : undefined);
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
getCompletionRange(model: monaco.editor.ITextModel, position: monaco.Position, triggerCharacter: string): monaco.Range | undefined {
|
|
@@ -65,7 +80,7 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
65
80
|
return undefined;
|
|
66
81
|
}
|
|
67
82
|
|
|
68
|
-
// Calculate the range from the position of the
|
|
83
|
+
// Calculate the range from the position of the trigger character
|
|
69
84
|
const wordInfo = model.getWordUntilPosition(position);
|
|
70
85
|
return new monaco.Range(
|
|
71
86
|
position.lineNumber,
|
|
@@ -83,7 +98,8 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
83
98
|
kind: monaco.languages.CompletionItemKind,
|
|
84
99
|
getId: (item: T) => string,
|
|
85
100
|
getName: (item: T) => string,
|
|
86
|
-
getDescription: (item: T) => string
|
|
101
|
+
getDescription: (item: T) => string,
|
|
102
|
+
command?: monaco.languages.Command
|
|
87
103
|
): ProviderResult<monaco.languages.CompletionList> {
|
|
88
104
|
const completionRange = this.getCompletionRange(model, position, triggerChar);
|
|
89
105
|
if (completionRange === undefined) {
|
|
@@ -95,6 +111,7 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
95
111
|
label: getName(item),
|
|
96
112
|
range: completionRange,
|
|
97
113
|
detail: getDescription(item),
|
|
114
|
+
command
|
|
98
115
|
}));
|
|
99
116
|
return { suggestions };
|
|
100
117
|
}
|
|
@@ -103,7 +120,7 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
103
120
|
return this.getSuggestions(
|
|
104
121
|
model,
|
|
105
122
|
position,
|
|
106
|
-
|
|
123
|
+
PromptText.AGENT_CHAR,
|
|
107
124
|
this.agentService.getAgents(),
|
|
108
125
|
monaco.languages.CompletionItemKind.Value,
|
|
109
126
|
agent => agent.id,
|
|
@@ -116,25 +133,92 @@ export class ChatViewLanguageContribution implements FrontendApplicationContribu
|
|
|
116
133
|
return this.getSuggestions(
|
|
117
134
|
model,
|
|
118
135
|
position,
|
|
119
|
-
|
|
136
|
+
PromptText.VARIABLE_CHAR,
|
|
120
137
|
this.variableService.getVariables(),
|
|
121
138
|
monaco.languages.CompletionItemKind.Variable,
|
|
122
139
|
variable => variable.name,
|
|
123
140
|
variable => variable.name,
|
|
124
|
-
variable => variable.description
|
|
141
|
+
variable => variable.description,
|
|
142
|
+
{
|
|
143
|
+
title: nls.localize('theia/ai/chat-ui/selectVariableArguments', 'Select variable arguments'),
|
|
144
|
+
id: VARIABLE_ARGUMENT_PICKER_COMMAND,
|
|
145
|
+
}
|
|
125
146
|
);
|
|
126
147
|
}
|
|
127
148
|
|
|
149
|
+
async provideVariableWithArgCompletions(model: monaco.editor.ITextModel, position: monaco.Position): Promise<monaco.languages.CompletionList> {
|
|
150
|
+
const variables = this.variableService.getVariables();
|
|
151
|
+
const suggestions: monaco.languages.CompletionItem[] = [];
|
|
152
|
+
for (const variable of variables) {
|
|
153
|
+
const provider = await this.variableService.getArgumentCompletionProvider(variable.name);
|
|
154
|
+
if (provider) {
|
|
155
|
+
const items = await provider(model, position);
|
|
156
|
+
if (items) {
|
|
157
|
+
suggestions.push(...items.map(item => ({
|
|
158
|
+
...item,
|
|
159
|
+
// trigger command to check if we should add a context variable
|
|
160
|
+
command: {
|
|
161
|
+
title: nls.localize('theia/ai/chat-ui/addContextVariable', 'Add context variable'),
|
|
162
|
+
id: VARIABLE_ADD_CONTEXT_COMMAND,
|
|
163
|
+
arguments: [variable.name, item.insertText]
|
|
164
|
+
}
|
|
165
|
+
})));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { suggestions };
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
provideToolCompletions(model: monaco.editor.ITextModel, position: monaco.Position): ProviderResult<monaco.languages.CompletionList> {
|
|
129
173
|
return this.getSuggestions(
|
|
130
174
|
model,
|
|
131
175
|
position,
|
|
132
|
-
|
|
133
|
-
this.
|
|
176
|
+
PromptText.FUNCTION_CHAR,
|
|
177
|
+
this.toolInvocationRegistry.getAllFunctions(),
|
|
134
178
|
monaco.languages.CompletionItemKind.Function,
|
|
135
179
|
tool => tool.id,
|
|
136
180
|
tool => tool.name,
|
|
137
181
|
tool => tool.description ?? ''
|
|
138
182
|
);
|
|
139
183
|
}
|
|
184
|
+
|
|
185
|
+
protected async triggerVariableArgumentPicker(): Promise<void> {
|
|
186
|
+
const inputEditor = monaco.editor.getEditors().find(editor => editor.hasTextFocus());
|
|
187
|
+
if (!inputEditor) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const model = inputEditor.getModel();
|
|
191
|
+
const position = inputEditor.getPosition();
|
|
192
|
+
if (!model || !position) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const variableName = model.getWordAtPosition(position)?.word;
|
|
196
|
+
if (!variableName) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const provider = await this.variableService.getArgumentPicker(variableName, VARIABLE_RESOLUTION_CONTEXT);
|
|
200
|
+
if (!provider) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const arg = await provider(VARIABLE_RESOLUTION_CONTEXT);
|
|
204
|
+
if (!arg) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
inputEditor.executeEdits('variable-argument-picker', [{
|
|
208
|
+
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
|
|
209
|
+
text: PromptText.VARIABLE_SEPARATOR_CHAR + arg
|
|
210
|
+
}]);
|
|
211
|
+
await this.addContextVariable(variableName, arg);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
protected async addContextVariable(variableName: string, arg: string | undefined): Promise<void> {
|
|
215
|
+
const variable = this.variableService.getVariable(variableName);
|
|
216
|
+
if (!variable || !AIContextVariable.is(variable)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const widget = this.shell.getWidgetById(ChatViewWidget.ID);
|
|
220
|
+
if (widget instanceof ChatViewWidget) {
|
|
221
|
+
widget.addContext({ variable, arg });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
140
224
|
}
|
|
@@ -21,6 +21,7 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'
|
|
|
21
21
|
import { AIChatInputWidget } from './chat-input-widget';
|
|
22
22
|
import { ChatViewTreeWidget } from './chat-tree-view/chat-view-tree-widget';
|
|
23
23
|
import { AIActivationService } from '@theia/ai-core/lib/browser/ai-activation-service';
|
|
24
|
+
import { AIVariableResolutionRequest } from '@theia/ai-core';
|
|
24
25
|
|
|
25
26
|
export namespace ChatViewWidget {
|
|
26
27
|
export interface State {
|
|
@@ -32,7 +33,7 @@ export namespace ChatViewWidget {
|
|
|
32
33
|
export class ChatViewWidget extends BaseWidget implements ExtractableWidget, StatefulWidget {
|
|
33
34
|
|
|
34
35
|
public static ID = 'chat-view-widget';
|
|
35
|
-
static LABEL =
|
|
36
|
+
static LABEL = `${nls.localizeByDefault('Chat')}`;
|
|
36
37
|
|
|
37
38
|
@inject(ChatService)
|
|
38
39
|
protected chatService: ChatService;
|
|
@@ -91,8 +92,10 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
91
92
|
this.chatSession = this.chatService.createSession();
|
|
92
93
|
|
|
93
94
|
this.inputWidget.onQuery = this.onQuery.bind(this);
|
|
95
|
+
this.inputWidget.onUnpin = this.onUnpin.bind(this);
|
|
94
96
|
this.inputWidget.onCancel = this.onCancel.bind(this);
|
|
95
97
|
this.inputWidget.chatModel = this.chatSession.model;
|
|
98
|
+
this.inputWidget.pinnedAgent = this.chatSession.pinnedAgent;
|
|
96
99
|
this.inputWidget.onDeleteChangeSet = this.onDeleteChangeSet.bind(this);
|
|
97
100
|
this.inputWidget.onDeleteChangeSetElement = this.onDeleteChangeSetElement.bind(this);
|
|
98
101
|
this.treeWidget.trackChatModel(this.chatSession.model);
|
|
@@ -117,6 +120,7 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
117
120
|
this.chatSession = session;
|
|
118
121
|
this.treeWidget.trackChatModel(this.chatSession.model);
|
|
119
122
|
this.inputWidget.chatModel = this.chatSession.model;
|
|
123
|
+
this.inputWidget.pinnedAgent = this.chatSession.pinnedAgent;
|
|
120
124
|
if (event.focus) {
|
|
121
125
|
this.show();
|
|
122
126
|
}
|
|
@@ -157,18 +161,19 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
157
161
|
return this.onStateChangedEmitter.event;
|
|
158
162
|
}
|
|
159
163
|
|
|
160
|
-
protected async onQuery(query: string): Promise<void> {
|
|
164
|
+
protected async onQuery(query: string, contextVariableRequests?: AIVariableResolutionRequest[]): Promise<void> {
|
|
161
165
|
if (query.length === 0) { return; }
|
|
162
166
|
|
|
163
|
-
const chatRequest: ChatRequest = {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const requestProgress = await this.chatService.sendRequest(this.chatSession.id, chatRequest);
|
|
167
|
+
const chatRequest: ChatRequest = { text: query };
|
|
168
|
+
const context = { variableRequests: contextVariableRequests ?? [] };
|
|
169
|
+
const requestProgress = await this.chatService.sendRequest(this.chatSession.id, chatRequest, context);
|
|
168
170
|
requestProgress?.responseCompleted.then(responseModel => {
|
|
169
171
|
if (responseModel.isError) {
|
|
170
|
-
this.messageService.error(responseModel.errorObject?.message ??
|
|
172
|
+
this.messageService.error(responseModel.errorObject?.message ??
|
|
173
|
+
nls.localize('theia/ai/chat-ui/errorChatInvocation', 'An error occurred during chat service invocation.'));
|
|
171
174
|
}
|
|
175
|
+
}).finally(() => {
|
|
176
|
+
this.inputWidget.pinnedAgent = this.chatSession.pinnedAgent;
|
|
172
177
|
});
|
|
173
178
|
if (!requestProgress) {
|
|
174
179
|
this.messageService.error(`Was not able to send request "${chatRequest.text}" to session ${this.chatSession.id}`);
|
|
@@ -177,6 +182,11 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
177
182
|
// Tree Widget currently tracks the ChatModel itself. Therefore no notification necessary.
|
|
178
183
|
}
|
|
179
184
|
|
|
185
|
+
protected onUnpin(): void {
|
|
186
|
+
this.chatSession.pinnedAgent = undefined;
|
|
187
|
+
this.inputWidget.pinnedAgent = this.chatSession.pinnedAgent;
|
|
188
|
+
}
|
|
189
|
+
|
|
180
190
|
protected onCancel(requestModel: ChatRequestModel): void {
|
|
181
191
|
this.chatService.cancelRequest(requestModel.session.id, requestModel.id);
|
|
182
192
|
}
|
|
@@ -204,4 +214,8 @@ export class ChatViewWidget extends BaseWidget implements ExtractableWidget, Sta
|
|
|
204
214
|
get isExtractable(): boolean {
|
|
205
215
|
return this.secondaryWindow === undefined;
|
|
206
216
|
}
|
|
217
|
+
|
|
218
|
+
addContext(variable: AIVariableResolutionRequest): void {
|
|
219
|
+
this.inputWidget.addContext(variable);
|
|
220
|
+
}
|
|
207
221
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { AIContextVariable, AIVariableResolutionRequest, AIVariableService, PromptText } from '@theia/ai-core';
|
|
18
|
+
import { QuickInputService } from '@theia/core';
|
|
19
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
20
|
+
|
|
21
|
+
const QUERY_CONTEXT = { type: 'context-variable-picker' };
|
|
22
|
+
|
|
23
|
+
@injectable()
|
|
24
|
+
export class ContextVariablePicker {
|
|
25
|
+
|
|
26
|
+
@inject(AIVariableService)
|
|
27
|
+
protected readonly variableService: AIVariableService;
|
|
28
|
+
|
|
29
|
+
@inject(QuickInputService)
|
|
30
|
+
protected readonly quickInputService: QuickInputService;
|
|
31
|
+
|
|
32
|
+
async pickContextVariable(): Promise<AIVariableResolutionRequest | undefined> {
|
|
33
|
+
const variables = this.variableService.getContextVariables();
|
|
34
|
+
const selection = await this.quickInputService.showQuickPick(
|
|
35
|
+
variables.map(v => ({
|
|
36
|
+
id: v.id,
|
|
37
|
+
label: v.label ?? v.name,
|
|
38
|
+
variable: v,
|
|
39
|
+
iconClasses: v.iconClasses,
|
|
40
|
+
})),
|
|
41
|
+
{ placeholder: 'Select a context variable to be attached to the message', }
|
|
42
|
+
);
|
|
43
|
+
if (!selection) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const variable = selection.variable;
|
|
48
|
+
if (!variable.args || variable.args.length === 0) {
|
|
49
|
+
return { variable };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const argumentPicker = await this.variableService.getArgumentPicker(variable.name, QUERY_CONTEXT);
|
|
53
|
+
if (!argumentPicker) {
|
|
54
|
+
return this.useGenericArgumentPicker(variable);
|
|
55
|
+
}
|
|
56
|
+
const arg = await argumentPicker(QUERY_CONTEXT);
|
|
57
|
+
if (!arg) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { variable, arg };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected async useGenericArgumentPicker(variable: AIContextVariable): Promise<AIVariableResolutionRequest | undefined> {
|
|
65
|
+
const args: string[] = [];
|
|
66
|
+
for (const argument of variable.args ?? []) {
|
|
67
|
+
const placeHolder = argument.description;
|
|
68
|
+
let input: string | undefined;
|
|
69
|
+
if (argument.enum) {
|
|
70
|
+
const picked = await this.quickInputService.pick(
|
|
71
|
+
argument.enum.map(enumItem => ({ label: enumItem })),
|
|
72
|
+
{ placeHolder, canPickMany: false }
|
|
73
|
+
);
|
|
74
|
+
input = picked?.label;
|
|
75
|
+
} else {
|
|
76
|
+
input = await this.quickInputService.input({ placeHolder });
|
|
77
|
+
}
|
|
78
|
+
if (!input && !argument.isOptional) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
args.push(input ?? '');
|
|
82
|
+
}
|
|
83
|
+
return { variable, arg: args.join(PromptText.VARIABLE_SEPARATOR_CHAR) };
|
|
84
|
+
}
|
|
85
|
+
}
|