@theia/ai-ide 1.67.0-next.59 → 1.67.0-next.86
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/address-pr-review-command-contribution.d.ts +9 -0
- package/lib/browser/address-pr-review-command-contribution.d.ts.map +1 -0
- package/lib/browser/address-pr-review-command-contribution.js +176 -0
- package/lib/browser/address-pr-review-command-contribution.js.map +1 -0
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +12 -6
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +156 -104
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/base/ai-card-grid-configuration-widget.d.ts +32 -0
- package/lib/browser/ai-configuration/base/ai-card-grid-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/base/ai-card-grid-configuration-widget.js +55 -0
- package/lib/browser/ai-configuration/base/ai-card-grid-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/base/ai-configuration-base-widget.d.ts +14 -0
- package/lib/browser/ai-configuration/base/ai-configuration-base-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/base/ai-configuration-base-widget.js +35 -0
- package/lib/browser/ai-configuration/base/ai-configuration-base-widget.js.map +1 -0
- package/lib/browser/ai-configuration/base/ai-hierarchical-configuration-widget.d.ts +23 -0
- package/lib/browser/ai-configuration/base/ai-hierarchical-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/base/ai-hierarchical-configuration-widget.js +60 -0
- package/lib/browser/ai-configuration/base/ai-hierarchical-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/base/ai-list-detail-configuration-widget.d.ts +56 -0
- package/lib/browser/ai-configuration/base/ai-list-detail-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/base/ai-list-detail-configuration-widget.js +96 -0
- package/lib/browser/ai-configuration/base/ai-list-detail-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/base/ai-table-configuration-widget.d.ts +45 -0
- package/lib/browser/ai-configuration/base/ai-table-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/base/ai-table-configuration-widget.js +66 -0
- package/lib/browser/ai-configuration/base/ai-table-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/components/configuration-section.d.ts +13 -0
- package/lib/browser/ai-configuration/components/configuration-section.d.ts.map +1 -0
- package/lib/browser/ai-configuration/components/configuration-section.js +28 -0
- package/lib/browser/ai-configuration/components/configuration-section.js.map +1 -0
- package/lib/browser/ai-configuration/components/empty-state.d.ts +11 -0
- package/lib/browser/ai-configuration/components/empty-state.d.ts.map +1 -0
- package/lib/browser/ai-configuration/components/empty-state.js +26 -0
- package/lib/browser/ai-configuration/components/empty-state.js.map +1 -0
- package/lib/browser/ai-configuration/components/expandable-section.d.ts +14 -0
- package/lib/browser/ai-configuration/components/expandable-section.d.ts.map +1 -0
- package/lib/browser/ai-configuration/components/expandable-section.js +31 -0
- package/lib/browser/ai-configuration/components/expandable-section.js.map +1 -0
- package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.js +23 -22
- package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
- package/lib/browser/ai-configuration/mcp-configuration-widget.d.ts +0 -1
- package/lib/browser/ai-configuration/mcp-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/mcp-configuration-widget.js +86 -80
- package/lib/browser/ai-configuration/mcp-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts +8 -22
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js +49 -78
- package/lib/browser/ai-configuration/model-aliases-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 -11
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/ai-configuration/token-usage-configuration-widget.d.ts +7 -8
- package/lib/browser/ai-configuration/token-usage-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/token-usage-configuration-widget.js +92 -78
- package/lib/browser/ai-configuration/token-usage-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/tools-configuration-widget.d.ts +12 -7
- package/lib/browser/ai-configuration/tools-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/tools-configuration-widget.js +43 -35
- package/lib/browser/ai-configuration/tools-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/variable-configuration-widget.d.ts +8 -4
- package/lib/browser/ai-configuration/variable-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/variable-configuration-widget.js +61 -28
- package/lib/browser/ai-configuration/variable-configuration-widget.js.map +1 -1
- package/lib/browser/analyze-gh-ticket-command-contribution.d.ts +9 -0
- package/lib/browser/analyze-gh-ticket-command-contribution.d.ts.map +1 -0
- package/lib/browser/analyze-gh-ticket-command-contribution.js +172 -0
- package/lib/browser/analyze-gh-ticket-command-contribution.js.map +1 -0
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +6 -0
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/implement-gh-ticket-command-contribution.d.ts +9 -0
- package/lib/browser/implement-gh-ticket-command-contribution.d.ts.map +1 -0
- package/lib/browser/implement-gh-ticket-command-contribution.js +156 -0
- package/lib/browser/implement-gh-ticket-command-contribution.js.map +1 -0
- package/package.json +21 -21
- package/src/browser/address-pr-review-command-contribution.ts +180 -0
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +256 -135
- package/src/browser/ai-configuration/base/ai-card-grid-configuration-widget.tsx +72 -0
- package/src/browser/ai-configuration/base/ai-configuration-base-widget.tsx +37 -0
- package/src/browser/ai-configuration/base/ai-hierarchical-configuration-widget.tsx +51 -0
- package/src/browser/ai-configuration/base/ai-list-detail-configuration-widget.tsx +140 -0
- package/src/browser/ai-configuration/base/ai-table-configuration-widget.tsx +107 -0
- package/src/browser/ai-configuration/components/configuration-section.tsx +37 -0
- package/src/browser/ai-configuration/components/empty-state.tsx +30 -0
- package/src/browser/ai-configuration/components/expandable-section.tsx +51 -0
- package/src/browser/ai-configuration/language-model-renderer.tsx +68 -63
- package/src/browser/ai-configuration/mcp-configuration-widget.tsx +80 -85
- package/src/browser/ai-configuration/model-aliases-configuration-widget.tsx +93 -107
- package/src/browser/ai-configuration/template-settings-renderer.tsx +25 -29
- package/src/browser/ai-configuration/token-usage-configuration-widget.tsx +130 -130
- package/src/browser/ai-configuration/tools-configuration-widget.tsx +68 -59
- package/src/browser/ai-configuration/variable-configuration-widget.tsx +95 -45
- package/src/browser/analyze-gh-ticket-command-contribution.ts +176 -0
- package/src/browser/frontend-module.ts +6 -0
- package/src/browser/implement-gh-ticket-command-contribution.ts +160 -0
- package/src/browser/style/ai-configuration-base.css +90 -0
- package/src/browser/style/ai-configuration-cards.css +60 -0
- package/src/browser/style/ai-configuration-hierarchical.css +61 -0
- package/src/browser/style/ai-configuration-list-detail.css +88 -0
- package/src/browser/style/ai-configuration-table.css +73 -0
- package/src/browser/style/index.css +458 -288
- package/src/browser/style/widgets/mcp-configuration.css +253 -0
- package/src/browser/style/widgets/model-aliases-configuration.css +74 -0
|
@@ -63,16 +63,11 @@ export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
|
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
return (
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{(variantIds.length > 1 || isInvalidVariant) && (
|
|
72
|
-
<>
|
|
73
|
-
<label htmlFor={`variant-selector-${promptVariantSet.id}`} className="template-select-label">
|
|
74
|
-
{nls.localize('theia/ai/core/templateSettings/selectVariant', 'Select Variant:')}
|
|
75
|
-
</label>
|
|
66
|
+
<>
|
|
67
|
+
<tr>
|
|
68
|
+
<td className="template-name-cell">{promptVariantSet.id}</td>
|
|
69
|
+
<td className="template-variant-cell">
|
|
70
|
+
{(variantIds.length > 1 || isInvalidVariant) && (
|
|
76
71
|
<select
|
|
77
72
|
id={`variant-selector-${promptVariantSet.id}`}
|
|
78
73
|
className={`theia-select template-variant-selector ${isInvalidVariant ? 'error' : ''}`}
|
|
@@ -81,7 +76,7 @@ export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
|
|
|
81
76
|
>
|
|
82
77
|
{isInvalidVariant && (
|
|
83
78
|
<option value="invalid" disabled>
|
|
84
|
-
{nls.localize('theia/ai/core/templateSettings/unavailableVariant', '
|
|
79
|
+
{nls.localize('theia/ai/core/templateSettings/unavailableVariant', 'Unavailable')}
|
|
85
80
|
</option>
|
|
86
81
|
)}
|
|
87
82
|
{variantIds.map(variantId => (
|
|
@@ -90,23 +85,24 @@ export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
|
|
|
90
85
|
</option>
|
|
91
86
|
))}
|
|
92
87
|
</select>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
88
|
+
)}
|
|
89
|
+
{variantIds.length === 1 && !isInvalidVariant && <span>{selectedVariant}</span>}
|
|
90
|
+
</td>
|
|
91
|
+
<td className="template-actions-cell">
|
|
92
|
+
<button
|
|
93
|
+
className="template-action-icon-button codicon codicon-edit"
|
|
94
|
+
onClick={openTemplate}
|
|
95
|
+
disabled={isInvalidVariant}
|
|
96
|
+
title={nls.localizeByDefault('Edit')}
|
|
97
|
+
/>
|
|
98
|
+
<button
|
|
99
|
+
className="template-action-icon-button codicon codicon-discard"
|
|
100
|
+
onClick={resetTemplate}
|
|
101
|
+
disabled={isInvalidVariant}
|
|
102
|
+
title={nls.localizeByDefault('Reset')}
|
|
103
|
+
/>
|
|
104
|
+
</td>
|
|
105
|
+
</tr>
|
|
106
|
+
</>
|
|
111
107
|
);
|
|
112
108
|
};
|
|
@@ -14,24 +14,19 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { ReactWidget } from '@theia/core/lib/browser';
|
|
18
17
|
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
19
18
|
import * as React from '@theia/core/shared/react';
|
|
20
19
|
import { MessageService, nls } from '@theia/core';
|
|
21
20
|
import { TokenUsageFrontendService, ModelTokenUsageData } from '@theia/ai-core/lib/browser/token-usage-frontend-service';
|
|
22
21
|
import { formatDistanceToNow } from 'date-fns';
|
|
23
|
-
|
|
24
|
-
// Using the interface from the token usage service
|
|
22
|
+
import { AITableConfigurationWidget, TableColumn } from './base/ai-table-configuration-widget';
|
|
25
23
|
|
|
26
24
|
@injectable()
|
|
27
|
-
export class AITokenUsageConfigurationWidget extends
|
|
25
|
+
export class AITokenUsageConfigurationWidget extends AITableConfigurationWidget<ModelTokenUsageData> {
|
|
28
26
|
|
|
29
27
|
static readonly ID = 'ai-token-usage-configuration-container-widget';
|
|
30
28
|
static readonly LABEL = nls.localize('theia/ai/tokenUsage/label', 'Token Usage');
|
|
31
29
|
|
|
32
|
-
// Data will be fetched from the service
|
|
33
|
-
protected tokenUsageData: ModelTokenUsageData[] = [];
|
|
34
|
-
|
|
35
30
|
@inject(MessageService)
|
|
36
31
|
protected readonly messageService: MessageService;
|
|
37
32
|
|
|
@@ -43,24 +38,30 @@ export class AITokenUsageConfigurationWidget extends ReactWidget {
|
|
|
43
38
|
this.id = AITokenUsageConfigurationWidget.ID;
|
|
44
39
|
this.title.label = AITokenUsageConfigurationWidget.LABEL;
|
|
45
40
|
this.title.closable = false;
|
|
41
|
+
this.addClass('ai-configuration-widget');
|
|
46
42
|
|
|
47
|
-
this.
|
|
43
|
+
this.loadItems().then(() => this.update());
|
|
48
44
|
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
this.toDispose.push(
|
|
46
|
+
this.tokenUsageService.onTokenUsageUpdated(data => {
|
|
47
|
+
this.items = data;
|
|
48
|
+
this.update();
|
|
49
|
+
})
|
|
50
|
+
);
|
|
53
51
|
}
|
|
54
52
|
|
|
55
|
-
protected async
|
|
53
|
+
protected async loadItems(): Promise<void> {
|
|
56
54
|
try {
|
|
57
|
-
this.
|
|
58
|
-
this.update();
|
|
55
|
+
this.items = await this.tokenUsageService.getTokenUsageData();
|
|
59
56
|
} catch (error) {
|
|
60
57
|
this.messageService.error(nls.localize('theia/ai/tokenUsage/failedToGetTokenUsageData', 'Failed to fetch token usage data: {0}', error));
|
|
61
58
|
}
|
|
62
59
|
}
|
|
63
60
|
|
|
61
|
+
protected getItemId(item: ModelTokenUsageData): string {
|
|
62
|
+
return item.modelId;
|
|
63
|
+
}
|
|
64
|
+
|
|
64
65
|
protected formatNumber(num: number): string {
|
|
65
66
|
return num.toLocaleString();
|
|
66
67
|
}
|
|
@@ -73,138 +74,137 @@ export class AITokenUsageConfigurationWidget extends ReactWidget {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
protected hasCacheData(): boolean {
|
|
76
|
-
return this.
|
|
77
|
+
return this.items.some(model =>
|
|
77
78
|
model.cachedInputTokens !== undefined ||
|
|
78
79
|
model.readCachedInputTokens !== undefined
|
|
79
80
|
);
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
protected
|
|
83
|
+
protected getColumns(): TableColumn<ModelTokenUsageData>[] {
|
|
83
84
|
const showCacheColumns = this.hasCacheData();
|
|
85
|
+
const columns: TableColumn<ModelTokenUsageData>[] = [
|
|
86
|
+
{
|
|
87
|
+
id: 'model',
|
|
88
|
+
label: nls.localize('theia/ai/tokenUsage/model', 'Model'),
|
|
89
|
+
className: 'token-usage-model-column',
|
|
90
|
+
renderCell: item => <span>{item.modelId}</span>
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'input-tokens',
|
|
94
|
+
label: nls.localize('theia/ai/tokenUsage/inputTokens', 'Input Tokens'),
|
|
95
|
+
className: 'token-usage-column',
|
|
96
|
+
renderCell: item => <span>{this.formatNumber(item.inputTokens)}</span>
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
if (showCacheColumns) {
|
|
101
|
+
columns.push(
|
|
102
|
+
{
|
|
103
|
+
id: 'cached-input-tokens',
|
|
104
|
+
label: nls.localize('theia/ai/tokenUsage/cachedInputTokens', 'Input Tokens Written to Cache'),
|
|
105
|
+
className: 'token-usage-column',
|
|
106
|
+
renderCell: item => (
|
|
107
|
+
<span title={nls.localize(
|
|
108
|
+
'theia/ai/tokenUsage/cachedInputTokensTooltip',
|
|
109
|
+
"Tracked additionally to 'Input Tokens'. Usually more expensive than non-cached tokens."
|
|
110
|
+
)}>
|
|
111
|
+
{item.cachedInputTokens !== undefined ? this.formatNumber(item.cachedInputTokens) : '-'}
|
|
112
|
+
</span>
|
|
113
|
+
)
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'read-cached-input-tokens',
|
|
117
|
+
label: nls.localize('theia/ai/tokenUsage/readCachedInputTokens', 'Input Tokens Read From Cache'),
|
|
118
|
+
className: 'token-usage-column',
|
|
119
|
+
renderCell: item => (
|
|
120
|
+
<span title={nls.localize(
|
|
121
|
+
'theia/ai/tokenUsage/readCachedInputTokensTooltip',
|
|
122
|
+
"Tracked additionally to 'Input Token'. Usually much less expensive than not cached. Usually does not count to rate limits."
|
|
123
|
+
)}>
|
|
124
|
+
{item.readCachedInputTokens !== undefined ? this.formatNumber(item.readCachedInputTokens) : '-'}
|
|
125
|
+
</span>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
84
130
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)}
|
|
116
|
-
>
|
|
117
|
-
{nls.localize('theia/ai/tokenUsage/totalTokens', 'Total Tokens')}
|
|
118
|
-
</th>
|
|
119
|
-
<th className="token-usage-column">{nls.localize('theia/ai/tokenUsage/lastUsed', 'Last Used')}</th>
|
|
120
|
-
</tr >
|
|
131
|
+
columns.push(
|
|
132
|
+
{
|
|
133
|
+
id: 'output-tokens',
|
|
134
|
+
label: nls.localize('theia/ai/tokenUsage/outputTokens', 'Output Tokens'),
|
|
135
|
+
className: 'token-usage-column',
|
|
136
|
+
renderCell: item => <span>{this.formatNumber(item.outputTokens)}</span>
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'total-tokens',
|
|
140
|
+
label: nls.localize('theia/ai/tokenUsage/totalTokens', 'Total Tokens'),
|
|
141
|
+
className: 'token-usage-column',
|
|
142
|
+
renderCell: item => {
|
|
143
|
+
const totalTokens = item.inputTokens + item.outputTokens + (item.cachedInputTokens ?? 0);
|
|
144
|
+
return (
|
|
145
|
+
<span title={nls.localize('theia/ai/tokenUsage/totalTokensTooltip', "'Input Tokens' + 'Output Tokens'")}>
|
|
146
|
+
{this.formatNumber(totalTokens)}
|
|
147
|
+
</span>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'last-used',
|
|
153
|
+
label: nls.localize('theia/ai/tokenUsage/lastUsed', 'Last Used'),
|
|
154
|
+
className: 'token-usage-column',
|
|
155
|
+
renderCell: item => {
|
|
156
|
+
const lastUsedDate = item.lastUsed ? new Date(item.lastUsed) : undefined;
|
|
157
|
+
const exactDateString = lastUsedDate ? lastUsedDate.toLocaleString() : '';
|
|
158
|
+
return <span title={exactDateString}>{this.formatDate(lastUsedDate)}</span>;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
121
161
|
);
|
|
122
|
-
}
|
|
123
162
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const exactDateString = lastUsedDate ? lastUsedDate.toLocaleString() : '';
|
|
127
|
-
const showCacheColumns = this.hasCacheData();
|
|
128
|
-
const totalTokens = model.inputTokens + model.outputTokens + (model.cachedInputTokens ?? 0);
|
|
163
|
+
return columns;
|
|
164
|
+
}
|
|
129
165
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<td className="token-usage-model-cell">{model.modelId}</td>
|
|
133
|
-
<td className="token-usage-cell">{this.formatNumber(model.inputTokens)}</td>
|
|
134
|
-
{showCacheColumns && (
|
|
135
|
-
<>
|
|
136
|
-
<td className="token-usage-cell">{model.cachedInputTokens !== undefined ? this.formatNumber(model.cachedInputTokens) : '-'}</td>
|
|
137
|
-
<td className="token-usage-cell">{model.readCachedInputTokens !== undefined ? this.formatNumber(model.readCachedInputTokens) : '-'}</td>
|
|
138
|
-
</>
|
|
139
|
-
)}
|
|
140
|
-
<td className="token-usage-cell">{this.formatNumber(model.outputTokens)}</td>
|
|
141
|
-
<td className="token-usage-cell">{this.formatNumber(totalTokens)}</td>
|
|
142
|
-
<td className="token-usage-cell" title={exactDateString}>{this.formatDate(lastUsedDate)}</td>
|
|
143
|
-
</tr>
|
|
144
|
-
);
|
|
166
|
+
protected override renderHeader(): React.ReactNode {
|
|
167
|
+
return undefined;
|
|
145
168
|
}
|
|
146
169
|
|
|
147
|
-
protected
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
170
|
+
protected override renderFooter(): React.ReactNode {
|
|
171
|
+
if (this.items.length === 0) {
|
|
172
|
+
return (
|
|
173
|
+
<div className="ai-empty-state-content">
|
|
174
|
+
<p>{nls.localize('theia/ai/tokenUsage/noData', 'No token usage data available yet.')}</p>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
151
177
|
}
|
|
152
178
|
|
|
153
|
-
const totalInputTokens = this.tokenUsageData.reduce((sum, model) => sum + model.inputTokens, 0);
|
|
154
|
-
const totalOutputTokens = this.tokenUsageData.reduce((sum, model) => sum + model.outputTokens, 0);
|
|
155
|
-
const totalCachedInputTokens = this.tokenUsageData.reduce(
|
|
156
|
-
(sum, model) => sum + (model.cachedInputTokens || 0), 0
|
|
157
|
-
);
|
|
158
|
-
const totalReadCachedInputTokens = this.tokenUsageData.reduce(
|
|
159
|
-
(sum, model) => sum + (model.readCachedInputTokens || 0), 0
|
|
160
|
-
);
|
|
161
|
-
const totalTokens = totalInputTokens + totalCachedInputTokens + totalOutputTokens;
|
|
162
|
-
|
|
163
179
|
const showCacheColumns = this.hasCacheData();
|
|
180
|
+
const totalInputTokens = this.items.reduce((sum, model) => sum + model.inputTokens, 0);
|
|
181
|
+
const totalOutputTokens = this.items.reduce((sum, model) => sum + model.outputTokens, 0);
|
|
182
|
+
const totalCachedInputTokens = this.items.reduce((sum, model) => sum + (model.cachedInputTokens || 0), 0);
|
|
183
|
+
const totalReadCachedInputTokens = this.items.reduce((sum, model) => sum + (model.readCachedInputTokens || 0), 0);
|
|
184
|
+
const totalTokens = totalInputTokens + totalCachedInputTokens + totalOutputTokens;
|
|
164
185
|
|
|
165
186
|
return (
|
|
166
|
-
<
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
<div className="token-usage-table-container">
|
|
188
|
-
{this.tokenUsageData.length > 0 ? (
|
|
189
|
-
<table className="token-usage-table">
|
|
190
|
-
<thead>
|
|
191
|
-
{this.renderHeaderRow()}
|
|
192
|
-
</thead>
|
|
193
|
-
<tbody>
|
|
194
|
-
{this.tokenUsageData.map(model => this.renderModelRow(model))}
|
|
195
|
-
{this.renderSummaryRow()}
|
|
196
|
-
</tbody>
|
|
197
|
-
</table>
|
|
198
|
-
) : (
|
|
199
|
-
<div className="token-usage-empty">
|
|
200
|
-
<p>{nls.localize('theia/ai/tokenUsage/noData', 'No token usage data available yet.')}</p>
|
|
201
|
-
</div>
|
|
202
|
-
)}
|
|
203
|
-
</div>
|
|
204
|
-
|
|
205
|
-
<div className="token-usage-notes">
|
|
206
|
-
<p className="token-usage-note">
|
|
207
|
-
<i className="codicon codicon-info"></i>
|
|
187
|
+
<div className="ai-configuration-footer-total">
|
|
188
|
+
<table className="ai-configuration-table">
|
|
189
|
+
<tfoot>
|
|
190
|
+
<tr className="ai-configuration-footer-total-row">
|
|
191
|
+
<td className="token-usage-model-column">{nls.localize('theia/ai/tokenUsage/total', 'Total')}</td>
|
|
192
|
+
<td className="token-usage-column">{this.formatNumber(totalInputTokens)}</td>
|
|
193
|
+
{showCacheColumns && (
|
|
194
|
+
<>
|
|
195
|
+
<td className="token-usage-column">{this.formatNumber(totalCachedInputTokens)}</td>
|
|
196
|
+
<td className="token-usage-column">{this.formatNumber(totalReadCachedInputTokens)}</td>
|
|
197
|
+
</>
|
|
198
|
+
)}
|
|
199
|
+
<td className="token-usage-column">{this.formatNumber(totalOutputTokens)}</td>
|
|
200
|
+
<td className="token-usage-column">{this.formatNumber(totalTokens)}</td>
|
|
201
|
+
<td className="token-usage-column"></td>
|
|
202
|
+
</tr>
|
|
203
|
+
</tfoot>
|
|
204
|
+
</table>
|
|
205
|
+
<div className="ai-configuration-info-box">
|
|
206
|
+
<p className="ai-configuration-info-text">
|
|
207
|
+
<i className="codicon codicon-info ai-configuration-info-icon"></i>
|
|
208
208
|
{nls.localize('theia/ai/tokenUsage/note', 'Token usage is tracked since the start of the application and is not persisted.')}
|
|
209
209
|
</p>
|
|
210
210
|
</div>
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { ConfirmDialog } from '@theia/core/lib/browser';
|
|
18
18
|
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
19
19
|
import * as React from '@theia/core/shared/react';
|
|
20
20
|
import { ToolInvocationRegistry } from '@theia/ai-core';
|
|
21
21
|
import { nls, PreferenceService } from '@theia/core';
|
|
22
22
|
import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
|
|
23
23
|
import { ToolConfirmationMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
|
|
24
|
+
import { AITableConfigurationWidget, TableColumn } from './base/ai-table-configuration-widget';
|
|
24
25
|
|
|
25
26
|
const TOOL_OPTIONS: { value: ToolConfirmationMode, label: string, icon: string }[] = [
|
|
26
27
|
{ value: ToolConfirmationMode.DISABLED, label: nls.localizeByDefault('Disabled'), icon: 'close' },
|
|
@@ -28,8 +29,12 @@ const TOOL_OPTIONS: { value: ToolConfirmationMode, label: string, icon: string }
|
|
|
28
29
|
{ value: ToolConfirmationMode.ALWAYS_ALLOW, label: nls.localize('theia/ai/ide/toolsConfiguration/toolOptions/alwaysAllow/label', 'Always Allow'), icon: 'thumbsup' },
|
|
29
30
|
];
|
|
30
31
|
|
|
32
|
+
interface ToolItem {
|
|
33
|
+
name: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
@injectable()
|
|
32
|
-
export class AIToolsConfigurationWidget extends
|
|
37
|
+
export class AIToolsConfigurationWidget extends AITableConfigurationWidget<ToolItem> {
|
|
33
38
|
static readonly ID = 'ai-tools-configuration-widget';
|
|
34
39
|
static readonly LABEL = nls.localize('theia/ai/ide/toolsConfiguration/label', 'Tools');
|
|
35
40
|
|
|
@@ -42,19 +47,17 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
42
47
|
@inject(ToolInvocationRegistry)
|
|
43
48
|
protected readonly toolInvocationRegistry: ToolInvocationRegistry;
|
|
44
49
|
|
|
45
|
-
// Mocked tool list and state
|
|
46
|
-
protected tools: string[] = [];
|
|
47
50
|
protected toolConfirmationModes: Record<string, ToolConfirmationMode> = {};
|
|
48
51
|
protected defaultState: ToolConfirmationMode;
|
|
49
|
-
protected loading = true;
|
|
50
52
|
|
|
51
53
|
@postConstruct()
|
|
52
54
|
protected init(): void {
|
|
53
55
|
this.id = AIToolsConfigurationWidget.ID;
|
|
54
56
|
this.title.label = AIToolsConfigurationWidget.LABEL;
|
|
55
57
|
this.title.closable = false;
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
+
this.addClass('ai-configuration-widget');
|
|
59
|
+
|
|
60
|
+
this.loadData().then(() => this.update());
|
|
58
61
|
this.toDispose.pushAll([
|
|
59
62
|
this.preferenceService.onPreferenceChanged(async e => {
|
|
60
63
|
if (e.preferenceName === 'ai-features.chat.toolConfirmation') {
|
|
@@ -64,23 +67,25 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
64
67
|
}
|
|
65
68
|
}),
|
|
66
69
|
this.toolInvocationRegistry.onDidChange(async () => {
|
|
67
|
-
|
|
70
|
+
await this.loadItems();
|
|
68
71
|
this.update();
|
|
69
72
|
})
|
|
70
73
|
]);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
protected async loadData(): Promise<void> {
|
|
74
|
-
|
|
75
|
-
this.tools = await this.loadTools();
|
|
77
|
+
await this.loadItems();
|
|
76
78
|
this.defaultState = await this.loadDefaultConfirmation();
|
|
77
79
|
this.toolConfirmationModes = await this.loadToolConfigurationModes();
|
|
78
|
-
this.loading = false;
|
|
79
|
-
this.update();
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
protected async
|
|
83
|
-
|
|
82
|
+
protected async loadItems(): Promise<void> {
|
|
83
|
+
const toolNames = this.toolInvocationRegistry.getAllFunctions().map(func => func.name);
|
|
84
|
+
this.items = toolNames.map(name => ({ name }));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
protected getItemId(item: ToolItem): string {
|
|
88
|
+
return item.name;
|
|
84
89
|
}
|
|
85
90
|
protected async loadDefaultConfirmation(): Promise<ToolConfirmationMode> {
|
|
86
91
|
return this.confirmationManager.getConfirmationMode('*', 'doesNotMatter');
|
|
@@ -95,9 +100,9 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
95
100
|
await this.confirmationManager.setConfirmationMode('*', state);
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
protected handleToolConfirmationModeChange = async (
|
|
103
|
+
protected handleToolConfirmationModeChange = async (toolName: string, event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
99
104
|
const newState = event.target.value as ToolConfirmationMode;
|
|
100
|
-
await this.updateToolConfirmationMode(
|
|
105
|
+
await this.updateToolConfirmationMode(toolName, newState);
|
|
101
106
|
};
|
|
102
107
|
protected handleDefaultStateChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
103
108
|
const newState = event.target.value as ToolConfirmationMode;
|
|
@@ -118,25 +123,23 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
protected
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<div className='ai-tools-configuration-default-label'>{nls.localize('theia/ai/ide/toolsConfiguration/default/label', 'Default Tool Confirmation Mode:')}</div>
|
|
126
|
+
protected override renderHeader(): React.ReactNode {
|
|
127
|
+
return (
|
|
128
|
+
<div className="ai-tools-configuration-header">
|
|
129
|
+
<div style={{ fontWeight: 500 }}>
|
|
130
|
+
{nls.localize('theia/ai/ide/toolsConfiguration/default/label', 'Default Tool Confirmation Mode:')}
|
|
131
|
+
</div>
|
|
128
132
|
<select
|
|
129
|
-
className="
|
|
133
|
+
className="theia-select"
|
|
130
134
|
value={this.defaultState}
|
|
131
135
|
onChange={this.handleDefaultStateChange}
|
|
132
|
-
style={{ marginLeft: 8 }}
|
|
133
136
|
>
|
|
134
137
|
{TOOL_OPTIONS.map(opt => (
|
|
135
138
|
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
136
139
|
))}
|
|
137
140
|
</select>
|
|
138
141
|
<button
|
|
139
|
-
className='ai-tools-
|
|
142
|
+
className='theia-button secondary ai-tools-reset-button'
|
|
140
143
|
style={{ marginLeft: 'auto' }}
|
|
141
144
|
title={nls.localize('theia/ai/ide/toolsConfiguration/resetAllTooltip', 'Reset all tools to default')}
|
|
142
145
|
onClick={() => this.resetAllToolsToDefault()}
|
|
@@ -144,38 +147,44 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
144
147
|
{nls.localize('theia/ai/ide/toolsConfiguration/resetAll', 'Reset All')}
|
|
145
148
|
</button>
|
|
146
149
|
</div>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
</
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
protected getColumns(): TableColumn<ToolItem>[] {
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
id: 'tool-name',
|
|
157
|
+
label: nls.localize('theia/ai/ide/toolsConfiguration/tools/label', 'Tool'),
|
|
158
|
+
className: 'tool-name-column',
|
|
159
|
+
renderCell: (item: ToolItem) => <span>{item.name}</span>
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'confirmation-mode',
|
|
163
|
+
label: nls.localize('theia/ai/ide/toolsConfiguration/confirmationMode/label', 'Confirmation Mode'),
|
|
164
|
+
className: 'confirmation-mode-column',
|
|
165
|
+
renderCell: (item: ToolItem) => {
|
|
166
|
+
const state = this.toolConfirmationModes[item.name] || this.defaultState;
|
|
167
|
+
return (
|
|
168
|
+
<select
|
|
169
|
+
className="theia-select"
|
|
170
|
+
value={state}
|
|
171
|
+
onChange={e => this.handleToolConfirmationModeChange(item.name, e)}
|
|
172
|
+
>
|
|
173
|
+
{TOOL_OPTIONS.map(opt => (
|
|
174
|
+
<option key={opt.value} value={opt.value}>
|
|
175
|
+
{opt.label}
|
|
176
|
+
</option>
|
|
177
|
+
))}
|
|
178
|
+
</select>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
protected override getRowClassName(item: ToolItem): string {
|
|
186
|
+
const state = this.toolConfirmationModes[item.name] || this.defaultState;
|
|
187
|
+
const isDefault = state === this.defaultState;
|
|
188
|
+
return isDefault ? 'default-mode' : 'custom-mode';
|
|
180
189
|
}
|
|
181
190
|
}
|