@theia/ai-chat 1.62.0-next.3 → 1.62.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.
Files changed (41) hide show
  1. package/lib/browser/custom-agent-frontend-application-contribution.d.ts +2 -2
  2. package/lib/browser/custom-agent-frontend-application-contribution.d.ts.map +1 -1
  3. package/lib/browser/custom-agent-frontend-application-contribution.js +2 -1
  4. package/lib/browser/custom-agent-frontend-application-contribution.js.map +1 -1
  5. package/lib/browser/task-context-service.js +1 -1
  6. package/lib/browser/task-context-service.js.map +1 -1
  7. package/lib/common/chat-agents.d.ts +3 -4
  8. package/lib/common/chat-agents.d.ts.map +1 -1
  9. package/lib/common/chat-agents.js +12 -23
  10. package/lib/common/chat-agents.js.map +1 -1
  11. package/lib/common/chat-model.d.ts +13 -0
  12. package/lib/common/chat-model.d.ts.map +1 -1
  13. package/lib/common/chat-model.js +19 -0
  14. package/lib/common/chat-model.js.map +1 -1
  15. package/lib/common/chat-session-naming-service.d.ts +3 -3
  16. package/lib/common/chat-session-naming-service.d.ts.map +1 -1
  17. package/lib/common/chat-session-naming-service.js +20 -24
  18. package/lib/common/chat-session-naming-service.js.map +1 -1
  19. package/lib/common/chat-session-summary-agent-prompt.d.ts +4 -1
  20. package/lib/common/chat-session-summary-agent-prompt.d.ts.map +1 -1
  21. package/lib/common/chat-session-summary-agent-prompt.js +17 -14
  22. package/lib/common/chat-session-summary-agent-prompt.js.map +1 -1
  23. package/lib/common/chat-session-summary-agent.d.ts +2 -2
  24. package/lib/common/chat-session-summary-agent.d.ts.map +1 -1
  25. package/lib/common/chat-session-summary-agent.js +1 -1
  26. package/lib/common/chat-session-summary-agent.js.map +1 -1
  27. package/lib/common/chat-tool-request-service.d.ts +2 -1
  28. package/lib/common/chat-tool-request-service.d.ts.map +1 -1
  29. package/lib/common/chat-tool-request-service.js.map +1 -1
  30. package/lib/common/custom-chat-agent.js +1 -1
  31. package/lib/common/custom-chat-agent.js.map +1 -1
  32. package/package.json +10 -11
  33. package/src/browser/custom-agent-frontend-application-contribution.ts +4 -4
  34. package/src/browser/task-context-service.ts +1 -1
  35. package/src/common/chat-agents.ts +14 -28
  36. package/src/common/chat-model.ts +27 -0
  37. package/src/common/chat-session-naming-service.ts +24 -31
  38. package/src/common/chat-session-summary-agent-prompt.ts +17 -14
  39. package/src/common/chat-session-summary-agent.ts +2 -2
  40. package/src/common/chat-tool-request-service.ts +2 -4
  41. package/src/common/custom-chat-agent.ts +1 -1
@@ -23,8 +23,8 @@ import {
23
23
  AgentSpecificVariables,
24
24
  AIVariableContext,
25
25
  AIVariableResolutionRequest,
26
- CommunicationRecordingService,
27
26
  getTextOfResponse,
27
+ isLanguageModelStreamResponsePart,
28
28
  isTextResponsePart,
29
29
  isThinkingResponsePart,
30
30
  isToolCallResponsePart,
@@ -36,8 +36,8 @@ import {
36
36
  LanguageModelService,
37
37
  LanguageModelStreamResponse,
38
38
  PromptService,
39
- PromptTemplate,
40
- ResolvedPromptTemplate,
39
+ ResolvedPromptFragment,
40
+ PromptVariantSet,
41
41
  TextMessage,
42
42
  ToolCall,
43
43
  ToolRequest,
@@ -75,7 +75,7 @@ export interface SystemMessageDescription {
75
75
  functionDescriptions?: Map<string, ToolRequest>;
76
76
  }
77
77
  export namespace SystemMessageDescription {
78
- export function fromResolvedPromptTemplate(resolvedPrompt: ResolvedPromptTemplate): SystemMessageDescription {
78
+ export function fromResolvedPromptFragment(resolvedPrompt: ResolvedPromptFragment): SystemMessageDescription {
79
79
  return {
80
80
  text: resolvedPrompt.text,
81
81
  functionDescriptions: resolvedPrompt.functionDescriptions
@@ -147,9 +147,6 @@ export abstract class AbstractChatAgent implements ChatAgent {
147
147
  @inject(DefaultResponseContentFactory)
148
148
  protected defaultContentFactory: DefaultResponseContentFactory;
149
149
 
150
- @inject(CommunicationRecordingService)
151
- protected recordingService: CommunicationRecordingService;
152
-
153
150
  readonly abstract id: string;
154
151
  readonly abstract name: string;
155
152
  readonly abstract languageModelRequirements: LanguageModelRequirement[];
@@ -158,7 +155,7 @@ export abstract class AbstractChatAgent implements ChatAgent {
158
155
  tags: string[] = ['Chat'];
159
156
  description: string = '';
160
157
  variables: string[] = [];
161
- promptTemplates: PromptTemplate[] = [];
158
+ prompts: PromptVariantSet[] = [];
162
159
  agentSpecificVariables: AgentSpecificVariables[] = [];
163
160
  functions: string[] = [];
164
161
  protected readonly abstract defaultLanguageModelPurpose: string;
@@ -245,8 +242,8 @@ export abstract class AbstractChatAgent implements ChatAgent {
245
242
  if (this.systemPromptId === undefined) {
246
243
  return undefined;
247
244
  }
248
- const resolvedPrompt = await this.promptService.getPrompt(this.systemPromptId, undefined, context);
249
- return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined;
245
+ const resolvedPrompt = await this.promptService.getResolvedPromptFragment(this.systemPromptId, undefined, context);
246
+ return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptFragment(resolvedPrompt) : undefined;
250
247
  }
251
248
 
252
249
  protected async getMessages(
@@ -289,12 +286,6 @@ export abstract class AbstractChatAgent implements ChatAgent {
289
286
  const agentSettings = this.getLlmSettings();
290
287
  const settings = { ...agentSettings, ...request.session.settings };
291
288
  const tools = toolRequests.length > 0 ? toolRequests : undefined;
292
- this.recordingService.recordRequest({
293
- agentId: this.id,
294
- sessionId: request.session.id,
295
- requestId: request.id,
296
- request: messages
297
- });
298
289
  return this.languageModelService.sendRequest(
299
290
  languageModel,
300
291
  {
@@ -323,15 +314,6 @@ export abstract class AbstractChatAgent implements ChatAgent {
323
314
  * Subclasses may override this method to perform additional actions or keep the response open for processing further requests.
324
315
  */
325
316
  protected async onResponseComplete(request: MutableChatRequestModel): Promise<void> {
326
- this.recordingService.recordResponse(
327
- {
328
- agentId: this.id,
329
- sessionId: request.session.id,
330
- requestId: request.id,
331
- response: request.response.response.content.flatMap(c =>
332
- c.toLanguageModelMessage?.() ?? ({ type: 'text', actor: 'ai', text: c.asDisplayString?.() ?? c.asString?.() ?? JSON.stringify(c) }))
333
- }
334
- );
335
317
  return request.response.complete();
336
318
  }
337
319
 
@@ -399,9 +381,13 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
399
381
  let startIndex = Math.max(0, request.response.response.content.length - 1);
400
382
 
401
383
  for await (const token of languageModelResponse.stream) {
384
+ // Skip unknown tokens. For example OpenAI sends empty tokens around tool calls
385
+ if (!isLanguageModelStreamResponsePart(token)) {
386
+ console.debug(`Unknown token: '${JSON.stringify(token)}'. Skipping`);
387
+ continue;
388
+ }
402
389
  const newContent = this.parse(token, request);
403
-
404
- if (!(isTextResponsePart(token) && token.content)) {
390
+ if (!isTextResponsePart(token)) {
405
391
  // For non-text tokens (like tool calls), add them directly
406
392
  if (isArray(newContent)) {
407
393
  request.response.response.addContents(newContent);
@@ -409,7 +395,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
409
395
  request.response.response.addContent(newContent);
410
396
  }
411
397
  // And reset the marker index and the text buffer as we skip matching across non-text tokens
412
- startIndex = request.response.response.content.length - 1;
398
+ startIndex = request.response.response.content.length;
413
399
  completeTextBuffer = '';
414
400
  } else {
415
401
  // parse the entire text so far (since beginning of the stream or last non-text token)
@@ -205,6 +205,12 @@ export interface ChangeSet extends Disposable {
205
205
  onDidChange: Event<ChangeSetChangeEvent>;
206
206
  readonly title: string;
207
207
  getElements(): ChangeSetElement[];
208
+ /**
209
+ * Find an element by URI.
210
+ * @param uri The URI to look for.
211
+ * @returns The element with the given URI, or undefined if not found.
212
+ */
213
+ getElementByURI(uri: URI): ChangeSetElement | undefined;
208
214
  dispose(): void;
209
215
  }
210
216
 
@@ -1037,6 +1043,21 @@ export class ChangeSetImpl implements ChangeSet {
1037
1043
  return this._elements;
1038
1044
  }
1039
1045
 
1046
+ /**
1047
+ * Find an element by URI.
1048
+ * @param uri The URI to look for.
1049
+ * @returns The element with the given URI, or undefined if not found.
1050
+ */
1051
+ getElementByURI(uri: URI): ChangeSetElement | undefined {
1052
+ const uriString = uri.toString();
1053
+ for (const element of this._elements) {
1054
+ if (element.uri.toString() === uriString) {
1055
+ return element;
1056
+ }
1057
+ }
1058
+ return undefined;
1059
+ }
1060
+
1040
1061
  /** Will replace any element that is already present, using URI as identity criterion. */
1041
1062
  addElements(...elements: ChangeSetElement[]): void {
1042
1063
  const added: URI[] = [];
@@ -1190,6 +1211,10 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
1190
1211
  return this._data[key] as T;
1191
1212
  }
1192
1213
 
1214
+ removeData(key: string): void {
1215
+ delete this._data[key];
1216
+ }
1217
+
1193
1218
  get id(): string {
1194
1219
  return this._id;
1195
1220
  }
@@ -1516,6 +1541,8 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
1516
1541
  if (nextChatResponseContent.id === this.id) {
1517
1542
  this._finished = nextChatResponseContent.finished;
1518
1543
  this._result = nextChatResponseContent.result;
1544
+ const args = nextChatResponseContent.arguments;
1545
+ this._arguments = (args && args.length > 0) ? args : this._arguments;
1519
1546
  return true;
1520
1547
  }
1521
1548
  if (nextChatResponseContent.name !== undefined) {
@@ -17,31 +17,33 @@
17
17
  import {
18
18
  Agent,
19
19
  AgentService,
20
- CommunicationRecordingService,
21
- CommunicationRequestEntryParam,
22
20
  getTextOfResponse,
23
21
  LanguageModelRegistry,
24
22
  LanguageModelRequirement,
23
+ LanguageModelService,
25
24
  PromptService,
26
- PromptTemplate,
25
+ PromptVariantSet,
27
26
  UserRequest
28
27
  } from '@theia/ai-core';
29
28
  import { inject, injectable } from '@theia/core/shared/inversify';
30
29
  import { ChatSession } from './chat-service';
31
30
  import { generateUuid } from '@theia/core';
32
31
 
33
- const CHAT_SESSION_NAMING_PROMPT = {
32
+ const CHAT_SESSION_NAMING_PROMPT: PromptVariantSet = {
34
33
  id: 'chat-session-naming-prompt',
35
- template: '{{!-- Made improvements or adaptations to this prompt template? We\'d love for you to share it with the community! Contribute back here: ' +
36
- 'https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}\n\n' +
37
- 'Provide a short and descriptive name for the given AI chat conversation of an AI-powered tool based on the conversation below.\n\n' +
38
- 'The purpose of the name is for users to recognize the chat conversation easily in a list of conversations. ' +
39
- 'Use the same language for the chat conversation name as used in the provided conversation, if in doubt default to English. ' +
40
- 'Start the chat conversation name with an upper-case letter. ' +
41
- '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' +
42
- 'IMPORTANT: Your answer MUST ONLY CONTAIN THE PROPOSED NAME and must not be preceded or followed by any other text.' +
43
- '\n\nOther session names:\n{{listOfSessionNames}}' +
44
- '\n\nConversation:\n{{conversation}}',
34
+ defaultVariant: {
35
+ id: 'chat-session-naming-prompt',
36
+ template: '{{!-- Made improvements or adaptations to this prompt template? We\'d love for you to share it with the community! Contribute back here: ' +
37
+ 'https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}\n\n' +
38
+ 'Provide a short and descriptive name for the given AI chat conversation of an AI-powered tool based on the conversation below.\n\n' +
39
+ 'The purpose of the name is for users to recognize the chat conversation easily in a list of conversations. ' +
40
+ 'Use the same language for the chat conversation name as used in the provided conversation, if in doubt default to English. ' +
41
+ 'Start the chat conversation name with an upper-case letter. ' +
42
+ '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' +
43
+ 'IMPORTANT: Your answer MUST ONLY CONTAIN THE PROPOSED NAME and must not be preceded or followed by any other text.' +
44
+ '\n\nOther session names:\n{{listOfSessionNames}}' +
45
+ '\n\nConversation:\n{{conversation}}',
46
+ }
45
47
  };
46
48
 
47
49
  @injectable()
@@ -58,12 +60,12 @@ export class ChatSessionNamingService {
58
60
 
59
61
  @injectable()
60
62
  export class ChatSessionNamingAgent implements Agent {
61
- static ID = 'chat-session-naming-agent';
63
+ static ID = 'Chat Session Naming';
62
64
  id = ChatSessionNamingAgent.ID;
63
- name = 'Chat Session Naming';
65
+ name = ChatSessionNamingAgent.ID;
64
66
  description = 'Agent for generating chat session names';
65
67
  variables = [];
66
- promptTemplates: PromptTemplate[] = [CHAT_SESSION_NAMING_PROMPT];
68
+ prompts = [CHAT_SESSION_NAMING_PROMPT];
67
69
  languageModelRequirements: LanguageModelRequirement[] = [{
68
70
  purpose: 'chat-session-naming',
69
71
  identifier: 'openai/gpt-4o-mini',
@@ -77,8 +79,8 @@ export class ChatSessionNamingAgent implements Agent {
77
79
  @inject(LanguageModelRegistry)
78
80
  protected readonly lmRegistry: LanguageModelRegistry;
79
81
 
80
- @inject(CommunicationRecordingService)
81
- protected recordingService: CommunicationRecordingService;
82
+ @inject(LanguageModelService)
83
+ protected readonly languageModelService: LanguageModelService;
82
84
 
83
85
  @inject(PromptService)
84
86
  protected promptService: PromptService;
@@ -98,7 +100,7 @@ export class ChatSessionNamingAgent implements Agent {
98
100
  .join('\n\n');
99
101
  const listOfSessionNames = otherNames.map(name => name).join(', ');
100
102
 
101
- const prompt = await this.promptService.getPrompt(CHAT_SESSION_NAMING_PROMPT.id, { conversation, listOfSessionNames });
103
+ const prompt = await this.promptService.getResolvedPromptFragment(CHAT_SESSION_NAMING_PROMPT.id, { conversation, listOfSessionNames });
102
104
  const message = prompt?.text;
103
105
  if (!message) {
104
106
  throw new Error('Unable to create prompt message for generating chat session name');
@@ -106,7 +108,7 @@ export class ChatSessionNamingAgent implements Agent {
106
108
 
107
109
  const sessionId = generateUuid();
108
110
  const requestId = generateUuid();
109
- const request: UserRequest = {
111
+ const request: UserRequest & { agentId: string } = {
110
112
  messages: [{
111
113
  actor: 'user',
112
114
  text: message,
@@ -116,18 +118,9 @@ export class ChatSessionNamingAgent implements Agent {
116
118
  sessionId,
117
119
  agentId: this.id
118
120
  };
119
- this.recordingService.recordRequest({ ...request, request: request.messages } satisfies CommunicationRequestEntryParam);
120
-
121
- const result = await lm.request(request);
121
+ const result = await this.languageModelService.sendRequest(lm, request);
122
122
  const response = await getTextOfResponse(result);
123
- this.recordingService.recordResponse({
124
- agentId: this.id,
125
- sessionId,
126
- requestId,
127
- response: [{ actor: 'ai', text: response, type: 'text' }]
128
- });
129
123
 
130
124
  return response.replace(/\s+/g, ' ').substring(0, 100);
131
125
  }
132
-
133
126
  }
@@ -11,18 +11,21 @@
11
11
  import { CHANGE_SET_SUMMARY_VARIABLE_ID } from './context-variables';
12
12
 
13
13
  export const CHAT_SESSION_SUMMARY_PROMPT = {
14
- id: 'chat-session-summary-prompt',
15
- template: `{{!-- !-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
16
- Made improvements or adaptations to this prompt template? We\'d love for you to share it with the community! Contribute back here: ' +
17
- 'https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}\n\n' +
18
- 'You are a chat agent for summarizing AI agent chat sessions for later use. \
19
- Review the conversation above and generate a concise summary that captures every crucial detail, \
20
- including all requirements, decisions, and pending tasks. \
21
- Ensure that the summary is sufficiently comprehensive to allow seamless continuation of the workflow. The summary will primarily be used by other AI agents, so tailor your \
22
- response for use by AI agents. \
23
- Also consider the system message.
24
- Make sure you include all necessary context information and use unique references (such as URIs, file paths, etc.).
25
- If the conversation was about a task, describe the state of the task, i.e. what has been completed and what is open.
26
- If a changeset is open in the session, describe the state of the suggested changes.
27
- {{${CHANGE_SET_SUMMARY_VARIABLE_ID}}}`,
14
+ id: 'chat-session-summary-system-prompt',
15
+ defaultVariant: {
16
+ id: 'chat-session-summary-prompt',
17
+ template: '{{!-- !-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).\n' +
18
+ 'Made improvements or adaptations to this prompt template? We\'d love for you to share it with the community! Contribute back here: ' +
19
+ 'https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}\n\n' +
20
+ 'You are a chat agent for summarizing AI agent chat sessions for later use. ' +
21
+ 'Review the conversation above and generate a concise summary that captures every crucial detail, ' +
22
+ 'including all requirements, decisions, and pending tasks. ' +
23
+ 'Ensure that the summary is sufficiently comprehensive to allow seamless continuation of the workflow. ' +
24
+ 'The summary will primarily be used by other AI agents, so tailor your response for use by AI agents. ' +
25
+ 'Also consider the system message. ' +
26
+ 'Make sure you include all necessary context information and use unique references(such as URIs, file paths, etc.). ' +
27
+ 'If the conversation was about a task, describe the state of the task, i.e.what has been completed and what is open. ' +
28
+ 'If a changeset is open in the session, describe the state of the suggested changes. ' +
29
+ `\n\n{{${CHANGE_SET_SUMMARY_VARIABLE_ID}}}`,
30
+ }
28
31
  };
@@ -16,7 +16,7 @@
16
16
 
17
17
  import {
18
18
  LanguageModelRequirement,
19
- PromptTemplate
19
+ PromptVariantSet
20
20
  } from '@theia/ai-core';
21
21
  import { injectable } from '@theia/core/shared/inversify';
22
22
  import { AbstractStreamParsingChatAgent, ChatAgent } from './chat-agents';
@@ -29,7 +29,7 @@ export class ChatSessionSummaryAgent extends AbstractStreamParsingChatAgent impl
29
29
  name = 'Chat Session Summary';
30
30
  override description = 'Agent for generating chat session summaries.';
31
31
  override variables = [];
32
- override promptTemplates: PromptTemplate[] = [CHAT_SESSION_SUMMARY_PROMPT];
32
+ override prompts: PromptVariantSet[] = [CHAT_SESSION_SUMMARY_PROMPT];
33
33
  protected readonly defaultLanguageModelPurpose = 'chat-session-summary';
34
34
  languageModelRequirements: LanguageModelRequirement[] = [{
35
35
  purpose: 'chat-session-summary',
@@ -19,10 +19,8 @@ import { injectable } from '@theia/core/shared/inversify';
19
19
  import { MutableChatRequestModel } from './chat-model';
20
20
 
21
21
  export interface ChatToolRequest extends ToolRequest {
22
- handler: (
23
- arg_string: string,
24
- context: MutableChatRequestModel,
25
- ) => Promise<unknown>;
22
+ handler(arg_string: string, context: MutableChatRequestModel): Promise<unknown>;
23
+ handler(arg_string: string, ctx?: unknown): Promise<unknown>;
26
24
  }
27
25
 
28
26
  /**
@@ -28,6 +28,6 @@ export class CustomChatAgent extends AbstractStreamParsingChatAgent {
28
28
  set prompt(prompt: string) {
29
29
  // the name is dynamic, so we set the propmptId here
30
30
  this.systemPromptId = `${this.name}_prompt`;
31
- this.promptTemplates.push({ id: `${this.name}_prompt`, template: prompt });
31
+ this.prompts.push({ id: this.systemPromptId, defaultVariant: { id: `${this.name}_prompt`, template: prompt } });
32
32
  }
33
33
  }