@theia/ai-chat 1.46.0-next.241

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +31 -0
  2. package/lib/browser/ai-chat-frontend-module.d.ts +4 -0
  3. package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -0
  4. package/lib/browser/ai-chat-frontend-module.js +79 -0
  5. package/lib/browser/ai-chat-frontend-module.js.map +1 -0
  6. package/lib/browser/ai-chat-preferences.d.ts +4 -0
  7. package/lib/browser/ai-chat-preferences.d.ts.map +1 -0
  8. package/lib/browser/ai-chat-preferences.js +32 -0
  9. package/lib/browser/ai-chat-preferences.js.map +1 -0
  10. package/lib/browser/custom-agent-factory.d.ts +4 -0
  11. package/lib/browser/custom-agent-factory.d.ts.map +1 -0
  12. package/lib/browser/custom-agent-factory.js +20 -0
  13. package/lib/browser/custom-agent-factory.js.map +1 -0
  14. package/lib/browser/custom-agent-frontend-application-contribution.d.ts +13 -0
  15. package/lib/browser/custom-agent-frontend-application-contribution.d.ts.map +1 -0
  16. package/lib/browser/custom-agent-frontend-application-contribution.js +82 -0
  17. package/lib/browser/custom-agent-frontend-application-contribution.js.map +1 -0
  18. package/lib/browser/frontend-chat-service.d.ts +8 -0
  19. package/lib/browser/frontend-chat-service.d.ts.map +1 -0
  20. package/lib/browser/frontend-chat-service.js +67 -0
  21. package/lib/browser/frontend-chat-service.js.map +1 -0
  22. package/lib/common/chat-agent-service.d.ts +45 -0
  23. package/lib/common/chat-agent-service.d.ts.map +1 -0
  24. package/lib/common/chat-agent-service.js +78 -0
  25. package/lib/common/chat-agent-service.js.map +1 -0
  26. package/lib/common/chat-agents-variable-contribution.d.ts +17 -0
  27. package/lib/common/chat-agents-variable-contribution.d.ts.map +1 -0
  28. package/lib/common/chat-agents-variable-contribution.js +51 -0
  29. package/lib/common/chat-agents-variable-contribution.js.map +1 -0
  30. package/lib/common/chat-agents.d.ts +108 -0
  31. package/lib/common/chat-agents.d.ts.map +1 -0
  32. package/lib/common/chat-agents.js +321 -0
  33. package/lib/common/chat-agents.js.map +1 -0
  34. package/lib/common/chat-history-entry.d.ts +7 -0
  35. package/lib/common/chat-history-entry.d.ts.map +1 -0
  36. package/lib/common/chat-history-entry.js +42 -0
  37. package/lib/common/chat-history-entry.js.map +1 -0
  38. package/lib/common/chat-model-util.d.ts +7 -0
  39. package/lib/common/chat-model-util.d.ts.map +1 -0
  40. package/lib/common/chat-model-util.js +50 -0
  41. package/lib/common/chat-model-util.js.map +1 -0
  42. package/lib/common/chat-model.d.ts +393 -0
  43. package/lib/common/chat-model.d.ts.map +1 -0
  44. package/lib/common/chat-model.js +612 -0
  45. package/lib/common/chat-model.js.map +1 -0
  46. package/lib/common/chat-request-parser.d.ts +20 -0
  47. package/lib/common/chat-request-parser.d.ts.map +1 -0
  48. package/lib/common/chat-request-parser.js +158 -0
  49. package/lib/common/chat-request-parser.js.map +1 -0
  50. package/lib/common/chat-request-parser.spec.d.ts +2 -0
  51. package/lib/common/chat-request-parser.spec.d.ts.map +1 -0
  52. package/lib/common/chat-request-parser.spec.js +108 -0
  53. package/lib/common/chat-request-parser.spec.js.map +1 -0
  54. package/lib/common/chat-service.d.ts +72 -0
  55. package/lib/common/chat-service.d.ts.map +1 -0
  56. package/lib/common/chat-service.js +170 -0
  57. package/lib/common/chat-service.js.map +1 -0
  58. package/lib/common/command-chat-agents.d.ts +33 -0
  59. package/lib/common/command-chat-agents.d.ts.map +1 -0
  60. package/lib/common/command-chat-agents.js +329 -0
  61. package/lib/common/command-chat-agents.js.map +1 -0
  62. package/lib/common/custom-chat-agent.d.ts +14 -0
  63. package/lib/common/custom-chat-agent.d.ts.map +1 -0
  64. package/lib/common/custom-chat-agent.js +43 -0
  65. package/lib/common/custom-chat-agent.js.map +1 -0
  66. package/lib/common/index.d.ts +12 -0
  67. package/lib/common/index.d.ts.map +1 -0
  68. package/lib/common/index.js +30 -0
  69. package/lib/common/index.js.map +1 -0
  70. package/lib/common/o1-chat-agent.d.ts +13 -0
  71. package/lib/common/o1-chat-agent.d.ts.map +1 -0
  72. package/lib/common/o1-chat-agent.js +45 -0
  73. package/lib/common/o1-chat-agent.js.map +1 -0
  74. package/lib/common/orchestrator-chat-agent.d.ts +22 -0
  75. package/lib/common/orchestrator-chat-agent.d.ts.map +1 -0
  76. package/lib/common/orchestrator-chat-agent.js +167 -0
  77. package/lib/common/orchestrator-chat-agent.js.map +1 -0
  78. package/lib/common/parse-contents.d.ts +11 -0
  79. package/lib/common/parse-contents.d.ts.map +1 -0
  80. package/lib/common/parse-contents.js +67 -0
  81. package/lib/common/parse-contents.js.map +1 -0
  82. package/lib/common/parse-contents.spec.d.ts +9 -0
  83. package/lib/common/parse-contents.spec.d.ts.map +1 -0
  84. package/lib/common/parse-contents.spec.js +134 -0
  85. package/lib/common/parse-contents.spec.js.map +1 -0
  86. package/lib/common/parsed-chat-request.d.ts +66 -0
  87. package/lib/common/parsed-chat-request.d.ts.map +1 -0
  88. package/lib/common/parsed-chat-request.js +83 -0
  89. package/lib/common/parsed-chat-request.js.map +1 -0
  90. package/lib/common/response-content-matcher.d.ts +63 -0
  91. package/lib/common/response-content-matcher.d.ts.map +1 -0
  92. package/lib/common/response-content-matcher.js +86 -0
  93. package/lib/common/response-content-matcher.js.map +1 -0
  94. package/lib/common/universal-chat-agent.d.ts +16 -0
  95. package/lib/common/universal-chat-agent.d.ts.map +1 -0
  96. package/lib/common/universal-chat-agent.js +109 -0
  97. package/lib/common/universal-chat-agent.js.map +1 -0
  98. package/package.json +54 -0
  99. package/src/browser/ai-chat-frontend-module.ts +98 -0
  100. package/src/browser/ai-chat-preferences.ts +32 -0
  101. package/src/browser/custom-agent-factory.ts +20 -0
  102. package/src/browser/custom-agent-frontend-application-contribution.ts +73 -0
  103. package/src/browser/frontend-chat-service.ts +66 -0
  104. package/src/common/chat-agent-service.ts +100 -0
  105. package/src/common/chat-agents-variable-contribution.ts +81 -0
  106. package/src/common/chat-agents.ts +392 -0
  107. package/src/common/chat-history-entry.ts +47 -0
  108. package/src/common/chat-model-util.ts +44 -0
  109. package/src/common/chat-model.ts +889 -0
  110. package/src/common/chat-request-parser.spec.ts +120 -0
  111. package/src/common/chat-request-parser.ts +220 -0
  112. package/src/common/chat-service.ts +236 -0
  113. package/src/common/command-chat-agents.ts +354 -0
  114. package/src/common/custom-chat-agent.ts +44 -0
  115. package/src/common/index.ts +26 -0
  116. package/src/common/o1-chat-agent.ts +51 -0
  117. package/src/common/orchestrator-chat-agent.ts +179 -0
  118. package/src/common/parse-contents.spec.ts +144 -0
  119. package/src/common/parse-contents.ts +93 -0
  120. package/src/common/parsed-chat-request.ts +112 -0
  121. package/src/common/response-content-matcher.ts +103 -0
  122. package/src/common/universal-chat-agent.ts +117 -0
@@ -0,0 +1,144 @@
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 { expect } from 'chai';
18
+ import { ChatRequestModelImpl, ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model';
19
+ import { parseContents } from './parse-contents';
20
+ import { CodeContentMatcher, ResponseContentMatcher } from './response-content-matcher';
21
+
22
+ export class CommandChatResponseContentImpl implements ChatResponseContent {
23
+ constructor(public readonly command: string) { }
24
+ kind = 'command';
25
+ }
26
+
27
+ export const CommandContentMatcher: ResponseContentMatcher = {
28
+ start: /^<command>$/m,
29
+ end: /^<\/command>$/m,
30
+ contentFactory: (content: string) => {
31
+ const code = content.replace(/^<command>\n|<\/command>$/g, '');
32
+ return new CommandChatResponseContentImpl(code.trim());
33
+ }
34
+ };
35
+
36
+ const fakeRequest = {} as ChatRequestModelImpl;
37
+
38
+ describe('parseContents', () => {
39
+ it('should parse code content', () => {
40
+ const text = '```typescript\nconsole.log("Hello World");\n```';
41
+ const result = parseContents(text, fakeRequest);
42
+ expect(result).to.deep.equal([new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript')]);
43
+ });
44
+
45
+ it('should parse markdown content', () => {
46
+ const text = 'Hello **World**';
47
+ const result = parseContents(text, fakeRequest);
48
+ expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Hello **World**')]);
49
+ });
50
+
51
+ it('should parse multiple content blocks', () => {
52
+ const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**';
53
+ const result = parseContents(text, fakeRequest);
54
+ expect(result).to.deep.equal([
55
+ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
56
+ new MarkdownChatResponseContentImpl('\nHello **World**')
57
+ ]);
58
+ });
59
+
60
+ it('should parse multiple content blocks with different languages', () => {
61
+ const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")\n```';
62
+ const result = parseContents(text, fakeRequest);
63
+ expect(result).to.deep.equal([
64
+ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
65
+ new CodeChatResponseContentImpl('print("Hello World")', 'python')
66
+ ]);
67
+ });
68
+
69
+ it('should parse multiple content blocks with different languages and markdown', () => {
70
+ const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**\n```python\nprint("Hello World")\n```';
71
+ const result = parseContents(text, fakeRequest);
72
+ expect(result).to.deep.equal([
73
+ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
74
+ new MarkdownChatResponseContentImpl('\nHello **World**\n'),
75
+ new CodeChatResponseContentImpl('print("Hello World")', 'python')
76
+ ]);
77
+ });
78
+
79
+ it('should parse content blocks with empty content', () => {
80
+ const text = '```typescript\n```\nHello **World**\n```python\nprint("Hello World")\n```';
81
+ const result = parseContents(text, fakeRequest);
82
+ expect(result).to.deep.equal([
83
+ new CodeChatResponseContentImpl('', 'typescript'),
84
+ new MarkdownChatResponseContentImpl('\nHello **World**\n'),
85
+ new CodeChatResponseContentImpl('print("Hello World")', 'python')
86
+ ]);
87
+ });
88
+
89
+ it('should parse content with markdown, code, and markdown', () => {
90
+ const text = 'Hello **World**\n```typescript\nconsole.log("Hello World");\n```\nGoodbye **World**';
91
+ const result = parseContents(text, fakeRequest);
92
+ expect(result).to.deep.equal([
93
+ new MarkdownChatResponseContentImpl('Hello **World**\n'),
94
+ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
95
+ new MarkdownChatResponseContentImpl('\nGoodbye **World**')
96
+ ]);
97
+ });
98
+
99
+ it('should handle text with no special content', () => {
100
+ const text = 'Just some plain text.';
101
+ const result = parseContents(text, fakeRequest);
102
+ expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Just some plain text.')]);
103
+ });
104
+
105
+ it('should handle text with only start code block', () => {
106
+ const text = '```typescript\nconsole.log("Hello World");';
107
+ const result = parseContents(text, fakeRequest);
108
+ expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('```typescript\nconsole.log("Hello World");')]);
109
+ });
110
+
111
+ it('should handle text with only end code block', () => {
112
+ const text = 'console.log("Hello World");\n```';
113
+ const result = parseContents(text, fakeRequest);
114
+ expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('console.log("Hello World");\n```')]);
115
+ });
116
+
117
+ it('should handle text with unmatched code block', () => {
118
+ const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")';
119
+ const result = parseContents(text, fakeRequest);
120
+ expect(result).to.deep.equal([
121
+ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
122
+ new MarkdownChatResponseContentImpl('\n```python\nprint("Hello World")')
123
+ ]);
124
+ });
125
+
126
+ it('should parse code block without newline after language', () => {
127
+ const text = '```typescript console.log("Hello World");```';
128
+ const result = parseContents(text, fakeRequest);
129
+ expect(result).to.deep.equal([
130
+ new MarkdownChatResponseContentImpl('```typescript console.log("Hello World");```')
131
+ ]);
132
+ });
133
+
134
+ it('should parse with matches of multiple different matchers and default', () => {
135
+ const text = '<command>\nMY_SPECIAL_COMMAND\n</command>\nHello **World**\n```python\nprint("Hello World")\n```\n<command>\nMY_SPECIAL_COMMAND2\n</command>';
136
+ const result = parseContents(text, fakeRequest, [CodeContentMatcher, CommandContentMatcher]);
137
+ expect(result).to.deep.equal([
138
+ new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND'),
139
+ new MarkdownChatResponseContentImpl('\nHello **World**\n'),
140
+ new CodeChatResponseContentImpl('print("Hello World")', 'python'),
141
+ new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND2'),
142
+ ]);
143
+ });
144
+ });
@@ -0,0 +1,93 @@
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 { ChatRequestModelImpl, ChatResponseContent } from './chat-model';
17
+ import { CodeContentMatcher, MarkdownContentFactory, ResponseContentFactory, ResponseContentMatcher } from './response-content-matcher';
18
+
19
+ interface Match {
20
+ matcher: ResponseContentMatcher;
21
+ index: number;
22
+ content: string;
23
+ }
24
+
25
+ export function parseContents(
26
+ text: string,
27
+ request: ChatRequestModelImpl,
28
+ contentMatchers: ResponseContentMatcher[] = [CodeContentMatcher],
29
+ defaultContentFactory: ResponseContentFactory = MarkdownContentFactory
30
+ ): ChatResponseContent[] {
31
+ const result: ChatResponseContent[] = [];
32
+
33
+ let currentIndex = 0;
34
+ while (currentIndex < text.length) {
35
+ const remainingText = text.substring(currentIndex);
36
+ const match = findFirstMatch(contentMatchers, remainingText);
37
+ if (!match) {
38
+ // Add the remaining text as default content
39
+ if (remainingText.length > 0) {
40
+ result.push(defaultContentFactory(remainingText, request));
41
+ }
42
+ break;
43
+ }
44
+ // We have a match
45
+ // 1. Add preceding text as default content
46
+ if (match.index > 0) {
47
+ const precedingContent = remainingText.substring(0, match.index);
48
+ if (precedingContent.trim().length > 0) {
49
+ result.push(defaultContentFactory(precedingContent, request));
50
+ }
51
+ }
52
+ // 2. Add the matched content object
53
+ result.push(match.matcher.contentFactory(match.content, request));
54
+ // Update currentIndex to the end of the end of the match
55
+ // And continue with the search after the end of the match
56
+ currentIndex += match.index + match.content.length;
57
+ }
58
+
59
+ return result;
60
+ }
61
+
62
+ export function findFirstMatch(contentMatchers: ResponseContentMatcher[], text: string): Match | undefined {
63
+ let firstMatch: { matcher: ResponseContentMatcher, index: number, content: string } | undefined;
64
+ for (const matcher of contentMatchers) {
65
+ const startMatch = matcher.start.exec(text);
66
+ if (!startMatch) {
67
+ // No start match found, try next matcher.
68
+ continue;
69
+ }
70
+ const endOfStartMatch = startMatch.index + startMatch[0].length;
71
+ if (endOfStartMatch >= text.length) {
72
+ // There is no text after the start match.
73
+ // No need to search for the end match yet, try next matcher.
74
+ continue;
75
+ }
76
+ const remainingTextAfterStartMatch = text.substring(endOfStartMatch);
77
+ const endMatch = matcher.end.exec(remainingTextAfterStartMatch);
78
+ if (!endMatch) {
79
+ // No end match found, try next matcher.
80
+ continue;
81
+ }
82
+ // Found start and end match.
83
+ // Record the full match, if it is the earliest found so far.
84
+ const index = startMatch.index;
85
+ const contentEnd = index + startMatch[0].length + endMatch.index + endMatch[0].length;
86
+ const content = text.substring(index, contentEnd);
87
+ if (!firstMatch || index < firstMatch.index) {
88
+ firstMatch = { matcher, index, content };
89
+ }
90
+ }
91
+ return firstMatch;
92
+ }
93
+
@@ -0,0 +1,112 @@
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
+ * Copyright (c) Microsoft Corporation. All rights reserved.
18
+ * Licensed under the MIT License. See License.txt in the project root for license information.
19
+ *--------------------------------------------------------------------------------------------*/
20
+ // Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatParserTypes.ts
21
+ // Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/editor/common/core/offsetRange.ts
22
+
23
+ import { AIVariable, ResolvedAIVariable, ToolRequest, toolRequestToPromptText } from '@theia/ai-core';
24
+ import { ChatRequest } from './chat-model';
25
+
26
+ export const chatVariableLeader = '#';
27
+ export const chatAgentLeader = '@';
28
+ export const chatFunctionLeader = '~';
29
+ export const chatSubcommandLeader = '/';
30
+
31
+ /**********************
32
+ * INTERFACES AND TYPE GUARDS
33
+ **********************/
34
+
35
+ export interface OffsetRange {
36
+ readonly start: number;
37
+ readonly endExclusive: number;
38
+ }
39
+
40
+ export interface ParsedChatRequest {
41
+ readonly request: ChatRequest;
42
+ readonly parts: ParsedChatRequestPart[];
43
+ readonly toolRequests: Map<string, ToolRequest>;
44
+ readonly variables: Map<string, AIVariable>;
45
+ }
46
+
47
+ export interface ParsedChatRequestPart {
48
+ readonly kind: string;
49
+ /**
50
+ * The text as represented in the ChatRequest
51
+ */
52
+ readonly text: string;
53
+ /**
54
+ * The text as will be sent to the LLM
55
+ */
56
+ readonly promptText: string;
57
+
58
+ readonly range: OffsetRange;
59
+ }
60
+
61
+ export class ParsedChatRequestTextPart implements ParsedChatRequestPart {
62
+ readonly kind: 'text';
63
+
64
+ constructor(readonly range: OffsetRange, readonly text: string) { }
65
+
66
+ get promptText(): string {
67
+ return this.text;
68
+ }
69
+ }
70
+
71
+ export class ParsedChatRequestVariablePart implements ParsedChatRequestPart {
72
+ readonly kind: 'var';
73
+
74
+ public resolution: ResolvedAIVariable;
75
+
76
+ constructor(readonly range: OffsetRange, readonly variableName: string, readonly variableArg: string | undefined) { }
77
+
78
+ get text(): string {
79
+ const argPart = this.variableArg ? `:${this.variableArg}` : '';
80
+ return `${chatVariableLeader}${this.variableName}${argPart}`;
81
+ }
82
+
83
+ get promptText(): string {
84
+ return this.resolution?.value ?? this.text;
85
+ }
86
+ }
87
+
88
+ export class ParsedChatRequestFunctionPart implements ParsedChatRequestPart {
89
+ readonly kind: 'function';
90
+ constructor(readonly range: OffsetRange, readonly toolRequest: ToolRequest) { }
91
+
92
+ get text(): string {
93
+ return `${chatFunctionLeader}${this.toolRequest.id}`;
94
+ }
95
+
96
+ get promptText(): string {
97
+ return toolRequestToPromptText(this.toolRequest);
98
+ }
99
+ }
100
+
101
+ export class ParsedChatRequestAgentPart implements ParsedChatRequestPart {
102
+ readonly kind: 'agent';
103
+ constructor(readonly range: OffsetRange, readonly agentId: string, readonly agentName: string) { }
104
+
105
+ get text(): string {
106
+ return `${chatAgentLeader}${this.agentName}`;
107
+ }
108
+
109
+ get promptText(): string {
110
+ return '';
111
+ }
112
+ }
@@ -0,0 +1,103 @@
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 {
17
+ ChatRequestModelImpl,
18
+ ChatResponseContent,
19
+ CodeChatResponseContentImpl,
20
+ MarkdownChatResponseContentImpl
21
+ } from './chat-model';
22
+ import { injectable } from '@theia/core/shared/inversify';
23
+
24
+ export type ResponseContentFactory = (content: string, request: ChatRequestModelImpl) => ChatResponseContent;
25
+
26
+ export const MarkdownContentFactory: ResponseContentFactory = (content: string) =>
27
+ new MarkdownChatResponseContentImpl(content);
28
+
29
+ /**
30
+ * Default response content factory used if no other `ResponseContentMatcher` applies.
31
+ * By default, this factory creates a markdown content object.
32
+ *
33
+ * @see MarkdownChatResponseContentImpl
34
+ */
35
+ @injectable()
36
+ export class DefaultResponseContentFactory {
37
+ create(content: string, request: ChatRequestModelImpl): ChatResponseContent {
38
+ return MarkdownContentFactory(content, request);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Clients can contribute response content matchers to parse a chat response into specific
44
+ * `ChatResponseContent` instances.
45
+ */
46
+ export interface ResponseContentMatcher {
47
+ /** Regular expression for finding the start delimiter. */
48
+ start: RegExp;
49
+ /** Regular expression for finding the start delimiter. */
50
+ end: RegExp;
51
+ /**
52
+ * The factory creating a response content from the matching content,
53
+ * from start index to end index of the match (including delimiters).
54
+ */
55
+ contentFactory: ResponseContentFactory;
56
+ }
57
+
58
+ export const CodeContentMatcher: ResponseContentMatcher = {
59
+ start: /^```.*?$/m,
60
+ end: /^```$/m,
61
+ contentFactory: (content: string) => {
62
+ const language = content.match(/^```(\w+)/)?.[1] || '';
63
+ const code = content.replace(/^```(\w+)\n|```$/g, '');
64
+ return new CodeChatResponseContentImpl(code.trim(), language);
65
+ }
66
+ };
67
+
68
+ /**
69
+ * Clients can contribute response content matchers to parse the response content.
70
+ *
71
+ * The default chat user interface will collect all contributed matchers and use them
72
+ * to parse the response into structured content parts (e.g. code blocks, markdown blocks),
73
+ * which are then rendered with a `ChatResponsePartRenderer` registered for the respective
74
+ * content part type.
75
+ *
76
+ * ### Example
77
+ * ```ts
78
+ * bind(ResponseContentMatcherProvider).to(MyResponseContentMatcherProvider);
79
+ * ...
80
+ * @injectable()
81
+ * export class MyResponseContentMatcherProvider implements ResponseContentMatcherProvider {
82
+ * readonly matchers: ResponseContentMatcher[] = [{
83
+ * start: /^<command>$/m,
84
+ * end: /^</command>$/m,
85
+ * contentFactory: (content: string) => {
86
+ * const command = content.replace(/^<command>\n|<\/command>$/g, '');
87
+ * return new MyChatResponseContentImpl(command.trim());
88
+ * }
89
+ * }];
90
+ * }
91
+ * ```
92
+ *
93
+ * @see ResponseContentMatcher
94
+ */
95
+ export const ResponseContentMatcherProvider = Symbol('ResponseContentMatcherProvider');
96
+ export interface ResponseContentMatcherProvider {
97
+ readonly matchers: ResponseContentMatcher[];
98
+ }
99
+
100
+ @injectable()
101
+ export class DefaultResponseContentMatcherProvider implements ResponseContentMatcherProvider {
102
+ readonly matchers: ResponseContentMatcher[] = [CodeContentMatcher];
103
+ }
@@ -0,0 +1,117 @@
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 { AgentSpecificVariables } from '@theia/ai-core';
18
+ import {
19
+ PromptTemplate
20
+ } from '@theia/ai-core/lib/common';
21
+ import { injectable } from '@theia/core/shared/inversify';
22
+ import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents';
23
+
24
+ export const universalTemplate: PromptTemplate = {
25
+ id: 'universal-system',
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
29
+
30
+ You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by
31
+ providing concise and accurate answers to programming-related questions. Your role is to enhance the
32
+ developer's productivity by offering quick solutions, explanations, and best practices.
33
+ Keep responses short and to the point, focusing on delivering valuable insights, best practices and
34
+ simple solutions.
35
+
36
+ ### Guidelines
37
+
38
+ 1. **Understand Context:**
39
+ - Assess the context of the code or issue when available.
40
+ - Tailor responses to be relevant to the programming language, framework, or tools like Eclipse Theia.
41
+ - Ask clarifying questions if necessary to provide accurate assistance.
42
+
43
+ 2. **Provide Clear Solutions:**
44
+ - Offer direct answers or code snippets that solve the problem or clarify the concept.
45
+ - Avoid lengthy explanations unless necessary for understanding.
46
+
47
+ 3. **Promote Best Practices:**
48
+ - Suggest best practices and common patterns relevant to the question.
49
+ - Provide links to official documentation for further reading when applicable.
50
+
51
+ 4. **Support Multiple Languages and Tools:**
52
+ - Be familiar with popular programming languages, frameworks, IDEs like Eclipse Theia, and command-line tools.
53
+ - Adapt advice based on the language, environment, or tools specified by the developer.
54
+
55
+ 5. **Facilitate Learning:**
56
+ - Encourage learning by explaining why a solution works or why a particular approach is recommended.
57
+ - Keep explanations concise and educational.
58
+
59
+ 6. **Maintain Professional Tone:**
60
+ - Communicate in a friendly, professional manner.
61
+ - Use technical jargon appropriately, ensuring clarity for the target audience.
62
+
63
+ 7. **Stay on Topic:**
64
+ - Limit responses strictly to topics related to software development, frameworks, Eclipse Theia, terminal usage, and relevant technologies.
65
+ - Politely decline to answer questions unrelated to these areas by saying, "I'm here to assist with programming-related questions.
66
+ For other topics, please refer to a specialized source."
67
+
68
+ ### Example Interactions
69
+
70
+ - **Question:** "What's the difference between \`let\` and \`var\` in JavaScript?"
71
+ **Answer:** "\`let\` is block-scoped, while \`var\` is function-scoped. Prefer \`let\` to avoid scope-related bugs."
72
+
73
+ - **Question:** "How do I handle exceptions in Java?"
74
+ **Answer:** "Use try-catch blocks: \`\`\`java try { /* code */ } catch (ExceptionType e) { /* handle exception */ }\`\`\`."
75
+
76
+ - **Question:** "What is the capital of France?"
77
+ **Answer:** "I'm here to assist with programming-related queries. For other topics, please refer to a specialized source."
78
+ `
79
+ };
80
+
81
+ export const universalTemplateVariant: PromptTemplate = {
82
+ id: 'universal-system-empty',
83
+ template: '',
84
+ variantOf: universalTemplate.id,
85
+ };
86
+
87
+ @injectable()
88
+ export class UniversalChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent {
89
+ name: string;
90
+ description: string;
91
+ variables: string[];
92
+ promptTemplates: PromptTemplate[];
93
+ readonly functions: string[];
94
+ readonly agentSpecificVariables: AgentSpecificVariables[];
95
+
96
+ constructor() {
97
+ super('Universal', [{
98
+ purpose: 'chat',
99
+ identifier: 'openai/gpt-4o',
100
+ }], 'chat');
101
+ this.name = 'Universal';
102
+ this.description = 'This agent is designed to help software developers by providing concise and accurate '
103
+ + 'answers to general programming and software development questions. It is also the fall-back for any generic '
104
+ + 'questions the user might ask. The universal agent currently does not have any context by default, i.e. it cannot '
105
+ + 'access the current user context or the workspace.';
106
+ this.variables = [];
107
+ this.promptTemplates = [universalTemplate, universalTemplateVariant];
108
+ this.functions = [];
109
+ this.agentSpecificVariables = [];
110
+ }
111
+
112
+ protected override async getSystemMessageDescription(): Promise<SystemMessageDescription | undefined> {
113
+ const resolvedPrompt = await this.promptService.getPrompt(universalTemplate.id);
114
+ return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined;
115
+ }
116
+
117
+ }