@theia/ai-chat 1.55.0 → 1.56.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 (53) hide show
  1. package/lib/common/chat-agents.d.ts +12 -4
  2. package/lib/common/chat-agents.d.ts.map +1 -1
  3. package/lib/common/chat-agents.js +37 -46
  4. package/lib/common/chat-agents.js.map +1 -1
  5. package/lib/common/chat-history-entry.d.ts +7 -0
  6. package/lib/common/chat-history-entry.d.ts.map +1 -0
  7. package/lib/common/chat-history-entry.js +42 -0
  8. package/lib/common/chat-history-entry.js.map +1 -0
  9. package/lib/common/chat-model-util.d.ts +7 -0
  10. package/lib/common/chat-model-util.d.ts.map +1 -0
  11. package/lib/common/chat-model-util.js +50 -0
  12. package/lib/common/chat-model-util.js.map +1 -0
  13. package/lib/common/chat-model.d.ts +59 -0
  14. package/lib/common/chat-model.d.ts.map +1 -1
  15. package/lib/common/chat-model.js +70 -2
  16. package/lib/common/chat-model.js.map +1 -1
  17. package/lib/common/command-chat-agents.d.ts.map +1 -1
  18. package/lib/common/command-chat-agents.js +3 -1
  19. package/lib/common/command-chat-agents.js.map +1 -1
  20. package/lib/common/index.d.ts +1 -0
  21. package/lib/common/index.d.ts.map +1 -1
  22. package/lib/common/index.js +1 -0
  23. package/lib/common/index.js.map +1 -1
  24. package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
  25. package/lib/common/orchestrator-chat-agent.js +16 -14
  26. package/lib/common/orchestrator-chat-agent.js.map +1 -1
  27. package/lib/common/parse-contents.d.ts +2 -2
  28. package/lib/common/parse-contents.d.ts.map +1 -1
  29. package/lib/common/parse-contents.js +4 -4
  30. package/lib/common/parse-contents.js.map +1 -1
  31. package/lib/common/parse-contents.spec.d.ts.map +1 -1
  32. package/lib/common/parse-contents.spec.js +14 -13
  33. package/lib/common/parse-contents.spec.js.map +1 -1
  34. package/lib/common/response-content-matcher.d.ts +3 -3
  35. package/lib/common/response-content-matcher.d.ts.map +1 -1
  36. package/lib/common/response-content-matcher.js +2 -2
  37. package/lib/common/response-content-matcher.js.map +1 -1
  38. package/lib/common/universal-chat-agent.d.ts +1 -0
  39. package/lib/common/universal-chat-agent.d.ts.map +1 -1
  40. package/lib/common/universal-chat-agent.js +10 -3
  41. package/lib/common/universal-chat-agent.js.map +1 -1
  42. package/package.json +8 -8
  43. package/src/common/chat-agents.ts +44 -54
  44. package/src/common/chat-history-entry.ts +47 -0
  45. package/src/common/chat-model-util.ts +44 -0
  46. package/src/common/chat-model.ts +89 -0
  47. package/src/common/command-chat-agents.ts +3 -1
  48. package/src/common/index.ts +1 -0
  49. package/src/common/orchestrator-chat-agent.ts +17 -14
  50. package/src/common/parse-contents.spec.ts +16 -14
  51. package/src/common/parse-contents.ts +5 -4
  52. package/src/common/response-content-matcher.ts +4 -3
  53. package/src/common/universal-chat-agent.ts +10 -2
@@ -23,10 +23,13 @@ import { ChatAgentService } from './chat-agent-service';
23
23
  import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents';
24
24
  import { ChatRequestModelImpl, InformationalChatResponseContentImpl } from './chat-model';
25
25
  import { generateUuid } from '@theia/core';
26
+ import { ChatHistoryEntry } from './chat-history-entry';
26
27
 
27
28
  export const orchestratorTemplate: PromptTemplate = {
28
29
  id: 'orchestrator-system',
29
- template: `# Instructions
30
+ template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
31
+ https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
32
+ # Instructions
30
33
 
31
34
  Your task is to identify which Chat Agent(s) should best reply a given user's message.
32
35
  You consider all messages of the conversation to ensure consistency and avoid agent switches without a clear context change.
@@ -60,7 +63,7 @@ You must only use the \`id\` attribute of the agent, never the name.
60
63
  `};
61
64
 
62
65
  export const OrchestratorChatAgentId = 'Orchestrator';
63
- const OrchestatorRequestIdKey = 'orchestatorRequestIdKey';
66
+ const OrchestratorRequestIdKey = 'orchestatorRequestIdKey';
64
67
 
65
68
  @injectable()
66
69
  export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent {
@@ -93,16 +96,17 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implem
93
96
  override async invoke(request: ChatRequestModelImpl): Promise<void> {
94
97
  request.response.addProgressMessage({ content: 'Determining the most appropriate agent', status: 'inProgress' });
95
98
  // We generate a dedicated ID for recording the orchestrator request/response, as we will forward the original request to another agent
96
- const orchestartorRequestId = generateUuid();
97
- request.addData(OrchestatorRequestIdKey, orchestartorRequestId);
98
- const userPrompt = request.request.text;
99
- this.recordingService.recordRequest({
100
- agentId: this.id,
101
- sessionId: request.session.id,
102
- timestamp: Date.now(),
103
- requestId: orchestartorRequestId,
104
- request: userPrompt,
105
- });
99
+ const orchestratorRequestId = generateUuid();
100
+ request.addData(OrchestratorRequestIdKey, orchestratorRequestId);
101
+ const messages = await this.getMessages(request.session);
102
+ const systemMessage = (await this.getSystemMessageDescription())?.text;
103
+ this.recordingService.recordRequest(
104
+ ChatHistoryEntry.fromRequest(this.id, request, {
105
+ requestId: orchestratorRequestId,
106
+ messages,
107
+ systemMessage
108
+ })
109
+ );
106
110
  return super.invoke(request);
107
111
  }
108
112
 
@@ -115,12 +119,11 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implem
115
119
  let agentIds: string[] = [];
116
120
  const responseText = await getTextOfResponse(response);
117
121
  // We use the previously generated, dedicated ID to log the orchestrator response before we forward the original request
118
- const orchestratorRequestId = request.getDataByKey(OrchestatorRequestIdKey);
122
+ const orchestratorRequestId = request.getDataByKey(OrchestratorRequestIdKey);
119
123
  if (typeof orchestratorRequestId === 'string') {
120
124
  this.recordingService.recordResponse({
121
125
  agentId: this.id,
122
126
  sessionId: request.session.id,
123
- timestamp: Date.now(),
124
127
  requestId: orchestratorRequestId,
125
128
  response: responseText,
126
129
  });
@@ -15,7 +15,7 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { expect } from 'chai';
18
- import { ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model';
18
+ import { ChatRequestModelImpl, ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model';
19
19
  import { parseContents } from './parse-contents';
20
20
  import { CodeContentMatcher, ResponseContentMatcher } from './response-content-matcher';
21
21
 
@@ -33,22 +33,24 @@ export const CommandContentMatcher: ResponseContentMatcher = {
33
33
  }
34
34
  };
35
35
 
36
+ const fakeRequest = {} as ChatRequestModelImpl;
37
+
36
38
  describe('parseContents', () => {
37
39
  it('should parse code content', () => {
38
40
  const text = '```typescript\nconsole.log("Hello World");\n```';
39
- const result = parseContents(text);
41
+ const result = parseContents(text, fakeRequest);
40
42
  expect(result).to.deep.equal([new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript')]);
41
43
  });
42
44
 
43
45
  it('should parse markdown content', () => {
44
46
  const text = 'Hello **World**';
45
- const result = parseContents(text);
47
+ const result = parseContents(text, fakeRequest);
46
48
  expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Hello **World**')]);
47
49
  });
48
50
 
49
51
  it('should parse multiple content blocks', () => {
50
52
  const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**';
51
- const result = parseContents(text);
53
+ const result = parseContents(text, fakeRequest);
52
54
  expect(result).to.deep.equal([
53
55
  new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
54
56
  new MarkdownChatResponseContentImpl('\nHello **World**')
@@ -57,7 +59,7 @@ describe('parseContents', () => {
57
59
 
58
60
  it('should parse multiple content blocks with different languages', () => {
59
61
  const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")\n```';
60
- const result = parseContents(text);
62
+ const result = parseContents(text, fakeRequest);
61
63
  expect(result).to.deep.equal([
62
64
  new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
63
65
  new CodeChatResponseContentImpl('print("Hello World")', 'python')
@@ -66,7 +68,7 @@ describe('parseContents', () => {
66
68
 
67
69
  it('should parse multiple content blocks with different languages and markdown', () => {
68
70
  const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**\n```python\nprint("Hello World")\n```';
69
- const result = parseContents(text);
71
+ const result = parseContents(text, fakeRequest);
70
72
  expect(result).to.deep.equal([
71
73
  new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
72
74
  new MarkdownChatResponseContentImpl('\nHello **World**\n'),
@@ -76,7 +78,7 @@ describe('parseContents', () => {
76
78
 
77
79
  it('should parse content blocks with empty content', () => {
78
80
  const text = '```typescript\n```\nHello **World**\n```python\nprint("Hello World")\n```';
79
- const result = parseContents(text);
81
+ const result = parseContents(text, fakeRequest);
80
82
  expect(result).to.deep.equal([
81
83
  new CodeChatResponseContentImpl('', 'typescript'),
82
84
  new MarkdownChatResponseContentImpl('\nHello **World**\n'),
@@ -86,7 +88,7 @@ describe('parseContents', () => {
86
88
 
87
89
  it('should parse content with markdown, code, and markdown', () => {
88
90
  const text = 'Hello **World**\n```typescript\nconsole.log("Hello World");\n```\nGoodbye **World**';
89
- const result = parseContents(text);
91
+ const result = parseContents(text, fakeRequest);
90
92
  expect(result).to.deep.equal([
91
93
  new MarkdownChatResponseContentImpl('Hello **World**\n'),
92
94
  new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
@@ -96,25 +98,25 @@ describe('parseContents', () => {
96
98
 
97
99
  it('should handle text with no special content', () => {
98
100
  const text = 'Just some plain text.';
99
- const result = parseContents(text);
101
+ const result = parseContents(text, fakeRequest);
100
102
  expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Just some plain text.')]);
101
103
  });
102
104
 
103
105
  it('should handle text with only start code block', () => {
104
106
  const text = '```typescript\nconsole.log("Hello World");';
105
- const result = parseContents(text);
107
+ const result = parseContents(text, fakeRequest);
106
108
  expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('```typescript\nconsole.log("Hello World");')]);
107
109
  });
108
110
 
109
111
  it('should handle text with only end code block', () => {
110
112
  const text = 'console.log("Hello World");\n```';
111
- const result = parseContents(text);
113
+ const result = parseContents(text, fakeRequest);
112
114
  expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('console.log("Hello World");\n```')]);
113
115
  });
114
116
 
115
117
  it('should handle text with unmatched code block', () => {
116
118
  const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")';
117
- const result = parseContents(text);
119
+ const result = parseContents(text, fakeRequest);
118
120
  expect(result).to.deep.equal([
119
121
  new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
120
122
  new MarkdownChatResponseContentImpl('\n```python\nprint("Hello World")')
@@ -123,7 +125,7 @@ describe('parseContents', () => {
123
125
 
124
126
  it('should parse code block without newline after language', () => {
125
127
  const text = '```typescript console.log("Hello World");```';
126
- const result = parseContents(text);
128
+ const result = parseContents(text, fakeRequest);
127
129
  expect(result).to.deep.equal([
128
130
  new MarkdownChatResponseContentImpl('```typescript console.log("Hello World");```')
129
131
  ]);
@@ -131,7 +133,7 @@ describe('parseContents', () => {
131
133
 
132
134
  it('should parse with matches of multiple different matchers and default', () => {
133
135
  const text = '<command>\nMY_SPECIAL_COMMAND\n</command>\nHello **World**\n```python\nprint("Hello World")\n```\n<command>\nMY_SPECIAL_COMMAND2\n</command>';
134
- const result = parseContents(text, [CodeContentMatcher, CommandContentMatcher]);
136
+ const result = parseContents(text, fakeRequest, [CodeContentMatcher, CommandContentMatcher]);
135
137
  expect(result).to.deep.equal([
136
138
  new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND'),
137
139
  new MarkdownChatResponseContentImpl('\nHello **World**\n'),
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  */
16
- import { ChatResponseContent } from './chat-model';
16
+ import { ChatRequestModelImpl, ChatResponseContent } from './chat-model';
17
17
  import { CodeContentMatcher, MarkdownContentFactory, ResponseContentFactory, ResponseContentMatcher } from './response-content-matcher';
18
18
 
19
19
  interface Match {
@@ -24,6 +24,7 @@ interface Match {
24
24
 
25
25
  export function parseContents(
26
26
  text: string,
27
+ request: ChatRequestModelImpl,
27
28
  contentMatchers: ResponseContentMatcher[] = [CodeContentMatcher],
28
29
  defaultContentFactory: ResponseContentFactory = MarkdownContentFactory
29
30
  ): ChatResponseContent[] {
@@ -36,7 +37,7 @@ export function parseContents(
36
37
  if (!match) {
37
38
  // Add the remaining text as default content
38
39
  if (remainingText.length > 0) {
39
- result.push(defaultContentFactory(remainingText));
40
+ result.push(defaultContentFactory(remainingText, request));
40
41
  }
41
42
  break;
42
43
  }
@@ -45,11 +46,11 @@ export function parseContents(
45
46
  if (match.index > 0) {
46
47
  const precedingContent = remainingText.substring(0, match.index);
47
48
  if (precedingContent.trim().length > 0) {
48
- result.push(defaultContentFactory(precedingContent));
49
+ result.push(defaultContentFactory(precedingContent, request));
49
50
  }
50
51
  }
51
52
  // 2. Add the matched content object
52
- result.push(match.matcher.contentFactory(match.content));
53
+ result.push(match.matcher.contentFactory(match.content, request));
53
54
  // Update currentIndex to the end of the end of the match
54
55
  // And continue with the search after the end of the match
55
56
  currentIndex += match.index + match.content.length;
@@ -14,13 +14,14 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  */
16
16
  import {
17
+ ChatRequestModelImpl,
17
18
  ChatResponseContent,
18
19
  CodeChatResponseContentImpl,
19
20
  MarkdownChatResponseContentImpl
20
21
  } from './chat-model';
21
22
  import { injectable } from '@theia/core/shared/inversify';
22
23
 
23
- export type ResponseContentFactory = (content: string) => ChatResponseContent;
24
+ export type ResponseContentFactory = (content: string, request: ChatRequestModelImpl) => ChatResponseContent;
24
25
 
25
26
  export const MarkdownContentFactory: ResponseContentFactory = (content: string) =>
26
27
  new MarkdownChatResponseContentImpl(content);
@@ -33,8 +34,8 @@ export const MarkdownContentFactory: ResponseContentFactory = (content: string)
33
34
  */
34
35
  @injectable()
35
36
  export class DefaultResponseContentFactory {
36
- create(content: string): ChatResponseContent {
37
- return MarkdownContentFactory(content);
37
+ create(content: string, request: ChatRequestModelImpl): ChatResponseContent {
38
+ return MarkdownContentFactory(content, request);
38
39
  }
39
40
  }
40
41
 
@@ -23,7 +23,9 @@ import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } f
23
23
 
24
24
  export const universalTemplate: PromptTemplate = {
25
25
  id: 'universal-system',
26
- template: `# Instructions
26
+ template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
27
+ https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
28
+ # Instructions
27
29
 
28
30
  You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by
29
31
  providing concise and accurate answers to programming-related questions. Your role is to enhance the
@@ -76,6 +78,12 @@ simple solutions.
76
78
  `
77
79
  };
78
80
 
81
+ export const universalTemplateVariant: PromptTemplate = {
82
+ id: 'universal-system-empty',
83
+ template: '',
84
+ variantOf: universalTemplate.id,
85
+ };
86
+
79
87
  @injectable()
80
88
  export class UniversalChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent {
81
89
  name: string;
@@ -96,7 +104,7 @@ export class UniversalChatAgent extends AbstractStreamParsingChatAgent implement
96
104
  + 'questions the user might ask. The universal agent currently does not have any context by default, i.e. it cannot '
97
105
  + 'access the current user context or the workspace.';
98
106
  this.variables = [];
99
- this.promptTemplates = [universalTemplate];
107
+ this.promptTemplates = [universalTemplate, universalTemplateVariant];
100
108
  this.functions = [];
101
109
  this.agentSpecificVariables = [];
102
110
  }