@theia/ai-history 1.46.0-next.241
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/README.md +31 -0
- package/lib/browser/ai-history-communication-card.d.ts +8 -0
- package/lib/browser/ai-history-communication-card.d.ts.map +1 -0
- package/lib/browser/ai-history-communication-card.js +40 -0
- package/lib/browser/ai-history-communication-card.js.map +1 -0
- package/lib/browser/ai-history-contribution.d.ts +22 -0
- package/lib/browser/ai-history-contribution.d.ts.map +1 -0
- package/lib/browser/ai-history-contribution.js +133 -0
- package/lib/browser/ai-history-contribution.js.map +1 -0
- package/lib/browser/ai-history-frontend-module.d.ts +5 -0
- package/lib/browser/ai-history-frontend-module.d.ts.map +1 -0
- package/lib/browser/ai-history-frontend-module.js +42 -0
- package/lib/browser/ai-history-frontend-module.js.map +1 -0
- package/lib/browser/ai-history-widget.d.ts +32 -0
- package/lib/browser/ai-history-widget.d.ts.map +1 -0
- package/lib/browser/ai-history-widget.js +139 -0
- package/lib/browser/ai-history-widget.js.map +1 -0
- package/lib/common/communication-recording-service.d.ts +18 -0
- package/lib/common/communication-recording-service.d.ts.map +1 -0
- package/lib/common/communication-recording-service.js +65 -0
- package/lib/common/communication-recording-service.js.map +1 -0
- package/lib/common/communication-recording-service.spec.d.ts +2 -0
- package/lib/common/communication-recording-service.spec.d.ts.map +1 -0
- package/lib/common/communication-recording-service.spec.js +41 -0
- package/lib/common/communication-recording-service.spec.js.map +1 -0
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.d.ts.map +1 -0
- package/lib/common/index.js +20 -0
- package/lib/common/index.js.map +1 -0
- package/package.json +54 -0
- package/src/browser/ai-history-communication-card.tsx +71 -0
- package/src/browser/ai-history-contribution.ts +136 -0
- package/src/browser/ai-history-frontend-module.ts +44 -0
- package/src/browser/ai-history-widget.tsx +149 -0
- package/src/browser/style/ai-history.css +92 -0
- package/src/common/communication-recording-service.spec.ts +62 -0
- package/src/common/communication-recording-service.ts +88 -0
- package/src/common/index.ts +17 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mock_logger_1 = require("@theia/core/lib/common/test/mock-logger");
|
|
4
|
+
const communication_recording_service_1 = require("./communication-recording-service");
|
|
5
|
+
const chai_1 = require("chai");
|
|
6
|
+
describe('DefaultCommunicationRecordingService', () => {
|
|
7
|
+
it('records history', () => {
|
|
8
|
+
const service = new communication_recording_service_1.DefaultCommunicationRecordingService();
|
|
9
|
+
service.logger = new mock_logger_1.MockLogger();
|
|
10
|
+
service.recordRequest({ agentId: 'agent', requestId: '1', sessionId: '1', timestamp: 100, request: 'dummy request' });
|
|
11
|
+
const history1 = service.getHistory('agent');
|
|
12
|
+
(0, chai_1.expect)(history1[0].request).to.eq('dummy request');
|
|
13
|
+
service.recordResponse({ agentId: 'agent', requestId: '1', sessionId: '1', timestamp: 200, response: 'dummy response' });
|
|
14
|
+
const history2 = service.getHistory('agent');
|
|
15
|
+
(0, chai_1.expect)(history2[0].request).to.eq('dummy request');
|
|
16
|
+
(0, chai_1.expect)(history2[0].response).to.eq('dummy response');
|
|
17
|
+
});
|
|
18
|
+
it('returns session history', () => {
|
|
19
|
+
const service = new communication_recording_service_1.DefaultCommunicationRecordingService();
|
|
20
|
+
service.logger = new mock_logger_1.MockLogger();
|
|
21
|
+
// some requests and responses for session 1
|
|
22
|
+
service.recordRequest({ agentId: 'agent', requestId: '1', sessionId: '1', timestamp: 100, request: 'session 1 request 1' });
|
|
23
|
+
service.recordResponse({ agentId: 'agent', requestId: '1', sessionId: '1', timestamp: 200, response: 'session 1 response 1' });
|
|
24
|
+
service.recordRequest({ agentId: 'agent2', requestId: '2', sessionId: '1', timestamp: 100, request: 'session 1 request 2' });
|
|
25
|
+
service.recordResponse({ agentId: 'agent2', requestId: '2', sessionId: '1', timestamp: 200, response: 'session 1 response 2' });
|
|
26
|
+
// some requests and responses for session 2
|
|
27
|
+
service.recordRequest({ agentId: 'agent', requestId: '3', sessionId: '2', timestamp: 100, request: 'different session request' });
|
|
28
|
+
service.recordResponse({ agentId: 'agent', requestId: '3', sessionId: '2', timestamp: 200, response: 'different session request' });
|
|
29
|
+
const history1 = service.getSessionHistory('1');
|
|
30
|
+
(0, chai_1.expect)(history1.length).to.eq(2);
|
|
31
|
+
(0, chai_1.expect)(history1[0].request).to.eq('session 1 request 1');
|
|
32
|
+
(0, chai_1.expect)(history1[0].response).to.eq('session 1 response 1');
|
|
33
|
+
(0, chai_1.expect)(history1[1].request).to.eq('session 1 request 2');
|
|
34
|
+
(0, chai_1.expect)(history1[1].response).to.eq('session 1 response 2');
|
|
35
|
+
const history2 = service.getSessionHistory('2');
|
|
36
|
+
(0, chai_1.expect)(history2.length).to.eq(1);
|
|
37
|
+
(0, chai_1.expect)(history2[0].request).to.eq('different session request');
|
|
38
|
+
(0, chai_1.expect)(history2[0].response).to.eq('different session request');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=communication-recording-service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"communication-recording-service.spec.js","sourceRoot":"","sources":["../../src/common/communication-recording-service.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAgBA,cAAc,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
const tslib_1 = require("tslib");
|
|
19
|
+
tslib_1.__exportStar(require("./communication-recording-service"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;AAEhF,4EAAkD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@theia/ai-history",
|
|
3
|
+
"version": "1.46.0-next.241+4295c1a8c",
|
|
4
|
+
"description": "Theia - AI communication history",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@theia/ai-core": "1.46.0-next.241+4295c1a8c",
|
|
7
|
+
"@theia/core": "1.46.0-next.241+4295c1a8c",
|
|
8
|
+
"@theia/filesystem": "1.46.0-next.241+4295c1a8c",
|
|
9
|
+
"@theia/output": "1.46.0-next.241+4295c1a8c",
|
|
10
|
+
"@theia/workspace": "1.46.0-next.241+4295c1a8c",
|
|
11
|
+
"minimatch": "^5.1.0",
|
|
12
|
+
"tslib": "^2.6.2"
|
|
13
|
+
},
|
|
14
|
+
"main": "lib/common",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"theiaExtensions": [
|
|
19
|
+
{
|
|
20
|
+
"frontend": "lib/browser/ai-history-frontend-module"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"theia-extension"
|
|
25
|
+
],
|
|
26
|
+
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/eclipse-theia/theia.git"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/eclipse-theia/theia/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/eclipse-theia/theia",
|
|
35
|
+
"files": [
|
|
36
|
+
"lib",
|
|
37
|
+
"src"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "theiaext build",
|
|
41
|
+
"clean": "theiaext clean",
|
|
42
|
+
"compile": "theiaext compile",
|
|
43
|
+
"lint": "theiaext lint",
|
|
44
|
+
"test": "theiaext test",
|
|
45
|
+
"watch": "theiaext watch"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@theia/ext-scripts": "1.56.0"
|
|
49
|
+
},
|
|
50
|
+
"nyc": {
|
|
51
|
+
"extends": "../../configs/nyc.json"
|
|
52
|
+
},
|
|
53
|
+
"gitHead": "4295c1a8c8b5d23471f274265c798bac9e6baecc"
|
|
54
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 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 { CommunicationHistoryEntry } from '@theia/ai-core';
|
|
17
|
+
import * as React from '@theia/core/shared/react';
|
|
18
|
+
|
|
19
|
+
export interface CommunicationCardProps {
|
|
20
|
+
entry: CommunicationHistoryEntry;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const CommunicationCard: React.FC<CommunicationCardProps> = ({ entry }) => (
|
|
24
|
+
<div className='theia-card'>
|
|
25
|
+
<div className='theia-card-meta'>
|
|
26
|
+
<span className='theia-card-request-id'>Request ID: {entry.requestId}</span>
|
|
27
|
+
<span className='theia-card-session-id'>Session ID: {entry.sessionId}</span>
|
|
28
|
+
</div>
|
|
29
|
+
<div className='theia-card-content'>
|
|
30
|
+
{entry.request && (
|
|
31
|
+
<div className='theia-card-request'>
|
|
32
|
+
<h2>Request</h2>
|
|
33
|
+
<pre>{entry.request}</pre>
|
|
34
|
+
</div>
|
|
35
|
+
)}
|
|
36
|
+
{entry.response && (
|
|
37
|
+
<div className='theia-card-response'>
|
|
38
|
+
<h2>Response</h2>
|
|
39
|
+
<pre>{entry.response}</pre>
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
{(entry.systemMessage || (entry.messages && entry.messages.length > 0)) && (
|
|
43
|
+
<div className='theia-card-context'>
|
|
44
|
+
<details>
|
|
45
|
+
<summary><h2>Context</h2></summary>
|
|
46
|
+
{(entry.systemMessage && (
|
|
47
|
+
<div className='theia-context-system-message'>
|
|
48
|
+
<h3>System Message</h3>
|
|
49
|
+
<pre>{entry.systemMessage}</pre>
|
|
50
|
+
</div>
|
|
51
|
+
))}
|
|
52
|
+
{(entry.messages && entry.messages.length > 0) && (
|
|
53
|
+
<div className='theia-context-messages'>
|
|
54
|
+
<h3>Messages</h3>
|
|
55
|
+
<ul>
|
|
56
|
+
{entry.messages.map((message, index) => (
|
|
57
|
+
<li key={index}><pre>{JSON.stringify(message, undefined, 2)}</pre></li>
|
|
58
|
+
))}
|
|
59
|
+
</ul>
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
</details>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
<div className='theia-card-meta'>
|
|
67
|
+
<span className='theia-card-timestamp'>Timestamp: {new Date(entry.timestamp).toLocaleString()}</span>
|
|
68
|
+
{entry.responseTime && <span className='theia-card-response-time'>Response Time: {entry.responseTime}ms</span>}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 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 { FrontendApplication, codicon } from '@theia/core/lib/browser';
|
|
17
|
+
import { AIViewContribution } from '@theia/ai-core/lib/browser';
|
|
18
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
19
|
+
import { AIHistoryView } from './ai-history-widget';
|
|
20
|
+
import { Command, CommandRegistry, Emitter } from '@theia/core';
|
|
21
|
+
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
22
|
+
import { CommunicationRecordingService } from '@theia/ai-core';
|
|
23
|
+
|
|
24
|
+
export const AI_HISTORY_TOGGLE_COMMAND_ID = 'aiHistory:toggle';
|
|
25
|
+
export const OPEN_AI_HISTORY_VIEW = Command.toLocalizedCommand({
|
|
26
|
+
id: 'aiHistory:open',
|
|
27
|
+
label: 'Open AI History view',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY = Command.toLocalizedCommand({
|
|
31
|
+
id: 'aiHistory:sortChronologically',
|
|
32
|
+
label: 'AI History: Sort chronologically',
|
|
33
|
+
iconClass: codicon('arrow-down')
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY = Command.toLocalizedCommand({
|
|
37
|
+
id: 'aiHistory:sortReverseChronologically',
|
|
38
|
+
label: 'AI History: Sort reverse chronologically',
|
|
39
|
+
iconClass: codicon('arrow-up')
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const AI_HISTORY_VIEW_CLEAR = Command.toLocalizedCommand({
|
|
43
|
+
id: 'aiHistory:clear',
|
|
44
|
+
label: 'AI History: Clear History',
|
|
45
|
+
iconClass: codicon('clear-all')
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
@injectable()
|
|
49
|
+
export class AIHistoryViewContribution extends AIViewContribution<AIHistoryView> implements TabBarToolbarContribution {
|
|
50
|
+
@inject(CommunicationRecordingService) private recordingService: CommunicationRecordingService;
|
|
51
|
+
|
|
52
|
+
protected readonly chronologicalChangedEmitter = new Emitter<void>();
|
|
53
|
+
protected readonly chronologicalStateChanged = this.chronologicalChangedEmitter.event;
|
|
54
|
+
|
|
55
|
+
constructor() {
|
|
56
|
+
super({
|
|
57
|
+
widgetId: AIHistoryView.ID,
|
|
58
|
+
widgetName: AIHistoryView.LABEL,
|
|
59
|
+
defaultWidgetOptions: {
|
|
60
|
+
area: 'bottom',
|
|
61
|
+
rank: 100
|
|
62
|
+
},
|
|
63
|
+
toggleCommandId: AI_HISTORY_TOGGLE_COMMAND_ID,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async initializeLayout(_app: FrontendApplication): Promise<void> {
|
|
68
|
+
await this.openView();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
override registerCommands(registry: CommandRegistry): void {
|
|
72
|
+
super.registerCommands(registry);
|
|
73
|
+
registry.registerCommand(OPEN_AI_HISTORY_VIEW, {
|
|
74
|
+
execute: () => this.openView({ activate: true }),
|
|
75
|
+
});
|
|
76
|
+
registry.registerCommand(AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY, {
|
|
77
|
+
isEnabled: widget => this.withHistoryWidget(widget, historyView => !historyView.isChronological),
|
|
78
|
+
isVisible: widget => this.withHistoryWidget(widget, historyView => !historyView.isChronological),
|
|
79
|
+
execute: widget => this.withHistoryWidget(widget, historyView => {
|
|
80
|
+
historyView.sortHistory(true);
|
|
81
|
+
this.chronologicalChangedEmitter.fire();
|
|
82
|
+
return true;
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
registry.registerCommand(AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY, {
|
|
86
|
+
isEnabled: widget => this.withHistoryWidget(widget, historyView => historyView.isChronological),
|
|
87
|
+
isVisible: widget => this.withHistoryWidget(widget, historyView => historyView.isChronological),
|
|
88
|
+
execute: widget => this.withHistoryWidget(widget, historyView => {
|
|
89
|
+
historyView.sortHistory(false);
|
|
90
|
+
this.chronologicalChangedEmitter.fire();
|
|
91
|
+
return true;
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
registry.registerCommand(AI_HISTORY_VIEW_CLEAR, {
|
|
95
|
+
isEnabled: widget => this.withHistoryWidget(widget),
|
|
96
|
+
isVisible: widget => this.withHistoryWidget(widget),
|
|
97
|
+
execute: widget => this.withHistoryWidget(widget, () => {
|
|
98
|
+
this.clearHistory();
|
|
99
|
+
return true;
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
public clearHistory(): void {
|
|
104
|
+
this.recordingService.clearHistory();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected withHistoryWidget(
|
|
108
|
+
widget: unknown = this.tryGetWidget(),
|
|
109
|
+
predicate: (output: AIHistoryView) => boolean = () => true
|
|
110
|
+
): boolean | false {
|
|
111
|
+
return widget instanceof AIHistoryView ? predicate(widget) : false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
registerToolbarItems(registry: TabBarToolbarRegistry): void {
|
|
115
|
+
registry.registerItem({
|
|
116
|
+
id: AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY.id,
|
|
117
|
+
command: AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY.id,
|
|
118
|
+
tooltip: 'Sort chronologically',
|
|
119
|
+
isVisible: widget => this.withHistoryWidget(widget),
|
|
120
|
+
onDidChange: this.chronologicalStateChanged
|
|
121
|
+
});
|
|
122
|
+
registry.registerItem({
|
|
123
|
+
id: AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY.id,
|
|
124
|
+
command: AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY.id,
|
|
125
|
+
tooltip: 'Sort reverse chronologically',
|
|
126
|
+
isVisible: widget => this.withHistoryWidget(widget),
|
|
127
|
+
onDidChange: this.chronologicalStateChanged
|
|
128
|
+
});
|
|
129
|
+
registry.registerItem({
|
|
130
|
+
id: AI_HISTORY_VIEW_CLEAR.id,
|
|
131
|
+
command: AI_HISTORY_VIEW_CLEAR.id,
|
|
132
|
+
tooltip: 'Clear History of all agents',
|
|
133
|
+
isVisible: widget => this.withHistoryWidget(widget)
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 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 { CommunicationRecordingService } from '@theia/ai-core';
|
|
17
|
+
import { ContainerModule } from '@theia/core/shared/inversify';
|
|
18
|
+
import { DefaultCommunicationRecordingService } from '../common/communication-recording-service';
|
|
19
|
+
import { bindViewContribution, WidgetFactory } from '@theia/core/lib/browser';
|
|
20
|
+
import { ILogger } from '@theia/core';
|
|
21
|
+
import { AIHistoryViewContribution } from './ai-history-contribution';
|
|
22
|
+
import { AIHistoryView } from './ai-history-widget';
|
|
23
|
+
import '../../src/browser/style/ai-history.css';
|
|
24
|
+
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
|
|
25
|
+
|
|
26
|
+
export default new ContainerModule(bind => {
|
|
27
|
+
bind(DefaultCommunicationRecordingService).toSelf().inSingletonScope();
|
|
28
|
+
bind(CommunicationRecordingService).toService(DefaultCommunicationRecordingService);
|
|
29
|
+
|
|
30
|
+
bind(ILogger).toDynamicValue(ctx => {
|
|
31
|
+
const parentLogger = ctx.container.get<ILogger>(ILogger);
|
|
32
|
+
return parentLogger.child('llm-communication-recorder');
|
|
33
|
+
}).inSingletonScope().whenTargetNamed('llm-communication-recorder');
|
|
34
|
+
|
|
35
|
+
bindViewContribution(bind, AIHistoryViewContribution);
|
|
36
|
+
|
|
37
|
+
bind(AIHistoryView).toSelf();
|
|
38
|
+
bind(WidgetFactory).toDynamicValue(context => ({
|
|
39
|
+
id: AIHistoryView.ID,
|
|
40
|
+
createWidget: () => context.container.get<AIHistoryView>(AIHistoryView)
|
|
41
|
+
})).inSingletonScope();
|
|
42
|
+
bind(TabBarToolbarContribution).toService(AIHistoryViewContribution);
|
|
43
|
+
|
|
44
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 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 { Agent, AgentService, CommunicationRecordingService, CommunicationRequestEntry, CommunicationResponseEntry } from '@theia/ai-core';
|
|
17
|
+
import { codicon, ReactWidget, StatefulWidget } from '@theia/core/lib/browser';
|
|
18
|
+
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
19
|
+
import * as React from '@theia/core/shared/react';
|
|
20
|
+
import { CommunicationCard } from './ai-history-communication-card';
|
|
21
|
+
import { SelectComponent, SelectOption } from '@theia/core/lib/browser/widgets/select-component';
|
|
22
|
+
import { deepClone } from '@theia/core';
|
|
23
|
+
|
|
24
|
+
namespace AIHistoryView {
|
|
25
|
+
export interface State {
|
|
26
|
+
chronological: boolean;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@injectable()
|
|
31
|
+
export class AIHistoryView extends ReactWidget implements StatefulWidget {
|
|
32
|
+
@inject(CommunicationRecordingService)
|
|
33
|
+
protected recordingService: CommunicationRecordingService;
|
|
34
|
+
@inject(AgentService)
|
|
35
|
+
protected readonly agentService: AgentService;
|
|
36
|
+
|
|
37
|
+
public static ID = 'ai-history-widget';
|
|
38
|
+
static LABEL = '✨ AI Agent History [Experimental]';
|
|
39
|
+
|
|
40
|
+
protected selectedAgent?: Agent;
|
|
41
|
+
|
|
42
|
+
protected _state: AIHistoryView.State = { chronological: false };
|
|
43
|
+
|
|
44
|
+
constructor() {
|
|
45
|
+
super();
|
|
46
|
+
this.id = AIHistoryView.ID;
|
|
47
|
+
this.title.label = AIHistoryView.LABEL;
|
|
48
|
+
this.title.caption = AIHistoryView.LABEL;
|
|
49
|
+
this.title.closable = true;
|
|
50
|
+
this.title.iconClass = codicon('history');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected get state(): AIHistoryView.State {
|
|
54
|
+
return this._state;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected set state(state: AIHistoryView.State) {
|
|
58
|
+
this._state = state;
|
|
59
|
+
this.update();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
storeState(): object {
|
|
63
|
+
return this.state;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
restoreState(oldState: object & Partial<AIHistoryView.State>): void {
|
|
67
|
+
const copy = deepClone(this.state);
|
|
68
|
+
if (oldState.chronological) {
|
|
69
|
+
copy.chronological = oldState.chronological;
|
|
70
|
+
}
|
|
71
|
+
this.state = copy;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@postConstruct()
|
|
75
|
+
protected init(): void {
|
|
76
|
+
this.update();
|
|
77
|
+
this.toDispose.push(this.recordingService.onDidRecordRequest(entry => this.historyContentUpdated(entry)));
|
|
78
|
+
this.toDispose.push(this.recordingService.onDidRecordResponse(entry => this.historyContentUpdated(entry)));
|
|
79
|
+
this.toDispose.push(this.recordingService.onStructuralChange(() => this.update()));
|
|
80
|
+
this.selectAgent(this.agentService.getAllAgents()[0]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected selectAgent(agent: Agent | undefined): void {
|
|
84
|
+
this.selectedAgent = agent;
|
|
85
|
+
this.update();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected historyContentUpdated(entry: CommunicationRequestEntry | CommunicationResponseEntry): void {
|
|
89
|
+
if (entry.agentId === this.selectedAgent?.id) {
|
|
90
|
+
this.update();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
render(): React.ReactNode {
|
|
95
|
+
const selectionChange = (value: SelectOption) => {
|
|
96
|
+
this.selectedAgent = this.agentService.getAllAgents().find(agent => agent.id === value.value);
|
|
97
|
+
this.update();
|
|
98
|
+
};
|
|
99
|
+
const agents = this.agentService.getAllAgents();
|
|
100
|
+
if (agents.length === 0) {
|
|
101
|
+
return (
|
|
102
|
+
<div className='agent-history-widget'>
|
|
103
|
+
<div className='theia-card no-content'>No agent available.</div>
|
|
104
|
+
</div >);
|
|
105
|
+
}
|
|
106
|
+
return (
|
|
107
|
+
<div className='agent-history-widget'>
|
|
108
|
+
<SelectComponent
|
|
109
|
+
options={agents.map(agent => ({
|
|
110
|
+
value: agent.id,
|
|
111
|
+
label: agent.name,
|
|
112
|
+
description: agent.description || ''
|
|
113
|
+
}))}
|
|
114
|
+
onChange={selectionChange}
|
|
115
|
+
defaultValue={this.selectedAgent?.id} />
|
|
116
|
+
<div className='agent-history'>
|
|
117
|
+
{this.renderHistory()}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected renderHistory(): React.ReactNode {
|
|
124
|
+
if (!this.selectedAgent) {
|
|
125
|
+
return <div className='theia-card no-content'>No agent selected.</div>;
|
|
126
|
+
}
|
|
127
|
+
const history = [...this.recordingService.getHistory(this.selectedAgent.id)];
|
|
128
|
+
if (history.length === 0) {
|
|
129
|
+
return <div className='theia-card no-content'>No history available for the selected agent '{this.selectedAgent.name}'.</div>;
|
|
130
|
+
}
|
|
131
|
+
if (!this.state.chronological) {
|
|
132
|
+
history.reverse();
|
|
133
|
+
}
|
|
134
|
+
return history.map(entry => <CommunicationCard key={entry.requestId} entry={entry} />);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
protected onClick(e: React.MouseEvent<HTMLDivElement>, agent: Agent): void {
|
|
138
|
+
e.stopPropagation();
|
|
139
|
+
this.selectAgent(agent);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public sortHistory(chronological: boolean): void {
|
|
143
|
+
this.state = { ...deepClone(this.state), chronological: chronological };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get isChronological(): boolean {
|
|
147
|
+
return this.state.chronological === true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
.agent-history-widget {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
.agent-history-widget .theia-select-component {
|
|
9
|
+
margin: 10px 0;
|
|
10
|
+
width: 80%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.agent-history {
|
|
14
|
+
width: calc(80% + 16px);
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.theia-card {
|
|
21
|
+
background-color: var(--theia-sideBar-background);
|
|
22
|
+
border: 1px solid var(--theia-sideBarSectionHeader-border);
|
|
23
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
24
|
+
padding: 15px;
|
|
25
|
+
margin: 10px 0;
|
|
26
|
+
width: 100%;
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.theia-card-meta {
|
|
31
|
+
display: flex;
|
|
32
|
+
justify-content: space-between;
|
|
33
|
+
font-size: 0.9em;
|
|
34
|
+
padding: var(--theia-ui-padding) 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.theia-card-content {
|
|
38
|
+
color: var(--theia-font-color);
|
|
39
|
+
margin-bottom: 10px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.theia-card-content h2 {
|
|
43
|
+
font-size: var(--theia-ui-font-size2);
|
|
44
|
+
}
|
|
45
|
+
.theia-card-content h3 {
|
|
46
|
+
font-size: var(--theia-ui-font-size1);
|
|
47
|
+
font-weight: bold;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.theia-card-content .theia-card-context h2 {
|
|
51
|
+
display: inline;
|
|
52
|
+
}
|
|
53
|
+
.theia-card-content .theia-card-context ul {
|
|
54
|
+
padding-inline-start: 20px;
|
|
55
|
+
}
|
|
56
|
+
.theia-card-content .theia-card-context pre {
|
|
57
|
+
overflow: scroll;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.theia-card-content p {
|
|
61
|
+
margin: var(--theia-ui-padding) 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.theia-card-request, .theia-card-response {
|
|
65
|
+
margin-bottom: 10px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.theia-card-request pre,
|
|
69
|
+
.theia-card-response pre {
|
|
70
|
+
font-family: monospace;
|
|
71
|
+
white-space: pre-wrap;
|
|
72
|
+
word-wrap: break-word;
|
|
73
|
+
background-color: var(--theia-sideBar-background);
|
|
74
|
+
margin: var(--theia-ui-padding) 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.theia-card-request-id,
|
|
78
|
+
.theia-card-session-id,
|
|
79
|
+
.theia-card-timestamp,
|
|
80
|
+
.theia-card-response-time {
|
|
81
|
+
flex: 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.theia-card-request-id,
|
|
85
|
+
.theia-card-timestamp {
|
|
86
|
+
text-align: left;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.theia-card-session-id,
|
|
90
|
+
.theia-card-response-time {
|
|
91
|
+
text-align: right;
|
|
92
|
+
}
|