@theia/ai-chat 1.74.0-next.9 → 1.74.1
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/agent-delegation-tool.js +6 -7
- package/lib/browser/agent-delegation-tool.js.map +1 -1
- package/lib/browser/agent-delegation-tool.spec.js +13 -0
- package/lib/browser/agent-delegation-tool.spec.js.map +1 -1
- package/lib/browser/chat-request-command-resolution.spec.d.ts +2 -0
- package/lib/browser/chat-request-command-resolution.spec.d.ts.map +1 -0
- package/lib/browser/chat-request-command-resolution.spec.js +94 -0
- package/lib/browser/chat-request-command-resolution.spec.js.map +1 -0
- package/lib/browser/chat-session-store-impl.d.ts.map +1 -1
- package/lib/browser/chat-session-store-impl.js +2 -0
- package/lib/browser/chat-session-store-impl.js.map +1 -1
- package/lib/browser/chat-session-store-impl.spec.js +51 -2
- package/lib/browser/chat-session-store-impl.spec.js.map +1 -1
- package/lib/browser/chat-tool-preference-bindings.d.ts +18 -8
- package/lib/browser/chat-tool-preference-bindings.d.ts.map +1 -1
- package/lib/browser/chat-tool-preference-bindings.js +27 -28
- package/lib/browser/chat-tool-preference-bindings.js.map +1 -1
- package/lib/browser/chat-tool-preference-bindings.spec.js +43 -46
- package/lib/browser/chat-tool-preference-bindings.spec.js.map +1 -1
- package/lib/common/chat-agents.d.ts +8 -0
- package/lib/common/chat-agents.d.ts.map +1 -1
- package/lib/common/chat-agents.js +35 -1
- package/lib/common/chat-agents.js.map +1 -1
- package/lib/common/chat-agents.spec.js +94 -0
- package/lib/common/chat-agents.spec.js.map +1 -1
- package/lib/common/chat-auto-save.spec.js +13 -0
- package/lib/common/chat-auto-save.spec.js.map +1 -1
- package/lib/common/chat-content-deserializer.d.ts.map +1 -1
- package/lib/common/chat-content-deserializer.js +4 -0
- package/lib/common/chat-content-deserializer.js.map +1 -1
- package/lib/common/chat-model-serialization.d.ts +12 -1
- package/lib/common/chat-model-serialization.d.ts.map +1 -1
- package/lib/common/chat-model-serialization.js.map +1 -1
- package/lib/common/chat-model-serialization.spec.js +29 -0
- package/lib/common/chat-model-serialization.spec.js.map +1 -1
- package/lib/common/chat-model.d.ts +105 -2
- package/lib/common/chat-model.d.ts.map +1 -1
- package/lib/common/chat-model.js +159 -3
- package/lib/common/chat-model.js.map +1 -1
- package/lib/common/chat-model.spec.d.ts +2 -0
- package/lib/common/chat-model.spec.d.ts.map +1 -0
- package/lib/common/chat-model.spec.js +42 -0
- package/lib/common/chat-model.spec.js.map +1 -0
- package/lib/common/chat-request-parser.d.ts +9 -1
- package/lib/common/chat-request-parser.d.ts.map +1 -1
- package/lib/common/chat-request-parser.js +54 -3
- package/lib/common/chat-request-parser.js.map +1 -1
- package/lib/common/chat-request-parser.spec.js +333 -0
- package/lib/common/chat-request-parser.spec.js.map +1 -1
- package/lib/common/chat-response-model.spec.js +31 -0
- package/lib/common/chat-response-model.spec.js.map +1 -1
- package/lib/common/chat-service-deletion.spec.js +43 -0
- package/lib/common/chat-service-deletion.spec.js.map +1 -1
- package/lib/common/chat-service-pinned-agent.spec.js +52 -2
- package/lib/common/chat-service-pinned-agent.spec.js.map +1 -1
- package/lib/common/chat-service-session-events.spec.d.ts +2 -0
- package/lib/common/chat-service-session-events.spec.d.ts.map +1 -0
- package/lib/common/chat-service-session-events.spec.js +130 -0
- package/lib/common/chat-service-session-events.spec.js.map +1 -0
- package/lib/common/chat-service.d.ts +3 -0
- package/lib/common/chat-service.d.ts.map +1 -1
- package/lib/common/chat-service.js +60 -6
- package/lib/common/chat-service.js.map +1 -1
- package/lib/common/chat-service.spec.d.ts +2 -0
- package/lib/common/chat-service.spec.d.ts.map +1 -0
- package/lib/common/chat-service.spec.js +177 -0
- package/lib/common/chat-service.spec.js.map +1 -0
- package/lib/common/chat-session-status.spec.d.ts +2 -0
- package/lib/common/chat-session-status.spec.d.ts.map +1 -0
- package/lib/common/chat-session-status.spec.js +213 -0
- package/lib/common/chat-session-status.spec.js.map +1 -0
- package/lib/common/chat-session-store.d.ts +8 -0
- package/lib/common/chat-session-store.d.ts.map +1 -1
- package/lib/common/tool-call-response-content.spec.js +36 -0
- package/lib/common/tool-call-response-content.spec.js.map +1 -1
- package/package.json +11 -11
- package/src/browser/agent-delegation-tool.spec.ts +16 -0
- package/src/browser/agent-delegation-tool.ts +7 -7
- package/src/browser/chat-request-command-resolution.spec.ts +117 -0
- package/src/browser/chat-session-store-impl.spec.ts +62 -2
- package/src/browser/chat-session-store-impl.ts +2 -0
- package/src/browser/chat-tool-preference-bindings.spec.ts +43 -48
- package/src/browser/chat-tool-preference-bindings.ts +42 -30
- package/src/common/chat-agents.spec.ts +112 -2
- package/src/common/chat-agents.ts +44 -1
- package/src/common/chat-auto-save.spec.ts +18 -1
- package/src/common/chat-content-deserializer.ts +11 -0
- package/src/common/chat-model-serialization.spec.ts +38 -0
- package/src/common/chat-model-serialization.ts +12 -1
- package/src/common/chat-model.spec.ts +42 -0
- package/src/common/chat-model.ts +241 -3
- package/src/common/chat-request-parser.spec.ts +401 -1
- package/src/common/chat-request-parser.ts +60 -5
- package/src/common/chat-response-model.spec.ts +41 -1
- package/src/common/chat-service-deletion.spec.ts +52 -0
- package/src/common/chat-service-pinned-agent.spec.ts +58 -2
- package/src/common/chat-service-session-events.spec.ts +161 -0
- package/src/common/chat-service.spec.ts +211 -0
- package/src/common/chat-service.ts +64 -8
- package/src/common/chat-session-status.spec.ts +280 -0
- package/src/common/chat-session-store.ts +8 -0
- package/src/common/tool-call-response-content.spec.ts +42 -0
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
CapabilityAwareContext,
|
|
28
28
|
GenericCapabilitySelections,
|
|
29
29
|
getTextOfResponse,
|
|
30
|
+
isCompactionResponsePart,
|
|
30
31
|
isLanguageModelStreamResponsePart,
|
|
31
32
|
isServerToolCallResponsePart,
|
|
32
33
|
isTextResponsePart,
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
} from '@theia/ai-core';
|
|
52
53
|
import {
|
|
53
54
|
Agent,
|
|
55
|
+
FrontendLanguageModelRegistry,
|
|
54
56
|
isLanguageModelParsedResponse,
|
|
55
57
|
isLanguageModelStreamResponse,
|
|
56
58
|
isLanguageModelTextResponse,
|
|
@@ -65,6 +67,7 @@ import {
|
|
|
65
67
|
ChatRequestModel,
|
|
66
68
|
ChatResponseContent,
|
|
67
69
|
CommonChatSessionSettings,
|
|
70
|
+
CompactionChatResponseContentImpl,
|
|
68
71
|
ErrorChatResponseContentImpl,
|
|
69
72
|
MarkdownChatResponseContentImpl,
|
|
70
73
|
MutableChatRequestModel,
|
|
@@ -225,10 +228,12 @@ export abstract class AbstractChatAgent implements ChatAgent {
|
|
|
225
228
|
|
|
226
229
|
async invoke(request: MutableChatRequestModel): Promise<void> {
|
|
227
230
|
try {
|
|
228
|
-
const languageModel = await this.
|
|
231
|
+
const languageModel = await this.getLanguageModelForRequest(request, this.defaultLanguageModelPurpose);
|
|
229
232
|
if (!languageModel) {
|
|
230
233
|
throw new Error(nls.localize('theia/ai/chat/couldNotFindMatchingLM', 'Couldn\'t find a matching language model. Please check your setup!'));
|
|
231
234
|
}
|
|
235
|
+
// Record the model that actually handled this request so the chat thread can show it.
|
|
236
|
+
request.response.setLanguageModel(languageModel.id);
|
|
232
237
|
const context: ChatSessionContext = {
|
|
233
238
|
model: request.session,
|
|
234
239
|
request,
|
|
@@ -313,6 +318,36 @@ export abstract class AbstractChatAgent implements ChatAgent {
|
|
|
313
318
|
return this.selectLanguageModel(this.getLanguageModelSelector(languageModelPurpose));
|
|
314
319
|
}
|
|
315
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Resolves the language model for a request, honoring a per-session model override
|
|
323
|
+
* ({@link CommonChatSessionSettings.modelId}) when set and resolvable, and otherwise falling
|
|
324
|
+
* back to the agent's configured model for the given purpose.
|
|
325
|
+
*/
|
|
326
|
+
protected async getLanguageModelForRequest(request: MutableChatRequestModel, languageModelPurpose: string): Promise<LanguageModel> {
|
|
327
|
+
const overrideId = request.session.settings?.commonSettings?.modelId;
|
|
328
|
+
if (overrideId) {
|
|
329
|
+
// Resolve the override directly. We must not go through `selectLanguageModel` here, because
|
|
330
|
+
// that honors the per-agent model configured in the AI settings, which would take
|
|
331
|
+
// precedence over and thus ignore the session override.
|
|
332
|
+
const overridden = await this.resolveModelById(overrideId);
|
|
333
|
+
if (overridden) {
|
|
334
|
+
return overridden;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return this.getLanguageModel(languageModelPurpose);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Resolves a concrete language model (or alias) id to a ready model, or `undefined` if none is ready. */
|
|
341
|
+
protected async resolveModelById(modelId: string): Promise<LanguageModel | undefined> {
|
|
342
|
+
const registry = this.languageModelRegistry as LanguageModelRegistry & Partial<FrontendLanguageModelRegistry>;
|
|
343
|
+
if (typeof registry.getReadyLanguageModel === 'function') {
|
|
344
|
+
// Frontend registry: resolves aliases and only returns the model if it is ready.
|
|
345
|
+
return registry.getReadyLanguageModel(modelId);
|
|
346
|
+
}
|
|
347
|
+
const model = await registry.getLanguageModel(modelId);
|
|
348
|
+
return model?.status.status === 'ready' ? model : undefined;
|
|
349
|
+
}
|
|
350
|
+
|
|
316
351
|
protected async selectLanguageModel(selector: LanguageModelRequirement): Promise<LanguageModel> {
|
|
317
352
|
const languageModel = await this.languageModelRegistry.selectLanguageModel({ agent: this.id, ...selector });
|
|
318
353
|
if (!languageModel) {
|
|
@@ -547,6 +582,7 @@ export abstract class AbstractChatAgent implements ChatAgent {
|
|
|
547
582
|
serverTools: enabledServerTools.length > 0 ? enabledServerTools : undefined,
|
|
548
583
|
settings,
|
|
549
584
|
reasoning: commonSettings?.reasoning,
|
|
585
|
+
compaction: commonSettings?.compaction,
|
|
550
586
|
agentId: this.id,
|
|
551
587
|
sessionId: request.session.id,
|
|
552
588
|
requestId: request.id,
|
|
@@ -776,6 +812,13 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent {
|
|
|
776
812
|
request.response.setTokenUsage(this.mapUsageResponsePart(token));
|
|
777
813
|
return [];
|
|
778
814
|
}
|
|
815
|
+
if (isCompactionResponsePart(token)) {
|
|
816
|
+
return new CompactionChatResponseContentImpl(
|
|
817
|
+
token.compaction.provider,
|
|
818
|
+
token.compaction.data,
|
|
819
|
+
token.compaction.summary
|
|
820
|
+
);
|
|
821
|
+
}
|
|
779
822
|
return this.defaultContentFactory.create('', request);
|
|
780
823
|
}
|
|
781
824
|
|
|
@@ -25,7 +25,7 @@ import { ILogger } from '@theia/core';
|
|
|
25
25
|
import { ChatAgentLocation } from './chat-agents';
|
|
26
26
|
import { ChatContentDeserializerRegistry, ChatContentDeserializerRegistryImpl, DefaultChatContentDeserializerContribution } from './chat-content-deserializer';
|
|
27
27
|
import { ChangeSetElementDeserializerRegistry, ChangeSetElementDeserializerRegistryImpl } from './change-set-element-deserializer';
|
|
28
|
-
import { ChatModel } from './chat-model';
|
|
28
|
+
import { ChatModel, MutableChatModel } from './chat-model';
|
|
29
29
|
import { SerializedChatData } from './chat-model-serialization';
|
|
30
30
|
import { ParsedChatRequest, ParsedChatRequestTextPart } from './parsed-chat-request';
|
|
31
31
|
|
|
@@ -242,6 +242,23 @@ describe('Chat Auto-Save Mechanism', () => {
|
|
|
242
242
|
});
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
+
describe('Auto-save on settings changes', () => {
|
|
246
|
+
it('should auto-save when per-session settings change', async () => {
|
|
247
|
+
const session = chatService.createSession(ChatAgentLocation.Panel);
|
|
248
|
+
// Non-empty session so saveSession does not skip it.
|
|
249
|
+
await chatService.sendRequest(session.id, { text: 'Test request' });
|
|
250
|
+
sessionStore.reset();
|
|
251
|
+
|
|
252
|
+
// A selector-only / dialog-only settings change must trigger persistence.
|
|
253
|
+
(session.model as MutableChatModel).setSettings({ commonSettings: { modelId: 'test/model' } });
|
|
254
|
+
|
|
255
|
+
// Wait for auto-save to complete (debounce is 500ms + execution time)
|
|
256
|
+
await new Promise(resolve => setTimeout(resolve, 700));
|
|
257
|
+
|
|
258
|
+
expect(sessionStore.saveCount).to.be.greaterThan(0);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
245
262
|
describe('Auto-save for all sessions', () => {
|
|
246
263
|
it('should save all non-empty sessions', async () => {
|
|
247
264
|
const session1 = chatService.createSession(ChatAgentLocation.Panel);
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
ChatResponseContent,
|
|
20
20
|
CodeChatResponseContentImpl,
|
|
21
21
|
CommandChatResponseContentImpl,
|
|
22
|
+
CompactionChatResponseContentImpl,
|
|
23
|
+
CompactionContentData,
|
|
22
24
|
ErrorChatResponseContentImpl,
|
|
23
25
|
HorizontalLayoutChatResponseContentImpl,
|
|
24
26
|
InformationalChatResponseContentImpl,
|
|
@@ -250,6 +252,15 @@ export class DefaultChatContentDeserializerContribution implements ChatContentDe
|
|
|
250
252
|
)
|
|
251
253
|
});
|
|
252
254
|
|
|
255
|
+
registry.register({
|
|
256
|
+
kind: 'compaction',
|
|
257
|
+
deserialize: (data: CompactionContentData) => new CompactionChatResponseContentImpl(
|
|
258
|
+
data.provider,
|
|
259
|
+
data.data,
|
|
260
|
+
data.summary
|
|
261
|
+
)
|
|
262
|
+
});
|
|
263
|
+
|
|
253
264
|
registry.register({
|
|
254
265
|
kind: 'markdownContent',
|
|
255
266
|
deserialize: (data: MarkdownContentData) => new MarkdownChatResponseContentImpl(data.content)
|
|
@@ -700,4 +700,42 @@ describe('ChatModel Serialization and Restoration', () => {
|
|
|
700
700
|
expect(restoredPart.resolution?.variable.description).to.equal('File variable');
|
|
701
701
|
});
|
|
702
702
|
});
|
|
703
|
+
|
|
704
|
+
describe('Session settings and response model serialization', () => {
|
|
705
|
+
it('should serialize and restore the per-session model override', () => {
|
|
706
|
+
const model = new MutableChatModel(ChatAgentLocation.Panel);
|
|
707
|
+
model.addRequest(createParsedRequest('Hello'));
|
|
708
|
+
model.setSettings({ commonSettings: { modelId: 'anthropic/claude-opus-4-8' } });
|
|
709
|
+
|
|
710
|
+
const serialized = model.toSerializable();
|
|
711
|
+
expect(serialized.settings?.commonSettings?.modelId).to.equal('anthropic/claude-opus-4-8');
|
|
712
|
+
|
|
713
|
+
const restored = new MutableChatModel(serialized);
|
|
714
|
+
expect(restored.settings?.commonSettings?.modelId).to.equal('anthropic/claude-opus-4-8');
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it('should leave settings undefined when none were set', () => {
|
|
718
|
+
const model = new MutableChatModel(ChatAgentLocation.Panel);
|
|
719
|
+
model.addRequest(createParsedRequest('Hello'));
|
|
720
|
+
|
|
721
|
+
const serialized = model.toSerializable();
|
|
722
|
+
expect(serialized.settings).to.be.undefined;
|
|
723
|
+
|
|
724
|
+
const restored = new MutableChatModel(serialized);
|
|
725
|
+
expect(restored.settings).to.be.undefined;
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('should serialize and restore the language model recorded on a response', () => {
|
|
729
|
+
const model = new MutableChatModel(ChatAgentLocation.Panel);
|
|
730
|
+
const request = model.addRequest(createParsedRequest('Hello'));
|
|
731
|
+
request.response.setLanguageModel('anthropic/claude-opus-4-8');
|
|
732
|
+
request.response.complete();
|
|
733
|
+
|
|
734
|
+
const serialized = model.toSerializable();
|
|
735
|
+
expect(serialized.responses[0].languageModel).to.equal('anthropic/claude-opus-4-8');
|
|
736
|
+
|
|
737
|
+
const restored = new MutableChatModel(serialized);
|
|
738
|
+
expect(restored.getAllRequests()[0].response.languageModel).to.equal('anthropic/claude-opus-4-8');
|
|
739
|
+
});
|
|
740
|
+
});
|
|
703
741
|
});
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { GenericCapabilitySelections } from '@theia/ai-core';
|
|
18
18
|
import { ChatAgentLocation } from './chat-agents';
|
|
19
|
-
import { ResponseTokenUsage } from './chat-model';
|
|
19
|
+
import { ChatSessionSettings, ResponseTokenUsage } from './chat-model';
|
|
20
20
|
|
|
21
21
|
export interface SerializableChangeSetElement {
|
|
22
22
|
kind?: string;
|
|
@@ -138,6 +138,8 @@ export interface SerializableChatResponseData {
|
|
|
138
138
|
errorMessage?: string;
|
|
139
139
|
promptVariantId?: string;
|
|
140
140
|
isPromptVariantEdited?: boolean;
|
|
141
|
+
/** Identifier of the language model that produced this response, if recorded. */
|
|
142
|
+
languageModel?: string;
|
|
141
143
|
tokenUsage?: ResponseTokenUsage;
|
|
142
144
|
content: SerializableChatResponseContentData[];
|
|
143
145
|
}
|
|
@@ -189,6 +191,11 @@ export interface SerializedChatModel {
|
|
|
189
191
|
requests: SerializableChatRequestData[];
|
|
190
192
|
/** All responses for the requests */
|
|
191
193
|
responses: SerializableChatResponseData[];
|
|
194
|
+
/**
|
|
195
|
+
* Per-session settings (e.g. the per-session model override) so they survive a reload and the
|
|
196
|
+
* chat input can restore the correct selection.
|
|
197
|
+
*/
|
|
198
|
+
settings?: ChatSessionSettings;
|
|
192
199
|
}
|
|
193
200
|
|
|
194
201
|
/**
|
|
@@ -201,6 +208,10 @@ export interface SerializedChatData {
|
|
|
201
208
|
title?: string;
|
|
202
209
|
model: SerializedChatModel;
|
|
203
210
|
saveDate: number;
|
|
211
|
+
/** ID of the root session in the delegation chain. */
|
|
212
|
+
rootSessionId?: string;
|
|
213
|
+
/** ID of the immediate parent session that delegated this one. */
|
|
214
|
+
parentSessionId?: string;
|
|
204
215
|
}
|
|
205
216
|
|
|
206
217
|
export interface SerializableChatsData {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { CompactionChatResponseContentImpl, CompactionChatResponseContent } from './chat-model';
|
|
19
|
+
|
|
20
|
+
describe('CompactionChatResponseContent', () => {
|
|
21
|
+
it('round-trips through serialization', () => {
|
|
22
|
+
const original = new CompactionChatResponseContentImpl('anthropic', { block: 'opaque' }, 'summary text');
|
|
23
|
+
const serialized = original.toSerializable();
|
|
24
|
+
expect(serialized.kind).to.equal('compaction');
|
|
25
|
+
const restored = new CompactionChatResponseContentImpl(
|
|
26
|
+
serialized.data.provider, serialized.data.data, serialized.data.summary);
|
|
27
|
+
expect(restored.provider).to.equal('anthropic');
|
|
28
|
+
expect(restored.summary).to.equal('summary text');
|
|
29
|
+
expect(restored.data).to.deep.equal({ block: 'opaque' });
|
|
30
|
+
});
|
|
31
|
+
it('produces a compaction language model message', () => {
|
|
32
|
+
const content = new CompactionChatResponseContentImpl('openai-responses', { item: 'x' });
|
|
33
|
+
const message = content.toLanguageModelMessage();
|
|
34
|
+
expect(message).to.deep.equal({ actor: 'ai', type: 'compaction', provider: 'openai-responses', data: { item: 'x' }, summary: undefined });
|
|
35
|
+
});
|
|
36
|
+
it('is excluded from plain-text history but offers a display string', () => {
|
|
37
|
+
const content = new CompactionChatResponseContentImpl('anthropic', {}, 'sum');
|
|
38
|
+
expect(content.asString()).to.equal(undefined);
|
|
39
|
+
expect(content.asDisplayString()).to.equal('sum');
|
|
40
|
+
expect(CompactionChatResponseContent.is(content)).to.equal(true);
|
|
41
|
+
});
|
|
42
|
+
});
|