@theia/ai-core 1.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +30 -0
  2. package/data/prompttemplate.tmLanguage.json +52 -0
  3. package/lib/browser/ai-activation-service.d.ts +17 -0
  4. package/lib/browser/ai-activation-service.d.ts.map +1 -0
  5. package/lib/browser/ai-activation-service.js +63 -0
  6. package/lib/browser/ai-activation-service.js.map +1 -0
  7. package/lib/browser/ai-command-handler-factory.d.ts +4 -0
  8. package/lib/browser/ai-command-handler-factory.d.ts.map +1 -0
  9. package/lib/browser/ai-command-handler-factory.js +20 -0
  10. package/lib/browser/ai-command-handler-factory.js.map +1 -0
  11. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +27 -0
  12. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -0
  13. package/lib/browser/ai-configuration/agent-configuration-widget.js +231 -0
  14. package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -0
  15. package/lib/browser/ai-configuration/ai-configuration-service.d.ts +13 -0
  16. package/lib/browser/ai-configuration/ai-configuration-service.d.ts.map +1 -0
  17. package/lib/browser/ai-configuration/ai-configuration-service.js +44 -0
  18. package/lib/browser/ai-configuration/ai-configuration-service.js.map +1 -0
  19. package/lib/browser/ai-configuration/ai-configuration-view-contribution.d.ts +12 -0
  20. package/lib/browser/ai-configuration/ai-configuration-view-contribution.d.ts.map +1 -0
  21. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +41 -0
  22. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -0
  23. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +20 -0
  24. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -0
  25. package/lib/browser/ai-configuration/ai-configuration-widget.js +88 -0
  26. package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -0
  27. package/lib/browser/ai-configuration/language-model-renderer.d.ts +13 -0
  28. package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -0
  29. package/lib/browser/ai-configuration/language-model-renderer.js +104 -0
  30. package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -0
  31. package/lib/browser/ai-configuration/template-settings-renderer.d.ts +11 -0
  32. package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -0
  33. package/lib/browser/ai-configuration/template-settings-renderer.js +33 -0
  34. package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -0
  35. package/lib/browser/ai-configuration/variable-configuration-widget.d.ts +20 -0
  36. package/lib/browser/ai-configuration/variable-configuration-widget.d.ts.map +1 -0
  37. package/lib/browser/ai-configuration/variable-configuration-widget.js +99 -0
  38. package/lib/browser/ai-configuration/variable-configuration-widget.js.map +1 -0
  39. package/lib/browser/ai-core-frontend-application-contribution.d.ts +8 -0
  40. package/lib/browser/ai-core-frontend-application-contribution.d.ts.map +1 -0
  41. package/lib/browser/ai-core-frontend-application-contribution.js +46 -0
  42. package/lib/browser/ai-core-frontend-application-contribution.js.map +1 -0
  43. package/lib/browser/ai-core-frontend-module.d.ts +4 -0
  44. package/lib/browser/ai-core-frontend-module.d.ts.map +1 -0
  45. package/lib/browser/ai-core-frontend-module.js +122 -0
  46. package/lib/browser/ai-core-frontend-module.js.map +1 -0
  47. package/lib/browser/ai-core-preferences.d.ts +14 -0
  48. package/lib/browser/ai-core-preferences.d.ts.map +1 -0
  49. package/lib/browser/ai-core-preferences.js +69 -0
  50. package/lib/browser/ai-core-preferences.js.map +1 -0
  51. package/lib/browser/ai-settings-service.d.ts +14 -0
  52. package/lib/browser/ai-settings-service.d.ts.map +1 -0
  53. package/lib/browser/ai-settings-service.js +56 -0
  54. package/lib/browser/ai-settings-service.js.map +1 -0
  55. package/lib/browser/ai-view-contribution.d.ts +14 -0
  56. package/lib/browser/ai-view-contribution.d.ts.map +1 -0
  57. package/lib/browser/ai-view-contribution.js +71 -0
  58. package/lib/browser/ai-view-contribution.js.map +1 -0
  59. package/lib/browser/frontend-language-model-registry.d.ts +39 -0
  60. package/lib/browser/frontend-language-model-registry.d.ts.map +1 -0
  61. package/lib/browser/frontend-language-model-registry.js +321 -0
  62. package/lib/browser/frontend-language-model-registry.js.map +1 -0
  63. package/lib/browser/frontend-prompt-customization-service.d.ts +31 -0
  64. package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -0
  65. package/lib/browser/frontend-prompt-customization-service.js +194 -0
  66. package/lib/browser/frontend-prompt-customization-service.js.map +1 -0
  67. package/lib/browser/frontend-variable-service.d.ts +6 -0
  68. package/lib/browser/frontend-variable-service.d.ts.map +1 -0
  69. package/lib/browser/frontend-variable-service.js +31 -0
  70. package/lib/browser/frontend-variable-service.js.map +1 -0
  71. package/lib/browser/index.d.ts +11 -0
  72. package/lib/browser/index.d.ts.map +1 -0
  73. package/lib/browser/index.js +29 -0
  74. package/lib/browser/index.js.map +1 -0
  75. package/lib/browser/prompttemplate-contribution.d.ts +28 -0
  76. package/lib/browser/prompttemplate-contribution.d.ts.map +1 -0
  77. package/lib/browser/prompttemplate-contribution.js +207 -0
  78. package/lib/browser/prompttemplate-contribution.js.map +1 -0
  79. package/lib/browser/theia-variable-contribution.d.ts +13 -0
  80. package/lib/browser/theia-variable-contribution.d.ts.map +1 -0
  81. package/lib/browser/theia-variable-contribution.js +64 -0
  82. package/lib/browser/theia-variable-contribution.js.map +1 -0
  83. package/lib/common/agent-service.d.ts +48 -0
  84. package/lib/common/agent-service.d.ts.map +1 -0
  85. package/lib/common/agent-service.js +88 -0
  86. package/lib/common/agent-service.js.map +1 -0
  87. package/lib/common/agent.d.ts +45 -0
  88. package/lib/common/agent.d.ts.map +1 -0
  89. package/lib/common/agent.js +20 -0
  90. package/lib/common/agent.js.map +1 -0
  91. package/lib/common/agents-variable-contribution.d.ts +19 -0
  92. package/lib/common/agents-variable-contribution.d.ts.map +1 -0
  93. package/lib/common/agents-variable-contribution.js +56 -0
  94. package/lib/common/agents-variable-contribution.js.map +1 -0
  95. package/lib/common/communication-recording-service.d.ts +23 -0
  96. package/lib/common/communication-recording-service.d.ts.map +1 -0
  97. package/lib/common/communication-recording-service.js +20 -0
  98. package/lib/common/communication-recording-service.js.map +1 -0
  99. package/lib/common/index.d.ts +16 -0
  100. package/lib/common/index.d.ts.map +1 -0
  101. package/lib/common/index.js +34 -0
  102. package/lib/common/index.js.map +1 -0
  103. package/lib/common/language-model-delegate.d.ts +24 -0
  104. package/lib/common/language-model-delegate.d.ts.map +1 -0
  105. package/lib/common/language-model-delegate.js +26 -0
  106. package/lib/common/language-model-delegate.js.map +1 -0
  107. package/lib/common/language-model-util.d.ts +5 -0
  108. package/lib/common/language-model-util.d.ts.map +1 -0
  109. package/lib/common/language-model-util.js +75 -0
  110. package/lib/common/language-model-util.js.map +1 -0
  111. package/lib/common/language-model.d.ts +137 -0
  112. package/lib/common/language-model.d.ts.map +1 -0
  113. package/lib/common/language-model.js +142 -0
  114. package/lib/common/language-model.js.map +1 -0
  115. package/lib/common/language-model.spec.d.ts +2 -0
  116. package/lib/common/language-model.spec.d.ts.map +1 -0
  117. package/lib/common/language-model.spec.js +62 -0
  118. package/lib/common/language-model.spec.js.map +1 -0
  119. package/lib/common/prompt-service-util.d.ts +5 -0
  120. package/lib/common/prompt-service-util.d.ts.map +1 -0
  121. package/lib/common/prompt-service-util.js +23 -0
  122. package/lib/common/prompt-service-util.js.map +1 -0
  123. package/lib/common/prompt-service.d.ts +101 -0
  124. package/lib/common/prompt-service.d.ts.map +1 -0
  125. package/lib/common/prompt-service.js +139 -0
  126. package/lib/common/prompt-service.js.map +1 -0
  127. package/lib/common/prompt-service.spec.d.ts +2 -0
  128. package/lib/common/prompt-service.spec.d.ts.map +1 -0
  129. package/lib/common/prompt-service.spec.js +86 -0
  130. package/lib/common/prompt-service.spec.js.map +1 -0
  131. package/lib/common/protocol.d.ts +7 -0
  132. package/lib/common/protocol.d.ts.map +1 -0
  133. package/lib/common/protocol.js +20 -0
  134. package/lib/common/protocol.js.map +1 -0
  135. package/lib/common/settings-service.d.ts +18 -0
  136. package/lib/common/settings-service.d.ts.map +1 -0
  137. package/lib/common/settings-service.js +5 -0
  138. package/lib/common/settings-service.js.map +1 -0
  139. package/lib/common/today-variable-contribution.d.ts +17 -0
  140. package/lib/common/today-variable-contribution.d.ts.map +1 -0
  141. package/lib/common/today-variable-contribution.js +48 -0
  142. package/lib/common/today-variable-contribution.js.map +1 -0
  143. package/lib/common/tomorrow-variable-contribution.d.ts +17 -0
  144. package/lib/common/tomorrow-variable-contribution.d.ts.map +1 -0
  145. package/lib/common/tomorrow-variable-contribution.js +48 -0
  146. package/lib/common/tomorrow-variable-contribution.js.map +1 -0
  147. package/lib/common/tool-invocation-registry.d.ts +23 -0
  148. package/lib/common/tool-invocation-registry.d.ts.map +1 -0
  149. package/lib/common/tool-invocation-registry.js +72 -0
  150. package/lib/common/tool-invocation-registry.js.map +1 -0
  151. package/lib/common/variable-service.d.ts +70 -0
  152. package/lib/common/variable-service.d.ts.map +1 -0
  153. package/lib/common/variable-service.js +122 -0
  154. package/lib/common/variable-service.js.map +1 -0
  155. package/lib/node/ai-core-backend-module.d.ts +4 -0
  156. package/lib/node/ai-core-backend-module.d.ts.map +1 -0
  157. package/lib/node/ai-core-backend-module.js +47 -0
  158. package/lib/node/ai-core-backend-module.js.map +1 -0
  159. package/lib/node/backend-language-model-registry.d.ts +12 -0
  160. package/lib/node/backend-language-model-registry.d.ts.map +1 -0
  161. package/lib/node/backend-language-model-registry.js +61 -0
  162. package/lib/node/backend-language-model-registry.js.map +1 -0
  163. package/lib/node/language-model-frontend-delegate.d.ts +18 -0
  164. package/lib/node/language-model-frontend-delegate.d.ts.map +1 -0
  165. package/lib/node/language-model-frontend-delegate.js +100 -0
  166. package/lib/node/language-model-frontend-delegate.js.map +1 -0
  167. package/package.json +59 -0
  168. package/src/browser/ai-activation-service.ts +56 -0
  169. package/src/browser/ai-command-handler-factory.ts +20 -0
  170. package/src/browser/ai-configuration/agent-configuration-widget.tsx +299 -0
  171. package/src/browser/ai-configuration/ai-configuration-service.ts +43 -0
  172. package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +54 -0
  173. package/src/browser/ai-configuration/ai-configuration-widget.tsx +80 -0
  174. package/src/browser/ai-configuration/language-model-renderer.tsx +113 -0
  175. package/src/browser/ai-configuration/template-settings-renderer.tsx +39 -0
  176. package/src/browser/ai-configuration/variable-configuration-widget.tsx +110 -0
  177. package/src/browser/ai-core-frontend-application-contribution.ts +40 -0
  178. package/src/browser/ai-core-frontend-module.ts +161 -0
  179. package/src/browser/ai-core-preferences.ts +76 -0
  180. package/src/browser/ai-settings-service.ts +50 -0
  181. package/src/browser/ai-view-contribution.ts +77 -0
  182. package/src/browser/frontend-language-model-registry.ts +405 -0
  183. package/src/browser/frontend-prompt-customization-service.ts +197 -0
  184. package/src/browser/frontend-variable-service.ts +26 -0
  185. package/src/browser/index.ts +26 -0
  186. package/src/browser/prompttemplate-contribution.ts +250 -0
  187. package/src/browser/style/index.css +90 -0
  188. package/src/browser/theia-variable-contribution.ts +58 -0
  189. package/src/common/agent-service.ts +108 -0
  190. package/src/common/agent.ts +70 -0
  191. package/src/common/agents-variable-contribution.ts +64 -0
  192. package/src/common/communication-recording-service.ts +44 -0
  193. package/src/common/index.ts +30 -0
  194. package/src/common/language-model-delegate.ts +45 -0
  195. package/src/common/language-model-util.ts +67 -0
  196. package/src/common/language-model.spec.ts +86 -0
  197. package/src/common/language-model.ts +237 -0
  198. package/src/common/prompt-service-util.ts +21 -0
  199. package/src/common/prompt-service.spec.ts +98 -0
  200. package/src/common/prompt-service.ts +213 -0
  201. package/src/common/protocol.ts +23 -0
  202. package/src/common/settings-service.ts +33 -0
  203. package/src/common/today-variable-contribution.ts +67 -0
  204. package/src/common/tomorrow-variable-contribution.ts +66 -0
  205. package/src/common/tool-invocation-registry.ts +79 -0
  206. package/src/common/variable-service.ts +177 -0
  207. package/src/node/ai-core-backend-module.ts +83 -0
  208. package/src/node/backend-language-model-registry.ts +59 -0
  209. package/src/node/language-model-frontend-delegate.ts +116 -0
@@ -0,0 +1,50 @@
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 { DisposableCollection, Emitter, Event } from '@theia/core';
17
+ import { PreferenceScope, PreferenceService } from '@theia/core/lib/browser';
18
+ import { inject, injectable } from '@theia/core/shared/inversify';
19
+ import { JSONObject } from '@theia/core/shared/@phosphor/coreutils';
20
+ import { AISettings, AISettingsService, AgentSettings } from '../common';
21
+
22
+ @injectable()
23
+ export class AISettingsServiceImpl implements AISettingsService {
24
+ @inject(PreferenceService) protected preferenceService: PreferenceService;
25
+ static readonly PREFERENCE_NAME = 'ai-features.agentSettings';
26
+
27
+ protected toDispose = new DisposableCollection();
28
+
29
+ protected readonly onDidChangeEmitter = new Emitter<void>();
30
+ onDidChange: Event<void> = this.onDidChangeEmitter.event;
31
+
32
+ async updateAgentSettings(agent: string, agentSettings: Partial<AgentSettings>): Promise<void> {
33
+ const settings = await this.getSettings();
34
+ const newAgentSettings = { ...settings[agent], ...agentSettings };
35
+ settings[agent] = newAgentSettings;
36
+ this.preferenceService.set(AISettingsServiceImpl.PREFERENCE_NAME, settings, PreferenceScope.User);
37
+ this.onDidChangeEmitter.fire();
38
+ }
39
+
40
+ async getAgentSettings(agent: string): Promise<AgentSettings | undefined> {
41
+ const settings = await this.getSettings();
42
+ return settings[agent];
43
+ }
44
+
45
+ async getSettings(): Promise<AISettings> {
46
+ await this.preferenceService.ready;
47
+ const pref = this.preferenceService.inspect<AISettings & JSONObject>(AISettingsServiceImpl.PREFERENCE_NAME);
48
+ return pref?.value ? pref.value : {};
49
+ }
50
+ }
@@ -0,0 +1,77 @@
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 { CommandRegistry, MenuModelRegistry } from '@theia/core';
17
+ import { AbstractViewContribution, CommonMenus, KeybindingRegistry, PreferenceService, Widget } from '@theia/core/lib/browser';
18
+ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
19
+ import { AIActivationService, EXPERIMENTAL_AI_CONTEXT_KEY } from './ai-activation-service';
20
+ import { AICommandHandlerFactory } from './ai-command-handler-factory';
21
+
22
+ @injectable()
23
+ export class AIViewContribution<T extends Widget> extends AbstractViewContribution<T> {
24
+
25
+ @inject(PreferenceService)
26
+ protected readonly preferenceService: PreferenceService;
27
+
28
+ @inject(AIActivationService)
29
+ protected readonly activationService: AIActivationService;
30
+
31
+ @inject(AICommandHandlerFactory)
32
+ protected readonly commandHandlerFactory: AICommandHandlerFactory;
33
+
34
+ @postConstruct()
35
+ protected init(): void {
36
+ this.activationService.onDidChangeActiveStatus(active => {
37
+ if (!active) {
38
+ this.closeView();
39
+ }
40
+ });
41
+ }
42
+
43
+ override registerCommands(commands: CommandRegistry): void {
44
+ if (this.toggleCommand) {
45
+
46
+ commands.registerCommand(this.toggleCommand, this.commandHandlerFactory({
47
+ execute: () => this.toggleView(),
48
+ }));
49
+ }
50
+ this.quickView?.registerItem({
51
+ label: this.viewLabel,
52
+ when: EXPERIMENTAL_AI_CONTEXT_KEY,
53
+ open: () => this.openView({ activate: true })
54
+ });
55
+
56
+ }
57
+
58
+ override registerMenus(menus: MenuModelRegistry): void {
59
+ if (this.toggleCommand) {
60
+ menus.registerMenuAction(CommonMenus.VIEW_VIEWS, {
61
+ commandId: this.toggleCommand.id,
62
+ when: EXPERIMENTAL_AI_CONTEXT_KEY,
63
+ label: this.viewLabel
64
+ });
65
+ }
66
+ }
67
+ override registerKeybindings(keybindings: KeybindingRegistry): void {
68
+ if (this.toggleCommand && this.options.toggleKeybinding) {
69
+ keybindings.registerKeybinding({
70
+ command: this.toggleCommand.id,
71
+ when: EXPERIMENTAL_AI_CONTEXT_KEY,
72
+ keybinding: this.options.toggleKeybinding
73
+ });
74
+ }
75
+ }
76
+ }
77
+
@@ -0,0 +1,405 @@
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 { CancellationToken, ILogger } from '@theia/core';
17
+ import {
18
+ inject,
19
+ injectable,
20
+ postConstruct,
21
+ } from '@theia/core/shared/inversify';
22
+ import {
23
+ OutputChannel,
24
+ OutputChannelManager,
25
+ OutputChannelSeverity,
26
+ } from '@theia/output/lib/browser/output-channel';
27
+ import {
28
+ AISettingsService,
29
+ DefaultLanguageModelRegistryImpl,
30
+ isLanguageModelParsedResponse,
31
+ isLanguageModelStreamResponse,
32
+ isLanguageModelStreamResponseDelegate,
33
+ isLanguageModelTextResponse,
34
+ isModelMatching,
35
+ LanguageModel,
36
+ LanguageModelDelegateClient,
37
+ LanguageModelFrontendDelegate,
38
+ LanguageModelMetaData,
39
+ LanguageModelRegistryClient,
40
+ LanguageModelRegistryFrontendDelegate,
41
+ LanguageModelRequest,
42
+ LanguageModelResponse,
43
+ LanguageModelSelector,
44
+ LanguageModelStreamResponsePart,
45
+ } from '../common';
46
+
47
+ @injectable()
48
+ export class LanguageModelDelegateClientImpl
49
+ implements LanguageModelDelegateClient, LanguageModelRegistryClient {
50
+ protected receiver: FrontendLanguageModelRegistryImpl;
51
+
52
+ setReceiver(receiver: FrontendLanguageModelRegistryImpl): void {
53
+ this.receiver = receiver;
54
+ }
55
+
56
+ send(id: string, token: LanguageModelStreamResponsePart | undefined): void {
57
+ this.receiver.send(id, token);
58
+ }
59
+
60
+ toolCall(requestId: string, toolId: string, args_string: string): Promise<unknown> {
61
+ return this.receiver.toolCall(requestId, toolId, args_string);
62
+ }
63
+
64
+ languageModelAdded(metadata: LanguageModelMetaData): void {
65
+ this.receiver.languageModelAdded(metadata);
66
+ }
67
+
68
+ languageModelRemoved(id: string): void {
69
+ this.receiver.languageModelRemoved(id);
70
+ }
71
+ }
72
+
73
+ interface StreamState {
74
+ id: string;
75
+ tokens: (LanguageModelStreamResponsePart | undefined)[];
76
+ resolve?: (_: unknown) => void;
77
+ }
78
+
79
+ @injectable()
80
+ export class FrontendLanguageModelRegistryImpl
81
+ extends DefaultLanguageModelRegistryImpl {
82
+
83
+ // called by backend
84
+ languageModelAdded(metadata: LanguageModelMetaData): void {
85
+ this.addLanguageModels([metadata]);
86
+ }
87
+ // called by backend
88
+ languageModelRemoved(id: string): void {
89
+ this.removeLanguageModels([id]);
90
+ }
91
+ @inject(LanguageModelRegistryFrontendDelegate)
92
+ protected registryDelegate: LanguageModelRegistryFrontendDelegate;
93
+
94
+ @inject(LanguageModelFrontendDelegate)
95
+ protected providerDelegate: LanguageModelFrontendDelegate;
96
+
97
+ @inject(LanguageModelDelegateClientImpl)
98
+ protected client: LanguageModelDelegateClientImpl;
99
+
100
+ @inject(ILogger)
101
+ protected override logger: ILogger;
102
+
103
+ @inject(OutputChannelManager)
104
+ protected outputChannelManager: OutputChannelManager;
105
+
106
+ @inject(AISettingsService)
107
+ protected settingsService: AISettingsService;
108
+
109
+ private static requestCounter: number = 0;
110
+
111
+ override addLanguageModels(models: LanguageModelMetaData[] | LanguageModel[]): void {
112
+ let modelAdded = false;
113
+ for (const model of models) {
114
+ if (this.languageModels.find(m => m.id === model.id)) {
115
+ console.warn(`Tried to add an existing model ${model.id}`);
116
+ continue;
117
+ }
118
+ if (LanguageModel.is(model)) {
119
+ this.languageModels.push(
120
+ new Proxy(
121
+ model,
122
+ languageModelOutputHandler(
123
+ () => this.outputChannelManager.getChannel(
124
+ model.id
125
+ )
126
+ )
127
+ )
128
+ );
129
+ modelAdded = true;
130
+ } else {
131
+ this.languageModels.push(
132
+ new Proxy(
133
+ this.createFrontendLanguageModel(
134
+ model
135
+ ),
136
+ languageModelOutputHandler(
137
+ () => this.outputChannelManager.getChannel(
138
+ model.id
139
+ )
140
+ )
141
+ )
142
+ );
143
+ modelAdded = true;
144
+ }
145
+ }
146
+ if (modelAdded) {
147
+ this.changeEmitter.fire({ models: this.languageModels });
148
+ }
149
+ }
150
+
151
+ @postConstruct()
152
+ protected override init(): void {
153
+ this.client.setReceiver(this);
154
+
155
+ const contributions =
156
+ this.languageModelContributions.getContributions();
157
+ const promises = contributions.map(provider => provider());
158
+ const backendDescriptions =
159
+ this.registryDelegate.getLanguageModelDescriptions();
160
+
161
+ Promise.allSettled([backendDescriptions, ...promises]).then(
162
+ results => {
163
+ const backendDescriptionsResult = results[0];
164
+ if (backendDescriptionsResult.status === 'fulfilled') {
165
+ this.addLanguageModels(backendDescriptionsResult.value);
166
+ } else {
167
+ this.logger.error(
168
+ 'Failed to add language models contributed from the backend',
169
+ backendDescriptionsResult.reason
170
+ );
171
+ }
172
+ for (let i = 1; i < results.length; i++) {
173
+ // assert that index > 0 contains only language models
174
+ const languageModelResult = results[i] as
175
+ | PromiseRejectedResult
176
+ | PromiseFulfilledResult<LanguageModel[]>;
177
+ if (languageModelResult.status === 'fulfilled') {
178
+ this.addLanguageModels(languageModelResult.value);
179
+ } else {
180
+ this.logger.error(
181
+ 'Failed to add some language models:',
182
+ languageModelResult.reason
183
+ );
184
+ }
185
+ }
186
+ this.markInitialized();
187
+ }
188
+ );
189
+ }
190
+
191
+ createFrontendLanguageModel(
192
+ description: LanguageModelMetaData
193
+ ): LanguageModel {
194
+ return {
195
+ ...description,
196
+ request: async (request: LanguageModelRequest, cancellationToken?: CancellationToken) => {
197
+ const requestId = `${FrontendLanguageModelRegistryImpl.requestCounter++}`;
198
+ this.requests.set(requestId, request);
199
+ cancellationToken?.onCancellationRequested(() => {
200
+ this.providerDelegate.cancel(requestId);
201
+ });
202
+ const response = await this.providerDelegate.request(
203
+ description.id,
204
+ request,
205
+ requestId,
206
+ cancellationToken
207
+ );
208
+ if (isLanguageModelTextResponse(response) || isLanguageModelParsedResponse(response)) {
209
+ return response;
210
+ }
211
+ if (isLanguageModelStreamResponseDelegate(response)) {
212
+ if (!this.streams.has(response.streamId)) {
213
+ const newStreamState = {
214
+ id: response.streamId,
215
+ tokens: [],
216
+ };
217
+ this.streams.set(response.streamId, newStreamState);
218
+ }
219
+ const streamState = this.streams.get(response.streamId)!;
220
+ return {
221
+ stream: this.getIterable(streamState),
222
+ };
223
+ }
224
+ this.logger.error(
225
+ `Received unknown response in frontend for request to language model ${description.id}. Trying to continue without touching the response.`,
226
+ response
227
+ );
228
+ return response;
229
+ },
230
+ };
231
+ }
232
+
233
+ private streams = new Map<string, StreamState>();
234
+ private requests = new Map<string, LanguageModelRequest>();
235
+
236
+ async *getIterable(
237
+ state: StreamState
238
+ ): AsyncIterable<LanguageModelStreamResponsePart> {
239
+ let current = -1;
240
+ while (true) {
241
+ if (current < state.tokens.length - 1) {
242
+ current++;
243
+ const token = state.tokens[current];
244
+ if (token === undefined) {
245
+ // message is finished
246
+ break;
247
+ }
248
+ if (token !== undefined) {
249
+ yield token;
250
+ }
251
+ } else {
252
+ await new Promise(resolve => {
253
+ state.resolve = resolve;
254
+ });
255
+ }
256
+ }
257
+ this.streams.delete(state.id);
258
+ }
259
+
260
+ // called by backend via the "delegate client" with new tokens
261
+ send(id: string, token: LanguageModelStreamResponsePart | undefined): void {
262
+ if (!this.streams.has(id)) {
263
+ const newStreamState = {
264
+ id,
265
+ tokens: [],
266
+ };
267
+ this.streams.set(id, newStreamState);
268
+ }
269
+ const streamState = this.streams.get(id)!;
270
+ streamState.tokens.push(token);
271
+ if (streamState.resolve) {
272
+ streamState.resolve(token);
273
+ }
274
+ }
275
+
276
+ // called by backend once tool is invoked
277
+ toolCall(id: string, toolId: string, arg_string: string): Promise<unknown> {
278
+ if (!this.requests.has(id)) {
279
+ throw new Error('Somehow we got a callback for a non existing request!');
280
+ }
281
+ const request = this.requests.get(id)!;
282
+ const tool = request.tools?.find(t => t.id === toolId);
283
+ if (tool) {
284
+ return tool.handler(arg_string);
285
+ }
286
+ throw new Error(`Could not find a tool for ${toolId}!`);
287
+ }
288
+
289
+ override async selectLanguageModels(request: LanguageModelSelector): Promise<LanguageModel[]> {
290
+ await this.initialized;
291
+ const userSettings = (await this.settingsService.getAgentSettings(request.agent))?.languageModelRequirements?.find(req => req.purpose === request.purpose);
292
+ if (userSettings?.identifier) {
293
+ const model = await this.getLanguageModel(userSettings.identifier);
294
+ if (model) {
295
+ return [model];
296
+ }
297
+ }
298
+ return this.languageModels.filter(model => isModelMatching(request, model));
299
+ }
300
+
301
+ override async selectLanguageModel(request: LanguageModelSelector): Promise<LanguageModel | undefined> {
302
+ return (await this.selectLanguageModels(request))[0];
303
+ }
304
+ }
305
+
306
+ const formatJsonWithIndentation = (obj: unknown): string[] => {
307
+ // eslint-disable-next-line no-null/no-null
308
+ const jsonString = JSON.stringify(obj, null, 2);
309
+ const lines = jsonString.split('\n');
310
+ const formattedLines: string[] = [];
311
+
312
+ lines.forEach(line => {
313
+ const subLines = line.split('\\n');
314
+ const index = indexOfValue(subLines[0]) + 1;
315
+ formattedLines.push(subLines[0]);
316
+ const prefix = index > 0 ? ' '.repeat(index) : '';
317
+ if (index !== -1) {
318
+ for (let i = 1; i < subLines.length; i++) {
319
+ formattedLines.push(prefix + subLines[i]);
320
+ }
321
+ }
322
+ });
323
+
324
+ return formattedLines;
325
+ };
326
+
327
+ const indexOfValue = (jsonLine: string): number => {
328
+ const pattern = /"([^"]+)"\s*:\s*/g;
329
+ const match = pattern.exec(jsonLine);
330
+ return match ? match.index + match[0].length : -1;
331
+ };
332
+
333
+ const languageModelOutputHandler = (
334
+ outputChannelGetter: () => OutputChannel
335
+ ): ProxyHandler<LanguageModel> => ({
336
+ get<K extends keyof LanguageModel>(
337
+ target: LanguageModel,
338
+ prop: K,
339
+ ): LanguageModel[K] | LanguageModel['request'] {
340
+ const original = target[prop];
341
+ if (prop === 'request' && typeof original === 'function') {
342
+ return async function (
343
+ ...args: Parameters<LanguageModel['request']>
344
+ ): Promise<LanguageModelResponse> {
345
+ const outputChannel = outputChannelGetter();
346
+ outputChannel.appendLine(
347
+ 'Sending request:'
348
+ );
349
+ const formattedRequest = formatJsonWithIndentation(args[0]);
350
+ formattedRequest.forEach(line => outputChannel.appendLine(line));
351
+ if (args[1]) {
352
+ args[1] = new Proxy(args[1], {
353
+ get<CK extends keyof CancellationToken>(
354
+ cTarget: CancellationToken,
355
+ cProp: CK
356
+ ): CancellationToken[CK] | CancellationToken['onCancellationRequested'] {
357
+ if (cProp === 'onCancellationRequested') {
358
+ return (...cargs: Parameters<CancellationToken['onCancellationRequested']>) => cTarget.onCancellationRequested(() => {
359
+ outputChannel.appendLine('\nCancel requested', OutputChannelSeverity.Warning);
360
+ cargs[0]();
361
+ }, cargs[1], cargs[2]);
362
+ }
363
+ return cTarget[cProp];
364
+ }
365
+ });
366
+ }
367
+ try {
368
+ const result = await original.apply(target, args);
369
+ if (isLanguageModelStreamResponse(result)) {
370
+ outputChannel.appendLine('Received a response stream');
371
+ const stream = result.stream;
372
+ const loggedStream = {
373
+ async *[Symbol.asyncIterator](): AsyncIterator<LanguageModelStreamResponsePart> {
374
+ for await (const part of stream) {
375
+ outputChannel.append(part.content || '');
376
+ yield part;
377
+ }
378
+ outputChannel.append('\n');
379
+ outputChannel.appendLine('End of stream');
380
+ },
381
+ };
382
+ return {
383
+ ...result,
384
+ stream: loggedStream,
385
+ };
386
+ } else {
387
+ outputChannel.appendLine('Received a response');
388
+ outputChannel.appendLine(JSON.stringify(result));
389
+ return result;
390
+ }
391
+ } catch (err) {
392
+ outputChannel.appendLine('An error occurred');
393
+ if (err instanceof Error) {
394
+ outputChannel.appendLine(
395
+ err.message,
396
+ OutputChannelSeverity.Error
397
+ );
398
+ }
399
+ throw err;
400
+ }
401
+ };
402
+ }
403
+ return original;
404
+ },
405
+ });