@theia/ai-chat 1.60.0-next.47 → 1.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-frontend-module.js +4 -0
- package/lib/browser/ai-chat-frontend-module.js.map +1 -1
- package/lib/browser/file-chat-variable-contribution.d.ts +1 -1
- package/lib/browser/file-chat-variable-contribution.d.ts.map +1 -1
- package/lib/browser/file-chat-variable-contribution.js +4 -3
- package/lib/browser/file-chat-variable-contribution.js.map +1 -1
- package/lib/common/chat-agents.d.ts +8 -19
- package/lib/common/chat-agents.d.ts.map +1 -1
- package/lib/common/chat-agents.js +59 -35
- package/lib/common/chat-agents.js.map +1 -1
- package/lib/common/chat-model.d.ts +41 -4
- package/lib/common/chat-model.d.ts.map +1 -1
- package/lib/common/chat-model.js +89 -1
- package/lib/common/chat-model.js.map +1 -1
- package/lib/common/chat-request-parser.spec.js +10 -2
- package/lib/common/chat-request-parser.spec.js.map +1 -1
- package/lib/common/chat-service.d.ts +20 -4
- package/lib/common/chat-service.d.ts.map +1 -1
- package/lib/common/chat-service.js +52 -5
- package/lib/common/chat-service.js.map +1 -1
- package/lib/common/chat-session-naming-service.d.ts +26 -0
- package/lib/common/chat-session-naming-service.d.ts.map +1 -0
- package/lib/common/chat-session-naming-service.js +132 -0
- package/lib/common/chat-session-naming-service.js.map +1 -0
- package/package.json +11 -11
- package/src/browser/ai-chat-frontend-module.ts +5 -0
- package/src/browser/file-chat-variable-contribution.ts +5 -3
- package/src/common/chat-agents.ts +85 -69
- package/src/common/chat-model.ts +115 -5
- package/src/common/chat-request-parser.spec.ts +10 -2
- package/src/common/chat-service.ts +63 -5
- package/src/common/chat-session-naming-service.ts +135 -0
- package/lib/common/chat-history-entry.d.ts +0 -7
- package/lib/common/chat-history-entry.d.ts.map +0 -1
- package/lib/common/chat-history-entry.js +0 -42
- package/lib/common/chat-history-entry.js.map +0 -1
- package/src/common/chat-history-entry.ts +0 -47
package/src/common/chat-model.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markd
|
|
|
24
24
|
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
25
25
|
import { ChatAgentLocation } from './chat-agents';
|
|
26
26
|
import { ParsedChatRequest } from './parsed-chat-request';
|
|
27
|
-
import { AIVariableResolutionRequest, ResolvedAIContextVariable } from '@theia/ai-core';
|
|
27
|
+
import { AIVariableResolutionRequest, LanguageModelMessage, ResolvedAIContextVariable, TextMessage, ThinkingMessage, ToolResultMessage, ToolUseMessage } from '@theia/ai-core';
|
|
28
28
|
|
|
29
29
|
/**********************
|
|
30
30
|
* INTERFACES AND TYPE GUARDS
|
|
@@ -95,6 +95,7 @@ export interface ChatModel {
|
|
|
95
95
|
readonly location: ChatAgentLocation;
|
|
96
96
|
readonly changeSet?: ChangeSet;
|
|
97
97
|
readonly context: ChatContextManager;
|
|
98
|
+
readonly settings?: { [key: string]: unknown };
|
|
98
99
|
getRequests(): ChatRequestModel[];
|
|
99
100
|
isEmpty(): boolean;
|
|
100
101
|
}
|
|
@@ -197,6 +198,7 @@ export interface ChatResponseContent {
|
|
|
197
198
|
asString?(): string | undefined;
|
|
198
199
|
asDisplayString?(): string | undefined;
|
|
199
200
|
merge?(nextChatResponseContent: ChatResponseContent): boolean;
|
|
201
|
+
toLanguageModelMessage?(): LanguageModelMessage | LanguageModelMessage[];
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
export namespace ChatResponseContent {
|
|
@@ -223,6 +225,11 @@ export namespace ChatResponseContent {
|
|
|
223
225
|
): obj is Required<Pick<ChatResponseContent, 'merge'>> & ChatResponseContent {
|
|
224
226
|
return typeof obj.merge === 'function';
|
|
225
227
|
}
|
|
228
|
+
export function hasToLanguageModelMessage(
|
|
229
|
+
obj: ChatResponseContent
|
|
230
|
+
): obj is Required<Pick<ChatResponseContent, 'toLanguageModelMessage'>> & ChatResponseContent {
|
|
231
|
+
return typeof obj.toLanguageModelMessage === 'function';
|
|
232
|
+
}
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
export interface TextChatResponseContent
|
|
@@ -250,7 +257,7 @@ export interface CodeChatResponseContent
|
|
|
250
257
|
location?: Location;
|
|
251
258
|
}
|
|
252
259
|
|
|
253
|
-
export interface HorizontalLayoutChatResponseContent extends
|
|
260
|
+
export interface HorizontalLayoutChatResponseContent extends ChatResponseContent {
|
|
254
261
|
kind: 'horizontal';
|
|
255
262
|
content: ChatResponseContent[];
|
|
256
263
|
}
|
|
@@ -264,6 +271,13 @@ export interface ToolCallChatResponseContent extends Required<ChatResponseConten
|
|
|
264
271
|
result?: string;
|
|
265
272
|
}
|
|
266
273
|
|
|
274
|
+
export interface ThinkingChatResponseContent
|
|
275
|
+
extends Required<ChatResponseContent> {
|
|
276
|
+
kind: 'thinking';
|
|
277
|
+
content: string;
|
|
278
|
+
signature: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
267
281
|
export interface Location {
|
|
268
282
|
uri: URI;
|
|
269
283
|
position: Position;
|
|
@@ -389,6 +403,17 @@ export namespace ErrorChatResponseContent {
|
|
|
389
403
|
}
|
|
390
404
|
}
|
|
391
405
|
|
|
406
|
+
export namespace ThinkingChatResponseContent {
|
|
407
|
+
export function is(obj: unknown): obj is ThinkingChatResponseContent {
|
|
408
|
+
return (
|
|
409
|
+
ChatResponseContent.is(obj) &&
|
|
410
|
+
obj.kind === 'thinking' &&
|
|
411
|
+
'content' in obj &&
|
|
412
|
+
typeof obj.content === 'string'
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
392
417
|
export type QuestionResponseHandler = (
|
|
393
418
|
selectedOption: { text: string, value?: string },
|
|
394
419
|
) => void;
|
|
@@ -498,6 +523,7 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
498
523
|
protected _id: string;
|
|
499
524
|
protected _changeSet?: ChangeSetImpl;
|
|
500
525
|
protected readonly _contextManager = new ChatContextManagerImpl();
|
|
526
|
+
protected _settings: { [key: string]: unknown };
|
|
501
527
|
|
|
502
528
|
constructor(public readonly location = ChatAgentLocation.Panel) {
|
|
503
529
|
// TODO accept serialized data as a parameter to restore a previously saved ChatModel
|
|
@@ -526,6 +552,14 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
526
552
|
return this._contextManager;
|
|
527
553
|
}
|
|
528
554
|
|
|
555
|
+
get settings(): { [key: string]: unknown } {
|
|
556
|
+
return this._settings;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
setSettings(settings: { [key: string]: unknown }): void {
|
|
560
|
+
this._settings = settings;
|
|
561
|
+
}
|
|
562
|
+
|
|
529
563
|
setChangeSet(changeSet: ChangeSetImpl | undefined): void {
|
|
530
564
|
if (!changeSet) {
|
|
531
565
|
return this.removeChangeSet();
|
|
@@ -714,8 +748,8 @@ export class MutableChatRequestModel implements ChatRequestModel {
|
|
|
714
748
|
this._data[key] = value;
|
|
715
749
|
}
|
|
716
750
|
|
|
717
|
-
getDataByKey(key: string):
|
|
718
|
-
return this._data[key];
|
|
751
|
+
getDataByKey<T = unknown>(key: string): T {
|
|
752
|
+
return this._data[key] as T;
|
|
719
753
|
}
|
|
720
754
|
|
|
721
755
|
get id(): string {
|
|
@@ -785,6 +819,57 @@ export class TextChatResponseContentImpl implements TextChatResponseContent {
|
|
|
785
819
|
this._content += nextChatResponseContent.content;
|
|
786
820
|
return true;
|
|
787
821
|
}
|
|
822
|
+
toLanguageModelMessage(): TextMessage {
|
|
823
|
+
return {
|
|
824
|
+
actor: 'ai',
|
|
825
|
+
type: 'text',
|
|
826
|
+
text: this.content
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
export class ThinkingChatResponseContentImpl implements ThinkingChatResponseContent {
|
|
831
|
+
readonly kind = 'thinking';
|
|
832
|
+
protected _content: string;
|
|
833
|
+
protected _signature: string;
|
|
834
|
+
|
|
835
|
+
constructor(content: string, signature: string) {
|
|
836
|
+
this._content = content;
|
|
837
|
+
this._signature = signature;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
get content(): string {
|
|
841
|
+
return this._content;
|
|
842
|
+
}
|
|
843
|
+
get signature(): string {
|
|
844
|
+
return this._signature;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
asString(): string {
|
|
848
|
+
return JSON.stringify({
|
|
849
|
+
type: 'thinking',
|
|
850
|
+
thinking: this.content,
|
|
851
|
+
signature: this.signature
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
asDisplayString(): string | undefined {
|
|
856
|
+
return `<Thinking>${this.content}</Thinking>`;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
merge(nextChatResponseContent: ThinkingChatResponseContent): boolean {
|
|
860
|
+
this._content += nextChatResponseContent.content;
|
|
861
|
+
this._signature += nextChatResponseContent.signature;
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
toLanguageModelMessage(): ThinkingMessage {
|
|
866
|
+
return {
|
|
867
|
+
actor: 'ai',
|
|
868
|
+
type: 'thinking',
|
|
869
|
+
thinking: this.content,
|
|
870
|
+
signature: this.signature
|
|
871
|
+
};
|
|
872
|
+
}
|
|
788
873
|
}
|
|
789
874
|
|
|
790
875
|
export class MarkdownChatResponseContentImpl implements MarkdownChatResponseContent {
|
|
@@ -811,6 +896,14 @@ export class MarkdownChatResponseContentImpl implements MarkdownChatResponseCont
|
|
|
811
896
|
this._content.appendMarkdown(nextChatResponseContent.content.value);
|
|
812
897
|
return true;
|
|
813
898
|
}
|
|
899
|
+
|
|
900
|
+
toLanguageModelMessage(): TextMessage {
|
|
901
|
+
return {
|
|
902
|
+
actor: 'ai',
|
|
903
|
+
type: 'text',
|
|
904
|
+
text: this.content.value
|
|
905
|
+
};
|
|
906
|
+
}
|
|
814
907
|
}
|
|
815
908
|
|
|
816
909
|
export class InformationalChatResponseContentImpl implements InformationalChatResponseContent {
|
|
@@ -911,6 +1004,7 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
911
1004
|
asDisplayString(): string {
|
|
912
1005
|
return `Tool call: ${this._name}(${this._arguments ?? ''})`;
|
|
913
1006
|
}
|
|
1007
|
+
|
|
914
1008
|
merge(nextChatResponseContent: ToolCallChatResponseContent): boolean {
|
|
915
1009
|
if (nextChatResponseContent.id === this.id) {
|
|
916
1010
|
this._finished = nextChatResponseContent.finished;
|
|
@@ -926,6 +1020,22 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
926
1020
|
this._arguments += `${nextChatResponseContent.arguments}`;
|
|
927
1021
|
return true;
|
|
928
1022
|
}
|
|
1023
|
+
|
|
1024
|
+
toLanguageModelMessage(): [ToolUseMessage, ToolResultMessage] {
|
|
1025
|
+
return [{
|
|
1026
|
+
actor: 'ai',
|
|
1027
|
+
type: 'tool_use',
|
|
1028
|
+
id: this.id ?? '',
|
|
1029
|
+
input: (this.arguments && JSON.parse(this.arguments)) ?? undefined,
|
|
1030
|
+
name: this.name ?? ''
|
|
1031
|
+
}, {
|
|
1032
|
+
actor: 'user',
|
|
1033
|
+
type: 'tool_result',
|
|
1034
|
+
tool_use_id: this.id ?? '',
|
|
1035
|
+
content: this.result,
|
|
1036
|
+
name: this.name ?? ''
|
|
1037
|
+
}];
|
|
1038
|
+
}
|
|
929
1039
|
}
|
|
930
1040
|
|
|
931
1041
|
export const COMMAND_CHAT_RESPONSE_COMMAND: Command = {
|
|
@@ -1097,7 +1207,7 @@ class ChatResponseImpl implements ChatResponse {
|
|
|
1097
1207
|
}
|
|
1098
1208
|
}
|
|
1099
1209
|
|
|
1100
|
-
class MutableChatResponseModel implements ChatResponseModel {
|
|
1210
|
+
export class MutableChatResponseModel implements ChatResponseModel {
|
|
1101
1211
|
protected readonly _onDidChangeEmitter = new Emitter<void>();
|
|
1102
1212
|
onDidChange: Event<void> = this._onDidChangeEmitter.event;
|
|
1103
1213
|
|
|
@@ -137,12 +137,20 @@ describe('ChatRequestParserImpl', () => {
|
|
|
137
137
|
const testTool1: ToolRequest = {
|
|
138
138
|
id: 'testTool1',
|
|
139
139
|
name: 'Test Tool 1',
|
|
140
|
-
handler: async () => undefined
|
|
140
|
+
handler: async () => undefined,
|
|
141
|
+
parameters: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {}
|
|
144
|
+
},
|
|
141
145
|
};
|
|
142
146
|
const testTool2: ToolRequest = {
|
|
143
147
|
id: 'testTool2',
|
|
144
148
|
name: 'Test Tool 2',
|
|
145
|
-
handler: async () => undefined
|
|
149
|
+
handler: async () => undefined,
|
|
150
|
+
parameters: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
properties: {}
|
|
153
|
+
},
|
|
146
154
|
};
|
|
147
155
|
// Configure the tool registry to return our test tools
|
|
148
156
|
toolInvocationRegistry.getFunction.withArgs(testTool1.id).returns(testTool1);
|
|
@@ -33,9 +33,11 @@ import {
|
|
|
33
33
|
ChatResponseModel,
|
|
34
34
|
ErrorChatResponseModel,
|
|
35
35
|
ChatContext,
|
|
36
|
+
MutableChatRequestModel,
|
|
36
37
|
} from './chat-model';
|
|
37
38
|
import { ChatRequestParser } from './chat-request-parser';
|
|
38
39
|
import { ParsedChatRequest, ParsedChatRequestAgentPart } from './parsed-chat-request';
|
|
40
|
+
import { ChatSessionNamingService } from './chat-session-naming-service';
|
|
39
41
|
|
|
40
42
|
export interface ChatRequestInvocation {
|
|
41
43
|
/**
|
|
@@ -55,16 +57,43 @@ export interface ChatRequestInvocation {
|
|
|
55
57
|
export interface ChatSession {
|
|
56
58
|
id: string;
|
|
57
59
|
title?: string;
|
|
60
|
+
lastInteraction?: Date;
|
|
58
61
|
model: ChatModel;
|
|
59
62
|
isActive: boolean;
|
|
60
63
|
pinnedAgent?: ChatAgent;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
export interface ActiveSessionChangedEvent {
|
|
67
|
+
type: 'activeChange';
|
|
64
68
|
sessionId: string | undefined;
|
|
65
69
|
focus?: boolean;
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
export function isActiveSessionChangedEvent(obj: unknown): obj is ActiveSessionChangedEvent {
|
|
73
|
+
// eslint-disable-next-line no-null/no-null
|
|
74
|
+
return typeof obj === 'object' && obj !== null && 'type' in obj && obj.type === 'activeChange';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface SessionCreatedEvent {
|
|
78
|
+
type: 'created';
|
|
79
|
+
sessionId: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isSessionCreatedEvent(obj: unknown): obj is SessionCreatedEvent {
|
|
83
|
+
// eslint-disable-next-line no-null/no-null
|
|
84
|
+
return typeof obj === 'object' && obj !== null && 'type' in obj && obj.type === 'created';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface SessionDeletedEvent {
|
|
88
|
+
type: 'deleted';
|
|
89
|
+
sessionId: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function isSessionDeletedEvent(obj: unknown): obj is SessionDeletedEvent {
|
|
93
|
+
// eslint-disable-next-line no-null/no-null
|
|
94
|
+
return typeof obj === 'object' && obj !== null && 'type' in obj && obj.type === 'deleted';
|
|
95
|
+
}
|
|
96
|
+
|
|
68
97
|
export interface SessionOptions {
|
|
69
98
|
focus?: boolean;
|
|
70
99
|
}
|
|
@@ -90,7 +119,7 @@ export type PinChatAgent = boolean;
|
|
|
90
119
|
|
|
91
120
|
export const ChatService = Symbol('ChatService');
|
|
92
121
|
export interface ChatService {
|
|
93
|
-
|
|
122
|
+
onSessionEvent: Event<ActiveSessionChangedEvent | SessionCreatedEvent | SessionDeletedEvent>
|
|
94
123
|
|
|
95
124
|
getSession(id: string): ChatSession | undefined;
|
|
96
125
|
getSessions(): ChatSession[];
|
|
@@ -115,8 +144,8 @@ interface ChatSessionInternal extends ChatSession {
|
|
|
115
144
|
|
|
116
145
|
@injectable()
|
|
117
146
|
export class ChatServiceImpl implements ChatService {
|
|
118
|
-
protected readonly
|
|
119
|
-
|
|
147
|
+
protected readonly onSessionEventEmitter = new Emitter<ActiveSessionChangedEvent | SessionCreatedEvent | SessionDeletedEvent>();
|
|
148
|
+
onSessionEvent = this.onSessionEventEmitter.event;
|
|
120
149
|
|
|
121
150
|
@inject(ChatAgentService)
|
|
122
151
|
protected chatAgentService: ChatAgentService;
|
|
@@ -127,6 +156,9 @@ export class ChatServiceImpl implements ChatService {
|
|
|
127
156
|
@inject(FallbackChatAgentId) @optional()
|
|
128
157
|
protected fallbackChatAgentId: FallbackChatAgentId | undefined;
|
|
129
158
|
|
|
159
|
+
@inject(ChatSessionNamingService) @optional()
|
|
160
|
+
protected chatSessionNamingService: ChatSessionNamingService | undefined;
|
|
161
|
+
|
|
130
162
|
@inject(PinChatAgent) @optional()
|
|
131
163
|
protected pinChatAgent: boolean | undefined;
|
|
132
164
|
|
|
@@ -159,6 +191,7 @@ export class ChatServiceImpl implements ChatService {
|
|
|
159
191
|
};
|
|
160
192
|
this._sessions.push(session);
|
|
161
193
|
this.setActiveSession(session.id, options);
|
|
194
|
+
this.onSessionEventEmitter.fire({ type: 'created', sessionId: session.id });
|
|
162
195
|
return session;
|
|
163
196
|
}
|
|
164
197
|
|
|
@@ -172,13 +205,14 @@ export class ChatServiceImpl implements ChatService {
|
|
|
172
205
|
}
|
|
173
206
|
session.model.dispose();
|
|
174
207
|
this._sessions.splice(sessionIndex, 1);
|
|
208
|
+
this.onSessionEventEmitter.fire({ type: 'deleted', sessionId: sessionId });
|
|
175
209
|
}
|
|
176
210
|
|
|
177
211
|
setActiveSession(sessionId: string | undefined, options?: SessionOptions): void {
|
|
178
212
|
this._sessions.forEach(session => {
|
|
179
213
|
session.isActive = session.id === sessionId;
|
|
180
214
|
});
|
|
181
|
-
this.
|
|
215
|
+
this.onSessionEventEmitter.fire({ type: 'activeChange', sessionId: sessionId, ...options });
|
|
182
216
|
}
|
|
183
217
|
|
|
184
218
|
async sendRequest(
|
|
@@ -189,7 +223,6 @@ export class ChatServiceImpl implements ChatService {
|
|
|
189
223
|
if (!session) {
|
|
190
224
|
return undefined;
|
|
191
225
|
}
|
|
192
|
-
session.title = request.text;
|
|
193
226
|
|
|
194
227
|
const resolutionContext: ChatSessionContext = { model: session.model };
|
|
195
228
|
const resolvedContext = await this.resolveChatContext(session.model.context.getVariables(), resolutionContext);
|
|
@@ -208,6 +241,7 @@ export class ChatServiceImpl implements ChatService {
|
|
|
208
241
|
}
|
|
209
242
|
|
|
210
243
|
const requestModel = session.model.addRequest(parsedRequest, agent?.id, resolvedContext);
|
|
244
|
+
this.updateSessionMetadata(session, requestModel);
|
|
211
245
|
resolutionContext.request = requestModel;
|
|
212
246
|
|
|
213
247
|
let resolveResponseCreated: (responseModel: ChatResponseModel) => void;
|
|
@@ -241,6 +275,30 @@ export class ChatServiceImpl implements ChatService {
|
|
|
241
275
|
return invocation;
|
|
242
276
|
}
|
|
243
277
|
|
|
278
|
+
protected updateSessionMetadata(session: ChatSessionInternal, request: MutableChatRequestModel): void {
|
|
279
|
+
session.lastInteraction = new Date();
|
|
280
|
+
if (session.title) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const requestText = request.request.displayText ?? request.request.text;
|
|
284
|
+
session.title = requestText;
|
|
285
|
+
if (this.chatSessionNamingService) {
|
|
286
|
+
const otherSessionNames = this._sessions.map(s => s.title).filter((title): title is string => title !== undefined);
|
|
287
|
+
const namingService = this.chatSessionNamingService;
|
|
288
|
+
let didGenerateName = false;
|
|
289
|
+
request.response.onDidChange(() => {
|
|
290
|
+
if (request.response.isComplete && !didGenerateName) {
|
|
291
|
+
namingService.generateChatSessionName(session, otherSessionNames).then(name => {
|
|
292
|
+
if (name && session.title === requestText) {
|
|
293
|
+
session.title = name;
|
|
294
|
+
}
|
|
295
|
+
didGenerateName = true;
|
|
296
|
+
}).catch(error => this.logger.error('Failed to generate chat session name', error));
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
244
302
|
protected async resolveChatContext(
|
|
245
303
|
resolutionRequests: readonly AIVariableResolutionRequest[],
|
|
246
304
|
context: ChatSessionContext,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
Agent,
|
|
19
|
+
AgentService,
|
|
20
|
+
CommunicationRecordingService,
|
|
21
|
+
getTextOfResponse,
|
|
22
|
+
LanguageModelRegistry,
|
|
23
|
+
LanguageModelRequest,
|
|
24
|
+
LanguageModelRequirement,
|
|
25
|
+
PromptService,
|
|
26
|
+
PromptTemplate
|
|
27
|
+
} from '@theia/ai-core';
|
|
28
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
29
|
+
import { ChatSession } from './chat-service';
|
|
30
|
+
import { generateUuid } from '@theia/core';
|
|
31
|
+
|
|
32
|
+
const CHAT_SESSION_NAMING_PROMPT = {
|
|
33
|
+
id: 'chat-session-naming-prompt',
|
|
34
|
+
template: '{{!-- Made improvements or adaptations to this prompt template? We\'d love for you to share it with the community! Contribute back here: ' +
|
|
35
|
+
'https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}\n\n' +
|
|
36
|
+
'Provide a short and descriptive name for the given AI chat conversation of an AI-powered tool based on the conversation below.\n\n' +
|
|
37
|
+
'The purpose of the name is for users to recognize the chat conversation easily in a list of conversations. ' +
|
|
38
|
+
'Use the same language for the chat conversation name as used in the provided conversation, if in doubt default to English. ' +
|
|
39
|
+
'Start the chat conversation name with an upper-case letter. ' +
|
|
40
|
+
'Below we also provide the already existing other conversation names, make sure your suggestion for a name is unique with respect to the existing ones.\n\n' +
|
|
41
|
+
'IMPORTANT: Your answer MUST ONLY CONTAIN THE PROPOSED NAME and must not be preceded or succeeded with any other text.' +
|
|
42
|
+
'\n\nOther session names:\n{{listOfSessionNames}}' +
|
|
43
|
+
'\n\nConversation:\n{{conversation}}',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
@injectable()
|
|
47
|
+
export class ChatSessionNamingService {
|
|
48
|
+
@inject(AgentService) protected agentService: AgentService;
|
|
49
|
+
async generateChatSessionName(chatSession: ChatSession, otherNames: string[]): Promise<string | undefined> {
|
|
50
|
+
const chatSessionNamingAgent = this.agentService.getAgents().find(agent => ChatSessionNamingAgent.ID === agent.id);
|
|
51
|
+
if (!(chatSessionNamingAgent instanceof ChatSessionNamingAgent)) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return chatSessionNamingAgent.generateChatSessionName(chatSession, otherNames);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@injectable()
|
|
59
|
+
export class ChatSessionNamingAgent implements Agent {
|
|
60
|
+
static ID = 'chat-session-naming-agent';
|
|
61
|
+
id = ChatSessionNamingAgent.ID;
|
|
62
|
+
name = 'Chat Session Naming';
|
|
63
|
+
description = 'Agent for generating chat session names';
|
|
64
|
+
variables = [];
|
|
65
|
+
promptTemplates: PromptTemplate[] = [CHAT_SESSION_NAMING_PROMPT];
|
|
66
|
+
languageModelRequirements: LanguageModelRequirement[] = [{
|
|
67
|
+
purpose: 'chat-session-naming',
|
|
68
|
+
identifier: 'openai/gpt-4o-mini',
|
|
69
|
+
}];
|
|
70
|
+
agentSpecificVariables = [
|
|
71
|
+
{ name: 'conversation', usedInPrompt: true, description: 'The content of the chat conversation.' },
|
|
72
|
+
{ name: 'listOfSessionNames', usedInPrompt: true, description: 'The list of existing session names.' }
|
|
73
|
+
];
|
|
74
|
+
functions = [];
|
|
75
|
+
|
|
76
|
+
@inject(LanguageModelRegistry)
|
|
77
|
+
protected readonly lmRegistry: LanguageModelRegistry;
|
|
78
|
+
|
|
79
|
+
@inject(CommunicationRecordingService)
|
|
80
|
+
protected recordingService: CommunicationRecordingService;
|
|
81
|
+
|
|
82
|
+
@inject(PromptService)
|
|
83
|
+
protected promptService: PromptService;
|
|
84
|
+
|
|
85
|
+
async generateChatSessionName(chatSession: ChatSession, otherNames: string[]): Promise<string> {
|
|
86
|
+
const lm = await this.lmRegistry.selectLanguageModel({ agent: this.id, ...this.languageModelRequirements[0] });
|
|
87
|
+
if (!lm) {
|
|
88
|
+
throw new Error('No language model found for chat session naming');
|
|
89
|
+
}
|
|
90
|
+
if (chatSession.model.getRequests().length < 1) {
|
|
91
|
+
throw new Error('No chat request available to generate chat session name');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const conversation = chatSession.model.getRequests()
|
|
95
|
+
.map(req => `<user>${req.request.text}</user>` +
|
|
96
|
+
(req.response.response ? `<assistant>${req.response.response.asString()}</assistant>` : ''))
|
|
97
|
+
.join('\n\n');
|
|
98
|
+
const listOfSessionNames = otherNames.map(name => name).join(', ');
|
|
99
|
+
|
|
100
|
+
const prompt = await this.promptService.getPrompt(CHAT_SESSION_NAMING_PROMPT.id, { conversation, listOfSessionNames });
|
|
101
|
+
const message = prompt?.text;
|
|
102
|
+
if (!message) {
|
|
103
|
+
throw new Error('Unable to create prompt message for generating chat session name');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const request: LanguageModelRequest = {
|
|
107
|
+
messages: [{
|
|
108
|
+
actor: 'user',
|
|
109
|
+
text: message,
|
|
110
|
+
type: 'text'
|
|
111
|
+
}]
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const sessionId = generateUuid();
|
|
115
|
+
const requestId = generateUuid();
|
|
116
|
+
this.recordingService.recordRequest({
|
|
117
|
+
agentId: this.id,
|
|
118
|
+
sessionId,
|
|
119
|
+
requestId,
|
|
120
|
+
...request
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const result = await lm.request(request);
|
|
124
|
+
const response = await getTextOfResponse(result);
|
|
125
|
+
this.recordingService.recordResponse({
|
|
126
|
+
agentId: this.id,
|
|
127
|
+
sessionId,
|
|
128
|
+
requestId,
|
|
129
|
+
response: [{ actor: 'ai', text: response, type: 'text' }]
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return response.replace(/\s+/g, ' ').substring(0, 100);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { CommunicationRequestEntryParam, CommunicationResponseEntryParam } from '@theia/ai-core/lib/common/communication-recording-service';
|
|
2
|
-
import { ChatRequestModel } from './chat-model';
|
|
3
|
-
export declare namespace ChatHistoryEntry {
|
|
4
|
-
function fromRequest(agentId: string, request: ChatRequestModel, args?: Partial<CommunicationRequestEntryParam>): CommunicationRequestEntryParam;
|
|
5
|
-
function fromResponse(agentId: string, request: ChatRequestModel, args?: Partial<CommunicationResponseEntryParam>): CommunicationResponseEntryParam;
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=chat-history-entry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-history-entry.d.ts","sourceRoot":"","sources":["../../src/common/chat-history-entry.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,8BAA8B,EAAE,+BAA+B,EAAE,MAAM,2DAA2D,CAAC;AAC5I,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,yBAAiB,gBAAgB,CAAC;IAC9B,SAAgB,WAAW,CACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,gBAAgB,EACzB,IAAI,GAAE,OAAO,CAAC,8BAA8B,CAAM,GACnD,8BAA8B,CAQhC;IACD,SAAgB,YAAY,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,gBAAgB,EACzB,IAAI,GAAE,OAAO,CAAC,+BAA+B,CAAM,GACpD,+BAA+B,CAQjC;CACJ"}
|
|
@@ -1,42 +0,0 @@
|
|
|
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
|
-
exports.ChatHistoryEntry = void 0;
|
|
19
|
-
var ChatHistoryEntry;
|
|
20
|
-
(function (ChatHistoryEntry) {
|
|
21
|
-
function fromRequest(agentId, request, args = {}) {
|
|
22
|
-
return {
|
|
23
|
-
agentId: agentId,
|
|
24
|
-
sessionId: request.session.id,
|
|
25
|
-
requestId: request.id,
|
|
26
|
-
request: request.request.text,
|
|
27
|
-
...args,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
ChatHistoryEntry.fromRequest = fromRequest;
|
|
31
|
-
function fromResponse(agentId, request, args = {}) {
|
|
32
|
-
return {
|
|
33
|
-
agentId: agentId,
|
|
34
|
-
sessionId: request.session.id,
|
|
35
|
-
requestId: request.id,
|
|
36
|
-
response: request.response.response.asDisplayString(),
|
|
37
|
-
...args,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
ChatHistoryEntry.fromResponse = fromResponse;
|
|
41
|
-
})(ChatHistoryEntry || (exports.ChatHistoryEntry = ChatHistoryEntry = {}));
|
|
42
|
-
//# sourceMappingURL=chat-history-entry.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-history-entry.js","sourceRoot":"","sources":["../../src/common/chat-history-entry.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;;;AAKhF,IAAiB,gBAAgB,CA2BhC;AA3BD,WAAiB,gBAAgB;IAC7B,SAAgB,WAAW,CACvB,OAAe,EACf,OAAyB,EACzB,OAAgD,EAAE;QAElD,OAAO;YACH,OAAO,EAAE,OAAO;YAChB,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YAC7B,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI;YAC7B,GAAG,IAAI;SACV,CAAC;IACN,CAAC;IAZe,4BAAW,cAY1B,CAAA;IACD,SAAgB,YAAY,CACxB,OAAe,EACf,OAAyB,EACzB,OAAiD,EAAE;QAEnD,OAAO;YACH,OAAO,EAAE,OAAO;YAChB,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YAC7B,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe,EAAE;YACrD,GAAG,IAAI;SACV,CAAC;IACN,CAAC;IAZe,6BAAY,eAY3B,CAAA;AACL,CAAC,EA3BgB,gBAAgB,gCAAhB,gBAAgB,QA2BhC"}
|
|
@@ -1,47 +0,0 @@
|
|
|
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
|
-
|
|
17
|
-
import { CommunicationRequestEntryParam, CommunicationResponseEntryParam } from '@theia/ai-core/lib/common/communication-recording-service';
|
|
18
|
-
import { ChatRequestModel } from './chat-model';
|
|
19
|
-
|
|
20
|
-
export namespace ChatHistoryEntry {
|
|
21
|
-
export function fromRequest(
|
|
22
|
-
agentId: string,
|
|
23
|
-
request: ChatRequestModel,
|
|
24
|
-
args: Partial<CommunicationRequestEntryParam> = {}
|
|
25
|
-
): CommunicationRequestEntryParam {
|
|
26
|
-
return {
|
|
27
|
-
agentId: agentId,
|
|
28
|
-
sessionId: request.session.id,
|
|
29
|
-
requestId: request.id,
|
|
30
|
-
request: request.request.text,
|
|
31
|
-
...args,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export function fromResponse(
|
|
35
|
-
agentId: string,
|
|
36
|
-
request: ChatRequestModel,
|
|
37
|
-
args: Partial<CommunicationResponseEntryParam> = {}
|
|
38
|
-
): CommunicationResponseEntryParam {
|
|
39
|
-
return {
|
|
40
|
-
agentId: agentId,
|
|
41
|
-
sessionId: request.session.id,
|
|
42
|
-
requestId: request.id,
|
|
43
|
-
response: request.response.response.asDisplayString(),
|
|
44
|
-
...args,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|