@theia/ai-ide 1.64.0-next.35 → 1.65.0-next.1
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-configuration/agent-configuration-widget.d.ts +5 -2
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +15 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-service.d.ts +6 -1
- package/lib/browser/ai-configuration/ai-configuration-service.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-service.js +10 -1
- package/lib/browser/ai-configuration/ai-configuration-service.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.js +7 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.d.ts +4 -2
- package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.js +49 -71
- package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts +41 -0
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js +225 -0
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +7 -3
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +35 -13
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js +11 -6
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/ai-ide-activation-service.d.ts +18 -0
- package/lib/browser/ai-ide-activation-service.d.ts.map +1 -0
- package/lib/browser/ai-ide-activation-service.js +72 -0
- package/lib/browser/ai-ide-activation-service.js.map +1 -0
- package/lib/browser/ai-ide-preferences.d.ts +4 -0
- package/lib/browser/ai-ide-preferences.d.ts.map +1 -0
- package/lib/browser/ai-ide-preferences.js +43 -0
- package/lib/browser/ai-ide-preferences.js.map +1 -0
- package/lib/browser/app-tester-chat-agent.js +1 -1
- package/lib/browser/app-tester-chat-agent.js.map +1 -1
- package/lib/browser/architect-agent.js +1 -1
- package/lib/browser/architect-agent.js.map +1 -1
- package/lib/browser/coder-agent.js +1 -1
- package/lib/browser/coder-agent.js.map +1 -1
- package/lib/browser/context-functions.d.ts.map +1 -1
- package/lib/browser/context-functions.js +12 -0
- package/lib/browser/context-functions.js.map +1 -1
- package/lib/browser/context-functions.spec.d.ts +2 -0
- package/lib/browser/context-functions.spec.d.ts.map +1 -0
- package/lib/browser/context-functions.spec.js +93 -0
- package/lib/browser/context-functions.spec.js.map +1 -0
- package/lib/browser/file-changeset-function.spec.d.ts +2 -0
- package/lib/browser/file-changeset-function.spec.d.ts.map +1 -0
- package/lib/browser/file-changeset-function.spec.js +45 -0
- package/lib/browser/file-changeset-function.spec.js.map +1 -0
- package/lib/browser/file-changeset-functions.d.ts +13 -3
- package/lib/browser/file-changeset-functions.d.ts.map +1 -1
- package/lib/browser/file-changeset-functions.js +100 -29
- package/lib/browser/file-changeset-functions.js.map +1 -1
- package/lib/browser/file-changeset-functions.spec.d.ts +2 -0
- package/lib/browser/file-changeset-functions.spec.d.ts.map +1 -0
- package/lib/browser/file-changeset-functions.spec.js +161 -0
- package/lib/browser/file-changeset-functions.spec.js.map +1 -0
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +20 -0
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/ide-chat-welcome-message-provider.js +2 -2
- package/lib/browser/ide-chat-welcome-message-provider.js.map +1 -1
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.d.ts +2 -0
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.d.ts.map +1 -0
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.js +52 -0
- package/lib/browser/test/tool-provider-cancellation-test-util.spec.js.map +1 -0
- package/lib/browser/workspace-functions.d.ts +3 -3
- package/lib/browser/workspace-functions.d.ts.map +1 -1
- package/lib/browser/workspace-functions.js +79 -28
- package/lib/browser/workspace-functions.js.map +1 -1
- package/lib/browser/workspace-functions.spec.d.ts +2 -0
- package/lib/browser/workspace-functions.spec.d.ts.map +1 -0
- package/lib/browser/workspace-functions.spec.js +161 -0
- package/lib/browser/workspace-functions.spec.js.map +1 -0
- package/lib/browser/workspace-launch-provider.d.ts +24 -0
- package/lib/browser/workspace-launch-provider.d.ts.map +1 -0
- package/lib/browser/workspace-launch-provider.js +216 -0
- package/lib/browser/workspace-launch-provider.js.map +1 -0
- package/lib/browser/workspace-launch-provider.spec.d.ts +2 -0
- package/lib/browser/workspace-launch-provider.spec.d.ts.map +1 -0
- package/lib/browser/workspace-launch-provider.spec.js +245 -0
- package/lib/browser/workspace-launch-provider.spec.js.map +1 -0
- package/lib/browser/workspace-search-provider.d.ts.map +1 -1
- package/lib/browser/workspace-search-provider.js +9 -0
- package/lib/browser/workspace-search-provider.js.map +1 -1
- package/lib/browser/workspace-search-provider.spec.js +59 -203
- package/lib/browser/workspace-search-provider.spec.js.map +1 -1
- package/lib/browser/workspace-task-provider.d.ts.map +1 -1
- package/lib/browser/workspace-task-provider.js +8 -1
- package/lib/browser/workspace-task-provider.js.map +1 -1
- package/lib/browser/workspace-task-provider.spec.d.ts +2 -0
- package/lib/browser/workspace-task-provider.spec.d.ts.map +1 -0
- package/lib/browser/workspace-task-provider.spec.js +109 -0
- package/lib/browser/workspace-task-provider.spec.js.map +1 -0
- package/lib/common/architect-prompt-template.d.ts.map +1 -1
- package/lib/common/architect-prompt-template.js +11 -0
- package/lib/common/architect-prompt-template.js.map +1 -1
- package/lib/common/command-chat-agents.js +1 -1
- package/lib/common/command-chat-agents.js.map +1 -1
- package/lib/common/orchestrator-chat-agent.js +1 -1
- package/lib/common/orchestrator-chat-agent.js.map +1 -1
- package/lib/common/universal-chat-agent.js +1 -1
- package/lib/common/universal-chat-agent.js.map +1 -1
- package/lib/common/workspace-functions.d.ts +3 -0
- package/lib/common/workspace-functions.d.ts.map +1 -1
- package/lib/common/workspace-functions.js +4 -1
- package/lib/common/workspace-functions.js.map +1 -1
- package/package.json +18 -17
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +18 -2
- package/src/browser/ai-configuration/ai-configuration-service.ts +14 -1
- package/src/browser/ai-configuration/ai-configuration-widget.tsx +7 -1
- package/src/browser/ai-configuration/language-model-renderer.tsx +87 -59
- package/src/browser/ai-configuration/model-aliases-configuration-widget.tsx +279 -0
- package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +43 -13
- package/src/browser/ai-configuration/template-settings-renderer.tsx +11 -7
- package/src/browser/ai-ide-activation-service.ts +65 -0
- package/src/browser/ai-ide-preferences.ts +44 -0
- package/src/browser/app-tester-chat-agent.ts +1 -1
- package/src/browser/architect-agent.ts +1 -1
- package/src/browser/coder-agent.ts +1 -1
- package/src/browser/context-functions.spec.ts +102 -0
- package/src/browser/context-functions.ts +11 -0
- package/src/browser/file-changeset-function.spec.ts +52 -0
- package/src/browser/file-changeset-functions.spec.ts +212 -0
- package/src/browser/file-changeset-functions.ts +102 -25
- package/src/browser/frontend-module.ts +29 -1
- package/src/browser/ide-chat-welcome-message-provider.tsx +4 -4
- package/src/browser/style/index.css +111 -6
- package/src/browser/test/tool-provider-cancellation-test-util.spec.ts +60 -0
- package/src/browser/workspace-functions.spec.ts +199 -0
- package/src/browser/workspace-functions.ts +105 -32
- package/src/browser/workspace-launch-provider.spec.ts +320 -0
- package/src/browser/workspace-launch-provider.ts +231 -0
- package/src/browser/workspace-search-provider.spec.ts +79 -229
- package/src/browser/workspace-search-provider.ts +10 -1
- package/src/browser/workspace-task-provider.spec.ts +125 -0
- package/src/browser/workspace-task-provider.ts +7 -2
- package/src/common/architect-prompt-template.ts +11 -0
- package/src/common/command-chat-agents.ts +1 -1
- package/src/common/orchestrator-chat-agent.ts +1 -1
- package/src/common/universal-chat-agent.ts +1 -1
- package/src/common/workspace-functions.ts +3 -0
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import * as React from '@theia/core/shared/react';
|
|
17
|
-
import { Agent, AISettingsService,
|
|
17
|
+
import { Agent, AISettingsService, FrontendLanguageModelRegistry, LanguageModel, LanguageModelRequirement } from '@theia/ai-core/lib/common';
|
|
18
|
+
import { LanguageModelAlias } from '@theia/ai-core/lib/common/language-model-alias';
|
|
18
19
|
import { Mutable } from '@theia/core';
|
|
19
20
|
import { nls } from '@theia/core/lib/common/nls';
|
|
20
21
|
|
|
@@ -22,11 +23,12 @@ export interface LanguageModelSettingsProps {
|
|
|
22
23
|
agent: Agent;
|
|
23
24
|
languageModels?: LanguageModel[];
|
|
24
25
|
aiSettingsService: AISettingsService;
|
|
25
|
-
languageModelRegistry:
|
|
26
|
+
languageModelRegistry: FrontendLanguageModelRegistry;
|
|
27
|
+
languageModelAliases: LanguageModelAlias[];
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
|
|
29
|
-
{ agent, languageModels, aiSettingsService, languageModelRegistry }) => {
|
|
31
|
+
{ agent, languageModels, aiSettingsService, languageModelRegistry, languageModelAliases: aliases }) => {
|
|
30
32
|
|
|
31
33
|
const findLanguageModelRequirement = async (purpose: string): Promise<LanguageModelRequirement | undefined> => {
|
|
32
34
|
const requirementSetting = await aiSettingsService.getAgentSettings(agent.id);
|
|
@@ -34,6 +36,7 @@ export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
|
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
const [lmRequirementMap, setLmRequirementMap] = React.useState<Record<string, LanguageModelRequirement>>({});
|
|
39
|
+
const [resolvedAliasModels, setResolvedAliasModels] = React.useState<Record<string, LanguageModel | undefined>>({});
|
|
37
40
|
|
|
38
41
|
React.useEffect(() => {
|
|
39
42
|
const computeLmRequirementMap = async () => {
|
|
@@ -54,32 +57,20 @@ export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
|
|
|
54
57
|
computeLmRequirementMap();
|
|
55
58
|
}, []);
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{languageModel.maxInputTokens &&
|
|
72
|
-
<p><strong>
|
|
73
|
-
{nls.localize('theia/ai/core/languageModelRenderer/minInputTokens', 'Min Input Tokens')}:
|
|
74
|
-
</strong> {languageModel.maxInputTokens}</p>}
|
|
75
|
-
{languageModel.maxOutputTokens &&
|
|
76
|
-
<p><strong>
|
|
77
|
-
{nls.localize('theia/ai/core/languageModelRenderer/maxOutputTokens', 'Max Output Tokens')}:
|
|
78
|
-
</strong> {languageModel.maxOutputTokens}</p>}
|
|
79
|
-
</div>
|
|
80
|
-
</>;
|
|
81
|
-
|
|
82
|
-
};
|
|
60
|
+
// Effect to resolve alias to model whenever requirements.identifier or aliases change
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
const resolveAliases = async () => {
|
|
63
|
+
const newResolved: Record<string, LanguageModel | undefined> = {};
|
|
64
|
+
await Promise.all(Object.values(lmRequirementMap).map(async requirements => {
|
|
65
|
+
const id = requirements.identifier;
|
|
66
|
+
if (id && aliases.some(a => a.id === id)) {
|
|
67
|
+
newResolved[id] = await languageModelRegistry.getReadyLanguageModel(id);
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
setResolvedAliasModels(newResolved);
|
|
71
|
+
};
|
|
72
|
+
resolveAliases();
|
|
73
|
+
}, [lmRequirementMap, aliases]);
|
|
83
74
|
|
|
84
75
|
const onSelectedModelChange = (purpose: string, event: React.ChangeEvent<HTMLSelectElement>): void => {
|
|
85
76
|
const newLmRequirementMap = { ...lmRequirementMap, [purpose]: { purpose, identifier: event.target.value } };
|
|
@@ -88,35 +79,72 @@ export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
|
|
|
88
79
|
};
|
|
89
80
|
|
|
90
81
|
return <div className='language-model-container'>
|
|
91
|
-
{Object.values(lmRequirementMap).map((
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<option
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
82
|
+
{Object.values(lmRequirementMap).map((requirement, index) => {
|
|
83
|
+
const isAlias = requirement.identifier && aliases.some(a => a.id === requirement.identifier);
|
|
84
|
+
const resolvedModel = isAlias ? resolvedAliasModels[requirement.identifier] : undefined;
|
|
85
|
+
return (
|
|
86
|
+
<React.Fragment key={index}>
|
|
87
|
+
<div className="ai-alias-evaluates-to-container">
|
|
88
|
+
<strong>{nls.localize('theia/ai/core/languageModelRenderer/purpose', 'Purpose')}:</strong> {requirement.purpose}
|
|
89
|
+
</div>
|
|
90
|
+
<div>
|
|
91
|
+
<div className="ai-alias-evaluates-to-container">
|
|
92
|
+
<label
|
|
93
|
+
className="theia-header no-select"
|
|
94
|
+
htmlFor={`model-select-${agent.id}`}>
|
|
95
|
+
{nls.localize('theia/ai/core/languageModelRenderer/languageModel', 'Language Model') + ': '}
|
|
96
|
+
</label>
|
|
97
|
+
<select
|
|
98
|
+
className="theia-select"
|
|
99
|
+
id={`model-select-${agent.id}-${requirement.purpose}`}
|
|
100
|
+
value={requirement.identifier}
|
|
101
|
+
onChange={event => onSelectedModelChange(requirement.purpose, event)}
|
|
102
|
+
>
|
|
103
|
+
<option value=""></option>
|
|
104
|
+
{/* Aliases first, then languange models */}
|
|
105
|
+
{aliases?.sort((a, b) => a.id.localeCompare(b.id)).map(alias => (
|
|
106
|
+
<option key={`alias/${alias.id}`} value={alias.id} className='ai-language-model-item-ready'>{`[alias] ${alias.id}`}</option>
|
|
107
|
+
))}
|
|
108
|
+
{languageModels?.sort((a, b) => (a.name ?? a.id).localeCompare(b.name ?? b.id)).map(model => {
|
|
109
|
+
const isNotReady = model.status.status !== 'ready';
|
|
110
|
+
return (
|
|
111
|
+
<option
|
|
112
|
+
key={model.id}
|
|
113
|
+
value={model.id}
|
|
114
|
+
className={isNotReady ? 'ai-language-model-item-not-ready' : 'ai-language-model-item-ready'}
|
|
115
|
+
title={isNotReady && model.status.message ? model.status.message : undefined}
|
|
116
|
+
>
|
|
117
|
+
{model.name ?? model.id} {isNotReady ? '✗' : '✓'}
|
|
118
|
+
</option>
|
|
119
|
+
);
|
|
120
|
+
})}
|
|
121
|
+
</select>
|
|
122
|
+
</div>
|
|
123
|
+
{/* If alias is selected, show what it currently evaluates to */}
|
|
124
|
+
{isAlias && (
|
|
125
|
+
<div className="ai-alias-evaluates-to-container">
|
|
126
|
+
<label className="ai-alias-evaluates-to-label">{nls.localize('theia/ai/core/modelAliasesConfiguration/evaluatesTo', 'Evaluates to')}:</label>
|
|
127
|
+
{resolvedModel ? (
|
|
128
|
+
<span className="ai-alias-evaluates-to-value">
|
|
129
|
+
{resolvedModel.name ?? resolvedModel.id}
|
|
130
|
+
{resolvedModel.status.status === 'ready' ? (
|
|
131
|
+
<span className="ai-model-status-ready" title="Ready">✓</span>
|
|
132
|
+
) : (
|
|
133
|
+
<span className="ai-model-status-not-ready" title={resolvedModel.status.message || 'Not ready'}>✗</span>
|
|
134
|
+
)}
|
|
135
|
+
</span>
|
|
136
|
+
) : (
|
|
137
|
+
<span className="ai-alias-evaluates-to-unresolved">
|
|
138
|
+
{nls.localize('theia/ai/core/modelAliasesConfiguration/noResolvedModel', 'No model ready for this alias.')}
|
|
139
|
+
<span className="ai-model-status-not-ready" title={'No model ready'}>✗</span>
|
|
140
|
+
</span>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
<hr />
|
|
145
|
+
</div>
|
|
146
|
+
</React.Fragment>
|
|
147
|
+
);
|
|
148
|
+
})}
|
|
121
149
|
</div>;
|
|
122
150
|
};
|
|
@@ -0,0 +1,279 @@
|
|
|
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
|
+
import * as React from '@theia/core/shared/react';
|
|
17
|
+
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
18
|
+
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
|
|
19
|
+
import { LanguageModelAliasRegistry, LanguageModelAlias } from '@theia/ai-core/lib/common/language-model-alias';
|
|
20
|
+
import { FrontendLanguageModelRegistry, LanguageModel, LanguageModelRegistry, LanguageModelRequirement } from '@theia/ai-core/lib/common/language-model';
|
|
21
|
+
import { nls } from '@theia/core/lib/common/nls';
|
|
22
|
+
import { AIConfigurationSelectionService } from './ai-configuration-service';
|
|
23
|
+
import { AgentService, AISettingsService } from '@theia/ai-core';
|
|
24
|
+
|
|
25
|
+
export interface ModelAliasesConfigurationProps {
|
|
26
|
+
languageModelAliasRegistry: LanguageModelAliasRegistry;
|
|
27
|
+
languageModelRegistry: LanguageModelRegistry;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@injectable()
|
|
31
|
+
export class ModelAliasesConfigurationWidget extends ReactWidget {
|
|
32
|
+
static readonly ID = 'ai-model-aliases-configuration-widget';
|
|
33
|
+
static readonly LABEL = nls.localize('theia/ai/core/modelAliasesConfiguration/label', 'Model Aliases');
|
|
34
|
+
|
|
35
|
+
@inject(LanguageModelAliasRegistry)
|
|
36
|
+
protected readonly languageModelAliasRegistry: LanguageModelAliasRegistry;
|
|
37
|
+
@inject(LanguageModelRegistry)
|
|
38
|
+
protected readonly languageModelRegistry: FrontendLanguageModelRegistry;
|
|
39
|
+
@inject(AIConfigurationSelectionService)
|
|
40
|
+
protected readonly aiConfigurationSelectionService: AIConfigurationSelectionService;
|
|
41
|
+
@inject(AISettingsService)
|
|
42
|
+
protected readonly aiSettingsService: AISettingsService;
|
|
43
|
+
@inject(AgentService)
|
|
44
|
+
protected readonly agentService: AgentService;
|
|
45
|
+
|
|
46
|
+
protected aliases: LanguageModelAlias[] = [];
|
|
47
|
+
protected languageModels: LanguageModel[] = [];
|
|
48
|
+
/**
|
|
49
|
+
* Map from alias ID to a list of agent IDs that have a language model requirement for that alias.
|
|
50
|
+
*/
|
|
51
|
+
protected matchingAgentIdsForAliasMap: Map<string, string[]> = new Map();
|
|
52
|
+
/**
|
|
53
|
+
* Map from alias ID to the resolved LanguageModel (what the alias currently evaluates to).
|
|
54
|
+
*/
|
|
55
|
+
protected resolvedModelForAlias: Map<string, LanguageModel | undefined> = new Map();
|
|
56
|
+
|
|
57
|
+
@postConstruct()
|
|
58
|
+
protected init(): void {
|
|
59
|
+
this.id = ModelAliasesConfigurationWidget.ID;
|
|
60
|
+
this.title.label = ModelAliasesConfigurationWidget.LABEL;
|
|
61
|
+
this.title.closable = false;
|
|
62
|
+
|
|
63
|
+
const aliasesPromise = this.loadAliases();
|
|
64
|
+
const languageModelsPromise = this.loadLanguageModels();
|
|
65
|
+
const matchingAgentsPromise = this.loadMatchingAgentIdsForAllAliases();
|
|
66
|
+
Promise.all([aliasesPromise, languageModelsPromise, matchingAgentsPromise]).then(() => this.update());
|
|
67
|
+
|
|
68
|
+
this.languageModelAliasRegistry.ready.then(() =>
|
|
69
|
+
this.toDispose.push(this.languageModelAliasRegistry.onDidChange(async () => {
|
|
70
|
+
await this.loadAliases();
|
|
71
|
+
this.update();
|
|
72
|
+
}))
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
this.toDispose.pushAll([
|
|
76
|
+
this.languageModelRegistry.onChange(async () => {
|
|
77
|
+
await this.loadAliases();
|
|
78
|
+
await this.loadLanguageModels();
|
|
79
|
+
this.update();
|
|
80
|
+
}),
|
|
81
|
+
this.aiSettingsService.onDidChange(async () => {
|
|
82
|
+
await this.loadMatchingAgentIdsForAllAliases();
|
|
83
|
+
this.update();
|
|
84
|
+
}),
|
|
85
|
+
this.aiConfigurationSelectionService.onDidAliasChange(() => this.update())
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected async loadAliases(): Promise<void> {
|
|
90
|
+
await this.languageModelAliasRegistry.ready;
|
|
91
|
+
this.aliases = this.languageModelAliasRegistry.getAliases();
|
|
92
|
+
// Set the initial selection if not set
|
|
93
|
+
if (this.aliases.length > 0 && !this.aiConfigurationSelectionService.getSelectedAliasId()) {
|
|
94
|
+
this.aiConfigurationSelectionService.setSelectedAliasId(this.aliases[0].id);
|
|
95
|
+
}
|
|
96
|
+
await this.loadMatchingAgentIdsForAllAliases();
|
|
97
|
+
// Resolve evaluated models for each alias
|
|
98
|
+
this.resolvedModelForAlias = new Map();
|
|
99
|
+
for (const alias of this.aliases) {
|
|
100
|
+
const model = await this.languageModelRegistry.getReadyLanguageModel(alias.id);
|
|
101
|
+
this.resolvedModelForAlias.set(alias.id, model);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
protected async loadLanguageModels(): Promise<void> {
|
|
106
|
+
this.languageModels = await this.languageModelRegistry.getLanguageModels();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Loads a map from alias ID to a list of agent IDs that have a language model requirement for that alias.
|
|
111
|
+
*/
|
|
112
|
+
protected async loadMatchingAgentIdsForAllAliases(): Promise<void> {
|
|
113
|
+
const agents = this.agentService.getAllAgents();
|
|
114
|
+
const aliasMap: Map<string, string[]> = new Map();
|
|
115
|
+
for (const alias of this.aliases) {
|
|
116
|
+
const matchingAgentIds: string[] = [];
|
|
117
|
+
for (const agent of agents) {
|
|
118
|
+
const requirementSetting = await this.aiSettingsService.getAgentSettings(agent.id);
|
|
119
|
+
if (requirementSetting?.languageModelRequirements) {
|
|
120
|
+
// requirement is set via settings, check if it is this alias
|
|
121
|
+
if (requirementSetting?.languageModelRequirements?.find(e => e.identifier === alias.id)) {
|
|
122
|
+
matchingAgentIds.push(agent.id);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
// requirement is NOT set via settings, check if this alias is the default for this agent
|
|
126
|
+
if (agent.languageModelRequirements.some((req: LanguageModelRequirement) => req.identifier === alias.id)) {
|
|
127
|
+
matchingAgentIds.push(agent.id);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
aliasMap.set(alias.id, matchingAgentIds);
|
|
132
|
+
}
|
|
133
|
+
this.matchingAgentIdsForAliasMap = aliasMap;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
protected handleAliasSelectedModelIdChange = (alias: LanguageModelAlias, event: React.ChangeEvent<HTMLSelectElement>): void => {
|
|
137
|
+
const newModelId = event.target.value || undefined;
|
|
138
|
+
const updatedAlias: LanguageModelAlias = {
|
|
139
|
+
...alias,
|
|
140
|
+
selectedModelId: newModelId
|
|
141
|
+
};
|
|
142
|
+
this.languageModelAliasRegistry.ready.then(() => {
|
|
143
|
+
this.languageModelAliasRegistry.addAlias(updatedAlias);
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
render(): React.ReactNode {
|
|
148
|
+
const selectedAliasId = this.aiConfigurationSelectionService.getSelectedAliasId();
|
|
149
|
+
const selectedAlias = this.aliases.find(alias => alias.id === selectedAliasId);
|
|
150
|
+
return (
|
|
151
|
+
<div className="model-alias-configuration-main">
|
|
152
|
+
<div className="model-alias-configuration-list preferences-tree-widget theia-TreeContainer ai-model-alias-list">
|
|
153
|
+
<ul>
|
|
154
|
+
{this.aliases.map(alias => (
|
|
155
|
+
<li
|
|
156
|
+
key={alias.id}
|
|
157
|
+
className={`theia-TreeNode theia-CompositeTreeNode${alias.id === selectedAliasId ? ' theia-mod-selected' : ''}`}
|
|
158
|
+
onClick={() => this.aiConfigurationSelectionService.setSelectedAliasId(alias.id)}
|
|
159
|
+
>
|
|
160
|
+
<span>{alias.id}</span>
|
|
161
|
+
</li>
|
|
162
|
+
))}
|
|
163
|
+
</ul>
|
|
164
|
+
</div>
|
|
165
|
+
<div className="model-alias-configuration-panel preferences-editor-widget">
|
|
166
|
+
{selectedAlias ? this.renderAliasDetail(selectedAlias, this.languageModels) : (
|
|
167
|
+
<div>
|
|
168
|
+
{nls.localize('theia/ai/core/modelAliasesConfiguration/selectAlias', 'Please select a Model Alias.')}
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected renderAliasDetail(alias: LanguageModelAlias, languageModels: LanguageModel[]): React.ReactNode {
|
|
177
|
+
const availableModelIds = languageModels.map(m => m.id);
|
|
178
|
+
const selectedModelId = alias.selectedModelId ?? '';
|
|
179
|
+
const isInvalidModel = !!selectedModelId && !availableModelIds.includes(alias.selectedModelId ?? '');
|
|
180
|
+
const agentIds = this.matchingAgentIdsForAliasMap.get(alias.id) || [];
|
|
181
|
+
const agents = this.agentService.getAllAgents().filter(agent => agentIds.includes(agent.id));
|
|
182
|
+
const resolvedModel = this.resolvedModelForAlias.get(alias.id);
|
|
183
|
+
return (
|
|
184
|
+
<div>
|
|
185
|
+
<div className="settings-section-title settings-section-category-title ai-alias-detail-title">
|
|
186
|
+
<span>{alias.id}</span>
|
|
187
|
+
</div>
|
|
188
|
+
{alias.description && <div className="ai-alias-detail-description">{alias.description}</div>}
|
|
189
|
+
<div className="ai-alias-detail-selected-model">
|
|
190
|
+
<label>{nls.localize('theia/ai/core/modelAliasesConfiguration/selectedModelId', 'Selected Model')}: </label>
|
|
191
|
+
<select
|
|
192
|
+
className={`theia-select template-variant-selector ${isInvalidModel ? 'error' : ''}`}
|
|
193
|
+
value={isInvalidModel ? 'invalid' : selectedModelId}
|
|
194
|
+
onChange={event => this.handleAliasSelectedModelIdChange(alias, event)}
|
|
195
|
+
>
|
|
196
|
+
{isInvalidModel && (
|
|
197
|
+
<option value="invalid" disabled>
|
|
198
|
+
{nls.localize('theia/ai/core/modelAliasesConfiguration/unavailableModel', 'Selected model is no longer available')}
|
|
199
|
+
</option>
|
|
200
|
+
)}
|
|
201
|
+
<option value="" className='ai-language-model-item-ready'>
|
|
202
|
+
{nls.localize('theia/ai/core/modelAliasesConfiguration/defaultList', '[Default list]')}
|
|
203
|
+
</option>
|
|
204
|
+
{[...languageModels]
|
|
205
|
+
.sort((a, b) => (a.name ?? a.id).localeCompare(b.name ?? b.id))
|
|
206
|
+
.map(model => {
|
|
207
|
+
const isNotReady = model.status.status !== 'ready';
|
|
208
|
+
return (
|
|
209
|
+
<option
|
|
210
|
+
key={model.id}
|
|
211
|
+
value={model.id}
|
|
212
|
+
className={isNotReady ? 'ai-language-model-item-not-ready' : 'ai-language-model-item-ready'}
|
|
213
|
+
title={isNotReady && model.status.message ? model.status.message : undefined}
|
|
214
|
+
>
|
|
215
|
+
{model.name ?? model.id} {isNotReady ? '✗' : '✓'}
|
|
216
|
+
</option>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
)}
|
|
220
|
+
</select>
|
|
221
|
+
</div>
|
|
222
|
+
{alias.selectedModelId === undefined &&
|
|
223
|
+
<><div className="ai-alias-detail-defaults">
|
|
224
|
+
<ol>
|
|
225
|
+
{alias.defaultModelIds.map(modelId => {
|
|
226
|
+
const model = this.languageModels.find(m => m.id === modelId);
|
|
227
|
+
const isReady = model?.status.status === 'ready';
|
|
228
|
+
return (
|
|
229
|
+
<li key={modelId}>
|
|
230
|
+
{isReady ? (
|
|
231
|
+
<span className={modelId === resolvedModel?.id ? 'ai-alias-priority-item-resolved' : 'ai-alias-priority-item-ready'}>
|
|
232
|
+
{modelId} <span className="ai-model-status-ready" title="Ready">✓</span>
|
|
233
|
+
</span>
|
|
234
|
+
) : (
|
|
235
|
+
<span className="ai-model-default-not-ready">
|
|
236
|
+
{modelId} <span className="ai-model-status-not-ready" title="Not ready">✗</span>
|
|
237
|
+
</span>
|
|
238
|
+
)}
|
|
239
|
+
</li>
|
|
240
|
+
);
|
|
241
|
+
})}
|
|
242
|
+
</ol>
|
|
243
|
+
</div><div className="ai-alias-evaluates-to-container">
|
|
244
|
+
<label className="ai-alias-evaluates-to-label">{nls.localize('theia/ai/core/modelAliasesConfiguration/evaluatesTo', 'Evaluates to')}:</label>
|
|
245
|
+
{resolvedModel ? (
|
|
246
|
+
<span className="ai-alias-evaluates-to-value">
|
|
247
|
+
{resolvedModel.name ?? resolvedModel.id}
|
|
248
|
+
{resolvedModel.status.status === 'ready' ? (
|
|
249
|
+
<span className="ai-model-status-ready" title="Ready">✓</span>
|
|
250
|
+
) : (
|
|
251
|
+
<span className="ai-model-status-not-ready" title={resolvedModel.status.message || 'Not ready'}>✗</span>
|
|
252
|
+
)}
|
|
253
|
+
</span>
|
|
254
|
+
) : (
|
|
255
|
+
<span className="ai-alias-evaluates-to-unresolved">
|
|
256
|
+
{nls.localize('theia/ai/core/modelAliasesConfiguration/noResolvedModel', 'No model ready for this alias.')}
|
|
257
|
+
</span>
|
|
258
|
+
)}
|
|
259
|
+
</div></>
|
|
260
|
+
}
|
|
261
|
+
<div className="ai-alias-detail-agents">
|
|
262
|
+
<label>{nls.localize('theia/ai/core/modelAliasesConfiguration/agents', 'Agents using this Alias')}:</label>
|
|
263
|
+
<ul>
|
|
264
|
+
{agents.length > 0 ? (
|
|
265
|
+
agents.map(agent => (
|
|
266
|
+
<li key={agent.id}>
|
|
267
|
+
<span>{agent.name}</span>
|
|
268
|
+
{agent.id !== agent.name && <span className="ai-alias-agent-id">({agent.id})</span>}
|
|
269
|
+
</li>
|
|
270
|
+
))
|
|
271
|
+
) : (
|
|
272
|
+
<span>{nls.localize('theia/ai/core/modelAliasesConfiguration/noAgents', 'No agents use this alias.')}</span>
|
|
273
|
+
)}
|
|
274
|
+
</ul>
|
|
275
|
+
</div>
|
|
276
|
+
</div >
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -77,15 +77,20 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
77
77
|
protected availableAgents: Agent[] = [];
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Maps prompt variant set IDs to their
|
|
80
|
+
* Maps prompt variant set IDs to their resolved variant IDs
|
|
81
81
|
*/
|
|
82
|
-
protected
|
|
82
|
+
protected effectiveVariantIds: Map<string, string | undefined> = new Map();
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Maps prompt variant set IDs to their default variant IDs
|
|
86
86
|
*/
|
|
87
87
|
protected defaultVariantIds: Map<string, string | undefined> = new Map();
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Maps prompt variant set IDs to their user selected variant IDs
|
|
91
|
+
*/
|
|
92
|
+
protected userSelectedVariantIds: Map<string, string | undefined> = new Map();
|
|
93
|
+
|
|
89
94
|
@inject(PromptService) protected promptService: PromptService;
|
|
90
95
|
@inject(AgentService) protected agentService: AgentService;
|
|
91
96
|
|
|
@@ -103,10 +108,6 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
103
108
|
this.promptService.onPromptsChange(() => {
|
|
104
109
|
this.loadPromptFragments();
|
|
105
110
|
}),
|
|
106
|
-
this.promptService.onSelectedVariantChange(notification => {
|
|
107
|
-
this.selectedVariantIds.set(notification.promptVariantSetId, notification.variantId);
|
|
108
|
-
this.update();
|
|
109
|
-
}),
|
|
110
111
|
this.agentService.onDidChangeAgents(() => {
|
|
111
112
|
this.loadAgents();
|
|
112
113
|
})
|
|
@@ -117,7 +118,7 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
117
118
|
* Loads all prompt fragments and prompt variant sets from the prompt service.
|
|
118
119
|
* Preserves UI expansion states and updates variant information.
|
|
119
120
|
*/
|
|
120
|
-
protected
|
|
121
|
+
protected loadPromptFragments(): void {
|
|
121
122
|
this.promptFragmentMap = this.promptService.getAllPromptFragments();
|
|
122
123
|
this.promptVariantsMap = this.promptService.getPromptVariantSets();
|
|
123
124
|
this.activePromptFragments = this.promptService.getActivePromptFragments();
|
|
@@ -154,11 +155,13 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
154
155
|
})
|
|
155
156
|
);
|
|
156
157
|
|
|
157
|
-
// Update variant information (selected/default) for prompt variant sets
|
|
158
|
+
// Update variant information (selected/default/effective) for prompt variant sets
|
|
158
159
|
for (const promptVariantSetId of this.promptVariantsMap.keys()) {
|
|
159
|
-
const
|
|
160
|
-
const defaultId =
|
|
161
|
-
this.
|
|
160
|
+
const effectiveId = this.promptService.getEffectiveVariantId(promptVariantSetId);
|
|
161
|
+
const defaultId = this.promptService.getDefaultVariantId(promptVariantSetId);
|
|
162
|
+
const selectedId = this.promptService.getSelectedVariantId(promptVariantSetId) ?? defaultId;
|
|
163
|
+
this.userSelectedVariantIds.set(promptVariantSetId, selectedId);
|
|
164
|
+
this.effectiveVariantIds.set(promptVariantSetId, effectiveId);
|
|
162
165
|
this.defaultVariantIds.set(promptVariantSetId, defaultId);
|
|
163
166
|
}
|
|
164
167
|
|
|
@@ -405,8 +408,9 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
405
408
|
protected renderPromptVariantSet(promptVariantSetId: string, variantIds: string[]): React.ReactNode {
|
|
406
409
|
const isSectionExpanded = this.expandedPromptVariantSetIds.has(promptVariantSetId);
|
|
407
410
|
|
|
408
|
-
// Get selected and default variant IDs from our class properties
|
|
409
|
-
const selectedVariantId = this.
|
|
411
|
+
// Get selected, effective, and default variant IDs from our class properties
|
|
412
|
+
const selectedVariantId = this.userSelectedVariantIds.get(promptVariantSetId);
|
|
413
|
+
const effectiveVariantId = this.effectiveVariantIds.get(promptVariantSetId);
|
|
410
414
|
const defaultVariantId = this.defaultVariantIds.get(promptVariantSetId);
|
|
411
415
|
|
|
412
416
|
// Get variant fragments grouped by ID
|
|
@@ -421,6 +425,31 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
421
425
|
|
|
422
426
|
const relatedAgents = this.getAgentsUsingPromptVariantId(promptVariantSetId);
|
|
423
427
|
|
|
428
|
+
// Determine warning/error state
|
|
429
|
+
let variantSetMessage: React.ReactNode | undefined = undefined;
|
|
430
|
+
if (effectiveVariantId === undefined) {
|
|
431
|
+
// Error: effectiveId is undefined, so nothing works
|
|
432
|
+
variantSetMessage = (
|
|
433
|
+
<div className="prompt-variant-error">
|
|
434
|
+
<span className="codicon codicon-error"></span>
|
|
435
|
+
{nls.localize('theia/ai/core/promptFragmentsConfiguration/variantSetError',
|
|
436
|
+
'The selected variant does not exist and no default could be found. Please check your configuration.')}
|
|
437
|
+
</div>
|
|
438
|
+
);
|
|
439
|
+
} else {
|
|
440
|
+
const needsWarning = selectedVariantId ? effectiveVariantId !== selectedVariantId : effectiveVariantId !== defaultVariantId;
|
|
441
|
+
if (needsWarning) {
|
|
442
|
+
// Warning: selectedId is set but does not exist, so default is used
|
|
443
|
+
variantSetMessage = (
|
|
444
|
+
<div className="prompt-variant-warning">
|
|
445
|
+
<span className="codicon codicon-warning"></span>
|
|
446
|
+
{nls.localize('theia/ai/core/promptFragmentsConfiguration/variantSetWarning',
|
|
447
|
+
'The selected variant does not exist. The default variant is being used instead.')}
|
|
448
|
+
</div>
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
424
453
|
return (
|
|
425
454
|
<div className="prompt-fragment-section" key={`variant-${promptVariantSetId}`}>
|
|
426
455
|
<div
|
|
@@ -446,6 +475,7 @@ export class AIPromptFragmentsConfigurationWidget extends ReactWidget {
|
|
|
446
475
|
</div>
|
|
447
476
|
{isSectionExpanded && (
|
|
448
477
|
<div className="prompt-fragment-body">
|
|
478
|
+
{variantSetMessage}
|
|
449
479
|
<div className="prompt-fragment-description">
|
|
450
480
|
<p>{nls.localize('theia/ai/core/promptFragmentsConfiguration/variantsOfSystemPrompt', 'Variants of this prompt variant set:')}</p>
|
|
451
481
|
</div>
|
|
@@ -33,14 +33,18 @@ export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
|
|
|
33
33
|
const [selectedVariant, setSelectedVariant] = React.useState<string>(defaultVariantId!);
|
|
34
34
|
|
|
35
35
|
React.useEffect(() => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
const currentVariant = promptService.getSelectedVariantId(promptVariantSet.id);
|
|
37
|
+
setSelectedVariant(currentVariant ?? defaultVariantId!);
|
|
38
|
+
|
|
39
|
+
const disposable = promptService.onSelectedVariantChange(notification => {
|
|
40
|
+
if (notification.promptVariantSetId === promptVariantSet.id) {
|
|
41
|
+
setSelectedVariant(notification.variantId ?? defaultVariantId!);
|
|
41
42
|
}
|
|
42
|
-
})
|
|
43
|
-
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
disposable.dispose();
|
|
46
|
+
};
|
|
47
|
+
}, [promptVariantSet.id, promptService, defaultVariantId]);
|
|
44
48
|
|
|
45
49
|
const isInvalidVariant = !variantIds.includes(selectedVariant);
|
|
46
50
|
|