@theia/ai-chat 1.55.1 → 1.57.0-next.112

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.
Files changed (88) hide show
  1. package/README.md +2 -1
  2. package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
  3. package/lib/browser/ai-chat-frontend-module.js +14 -4
  4. package/lib/browser/ai-chat-frontend-module.js.map +1 -1
  5. package/lib/browser/change-set-file-element.d.ts +44 -0
  6. package/lib/browser/change-set-file-element.d.ts.map +1 -0
  7. package/lib/browser/change-set-file-element.js +113 -0
  8. package/lib/browser/change-set-file-element.js.map +1 -0
  9. package/lib/browser/change-set-file-resource.d.ts +13 -0
  10. package/lib/browser/change-set-file-resource.d.ts.map +1 -0
  11. package/lib/browser/change-set-file-resource.js +73 -0
  12. package/lib/browser/change-set-file-resource.js.map +1 -0
  13. package/lib/browser/change-set-file-service.d.ts +26 -0
  14. package/lib/browser/change-set-file-service.d.ts.map +1 -0
  15. package/lib/browser/change-set-file-service.js +139 -0
  16. package/lib/browser/change-set-file-service.js.map +1 -0
  17. package/lib/common/chat-agents.d.ts +16 -9
  18. package/lib/common/chat-agents.d.ts.map +1 -1
  19. package/lib/common/chat-agents.js +51 -67
  20. package/lib/common/chat-agents.js.map +1 -1
  21. package/lib/common/chat-history-entry.d.ts +7 -0
  22. package/lib/common/chat-history-entry.d.ts.map +1 -0
  23. package/lib/common/chat-history-entry.js +42 -0
  24. package/lib/common/chat-history-entry.js.map +1 -0
  25. package/lib/common/chat-model-util.d.ts +7 -0
  26. package/lib/common/chat-model-util.d.ts.map +1 -0
  27. package/lib/common/chat-model-util.js +50 -0
  28. package/lib/common/chat-model-util.js.map +1 -0
  29. package/lib/common/chat-model.d.ts +176 -7
  30. package/lib/common/chat-model.d.ts.map +1 -1
  31. package/lib/common/chat-model.js +193 -9
  32. package/lib/common/chat-model.js.map +1 -1
  33. package/lib/common/chat-service.d.ts +6 -0
  34. package/lib/common/chat-service.d.ts.map +1 -1
  35. package/lib/common/chat-service.js +12 -0
  36. package/lib/common/chat-service.js.map +1 -1
  37. package/lib/common/chat-tool-request-service.d.ts +17 -0
  38. package/lib/common/chat-tool-request-service.d.ts.map +1 -0
  39. package/lib/common/chat-tool-request-service.js +52 -0
  40. package/lib/common/chat-tool-request-service.js.map +1 -0
  41. package/lib/common/command-chat-agents.d.ts.map +1 -1
  42. package/lib/common/command-chat-agents.js +4 -2
  43. package/lib/common/command-chat-agents.js.map +1 -1
  44. package/lib/common/index.d.ts +1 -0
  45. package/lib/common/index.d.ts.map +1 -1
  46. package/lib/common/index.js +1 -0
  47. package/lib/common/index.js.map +1 -1
  48. package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
  49. package/lib/common/orchestrator-chat-agent.js +16 -14
  50. package/lib/common/orchestrator-chat-agent.js.map +1 -1
  51. package/lib/common/parse-contents.d.ts +2 -2
  52. package/lib/common/parse-contents.d.ts.map +1 -1
  53. package/lib/common/parse-contents.js +4 -4
  54. package/lib/common/parse-contents.js.map +1 -1
  55. package/lib/common/parse-contents.spec.d.ts.map +1 -1
  56. package/lib/common/parse-contents.spec.js +14 -13
  57. package/lib/common/parse-contents.spec.js.map +1 -1
  58. package/lib/common/response-content-matcher.d.ts +3 -3
  59. package/lib/common/response-content-matcher.d.ts.map +1 -1
  60. package/lib/common/response-content-matcher.js +2 -2
  61. package/lib/common/response-content-matcher.js.map +1 -1
  62. package/lib/common/universal-chat-agent.d.ts +1 -0
  63. package/lib/common/universal-chat-agent.d.ts.map +1 -1
  64. package/lib/common/universal-chat-agent.js +10 -3
  65. package/lib/common/universal-chat-agent.js.map +1 -1
  66. package/package.json +10 -8
  67. package/src/browser/ai-chat-frontend-module.ts +17 -6
  68. package/src/browser/change-set-file-element.ts +137 -0
  69. package/src/browser/change-set-file-resource.ts +74 -0
  70. package/src/browser/change-set-file-service.ts +136 -0
  71. package/src/common/chat-agents.ts +56 -78
  72. package/src/common/chat-history-entry.ts +47 -0
  73. package/src/common/chat-model-util.ts +44 -0
  74. package/src/common/chat-model.ts +325 -14
  75. package/src/common/chat-service.ts +17 -0
  76. package/src/common/chat-tool-request-service.ts +59 -0
  77. package/src/common/command-chat-agents.ts +4 -2
  78. package/src/common/index.ts +1 -0
  79. package/src/common/orchestrator-chat-agent.ts +17 -14
  80. package/src/common/parse-contents.spec.ts +16 -14
  81. package/src/common/parse-contents.ts +5 -4
  82. package/src/common/response-content-matcher.ts +4 -3
  83. package/src/common/universal-chat-agent.ts +10 -2
  84. package/lib/common/o1-chat-agent.d.ts +0 -13
  85. package/lib/common/o1-chat-agent.d.ts.map +0 -1
  86. package/lib/common/o1-chat-agent.js +0 -45
  87. package/lib/common/o1-chat-agent.js.map +0 -1
  88. package/src/common/o1-chat-agent.ts +0 -51
@@ -0,0 +1,136 @@
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 { ILogger, UNTITLED_SCHEME, URI } from '@theia/core';
18
+ import { DiffUris, LabelProvider, OpenerService, open } from '@theia/core/lib/browser';
19
+ import { inject, injectable } from '@theia/core/shared/inversify';
20
+ import { EditorManager } from '@theia/editor/lib/browser';
21
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
22
+ import { MonacoWorkspace } from '@theia/monaco/lib/browser/monaco-workspace';
23
+ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
24
+ import { ChangeSetFileElement } from './change-set-file-element';
25
+
26
+ @injectable()
27
+ export class ChangeSetFileService {
28
+ @inject(ILogger)
29
+ protected readonly logger: ILogger;
30
+
31
+ @inject(WorkspaceService)
32
+ protected readonly wsService: WorkspaceService;
33
+
34
+ @inject(LabelProvider)
35
+ protected readonly labelProvider: LabelProvider;
36
+
37
+ @inject(OpenerService)
38
+ protected readonly openerService: OpenerService;
39
+
40
+ @inject(EditorManager)
41
+ protected readonly editorManager: EditorManager;
42
+
43
+ @inject(MonacoWorkspace)
44
+ protected readonly monacoWorkspace: MonacoWorkspace;
45
+
46
+ @inject(FileService)
47
+ protected readonly fileService: FileService;
48
+
49
+ async read(uri: URI): Promise<string | undefined> {
50
+ const exists = await this.fileService.exists(uri);
51
+ if (!exists) {
52
+ return undefined;
53
+ }
54
+ try {
55
+ const document = this.monacoWorkspace.getTextDocument(uri.toString());
56
+ if (document) {
57
+ return document.getText();
58
+ }
59
+ return (await this.fileService.readFile(uri)).value.toString();
60
+ } catch (error) {
61
+ this.logger.error('Failed to read original content of change set file element.', error);
62
+ return undefined;
63
+ }
64
+ }
65
+
66
+ getName(uri: URI): string {
67
+ return this.labelProvider.getName(uri);
68
+ }
69
+
70
+ getIcon(uri: URI): string | undefined {
71
+ return this.labelProvider.getIcon(uri);
72
+ }
73
+
74
+ getAdditionalInfo(uri: URI): string | undefined {
75
+ const wsUri = this.wsService.getWorkspaceRootUri(uri);
76
+ if (wsUri) {
77
+ const wsRelative = wsUri.relative(uri);
78
+ if (wsRelative?.hasDir) {
79
+ return `${wsRelative.dir.toString()}`;
80
+ }
81
+ return '';
82
+ }
83
+ return this.labelProvider.getLongName(uri.parent);
84
+ }
85
+
86
+ async open(element: ChangeSetFileElement): Promise<void> {
87
+ const exists = await this.fileService.exists(element.uri);
88
+ if (exists) {
89
+ await open(this.openerService, element.uri);
90
+ return;
91
+ }
92
+ await this.editorManager.open(element.changedUri, {
93
+ mode: 'reveal'
94
+ });
95
+ }
96
+
97
+ async openDiff(originalUri: URI, suggestedUri: URI): Promise<void> {
98
+ const exists = await this.fileService.exists(originalUri);
99
+ const openedUri = exists ? originalUri : originalUri.withScheme(UNTITLED_SCHEME);
100
+ // Currently we don't have a great way to show the suggestions in a diff editor with accept/reject buttons
101
+ // So we just use plain diffs with the suggestions as original and the current state as modified, so users can apply changes in their current state
102
+ // But this leads to wrong colors and wrong label (revert change instead of accept change)
103
+ const diffUri = DiffUris.encode(openedUri, suggestedUri,
104
+ `AI Changes: ${this.labelProvider.getName(originalUri)}`,
105
+ );
106
+ open(this.openerService, diffUri);
107
+ }
108
+
109
+ async delete(uri: URI): Promise<void> {
110
+ const exists = await this.fileService.exists(uri);
111
+ if (exists) {
112
+ await this.fileService.delete(uri);
113
+ }
114
+ }
115
+
116
+ async write(uri: URI, targetState: string): Promise<void> {
117
+ const exists = await this.fileService.exists(uri);
118
+ if (!exists) {
119
+ await this.fileService.create(uri, targetState);
120
+ }
121
+ await this.doWrite(uri, targetState);
122
+ }
123
+
124
+ protected async doWrite(uri: URI, text: string): Promise<void> {
125
+ const document = this.monacoWorkspace.getTextDocument(uri.toString());
126
+ if (document) {
127
+ await this.monacoWorkspace.applyBackgroundEdit(document, [{
128
+ range: document.textEditorModel.getFullModelRange(),
129
+ text
130
+ }], (editor, wasDirty) => editor === undefined || !wasDirty);
131
+ } else {
132
+ await this.fileService.write(uri, text);
133
+ }
134
+ }
135
+
136
+ }
@@ -38,12 +38,11 @@ import {
38
38
  LanguageModelStreamResponsePart,
39
39
  MessageActor,
40
40
  } from '@theia/ai-core/lib/common';
41
- import { CancellationToken, CancellationTokenSource, ContributionProvider, ILogger, isArray } from '@theia/core';
42
- import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
41
+ import { CancellationToken, ContributionProvider, ILogger, isArray } from '@theia/core';
42
+ import { inject, injectable, named, postConstruct, unmanaged } from '@theia/core/shared/inversify';
43
43
  import { ChatAgentService } from './chat-agent-service';
44
44
  import {
45
45
  ChatModel,
46
- ChatRequestModel,
47
46
  ChatRequestModelImpl,
48
47
  ChatResponseContent,
49
48
  ErrorChatResponseContentImpl,
@@ -52,6 +51,8 @@ import {
52
51
  } from './chat-model';
53
52
  import { findFirstMatch, parseContents } from './parse-contents';
54
53
  import { DefaultResponseContentFactory, ResponseContentMatcher, ResponseContentMatcherProvider } from './response-content-matcher';
54
+ import { ChatHistoryEntry } from './chat-history-entry';
55
+ import { ChatToolRequestService } from './chat-tool-request-service';
55
56
 
56
57
  /**
57
58
  * A conversation consists of a sequence of ChatMessages.
@@ -122,28 +123,35 @@ export abstract class AbstractChatAgent {
122
123
  @inject(LanguageModelRegistry) protected languageModelRegistry: LanguageModelRegistry;
123
124
  @inject(ILogger) protected logger: ILogger;
124
125
  @inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService;
126
+ @inject(ChatToolRequestService) protected chatToolRequestService: ChatToolRequestService;
125
127
  @inject(PromptService) protected promptService: PromptService;
126
128
 
127
129
  @inject(ContributionProvider) @named(ResponseContentMatcherProvider)
128
130
  protected contentMatcherProviders: ContributionProvider<ResponseContentMatcherProvider>;
131
+ protected additionalToolRequests: ToolRequest[] = [];
129
132
  protected contentMatchers: ResponseContentMatcher[] = [];
130
133
 
131
134
  @inject(DefaultResponseContentFactory)
132
135
  protected defaultContentFactory: DefaultResponseContentFactory;
133
136
 
134
137
  constructor(
135
- public id: string,
136
- public languageModelRequirements: LanguageModelRequirement[],
137
- protected defaultLanguageModelPurpose: string,
138
- public iconClass: string = 'codicon codicon-copilot',
139
- public locations: ChatAgentLocation[] = ChatAgentLocation.ALL,
140
- public tags: String[] = ['Chat'],
141
- public defaultLogging: boolean = true) {
138
+ @unmanaged() public id: string,
139
+ @unmanaged() public languageModelRequirements: LanguageModelRequirement[],
140
+ @unmanaged() protected defaultLanguageModelPurpose: string,
141
+ @unmanaged() public iconClass: string = 'codicon codicon-copilot',
142
+ @unmanaged() public locations: ChatAgentLocation[] = ChatAgentLocation.ALL,
143
+ @unmanaged() public tags: string[] = ['Chat'],
144
+ @unmanaged() public defaultLogging: boolean = true) {
142
145
  }
143
146
 
144
147
  @postConstruct()
145
148
  init(): void {
146
- this.contentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers);
149
+ this.initializeContentMatchers();
150
+ }
151
+
152
+ protected initializeContentMatchers(): void {
153
+ const contributedContentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers);
154
+ this.contentMatchers.push(...contributedContentMatchers);
147
155
  }
148
156
 
149
157
  async invoke(request: ChatRequestModelImpl): Promise<void> {
@@ -152,20 +160,19 @@ export abstract class AbstractChatAgent {
152
160
  if (!languageModel) {
153
161
  throw new Error('Couldn\'t find a matching language model. Please check your setup!');
154
162
  }
163
+
164
+ const systemMessageDescription = await this.getSystemMessageDescription();
155
165
  const messages = await this.getMessages(request.session);
156
166
  if (this.defaultLogging) {
157
- this.recordingService.recordRequest({
158
- agentId: this.id,
159
- sessionId: request.session.id,
160
- timestamp: Date.now(),
161
- requestId: request.id,
162
- request: request.request.text,
163
- messages
164
- });
167
+ this.recordingService.recordRequest(
168
+ ChatHistoryEntry.fromRequest(
169
+ this.id, request, {
170
+ messages,
171
+ systemMessage: systemMessageDescription?.text
172
+ })
173
+ );
165
174
  }
166
175
 
167
- const systemMessageDescription = await this.getSystemMessageDescription();
168
- const tools: Map<string, ToolRequest> = new Map();
169
176
  if (systemMessageDescription) {
170
177
  const systemMsg: ChatMessage = {
171
178
  actor: 'system',
@@ -174,44 +181,35 @@ export abstract class AbstractChatAgent {
174
181
  };
175
182
  // insert system message at the beginning of the request messages
176
183
  messages.unshift(systemMsg);
177
- systemMessageDescription.functionDescriptions?.forEach((tool, id) => {
178
- tools.set(id, tool);
179
- });
180
184
  }
181
- this.getTools(request)?.forEach(tool => tools.set(tool.id, tool));
182
185
 
183
- const cancellationToken = new CancellationTokenSource();
184
- request.response.onDidChange(() => {
185
- if (request.response.isCanceled) {
186
- cancellationToken.cancel();
187
- }
188
- });
186
+ const systemMessageToolRequests = systemMessageDescription?.functionDescriptions?.values();
187
+ const tools = [
188
+ ...this.chatToolRequestService.getChatToolRequests(request),
189
+ ...this.chatToolRequestService.toChatToolRequests(systemMessageToolRequests ? Array.from(systemMessageToolRequests) : [], request),
190
+ ...this.chatToolRequestService.toChatToolRequests(this.additionalToolRequests, request)
191
+ ];
189
192
 
190
193
  const languageModelResponse = await this.callLlm(
191
194
  languageModel,
192
195
  messages,
193
- tools.size > 0 ? Array.from(tools.values()) : undefined,
194
- cancellationToken.token
196
+ tools.length > 0 ? tools : undefined,
197
+ request.response.cancellationToken
195
198
  );
196
199
  await this.addContentsToResponse(languageModelResponse, request);
197
- request.response.complete();
200
+ await this.onResponseComplete(request);
198
201
  if (this.defaultLogging) {
199
- this.recordingService.recordResponse({
200
- agentId: this.id,
201
- sessionId: request.session.id,
202
- timestamp: Date.now(),
203
- requestId: request.response.requestId,
204
- response: request.response.response.asString()
205
- });
202
+ this.recordingService.recordResponse(ChatHistoryEntry.fromResponse(this.id, request));
206
203
  }
207
204
  } catch (e) {
208
205
  this.handleError(request, e);
209
206
  }
210
207
  }
211
208
 
212
- protected parseContents(text: string): ChatResponseContent[] {
209
+ protected parseContents(text: string, request: ChatRequestModelImpl): ChatResponseContent[] {
213
210
  return parseContents(
214
211
  text,
212
+ request,
215
213
  this.contentMatchers,
216
214
  this.defaultContentFactory?.create.bind(this.defaultContentFactory)
217
215
  );
@@ -264,15 +262,6 @@ export abstract class AbstractChatAgent {
264
262
  return requestMessages;
265
263
  }
266
264
 
267
- /**
268
- * @returns the list of tools used by this agent, or undefined if none is needed.
269
- */
270
- protected getTools(request: ChatRequestModel): ToolRequest[] | undefined {
271
- return request.message.toolRequests.size > 0
272
- ? [...request.message.toolRequests.values()]
273
- : undefined;
274
- }
275
-
276
265
  protected async callLlm(
277
266
  languageModel: LanguageModel,
278
267
  messages: ChatMessage[],
@@ -295,6 +284,16 @@ export abstract class AbstractChatAgent {
295
284
  return undefined;
296
285
  }
297
286
 
287
+ /**
288
+ * Invoked after the response by the LLM completed successfully.
289
+ *
290
+ * The default implementation sets the state of the response to `complete`.
291
+ * Subclasses may override this method to perform additional actions or keep the response open for processing further requests.
292
+ */
293
+ protected async onResponseComplete(request: ChatRequestModelImpl): Promise<void> {
294
+ return request.response.complete();
295
+ }
296
+
298
297
  protected abstract addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise<void>;
299
298
  }
300
299
 
@@ -318,33 +317,12 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
318
317
 
319
318
  protected override async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise<void> {
320
319
  if (isLanguageModelTextResponse(languageModelResponse)) {
321
- const contents = this.parseContents(languageModelResponse.text);
320
+ const contents = this.parseContents(languageModelResponse.text, request);
322
321
  request.response.response.addContents(contents);
323
- request.response.complete();
324
- if (this.defaultLogging) {
325
- this.recordingService.recordResponse({
326
- agentId: this.id,
327
- sessionId: request.session.id,
328
- timestamp: Date.now(),
329
- requestId: request.response.requestId,
330
- response: request.response.response.asString()
331
-
332
- });
333
- }
334
322
  return;
335
323
  }
336
324
  if (isLanguageModelStreamResponse(languageModelResponse)) {
337
325
  await this.addStreamResponse(languageModelResponse, request);
338
- request.response.complete();
339
- if (this.defaultLogging) {
340
- this.recordingService.recordResponse({
341
- agentId: this.id,
342
- sessionId: request.session.id,
343
- timestamp: Date.now(),
344
- requestId: request.response.requestId,
345
- response: request.response.response.asString()
346
- });
347
- }
348
326
  return;
349
327
  }
350
328
  this.logger.error(
@@ -359,7 +337,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
359
337
 
360
338
  protected async addStreamResponse(languageModelResponse: LanguageModelStreamResponse, request: ChatRequestModelImpl): Promise<void> {
361
339
  for await (const token of languageModelResponse.stream) {
362
- const newContents = this.parse(token, request.response.response.content);
340
+ const newContents = this.parse(token, request);
363
341
  if (isArray(newContents)) {
364
342
  request.response.response.addContents(newContents);
365
343
  } else {
@@ -375,7 +353,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
375
353
  return;
376
354
  }
377
355
 
378
- const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text) : [];
356
+ const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text, request) : [];
379
357
  if (result.length > 0) {
380
358
  request.response.response.addContents(result);
381
359
  } else {
@@ -384,11 +362,11 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
384
362
  }
385
363
  }
386
364
 
387
- protected parse(token: LanguageModelStreamResponsePart, previousContent: ChatResponseContent[]): ChatResponseContent | ChatResponseContent[] {
365
+ protected parse(token: LanguageModelStreamResponsePart, request: ChatRequestModelImpl): ChatResponseContent | ChatResponseContent[] {
388
366
  const content = token.content;
389
367
  // eslint-disable-next-line no-null/no-null
390
368
  if (content !== undefined && content !== null) {
391
- return this.defaultContentFactory.create(content);
369
+ return this.defaultContentFactory.create(content, request);
392
370
  }
393
371
  const toolCalls = token.tool_calls;
394
372
  if (toolCalls !== undefined) {
@@ -396,7 +374,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
396
374
  new ToolCallChatResponseContentImpl(toolCall.id, toolCall.function?.name, toolCall.function?.arguments, toolCall.finished, toolCall.result));
397
375
  return toolCallContents;
398
376
  }
399
- return this.defaultContentFactory.create('');
377
+ return this.defaultContentFactory.create('', request);
400
378
  }
401
379
 
402
380
  }
@@ -0,0 +1,47 @@
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.asString(),
44
+ ...args,
45
+ };
46
+ }
47
+ }
@@ -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 { ChatProgressMessage, ChatRequestModel, ChatResponse, ChatResponseContent, ChatResponseModel, QuestionResponseContent } from './chat-model';
17
+
18
+ export function lastResponseContent(request: ChatRequestModel): ChatResponseContent | undefined {
19
+ return lastContentOfResponse(request.response?.response);
20
+ }
21
+
22
+ export function lastContentOfResponse(response: ChatResponse | undefined): ChatResponseContent | undefined {
23
+ const content = response?.content;
24
+ return content && content.length > 0 ? content[content.length - 1] : undefined;
25
+ }
26
+
27
+ export function lastProgressMessage(request: ChatRequestModel): ChatProgressMessage | undefined {
28
+ return lastProgressMessageOfResponse(request.response);
29
+ }
30
+
31
+ export function lastProgressMessageOfResponse(response: ChatResponseModel | undefined): ChatProgressMessage | undefined {
32
+ const progressMessages = response?.progressMessages;
33
+ return progressMessages && progressMessages.length > 0 ? progressMessages[progressMessages.length - 1] : undefined;
34
+ }
35
+
36
+ export function unansweredQuestions(request: ChatRequestModel): QuestionResponseContent[] {
37
+ const response = request.response;
38
+ return unansweredQuestionsOfResponse(response);
39
+ }
40
+
41
+ function unansweredQuestionsOfResponse(response: ChatResponseModel | undefined): QuestionResponseContent[] {
42
+ if (!response || !response.response) { return []; }
43
+ return response.response.content.filter((c): c is QuestionResponseContent => QuestionResponseContent.is(c) && c.selectedOption === undefined);
44
+ }