@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.
- package/README.md +31 -0
- package/lib/browser/ai-chat-frontend-module.d.ts +4 -0
- package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -0
- package/lib/browser/ai-chat-frontend-module.js +79 -0
- package/lib/browser/ai-chat-frontend-module.js.map +1 -0
- package/lib/browser/ai-chat-preferences.d.ts +4 -0
- package/lib/browser/ai-chat-preferences.d.ts.map +1 -0
- package/lib/browser/ai-chat-preferences.js +32 -0
- package/lib/browser/ai-chat-preferences.js.map +1 -0
- package/lib/browser/custom-agent-factory.d.ts +4 -0
- package/lib/browser/custom-agent-factory.d.ts.map +1 -0
- package/lib/browser/custom-agent-factory.js +20 -0
- package/lib/browser/custom-agent-factory.js.map +1 -0
- package/lib/browser/custom-agent-frontend-application-contribution.d.ts +13 -0
- package/lib/browser/custom-agent-frontend-application-contribution.d.ts.map +1 -0
- package/lib/browser/custom-agent-frontend-application-contribution.js +82 -0
- package/lib/browser/custom-agent-frontend-application-contribution.js.map +1 -0
- package/lib/browser/frontend-chat-service.d.ts +8 -0
- package/lib/browser/frontend-chat-service.d.ts.map +1 -0
- package/lib/browser/frontend-chat-service.js +67 -0
- package/lib/browser/frontend-chat-service.js.map +1 -0
- package/lib/common/chat-agent-service.d.ts +45 -0
- package/lib/common/chat-agent-service.d.ts.map +1 -0
- package/lib/common/chat-agent-service.js +78 -0
- package/lib/common/chat-agent-service.js.map +1 -0
- package/lib/common/chat-agents-variable-contribution.d.ts +17 -0
- package/lib/common/chat-agents-variable-contribution.d.ts.map +1 -0
- package/lib/common/chat-agents-variable-contribution.js +51 -0
- package/lib/common/chat-agents-variable-contribution.js.map +1 -0
- package/lib/common/chat-agents.d.ts +108 -0
- package/lib/common/chat-agents.d.ts.map +1 -0
- package/lib/common/chat-agents.js +321 -0
- package/lib/common/chat-agents.js.map +1 -0
- package/lib/common/chat-history-entry.d.ts +7 -0
- package/lib/common/chat-history-entry.d.ts.map +1 -0
- package/lib/common/chat-history-entry.js +42 -0
- package/lib/common/chat-history-entry.js.map +1 -0
- package/lib/common/chat-model-util.d.ts +7 -0
- package/lib/common/chat-model-util.d.ts.map +1 -0
- package/lib/common/chat-model-util.js +50 -0
- package/lib/common/chat-model-util.js.map +1 -0
- package/lib/common/chat-model.d.ts +393 -0
- package/lib/common/chat-model.d.ts.map +1 -0
- package/lib/common/chat-model.js +612 -0
- package/lib/common/chat-model.js.map +1 -0
- package/lib/common/chat-request-parser.d.ts +20 -0
- package/lib/common/chat-request-parser.d.ts.map +1 -0
- package/lib/common/chat-request-parser.js +158 -0
- package/lib/common/chat-request-parser.js.map +1 -0
- package/lib/common/chat-request-parser.spec.d.ts +2 -0
- package/lib/common/chat-request-parser.spec.d.ts.map +1 -0
- package/lib/common/chat-request-parser.spec.js +108 -0
- package/lib/common/chat-request-parser.spec.js.map +1 -0
- package/lib/common/chat-service.d.ts +72 -0
- package/lib/common/chat-service.d.ts.map +1 -0
- package/lib/common/chat-service.js +170 -0
- package/lib/common/chat-service.js.map +1 -0
- package/lib/common/command-chat-agents.d.ts +33 -0
- package/lib/common/command-chat-agents.d.ts.map +1 -0
- package/lib/common/command-chat-agents.js +329 -0
- package/lib/common/command-chat-agents.js.map +1 -0
- package/lib/common/custom-chat-agent.d.ts +14 -0
- package/lib/common/custom-chat-agent.d.ts.map +1 -0
- package/lib/common/custom-chat-agent.js +43 -0
- package/lib/common/custom-chat-agent.js.map +1 -0
- package/lib/common/index.d.ts +12 -0
- package/lib/common/index.d.ts.map +1 -0
- package/lib/common/index.js +30 -0
- package/lib/common/index.js.map +1 -0
- package/lib/common/o1-chat-agent.d.ts +13 -0
- package/lib/common/o1-chat-agent.d.ts.map +1 -0
- package/lib/common/o1-chat-agent.js +45 -0
- package/lib/common/o1-chat-agent.js.map +1 -0
- package/lib/common/orchestrator-chat-agent.d.ts +22 -0
- package/lib/common/orchestrator-chat-agent.d.ts.map +1 -0
- package/lib/common/orchestrator-chat-agent.js +167 -0
- package/lib/common/orchestrator-chat-agent.js.map +1 -0
- 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 +134 -0
- package/lib/common/parse-contents.spec.js.map +1 -0
- package/lib/common/parsed-chat-request.d.ts +66 -0
- package/lib/common/parsed-chat-request.d.ts.map +1 -0
- package/lib/common/parsed-chat-request.js +83 -0
- package/lib/common/parsed-chat-request.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/lib/common/universal-chat-agent.d.ts +16 -0
- package/lib/common/universal-chat-agent.d.ts.map +1 -0
- package/lib/common/universal-chat-agent.js +109 -0
- package/lib/common/universal-chat-agent.js.map +1 -0
- package/package.json +54 -0
- package/src/browser/ai-chat-frontend-module.ts +98 -0
- package/src/browser/ai-chat-preferences.ts +32 -0
- package/src/browser/custom-agent-factory.ts +20 -0
- package/src/browser/custom-agent-frontend-application-contribution.ts +73 -0
- package/src/browser/frontend-chat-service.ts +66 -0
- package/src/common/chat-agent-service.ts +100 -0
- package/src/common/chat-agents-variable-contribution.ts +81 -0
- package/src/common/chat-agents.ts +392 -0
- package/src/common/chat-history-entry.ts +47 -0
- package/src/common/chat-model-util.ts +44 -0
- package/src/common/chat-model.ts +889 -0
- package/src/common/chat-request-parser.spec.ts +120 -0
- package/src/common/chat-request-parser.ts +220 -0
- package/src/common/chat-service.ts +236 -0
- package/src/common/command-chat-agents.ts +354 -0
- package/src/common/custom-chat-agent.ts +44 -0
- package/src/common/index.ts +26 -0
- package/src/common/o1-chat-agent.ts +51 -0
- package/src/common/orchestrator-chat-agent.ts +179 -0
- package/src/common/parse-contents.spec.ts +144 -0
- package/src/common/parse-contents.ts +93 -0
- package/src/common/parsed-chat-request.ts +112 -0
- package/src/common/response-content-matcher.ts +103 -0
- package/src/common/universal-chat-agent.ts +117 -0
|
@@ -0,0 +1,100 @@
|
|
|
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/chatAgents.ts
|
|
21
|
+
|
|
22
|
+
import { ContributionProvider, ILogger } from '@theia/core';
|
|
23
|
+
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
24
|
+
import { ChatAgent } from './chat-agents';
|
|
25
|
+
import { AgentService } from '@theia/ai-core';
|
|
26
|
+
|
|
27
|
+
export const ChatAgentService = Symbol('ChatAgentService');
|
|
28
|
+
/**
|
|
29
|
+
* The ChatAgentService provides access to the available chat agents.
|
|
30
|
+
*/
|
|
31
|
+
export interface ChatAgentService {
|
|
32
|
+
/**
|
|
33
|
+
* Returns all available agents.
|
|
34
|
+
*/
|
|
35
|
+
getAgents(): ChatAgent[];
|
|
36
|
+
/**
|
|
37
|
+
* Returns the specified agent, if available
|
|
38
|
+
*/
|
|
39
|
+
getAgent(id: string): ChatAgent | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Returns all agents, including disabled ones.
|
|
42
|
+
*/
|
|
43
|
+
getAllAgents(): ChatAgent[];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Allows to register a chat agent programmatically.
|
|
47
|
+
* @param agent the agent to register
|
|
48
|
+
*/
|
|
49
|
+
registerChatAgent(agent: ChatAgent): void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Allows to unregister a chat agent programmatically.
|
|
53
|
+
* @param agentId the agent id to unregister
|
|
54
|
+
*/
|
|
55
|
+
unregisterChatAgent(agentId: string): void;
|
|
56
|
+
}
|
|
57
|
+
@injectable()
|
|
58
|
+
export class ChatAgentServiceImpl implements ChatAgentService {
|
|
59
|
+
|
|
60
|
+
@inject(ContributionProvider) @named(ChatAgent)
|
|
61
|
+
protected readonly agentContributions: ContributionProvider<ChatAgent>;
|
|
62
|
+
|
|
63
|
+
@inject(ILogger)
|
|
64
|
+
protected logger: ILogger;
|
|
65
|
+
|
|
66
|
+
@inject(AgentService)
|
|
67
|
+
protected agentService: AgentService;
|
|
68
|
+
|
|
69
|
+
protected _agents: ChatAgent[] = [];
|
|
70
|
+
|
|
71
|
+
protected get agents(): ChatAgent[] {
|
|
72
|
+
// We can't collect the contributions at @postConstruct because this will lead to a circular dependency
|
|
73
|
+
// with chat agents reusing the chat agent service (e.g. orchestrator)
|
|
74
|
+
return [...this.agentContributions.getContributions(), ...this._agents];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
registerChatAgent(agent: ChatAgent): void {
|
|
78
|
+
this._agents.push(agent);
|
|
79
|
+
}
|
|
80
|
+
unregisterChatAgent(agentId: string): void {
|
|
81
|
+
this._agents = this._agents.filter(a => a.id !== agentId);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getAgent(id: string): ChatAgent | undefined {
|
|
85
|
+
if (!this._agentIsEnabled(id)) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return this.getAgents().find(agent => agent.id === id);
|
|
89
|
+
}
|
|
90
|
+
getAgents(): ChatAgent[] {
|
|
91
|
+
return this.agents.filter(a => this._agentIsEnabled(a.id));
|
|
92
|
+
}
|
|
93
|
+
getAllAgents(): ChatAgent[] {
|
|
94
|
+
return this.agents;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private _agentIsEnabled(id: string): boolean {
|
|
98
|
+
return this.agentService.isEnabled(id);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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 { MaybePromise } from '@theia/core';
|
|
17
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import {
|
|
19
|
+
AIVariable,
|
|
20
|
+
AIVariableContext,
|
|
21
|
+
AIVariableContribution,
|
|
22
|
+
AIVariableResolutionRequest,
|
|
23
|
+
AIVariableResolver,
|
|
24
|
+
AIVariableService,
|
|
25
|
+
ResolvedAIVariable
|
|
26
|
+
} from '@theia/ai-core';
|
|
27
|
+
import { ChatAgentService } from './chat-agent-service';
|
|
28
|
+
|
|
29
|
+
export const CHAT_AGENTS_VARIABLE: AIVariable = {
|
|
30
|
+
id: 'chatAgents',
|
|
31
|
+
name: 'chatAgents',
|
|
32
|
+
description: 'Returns the list of chat agents available in the system'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export interface ChatAgentDescriptor {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
description: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@injectable()
|
|
42
|
+
export class ChatAgentsVariableContribution implements AIVariableContribution, AIVariableResolver {
|
|
43
|
+
|
|
44
|
+
@inject(ChatAgentService)
|
|
45
|
+
protected readonly agents: ChatAgentService;
|
|
46
|
+
|
|
47
|
+
registerVariables(service: AIVariableService): void {
|
|
48
|
+
service.registerResolver(CHAT_AGENTS_VARIABLE, this);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
canResolve(request: AIVariableResolutionRequest, _context: AIVariableContext): MaybePromise<number> {
|
|
52
|
+
if (request.variable.name === CHAT_AGENTS_VARIABLE.name) {
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
return -1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
|
|
59
|
+
if (request.variable.name === CHAT_AGENTS_VARIABLE.name) {
|
|
60
|
+
return this.resolveAgentsVariable(request);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
resolveAgentsVariable(_request: AIVariableResolutionRequest): ResolvedAIVariable {
|
|
65
|
+
const agents = this.agents.getAgents().map(agent => ({
|
|
66
|
+
id: agent.id,
|
|
67
|
+
name: agent.name,
|
|
68
|
+
description: agent.description
|
|
69
|
+
}));
|
|
70
|
+
const value = agents.map(agent => prettyPrintInMd(agent)).join('\n');
|
|
71
|
+
return { variable: CHAT_AGENTS_VARIABLE, value };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function prettyPrintInMd(agent: { id: string; name: string; description: string; }): string {
|
|
76
|
+
return `- ${agent.id}
|
|
77
|
+
- *ID*: ${agent.id}
|
|
78
|
+
- *Name*: ${agent.name}
|
|
79
|
+
- *Description*: ${agent.description.replace(/\n/g, ' ')}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
@@ -0,0 +1,392 @@
|
|
|
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/chatAgents.ts
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
CommunicationRecordingService,
|
|
24
|
+
getTextOfResponse,
|
|
25
|
+
LanguageModel,
|
|
26
|
+
LanguageModelRequirement,
|
|
27
|
+
LanguageModelResponse,
|
|
28
|
+
LanguageModelStreamResponse,
|
|
29
|
+
PromptService,
|
|
30
|
+
ResolvedPromptTemplate,
|
|
31
|
+
ToolRequest,
|
|
32
|
+
} from '@theia/ai-core';
|
|
33
|
+
import {
|
|
34
|
+
Agent,
|
|
35
|
+
isLanguageModelStreamResponse,
|
|
36
|
+
isLanguageModelTextResponse,
|
|
37
|
+
LanguageModelRegistry,
|
|
38
|
+
LanguageModelStreamResponsePart,
|
|
39
|
+
MessageActor,
|
|
40
|
+
} from '@theia/ai-core/lib/common';
|
|
41
|
+
import { CancellationToken, CancellationTokenSource, ContributionProvider, ILogger, isArray } from '@theia/core';
|
|
42
|
+
import { inject, injectable, named, postConstruct, unmanaged } from '@theia/core/shared/inversify';
|
|
43
|
+
import { ChatAgentService } from './chat-agent-service';
|
|
44
|
+
import {
|
|
45
|
+
ChatModel,
|
|
46
|
+
ChatRequestModel,
|
|
47
|
+
ChatRequestModelImpl,
|
|
48
|
+
ChatResponseContent,
|
|
49
|
+
ErrorChatResponseContentImpl,
|
|
50
|
+
MarkdownChatResponseContentImpl,
|
|
51
|
+
ToolCallChatResponseContentImpl
|
|
52
|
+
} from './chat-model';
|
|
53
|
+
import { findFirstMatch, parseContents } from './parse-contents';
|
|
54
|
+
import { DefaultResponseContentFactory, ResponseContentMatcher, ResponseContentMatcherProvider } from './response-content-matcher';
|
|
55
|
+
import { ChatHistoryEntry } from './chat-history-entry';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A conversation consists of a sequence of ChatMessages.
|
|
59
|
+
* Each ChatMessage is either a user message, AI message or a system message.
|
|
60
|
+
*
|
|
61
|
+
* For now we only support text based messages.
|
|
62
|
+
*/
|
|
63
|
+
export interface ChatMessage {
|
|
64
|
+
actor: MessageActor;
|
|
65
|
+
type: 'text';
|
|
66
|
+
query: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* System message content, enriched with function descriptions.
|
|
71
|
+
*/
|
|
72
|
+
export interface SystemMessageDescription {
|
|
73
|
+
text: string;
|
|
74
|
+
/** All functions references in the system message. */
|
|
75
|
+
functionDescriptions?: Map<string, ToolRequest>;
|
|
76
|
+
}
|
|
77
|
+
export namespace SystemMessageDescription {
|
|
78
|
+
export function fromResolvedPromptTemplate(resolvedPrompt: ResolvedPromptTemplate): SystemMessageDescription {
|
|
79
|
+
return {
|
|
80
|
+
text: resolvedPrompt.text,
|
|
81
|
+
functionDescriptions: resolvedPrompt.functionDescriptions
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The location from where an chat agent may be invoked.
|
|
88
|
+
* Based on the location, a different context may be available.
|
|
89
|
+
*/
|
|
90
|
+
export enum ChatAgentLocation {
|
|
91
|
+
Panel = 'panel',
|
|
92
|
+
Terminal = 'terminal',
|
|
93
|
+
Notebook = 'notebook',
|
|
94
|
+
Editor = 'editor'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export namespace ChatAgentLocation {
|
|
98
|
+
export const ALL: ChatAgentLocation[] = [ChatAgentLocation.Panel, ChatAgentLocation.Terminal, ChatAgentLocation.Notebook, ChatAgentLocation.Editor];
|
|
99
|
+
|
|
100
|
+
export function fromRaw(value: string): ChatAgentLocation {
|
|
101
|
+
switch (value) {
|
|
102
|
+
case 'panel': return ChatAgentLocation.Panel;
|
|
103
|
+
case 'terminal': return ChatAgentLocation.Terminal;
|
|
104
|
+
case 'notebook': return ChatAgentLocation.Notebook;
|
|
105
|
+
case 'editor': return ChatAgentLocation.Editor;
|
|
106
|
+
}
|
|
107
|
+
return ChatAgentLocation.Panel;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const ChatAgent = Symbol('ChatAgent');
|
|
112
|
+
/**
|
|
113
|
+
* A chat agent is a specialized agent with a common interface for its invocation.
|
|
114
|
+
*/
|
|
115
|
+
export interface ChatAgent extends Agent {
|
|
116
|
+
locations: ChatAgentLocation[];
|
|
117
|
+
iconClass?: string;
|
|
118
|
+
invoke(request: ChatRequestModelImpl, chatAgentService?: ChatAgentService): Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@injectable()
|
|
122
|
+
export abstract class AbstractChatAgent {
|
|
123
|
+
@inject(LanguageModelRegistry) protected languageModelRegistry: LanguageModelRegistry;
|
|
124
|
+
@inject(ILogger) protected logger: ILogger;
|
|
125
|
+
@inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService;
|
|
126
|
+
@inject(PromptService) protected promptService: PromptService;
|
|
127
|
+
|
|
128
|
+
@inject(ContributionProvider) @named(ResponseContentMatcherProvider)
|
|
129
|
+
protected contentMatcherProviders: ContributionProvider<ResponseContentMatcherProvider>;
|
|
130
|
+
protected contentMatchers: ResponseContentMatcher[] = [];
|
|
131
|
+
|
|
132
|
+
@inject(DefaultResponseContentFactory)
|
|
133
|
+
protected defaultContentFactory: DefaultResponseContentFactory;
|
|
134
|
+
|
|
135
|
+
constructor(
|
|
136
|
+
@unmanaged() public id: string,
|
|
137
|
+
@unmanaged() public languageModelRequirements: LanguageModelRequirement[],
|
|
138
|
+
@unmanaged() protected defaultLanguageModelPurpose: string,
|
|
139
|
+
@unmanaged() public iconClass: string = 'codicon codicon-copilot',
|
|
140
|
+
@unmanaged() public locations: ChatAgentLocation[] = ChatAgentLocation.ALL,
|
|
141
|
+
@unmanaged() public tags: string[] = ['Chat'],
|
|
142
|
+
@unmanaged() public defaultLogging: boolean = true) {
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@postConstruct()
|
|
146
|
+
init(): void {
|
|
147
|
+
this.initializeContentMatchers();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
protected initializeContentMatchers(): void {
|
|
151
|
+
const contributedContentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers);
|
|
152
|
+
this.contentMatchers.push(...contributedContentMatchers);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async invoke(request: ChatRequestModelImpl): Promise<void> {
|
|
156
|
+
try {
|
|
157
|
+
const languageModel = await this.getLanguageModel(this.defaultLanguageModelPurpose);
|
|
158
|
+
if (!languageModel) {
|
|
159
|
+
throw new Error('Couldn\'t find a matching language model. Please check your setup!');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const systemMessageDescription = await this.getSystemMessageDescription();
|
|
163
|
+
const messages = await this.getMessages(request.session);
|
|
164
|
+
if (this.defaultLogging) {
|
|
165
|
+
this.recordingService.recordRequest(
|
|
166
|
+
ChatHistoryEntry.fromRequest(
|
|
167
|
+
this.id, request, {
|
|
168
|
+
messages,
|
|
169
|
+
systemMessage: systemMessageDescription?.text
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const tools: Map<string, ToolRequest> = new Map();
|
|
175
|
+
if (systemMessageDescription) {
|
|
176
|
+
const systemMsg: ChatMessage = {
|
|
177
|
+
actor: 'system',
|
|
178
|
+
type: 'text',
|
|
179
|
+
query: systemMessageDescription.text
|
|
180
|
+
};
|
|
181
|
+
// insert system message at the beginning of the request messages
|
|
182
|
+
messages.unshift(systemMsg);
|
|
183
|
+
systemMessageDescription.functionDescriptions?.forEach((tool, id) => {
|
|
184
|
+
tools.set(id, tool);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
this.getTools(request)?.forEach(tool => tools.set(tool.id, tool));
|
|
188
|
+
|
|
189
|
+
const cancellationToken = new CancellationTokenSource();
|
|
190
|
+
request.response.onDidChange(() => {
|
|
191
|
+
if (request.response.isCanceled) {
|
|
192
|
+
cancellationToken.cancel();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const languageModelResponse = await this.callLlm(
|
|
197
|
+
languageModel,
|
|
198
|
+
messages,
|
|
199
|
+
tools.size > 0 ? Array.from(tools.values()) : undefined,
|
|
200
|
+
cancellationToken.token
|
|
201
|
+
);
|
|
202
|
+
await this.addContentsToResponse(languageModelResponse, request);
|
|
203
|
+
await this.onResponseComplete(request);
|
|
204
|
+
if (this.defaultLogging) {
|
|
205
|
+
this.recordingService.recordResponse(ChatHistoryEntry.fromResponse(this.id, request));
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
this.handleError(request, e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
protected parseContents(text: string, request: ChatRequestModelImpl): ChatResponseContent[] {
|
|
213
|
+
return parseContents(
|
|
214
|
+
text,
|
|
215
|
+
request,
|
|
216
|
+
this.contentMatchers,
|
|
217
|
+
this.defaultContentFactory?.create.bind(this.defaultContentFactory)
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
protected handleError(request: ChatRequestModelImpl, error: Error): void {
|
|
222
|
+
request.response.response.addContent(new ErrorChatResponseContentImpl(error));
|
|
223
|
+
request.response.error(error);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
protected getLanguageModelSelector(languageModelPurpose: string): LanguageModelRequirement {
|
|
227
|
+
return this.languageModelRequirements.find(req => req.purpose === languageModelPurpose)!;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
protected async getLanguageModel(languageModelPurpose: string): Promise<LanguageModel> {
|
|
231
|
+
return this.selectLanguageModel(this.getLanguageModelSelector(languageModelPurpose));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
protected async selectLanguageModel(selector: LanguageModelRequirement): Promise<LanguageModel> {
|
|
235
|
+
const languageModel = await this.languageModelRegistry.selectLanguageModel({ agent: this.id, ...selector });
|
|
236
|
+
if (!languageModel) {
|
|
237
|
+
throw new Error('Couldn\'t find a language model. Please check your setup!');
|
|
238
|
+
}
|
|
239
|
+
return languageModel;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
protected abstract getSystemMessageDescription(): Promise<SystemMessageDescription | undefined>;
|
|
243
|
+
|
|
244
|
+
protected async getMessages(
|
|
245
|
+
model: ChatModel, includeResponseInProgress = false
|
|
246
|
+
): Promise<ChatMessage[]> {
|
|
247
|
+
const requestMessages = model.getRequests().flatMap(request => {
|
|
248
|
+
const messages: ChatMessage[] = [];
|
|
249
|
+
const text = request.message.parts.map(part => part.promptText).join('');
|
|
250
|
+
messages.push({
|
|
251
|
+
actor: 'user',
|
|
252
|
+
type: 'text',
|
|
253
|
+
query: text,
|
|
254
|
+
});
|
|
255
|
+
if (request.response.isComplete || includeResponseInProgress) {
|
|
256
|
+
messages.push({
|
|
257
|
+
actor: 'ai',
|
|
258
|
+
type: 'text',
|
|
259
|
+
query: request.response.response.asString(),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return messages;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return requestMessages;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @returns the list of tools used by this agent, or undefined if none is needed.
|
|
270
|
+
*/
|
|
271
|
+
protected getTools(request: ChatRequestModel): ToolRequest[] | undefined {
|
|
272
|
+
return request.message.toolRequests.size > 0
|
|
273
|
+
? [...request.message.toolRequests.values()]
|
|
274
|
+
: undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
protected async callLlm(
|
|
278
|
+
languageModel: LanguageModel,
|
|
279
|
+
messages: ChatMessage[],
|
|
280
|
+
tools: ToolRequest[] | undefined,
|
|
281
|
+
token: CancellationToken
|
|
282
|
+
): Promise<LanguageModelResponse> {
|
|
283
|
+
const settings = this.getLlmSettings();
|
|
284
|
+
const languageModelResponse = languageModel.request({
|
|
285
|
+
messages,
|
|
286
|
+
tools,
|
|
287
|
+
settings,
|
|
288
|
+
}, token);
|
|
289
|
+
return languageModelResponse;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @returns the settings, such as `temperature`, to be used in all language model requests. Returns `undefined` by default.
|
|
294
|
+
*/
|
|
295
|
+
protected getLlmSettings(): { [key: string]: unknown; } | undefined {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Invoked after the response by the LLM completed successfully.
|
|
301
|
+
*
|
|
302
|
+
* The default implementation sets the state of the response to `complete`.
|
|
303
|
+
* Subclasses may override this method to perform additional actions or keep the response open for processing further requests.
|
|
304
|
+
*/
|
|
305
|
+
protected async onResponseComplete(request: ChatRequestModelImpl): Promise<void> {
|
|
306
|
+
return request.response.complete();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
protected abstract addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise<void>;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@injectable()
|
|
313
|
+
export abstract class AbstractTextToModelParsingChatAgent<T> extends AbstractChatAgent {
|
|
314
|
+
|
|
315
|
+
protected async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise<void> {
|
|
316
|
+
const responseAsText = await getTextOfResponse(languageModelResponse);
|
|
317
|
+
const parsedCommand = await this.parseTextResponse(responseAsText);
|
|
318
|
+
const content = this.createResponseContent(parsedCommand, request);
|
|
319
|
+
request.response.response.addContent(content);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
protected abstract parseTextResponse(text: string): Promise<T>;
|
|
323
|
+
|
|
324
|
+
protected abstract createResponseContent(parsedModel: T, request: ChatRequestModelImpl): ChatResponseContent;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@injectable()
|
|
328
|
+
export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
|
|
329
|
+
|
|
330
|
+
protected override async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise<void> {
|
|
331
|
+
if (isLanguageModelTextResponse(languageModelResponse)) {
|
|
332
|
+
const contents = this.parseContents(languageModelResponse.text, request);
|
|
333
|
+
request.response.response.addContents(contents);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (isLanguageModelStreamResponse(languageModelResponse)) {
|
|
337
|
+
await this.addStreamResponse(languageModelResponse, request);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
this.logger.error(
|
|
341
|
+
'Received unknown response in agent. Return response as text'
|
|
342
|
+
);
|
|
343
|
+
request.response.response.addContent(
|
|
344
|
+
new MarkdownChatResponseContentImpl(
|
|
345
|
+
JSON.stringify(languageModelResponse)
|
|
346
|
+
)
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
protected async addStreamResponse(languageModelResponse: LanguageModelStreamResponse, request: ChatRequestModelImpl): Promise<void> {
|
|
351
|
+
for await (const token of languageModelResponse.stream) {
|
|
352
|
+
const newContents = this.parse(token, request);
|
|
353
|
+
if (isArray(newContents)) {
|
|
354
|
+
request.response.response.addContents(newContents);
|
|
355
|
+
} else {
|
|
356
|
+
request.response.response.addContent(newContents);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const lastContent = request.response.response.content.pop();
|
|
360
|
+
if (lastContent === undefined) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const text = lastContent.asString?.();
|
|
364
|
+
if (text === undefined) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text, request) : [];
|
|
369
|
+
if (result.length > 0) {
|
|
370
|
+
request.response.response.addContents(result);
|
|
371
|
+
} else {
|
|
372
|
+
request.response.response.addContent(lastContent);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
protected parse(token: LanguageModelStreamResponsePart, request: ChatRequestModelImpl): ChatResponseContent | ChatResponseContent[] {
|
|
378
|
+
const content = token.content;
|
|
379
|
+
// eslint-disable-next-line no-null/no-null
|
|
380
|
+
if (content !== undefined && content !== null) {
|
|
381
|
+
return this.defaultContentFactory.create(content, request);
|
|
382
|
+
}
|
|
383
|
+
const toolCalls = token.tool_calls;
|
|
384
|
+
if (toolCalls !== undefined) {
|
|
385
|
+
const toolCallContents = toolCalls.map(toolCall =>
|
|
386
|
+
new ToolCallChatResponseContentImpl(toolCall.id, toolCall.function?.name, toolCall.function?.arguments, toolCall.finished, toolCall.result));
|
|
387
|
+
return toolCallContents;
|
|
388
|
+
}
|
|
389
|
+
return this.defaultContentFactory.create('', request);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { CommunicationRequestEntryParam, CommunicationResponseEntryParam } from '@theia/ai-core/lib/common/communication-recording-service';
|
|
18
|
+
import { ChatRequestModel } from './chat-model';
|
|
19
|
+
|
|
20
|
+
export namespace ChatHistoryEntry {
|
|
21
|
+
export function fromRequest(
|
|
22
|
+
agentId: string,
|
|
23
|
+
request: ChatRequestModel,
|
|
24
|
+
args: Partial<CommunicationRequestEntryParam> = {}
|
|
25
|
+
): CommunicationRequestEntryParam {
|
|
26
|
+
return {
|
|
27
|
+
agentId: agentId,
|
|
28
|
+
sessionId: request.session.id,
|
|
29
|
+
requestId: request.id,
|
|
30
|
+
request: request.request.text,
|
|
31
|
+
...args,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function fromResponse(
|
|
35
|
+
agentId: string,
|
|
36
|
+
request: ChatRequestModel,
|
|
37
|
+
args: Partial<CommunicationResponseEntryParam> = {}
|
|
38
|
+
): CommunicationResponseEntryParam {
|
|
39
|
+
return {
|
|
40
|
+
agentId: agentId,
|
|
41
|
+
sessionId: request.session.id,
|
|
42
|
+
requestId: request.id,
|
|
43
|
+
response: request.response.response.asString(),
|
|
44
|
+
...args,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2024 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
import { ChatProgressMessage, ChatRequestModel, ChatResponse, ChatResponseContent, ChatResponseModel, QuestionResponseContent } from './chat-model';
|
|
17
|
+
|
|
18
|
+
export function lastResponseContent(request: ChatRequestModel): ChatResponseContent | undefined {
|
|
19
|
+
return lastContentOfResponse(request.response?.response);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function lastContentOfResponse(response: ChatResponse | undefined): ChatResponseContent | undefined {
|
|
23
|
+
const content = response?.content;
|
|
24
|
+
return content && content.length > 0 ? content[content.length - 1] : undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function lastProgressMessage(request: ChatRequestModel): ChatProgressMessage | undefined {
|
|
28
|
+
return lastProgressMessageOfResponse(request.response);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function lastProgressMessageOfResponse(response: ChatResponseModel | undefined): ChatProgressMessage | undefined {
|
|
32
|
+
const progressMessages = response?.progressMessages;
|
|
33
|
+
return progressMessages && progressMessages.length > 0 ? progressMessages[progressMessages.length - 1] : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function unansweredQuestions(request: ChatRequestModel): QuestionResponseContent[] {
|
|
37
|
+
const response = request.response;
|
|
38
|
+
return unansweredQuestionsOfResponse(response);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function unansweredQuestionsOfResponse(response: ChatResponseModel | undefined): QuestionResponseContent[] {
|
|
42
|
+
if (!response || !response.response) { return []; }
|
|
43
|
+
return response.response.content.filter((c): c is QuestionResponseContent => QuestionResponseContent.is(c) && c.selectedOption === undefined);
|
|
44
|
+
}
|