@theia/ai-chat 1.54.0 → 1.55.0-next.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-frontend-module.js +5 -0
- package/lib/browser/ai-chat-frontend-module.js.map +1 -1
- package/lib/common/chat-agents.d.ts +11 -4
- package/lib/common/chat-agents.d.ts.map +1 -1
- package/lib/common/chat-agents.js +58 -52
- package/lib/common/chat-agents.js.map +1 -1
- package/lib/common/chat-model.d.ts +2 -0
- package/lib/common/chat-model.d.ts.map +1 -1
- package/lib/common/chat-model.js +9 -2
- package/lib/common/chat-model.js.map +1 -1
- package/lib/common/parse-contents.d.ts +11 -0
- package/lib/common/parse-contents.d.ts.map +1 -0
- package/lib/common/parse-contents.js +67 -0
- package/lib/common/parse-contents.js.map +1 -0
- package/lib/common/parse-contents.spec.d.ts +9 -0
- package/lib/common/parse-contents.spec.d.ts.map +1 -0
- package/lib/common/parse-contents.spec.js +133 -0
- package/lib/common/parse-contents.spec.js.map +1 -0
- package/lib/common/response-content-matcher.d.ts +63 -0
- package/lib/common/response-content-matcher.d.ts.map +1 -0
- package/lib/common/response-content-matcher.js +86 -0
- package/lib/common/response-content-matcher.js.map +1 -0
- package/package.json +7 -7
- package/src/browser/ai-chat-frontend-module.ts +6 -0
- package/src/common/chat-agents.ts +59 -60
- package/src/common/chat-model.ts +13 -5
- package/src/common/parse-contents.spec.ts +142 -0
- package/src/common/parse-contents.ts +92 -0
- package/src/common/response-content-matcher.ts +102 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.CommandContentMatcher = exports.CommandChatResponseContentImpl = void 0;
|
|
19
|
+
const chai_1 = require("chai");
|
|
20
|
+
const chat_model_1 = require("./chat-model");
|
|
21
|
+
const parse_contents_1 = require("./parse-contents");
|
|
22
|
+
const response_content_matcher_1 = require("./response-content-matcher");
|
|
23
|
+
class CommandChatResponseContentImpl {
|
|
24
|
+
constructor(command) {
|
|
25
|
+
this.command = command;
|
|
26
|
+
this.kind = 'command';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.CommandChatResponseContentImpl = CommandChatResponseContentImpl;
|
|
30
|
+
exports.CommandContentMatcher = {
|
|
31
|
+
start: /^<command>$/m,
|
|
32
|
+
end: /^<\/command>$/m,
|
|
33
|
+
contentFactory: (content) => {
|
|
34
|
+
const code = content.replace(/^<command>\n|<\/command>$/g, '');
|
|
35
|
+
return new CommandChatResponseContentImpl(code.trim());
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
describe('parseContents', () => {
|
|
39
|
+
it('should parse code content', () => {
|
|
40
|
+
const text = '```typescript\nconsole.log("Hello World");\n```';
|
|
41
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
42
|
+
(0, chai_1.expect)(result).to.deep.equal([new chat_model_1.CodeChatResponseContentImpl('console.log("Hello World");', 'typescript')]);
|
|
43
|
+
});
|
|
44
|
+
it('should parse markdown content', () => {
|
|
45
|
+
const text = 'Hello **World**';
|
|
46
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
47
|
+
(0, chai_1.expect)(result).to.deep.equal([new chat_model_1.MarkdownChatResponseContentImpl('Hello **World**')]);
|
|
48
|
+
});
|
|
49
|
+
it('should parse multiple content blocks', () => {
|
|
50
|
+
const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**';
|
|
51
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
52
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
53
|
+
new chat_model_1.CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
|
|
54
|
+
new chat_model_1.MarkdownChatResponseContentImpl('\nHello **World**')
|
|
55
|
+
]);
|
|
56
|
+
});
|
|
57
|
+
it('should parse multiple content blocks with different languages', () => {
|
|
58
|
+
const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")\n```';
|
|
59
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
60
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
61
|
+
new chat_model_1.CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
|
|
62
|
+
new chat_model_1.CodeChatResponseContentImpl('print("Hello World")', 'python')
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
it('should parse multiple content blocks with different languages and markdown', () => {
|
|
66
|
+
const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**\n```python\nprint("Hello World")\n```';
|
|
67
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
68
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
69
|
+
new chat_model_1.CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
|
|
70
|
+
new chat_model_1.MarkdownChatResponseContentImpl('\nHello **World**\n'),
|
|
71
|
+
new chat_model_1.CodeChatResponseContentImpl('print("Hello World")', 'python')
|
|
72
|
+
]);
|
|
73
|
+
});
|
|
74
|
+
it('should parse content blocks with empty content', () => {
|
|
75
|
+
const text = '```typescript\n```\nHello **World**\n```python\nprint("Hello World")\n```';
|
|
76
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
77
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
78
|
+
new chat_model_1.CodeChatResponseContentImpl('', 'typescript'),
|
|
79
|
+
new chat_model_1.MarkdownChatResponseContentImpl('\nHello **World**\n'),
|
|
80
|
+
new chat_model_1.CodeChatResponseContentImpl('print("Hello World")', 'python')
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
it('should parse content with markdown, code, and markdown', () => {
|
|
84
|
+
const text = 'Hello **World**\n```typescript\nconsole.log("Hello World");\n```\nGoodbye **World**';
|
|
85
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
86
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
87
|
+
new chat_model_1.MarkdownChatResponseContentImpl('Hello **World**\n'),
|
|
88
|
+
new chat_model_1.CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
|
|
89
|
+
new chat_model_1.MarkdownChatResponseContentImpl('\nGoodbye **World**')
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
it('should handle text with no special content', () => {
|
|
93
|
+
const text = 'Just some plain text.';
|
|
94
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
95
|
+
(0, chai_1.expect)(result).to.deep.equal([new chat_model_1.MarkdownChatResponseContentImpl('Just some plain text.')]);
|
|
96
|
+
});
|
|
97
|
+
it('should handle text with only start code block', () => {
|
|
98
|
+
const text = '```typescript\nconsole.log("Hello World");';
|
|
99
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
100
|
+
(0, chai_1.expect)(result).to.deep.equal([new chat_model_1.MarkdownChatResponseContentImpl('```typescript\nconsole.log("Hello World");')]);
|
|
101
|
+
});
|
|
102
|
+
it('should handle text with only end code block', () => {
|
|
103
|
+
const text = 'console.log("Hello World");\n```';
|
|
104
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
105
|
+
(0, chai_1.expect)(result).to.deep.equal([new chat_model_1.MarkdownChatResponseContentImpl('console.log("Hello World");\n```')]);
|
|
106
|
+
});
|
|
107
|
+
it('should handle text with unmatched code block', () => {
|
|
108
|
+
const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")';
|
|
109
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
110
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
111
|
+
new chat_model_1.CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'),
|
|
112
|
+
new chat_model_1.MarkdownChatResponseContentImpl('\n```python\nprint("Hello World")')
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
it('should parse code block without newline after language', () => {
|
|
116
|
+
const text = '```typescript console.log("Hello World");```';
|
|
117
|
+
const result = (0, parse_contents_1.parseContents)(text);
|
|
118
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
119
|
+
new chat_model_1.MarkdownChatResponseContentImpl('```typescript console.log("Hello World");```')
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
it('should parse with matches of multiple different matchers and default', () => {
|
|
123
|
+
const text = '<command>\nMY_SPECIAL_COMMAND\n</command>\nHello **World**\n```python\nprint("Hello World")\n```\n<command>\nMY_SPECIAL_COMMAND2\n</command>';
|
|
124
|
+
const result = (0, parse_contents_1.parseContents)(text, [response_content_matcher_1.CodeContentMatcher, exports.CommandContentMatcher]);
|
|
125
|
+
(0, chai_1.expect)(result).to.deep.equal([
|
|
126
|
+
new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND'),
|
|
127
|
+
new chat_model_1.MarkdownChatResponseContentImpl('\nHello **World**\n'),
|
|
128
|
+
new chat_model_1.CodeChatResponseContentImpl('print("Hello World")', 'python'),
|
|
129
|
+
new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND2'),
|
|
130
|
+
]);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
//# sourceMappingURL=parse-contents.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-contents.spec.js","sourceRoot":"","sources":["../../src/common/parse-contents.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ChatResponseContent } from './chat-model';
|
|
2
|
+
export type ResponseContentFactory = (content: string) => ChatResponseContent;
|
|
3
|
+
export declare const MarkdownContentFactory: ResponseContentFactory;
|
|
4
|
+
/**
|
|
5
|
+
* Default response content factory used if no other `ResponseContentMatcher` applies.
|
|
6
|
+
* By default, this factory creates a markdown content object.
|
|
7
|
+
*
|
|
8
|
+
* @see MarkdownChatResponseContentImpl
|
|
9
|
+
*/
|
|
10
|
+
export declare class DefaultResponseContentFactory {
|
|
11
|
+
create(content: string): ChatResponseContent;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Clients can contribute response content matchers to parse a chat response into specific
|
|
15
|
+
* `ChatResponseContent` instances.
|
|
16
|
+
*/
|
|
17
|
+
export interface ResponseContentMatcher {
|
|
18
|
+
/** Regular expression for finding the start delimiter. */
|
|
19
|
+
start: RegExp;
|
|
20
|
+
/** Regular expression for finding the start delimiter. */
|
|
21
|
+
end: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* The factory creating a response content from the matching content,
|
|
24
|
+
* from start index to end index of the match (including delimiters).
|
|
25
|
+
*/
|
|
26
|
+
contentFactory: ResponseContentFactory;
|
|
27
|
+
}
|
|
28
|
+
export declare const CodeContentMatcher: ResponseContentMatcher;
|
|
29
|
+
/**
|
|
30
|
+
* Clients can contribute response content matchers to parse the response content.
|
|
31
|
+
*
|
|
32
|
+
* The default chat user interface will collect all contributed matchers and use them
|
|
33
|
+
* to parse the response into structured content parts (e.g. code blocks, markdown blocks),
|
|
34
|
+
* which are then rendered with a `ChatResponsePartRenderer` registered for the respective
|
|
35
|
+
* content part type.
|
|
36
|
+
*
|
|
37
|
+
* ### Example
|
|
38
|
+
* ```ts
|
|
39
|
+
* bind(ResponseContentMatcherProvider).to(MyResponseContentMatcherProvider);
|
|
40
|
+
* ...
|
|
41
|
+
* @injectable()
|
|
42
|
+
* export class MyResponseContentMatcherProvider implements ResponseContentMatcherProvider {
|
|
43
|
+
* readonly matchers: ResponseContentMatcher[] = [{
|
|
44
|
+
* start: /^<command>$/m,
|
|
45
|
+
* end: /^</command>$/m,
|
|
46
|
+
* contentFactory: (content: string) => {
|
|
47
|
+
* const command = content.replace(/^<command>\n|<\/command>$/g, '');
|
|
48
|
+
* return new MyChatResponseContentImpl(command.trim());
|
|
49
|
+
* }
|
|
50
|
+
* }];
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @see ResponseContentMatcher
|
|
55
|
+
*/
|
|
56
|
+
export declare const ResponseContentMatcherProvider: unique symbol;
|
|
57
|
+
export interface ResponseContentMatcherProvider {
|
|
58
|
+
readonly matchers: ResponseContentMatcher[];
|
|
59
|
+
}
|
|
60
|
+
export declare class DefaultResponseContentMatcherProvider implements ResponseContentMatcherProvider {
|
|
61
|
+
readonly matchers: ResponseContentMatcher[];
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=response-content-matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-content-matcher.d.ts","sourceRoot":"","sources":["../../src/common/response-content-matcher.ts"],"names":[],"mappings":"AAeA,OAAO,EACH,mBAAmB,EAGtB,MAAM,cAAc,CAAC;AAGtB,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,mBAAmB,CAAC;AAE9E,eAAO,MAAM,sBAAsB,EAAE,sBACW,CAAC;AAEjD;;;;;GAKG;AACH,qBACa,6BAA6B;IACtC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB;CAG/C;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACnC,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,EAAE,sBAAsB,CAAC;CAC1C;AAED,eAAO,MAAM,kBAAkB,EAAE,sBAQhC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AACvF,MAAM,WAAW,8BAA8B;IAC3C,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CAC/C;AAED,qBACa,qCAAsC,YAAW,8BAA8B;IACxF,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,EAAE,CAAwB;CACtE"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DefaultResponseContentMatcherProvider = exports.ResponseContentMatcherProvider = exports.CodeContentMatcher = exports.DefaultResponseContentFactory = exports.MarkdownContentFactory = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/*
|
|
6
|
+
* Copyright (C) 2024 EclipseSource GmbH.
|
|
7
|
+
*
|
|
8
|
+
* This program and the accompanying materials are made available under the
|
|
9
|
+
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
10
|
+
* http://www.eclipse.org/legal/epl-2.0.
|
|
11
|
+
*
|
|
12
|
+
* This Source Code may also be made available under the following Secondary
|
|
13
|
+
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
14
|
+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
15
|
+
* with the GNU Classpath Exception which is available at
|
|
16
|
+
* https://www.gnu.org/software/classpath/license.html.
|
|
17
|
+
*
|
|
18
|
+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
19
|
+
*/
|
|
20
|
+
const chat_model_1 = require("./chat-model");
|
|
21
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
22
|
+
const MarkdownContentFactory = (content) => new chat_model_1.MarkdownChatResponseContentImpl(content);
|
|
23
|
+
exports.MarkdownContentFactory = MarkdownContentFactory;
|
|
24
|
+
/**
|
|
25
|
+
* Default response content factory used if no other `ResponseContentMatcher` applies.
|
|
26
|
+
* By default, this factory creates a markdown content object.
|
|
27
|
+
*
|
|
28
|
+
* @see MarkdownChatResponseContentImpl
|
|
29
|
+
*/
|
|
30
|
+
let DefaultResponseContentFactory = class DefaultResponseContentFactory {
|
|
31
|
+
create(content) {
|
|
32
|
+
return (0, exports.MarkdownContentFactory)(content);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
exports.DefaultResponseContentFactory = DefaultResponseContentFactory;
|
|
36
|
+
exports.DefaultResponseContentFactory = DefaultResponseContentFactory = tslib_1.__decorate([
|
|
37
|
+
(0, inversify_1.injectable)()
|
|
38
|
+
], DefaultResponseContentFactory);
|
|
39
|
+
exports.CodeContentMatcher = {
|
|
40
|
+
start: /^```.*?$/m,
|
|
41
|
+
end: /^```$/m,
|
|
42
|
+
contentFactory: (content) => {
|
|
43
|
+
var _a;
|
|
44
|
+
const language = ((_a = content.match(/^```(\w+)/)) === null || _a === void 0 ? void 0 : _a[1]) || '';
|
|
45
|
+
const code = content.replace(/^```(\w+)\n|```$/g, '');
|
|
46
|
+
return new chat_model_1.CodeChatResponseContentImpl(code.trim(), language);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Clients can contribute response content matchers to parse the response content.
|
|
51
|
+
*
|
|
52
|
+
* The default chat user interface will collect all contributed matchers and use them
|
|
53
|
+
* to parse the response into structured content parts (e.g. code blocks, markdown blocks),
|
|
54
|
+
* which are then rendered with a `ChatResponsePartRenderer` registered for the respective
|
|
55
|
+
* content part type.
|
|
56
|
+
*
|
|
57
|
+
* ### Example
|
|
58
|
+
* ```ts
|
|
59
|
+
* bind(ResponseContentMatcherProvider).to(MyResponseContentMatcherProvider);
|
|
60
|
+
* ...
|
|
61
|
+
* @injectable()
|
|
62
|
+
* export class MyResponseContentMatcherProvider implements ResponseContentMatcherProvider {
|
|
63
|
+
* readonly matchers: ResponseContentMatcher[] = [{
|
|
64
|
+
* start: /^<command>$/m,
|
|
65
|
+
* end: /^</command>$/m,
|
|
66
|
+
* contentFactory: (content: string) => {
|
|
67
|
+
* const command = content.replace(/^<command>\n|<\/command>$/g, '');
|
|
68
|
+
* return new MyChatResponseContentImpl(command.trim());
|
|
69
|
+
* }
|
|
70
|
+
* }];
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @see ResponseContentMatcher
|
|
75
|
+
*/
|
|
76
|
+
exports.ResponseContentMatcherProvider = Symbol('ResponseContentMatcherProvider');
|
|
77
|
+
let DefaultResponseContentMatcherProvider = class DefaultResponseContentMatcherProvider {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.matchers = [exports.CodeContentMatcher];
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.DefaultResponseContentMatcherProvider = DefaultResponseContentMatcherProvider;
|
|
83
|
+
exports.DefaultResponseContentMatcherProvider = DefaultResponseContentMatcherProvider = tslib_1.__decorate([
|
|
84
|
+
(0, inversify_1.injectable)()
|
|
85
|
+
], DefaultResponseContentMatcherProvider);
|
|
86
|
+
//# sourceMappingURL=response-content-matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-content-matcher.js","sourceRoot":"","sources":["../../src/common/response-content-matcher.ts"],"names":[],"mappings":";;;;AAAA;;;;;;;;;;;;;;GAcG;AACH,6CAIsB;AACtB,4DAA0D;AAInD,MAAM,sBAAsB,GAA2B,CAAC,OAAe,EAAE,EAAE,CAC9E,IAAI,4CAA+B,CAAC,OAAO,CAAC,CAAC;AADpC,QAAA,sBAAsB,0BACc;AAEjD;;;;;GAKG;AAEI,IAAM,6BAA6B,GAAnC,MAAM,6BAA6B;IACtC,MAAM,CAAC,OAAe;QAClB,OAAO,IAAA,8BAAsB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;CACJ,CAAA;AAJY,sEAA6B;wCAA7B,6BAA6B;IADzC,IAAA,sBAAU,GAAE;GACA,6BAA6B,CAIzC;AAkBY,QAAA,kBAAkB,GAA2B;IACtD,KAAK,EAAE,WAAW;IAClB,GAAG,EAAE,QAAQ;IACb,cAAc,EAAE,CAAC,OAAe,EAAE,EAAE;;QAChC,MAAM,QAAQ,GAAG,CAAA,MAAA,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,0CAAG,CAAC,CAAC,KAAI,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QACtD,OAAO,IAAI,wCAA2B,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;IAClE,CAAC;CACJ,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACU,QAAA,8BAA8B,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAMhF,IAAM,qCAAqC,GAA3C,MAAM,qCAAqC;IAA3C;QACM,aAAQ,GAA6B,CAAC,0BAAkB,CAAC,CAAC;IACvE,CAAC;CAAA,CAAA;AAFY,sFAAqC;gDAArC,qCAAqC;IADjD,IAAA,sBAAU,GAAE;GACA,qCAAqC,CAEjD"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/ai-chat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0-next.14+d999fe031",
|
|
4
4
|
"description": "Theia - AI Chat Extension",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/ai-core": "1.
|
|
7
|
-
"@theia/ai-history": "1.
|
|
8
|
-
"@theia/core": "1.
|
|
9
|
-
"@theia/filesystem": "1.
|
|
10
|
-
"@theia/workspace": "1.
|
|
6
|
+
"@theia/ai-core": "1.55.0-next.14+d999fe031",
|
|
7
|
+
"@theia/ai-history": "1.55.0-next.14+d999fe031",
|
|
8
|
+
"@theia/core": "1.55.0-next.14+d999fe031",
|
|
9
|
+
"@theia/filesystem": "1.55.0-next.14+d999fe031",
|
|
10
|
+
"@theia/workspace": "1.55.0-next.14+d999fe031",
|
|
11
11
|
"minimatch": "^5.1.0",
|
|
12
12
|
"tslib": "^2.6.2"
|
|
13
13
|
},
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"nyc": {
|
|
51
51
|
"extends": "../../configs/nyc.json"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "d999fe0315a29bc5b154067621dbc1eb180fa567"
|
|
54
54
|
}
|
|
@@ -33,6 +33,7 @@ import { UniversalChatAgent } from '../common/universal-chat-agent';
|
|
|
33
33
|
import { aiChatPreferences } from './ai-chat-preferences';
|
|
34
34
|
import { ChatAgentsVariableContribution } from '../common/chat-agents-variable-contribution';
|
|
35
35
|
import { FrontendChatServiceImpl } from './frontend-chat-service';
|
|
36
|
+
import { DefaultResponseContentMatcherProvider, DefaultResponseContentFactory, ResponseContentMatcherProvider } from '../common/response-content-matcher';
|
|
36
37
|
|
|
37
38
|
export default new ContainerModule(bind => {
|
|
38
39
|
bindContributionProvider(bind, Agent);
|
|
@@ -42,6 +43,11 @@ export default new ContainerModule(bind => {
|
|
|
42
43
|
bind(ChatAgentService).toService(ChatAgentServiceImpl);
|
|
43
44
|
bind(DefaultChatAgentId).toConstantValue({ id: OrchestratorChatAgentId });
|
|
44
45
|
|
|
46
|
+
bindContributionProvider(bind, ResponseContentMatcherProvider);
|
|
47
|
+
bind(DefaultResponseContentMatcherProvider).toSelf().inSingletonScope();
|
|
48
|
+
bind(ResponseContentMatcherProvider).toService(DefaultResponseContentMatcherProvider);
|
|
49
|
+
bind(DefaultResponseContentFactory).toSelf().inSingletonScope();
|
|
50
|
+
|
|
45
51
|
bind(AIVariableContribution).to(ChatAgentsVariableContribution).inSingletonScope();
|
|
46
52
|
|
|
47
53
|
bind(ChatRequestParserImpl).toSelf().inSingletonScope();
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
LanguageModel,
|
|
26
26
|
LanguageModelRequirement,
|
|
27
27
|
LanguageModelResponse,
|
|
28
|
+
LanguageModelStreamResponse,
|
|
28
29
|
PromptService,
|
|
29
30
|
ResolvedPromptTemplate,
|
|
30
31
|
ToolRequest,
|
|
@@ -37,19 +38,20 @@ import {
|
|
|
37
38
|
LanguageModelStreamResponsePart,
|
|
38
39
|
MessageActor,
|
|
39
40
|
} from '@theia/ai-core/lib/common';
|
|
40
|
-
import { CancellationToken, CancellationTokenSource, ILogger, isArray } from '@theia/core';
|
|
41
|
-
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
41
|
+
import { CancellationToken, CancellationTokenSource, ContributionProvider, ILogger, isArray } from '@theia/core';
|
|
42
|
+
import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
|
|
42
43
|
import { ChatAgentService } from './chat-agent-service';
|
|
43
44
|
import {
|
|
44
45
|
ChatModel,
|
|
45
46
|
ChatRequestModel,
|
|
46
47
|
ChatRequestModelImpl,
|
|
47
48
|
ChatResponseContent,
|
|
48
|
-
CodeChatResponseContentImpl,
|
|
49
49
|
ErrorChatResponseContentImpl,
|
|
50
50
|
MarkdownChatResponseContentImpl,
|
|
51
51
|
ToolCallChatResponseContentImpl
|
|
52
52
|
} from './chat-model';
|
|
53
|
+
import { findFirstMatch, parseContents } from './parse-contents';
|
|
54
|
+
import { DefaultResponseContentFactory, ResponseContentMatcher, ResponseContentMatcherProvider } from './response-content-matcher';
|
|
53
55
|
|
|
54
56
|
/**
|
|
55
57
|
* A conversation consists of a sequence of ChatMessages.
|
|
@@ -121,6 +123,14 @@ export abstract class AbstractChatAgent {
|
|
|
121
123
|
@inject(ILogger) protected logger: ILogger;
|
|
122
124
|
@inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService;
|
|
123
125
|
@inject(PromptService) protected promptService: PromptService;
|
|
126
|
+
|
|
127
|
+
@inject(ContributionProvider) @named(ResponseContentMatcherProvider)
|
|
128
|
+
protected contentMatcherProviders: ContributionProvider<ResponseContentMatcherProvider>;
|
|
129
|
+
protected contentMatchers: ResponseContentMatcher[] = [];
|
|
130
|
+
|
|
131
|
+
@inject(DefaultResponseContentFactory)
|
|
132
|
+
protected defaultContentFactory: DefaultResponseContentFactory;
|
|
133
|
+
|
|
124
134
|
constructor(
|
|
125
135
|
public id: string,
|
|
126
136
|
public languageModelRequirements: LanguageModelRequirement[],
|
|
@@ -130,6 +140,11 @@ export abstract class AbstractChatAgent {
|
|
|
130
140
|
public tags: String[] = ['Chat']) {
|
|
131
141
|
}
|
|
132
142
|
|
|
143
|
+
@postConstruct()
|
|
144
|
+
init(): void {
|
|
145
|
+
this.contentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers);
|
|
146
|
+
}
|
|
147
|
+
|
|
133
148
|
async invoke(request: ChatRequestModelImpl): Promise<void> {
|
|
134
149
|
try {
|
|
135
150
|
const languageModel = await this.getLanguageModel(this.defaultLanguageModelPurpose);
|
|
@@ -189,6 +204,14 @@ export abstract class AbstractChatAgent {
|
|
|
189
204
|
}
|
|
190
205
|
}
|
|
191
206
|
|
|
207
|
+
protected parseContents(text: string): ChatResponseContent[] {
|
|
208
|
+
return parseContents(
|
|
209
|
+
text,
|
|
210
|
+
this.contentMatchers,
|
|
211
|
+
this.defaultContentFactory?.create.bind(this.defaultContentFactory)
|
|
212
|
+
);
|
|
213
|
+
};
|
|
214
|
+
|
|
192
215
|
protected handleError(request: ChatRequestModelImpl, error: Error): void {
|
|
193
216
|
request.response.response.addContent(new ErrorChatResponseContentImpl(error));
|
|
194
217
|
request.response.error(error);
|
|
@@ -281,9 +304,8 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
|
|
|
281
304
|
|
|
282
305
|
protected override async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise<void> {
|
|
283
306
|
if (isLanguageModelTextResponse(languageModelResponse)) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
);
|
|
307
|
+
const contents = this.parseContents(languageModelResponse.text);
|
|
308
|
+
request.response.response.addContents(contents);
|
|
287
309
|
request.response.complete();
|
|
288
310
|
this.recordingService.recordResponse({
|
|
289
311
|
agentId: this.id,
|
|
@@ -295,57 +317,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
|
|
|
295
317
|
return;
|
|
296
318
|
}
|
|
297
319
|
if (isLanguageModelStreamResponse(languageModelResponse)) {
|
|
298
|
-
|
|
299
|
-
const newContents = this.parse(token, request.response.response.content);
|
|
300
|
-
if (isArray(newContents)) {
|
|
301
|
-
newContents.forEach(newContent => request.response.response.addContent(newContent));
|
|
302
|
-
} else {
|
|
303
|
-
request.response.response.addContent(newContents);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const lastContent = request.response.response.content.pop();
|
|
307
|
-
if (lastContent === undefined) {
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const text = lastContent.asString?.();
|
|
311
|
-
if (text === undefined) {
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
let curSearchIndex = 0;
|
|
315
|
-
const result: ChatResponseContent[] = [];
|
|
316
|
-
while (curSearchIndex < text.length) {
|
|
317
|
-
// find start of code block: ```[language]\n<code>[\n]```
|
|
318
|
-
const codeStartIndex = text.indexOf('```', curSearchIndex);
|
|
319
|
-
if (codeStartIndex === -1) {
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// find language specifier if present
|
|
324
|
-
const newLineIndex = text.indexOf('\n', codeStartIndex + 3);
|
|
325
|
-
const language = codeStartIndex + 3 < newLineIndex ? text.substring(codeStartIndex + 3, newLineIndex) : undefined;
|
|
326
|
-
|
|
327
|
-
// find end of code block
|
|
328
|
-
const codeEndIndex = text.indexOf('```', codeStartIndex + 3);
|
|
329
|
-
if (codeEndIndex === -1) {
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// add text before code block as markdown content
|
|
334
|
-
result.push(new MarkdownChatResponseContentImpl(text.substring(curSearchIndex, codeStartIndex)));
|
|
335
|
-
// add code block as code content
|
|
336
|
-
const codeText = text.substring(newLineIndex + 1, codeEndIndex).trimEnd();
|
|
337
|
-
result.push(new CodeChatResponseContentImpl(codeText, language));
|
|
338
|
-
curSearchIndex = codeEndIndex + 3;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (result.length > 0) {
|
|
342
|
-
result.forEach(r => {
|
|
343
|
-
request.response.response.addContent(r);
|
|
344
|
-
});
|
|
345
|
-
} else {
|
|
346
|
-
request.response.response.addContent(lastContent);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
320
|
+
await this.addStreamResponse(languageModelResponse, request);
|
|
349
321
|
request.response.complete();
|
|
350
322
|
this.recordingService.recordResponse({
|
|
351
323
|
agentId: this.id,
|
|
@@ -366,11 +338,38 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
|
|
|
366
338
|
);
|
|
367
339
|
}
|
|
368
340
|
|
|
369
|
-
|
|
341
|
+
protected async addStreamResponse(languageModelResponse: LanguageModelStreamResponse, request: ChatRequestModelImpl): Promise<void> {
|
|
342
|
+
for await (const token of languageModelResponse.stream) {
|
|
343
|
+
const newContents = this.parse(token, request.response.response.content);
|
|
344
|
+
if (isArray(newContents)) {
|
|
345
|
+
request.response.response.addContents(newContents);
|
|
346
|
+
} else {
|
|
347
|
+
request.response.response.addContent(newContents);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const lastContent = request.response.response.content.pop();
|
|
351
|
+
if (lastContent === undefined) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const text = lastContent.asString?.();
|
|
355
|
+
if (text === undefined) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text) : [];
|
|
360
|
+
if (result.length > 0) {
|
|
361
|
+
request.response.response.addContents(result);
|
|
362
|
+
} else {
|
|
363
|
+
request.response.response.addContent(lastContent);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
protected parse(token: LanguageModelStreamResponsePart, previousContent: ChatResponseContent[]): ChatResponseContent | ChatResponseContent[] {
|
|
370
369
|
const content = token.content;
|
|
371
370
|
// eslint-disable-next-line no-null/no-null
|
|
372
371
|
if (content !== undefined && content !== null) {
|
|
373
|
-
return
|
|
372
|
+
return this.defaultContentFactory.create(content);
|
|
374
373
|
}
|
|
375
374
|
const toolCalls = token.tool_calls;
|
|
376
375
|
if (toolCalls !== undefined) {
|
|
@@ -378,7 +377,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
|
|
|
378
377
|
new ToolCallChatResponseContentImpl(toolCall.id, toolCall.function?.name, toolCall.function?.arguments, toolCall.finished, toolCall.result));
|
|
379
378
|
return toolCallContents;
|
|
380
379
|
}
|
|
381
|
-
return
|
|
380
|
+
return this.defaultContentFactory.create('');
|
|
382
381
|
}
|
|
383
382
|
|
|
384
383
|
}
|
package/src/common/chat-model.ts
CHANGED
|
@@ -601,10 +601,20 @@ class ChatResponseImpl implements ChatResponse {
|
|
|
601
601
|
return this._content;
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
addContents(contents: ChatResponseContent[]): void {
|
|
605
|
+
contents.forEach(c => this.doAddContent(c));
|
|
606
|
+
this._onDidChangeEmitter.fire();
|
|
607
|
+
}
|
|
608
|
+
|
|
604
609
|
addContent(nextContent: ChatResponseContent): void {
|
|
605
610
|
// TODO: Support more complex merges affecting different content than the last, e.g. via some kind of ProcessorRegistry
|
|
606
611
|
// TODO: Support more of the built-in VS Code behavior, see
|
|
607
612
|
// https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatModel.ts#L188-L244
|
|
613
|
+
this.doAddContent(nextContent);
|
|
614
|
+
this._onDidChangeEmitter.fire();
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
protected doAddContent(nextContent: ChatResponseContent): void {
|
|
608
618
|
if (ToolCallChatResponseContent.is(nextContent) && nextContent.id !== undefined) {
|
|
609
619
|
const fittingTool = this._content.find(c => ToolCallChatResponseContent.is(c) && c.id === nextContent.id);
|
|
610
620
|
if (fittingTool !== undefined) {
|
|
@@ -613,10 +623,9 @@ class ChatResponseImpl implements ChatResponse {
|
|
|
613
623
|
this._content.push(nextContent);
|
|
614
624
|
}
|
|
615
625
|
} else {
|
|
616
|
-
const lastElement =
|
|
617
|
-
this._content.length
|
|
618
|
-
|
|
619
|
-
: undefined;
|
|
626
|
+
const lastElement = this._content.length > 0
|
|
627
|
+
? this._content[this._content.length - 1]
|
|
628
|
+
: undefined;
|
|
620
629
|
if (lastElement?.kind === nextContent.kind && ChatResponseContent.hasMerge(lastElement)) {
|
|
621
630
|
const mergeSuccess = lastElement.merge(nextContent);
|
|
622
631
|
if (!mergeSuccess) {
|
|
@@ -627,7 +636,6 @@ class ChatResponseImpl implements ChatResponse {
|
|
|
627
636
|
}
|
|
628
637
|
}
|
|
629
638
|
this._updateResponseRepresentation();
|
|
630
|
-
this._onDidChangeEmitter.fire();
|
|
631
639
|
}
|
|
632
640
|
|
|
633
641
|
protected _updateResponseRepresentation(): void {
|