mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-01 02:52:20 -05:00
Resend all messages after summarization (#298236)
This commit is contained in:
@@ -103,7 +103,7 @@ export interface IToolCallingBuiltPromptEvent {
|
||||
tools: LanguageModelToolInformation[];
|
||||
}
|
||||
|
||||
export type ToolCallingLoopFetchOptions = Required<Pick<IMakeChatRequestOptions, 'messages' | 'finishedCb' | 'requestOptions' | 'userInitiatedRequest' | 'turnId'>> & Pick<IMakeChatRequestOptions, 'modelCapabilities'>;
|
||||
export type ToolCallingLoopFetchOptions = Required<Pick<IMakeChatRequestOptions, 'messages' | 'finishedCb' | 'requestOptions' | 'userInitiatedRequest' | 'turnId'>> & Pick<IMakeChatRequestOptions, 'modelCapabilities' | 'summarizedAtRoundId'>;
|
||||
|
||||
interface StartHookResult {
|
||||
/**
|
||||
@@ -1189,6 +1189,28 @@ export abstract class ToolCallingLoop<TOptions extends IToolCallingLoopOptions =
|
||||
this.turn.setMetadata(conversationSummary);
|
||||
}
|
||||
|
||||
// Find the latest summarized round.
|
||||
let summarizedAtRoundId: string | undefined;
|
||||
for (let i = this.toolCallRounds.length - 1; i >= 0; i--) {
|
||||
if (this.toolCallRounds[i].summary) {
|
||||
summarizedAtRoundId = this.toolCallRounds[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!summarizedAtRoundId) {
|
||||
for (const turn of [...context.history].reverse()) {
|
||||
for (const round of [...turn.rounds].reverse()) {
|
||||
if (round.summary) {
|
||||
summarizedAtRoundId = round.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (summarizedAtRoundId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = await this._endpointProvider.getChatEndpoint(this.options.request);
|
||||
const tokenizer = endpoint.acquireTokenizer();
|
||||
const promptTokenLength = await tokenizer.countMessagesTokens(effectiveBuildPromptResult.messages);
|
||||
@@ -1272,6 +1294,7 @@ export abstract class ToolCallingLoop<TOptions extends IToolCallingLoopOptions =
|
||||
const fetchResult = await this.fetch({
|
||||
messages: this.applyMessagePostProcessing(effectiveBuildPromptResult.messages, { stripOrphanedToolCalls: isGeminiFamily(endpoint) }),
|
||||
turnId: this.turn.id,
|
||||
summarizedAtRoundId,
|
||||
finishedCb: async (text, index, delta) => {
|
||||
fetchStreamSource?.update(text, delta);
|
||||
if (delta.copilotToolCalls) {
|
||||
|
||||
@@ -236,6 +236,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
opts.useFetcher,
|
||||
canRetryOnce,
|
||||
requestKindOptions,
|
||||
opts.summarizedAtRoundId,
|
||||
);
|
||||
response = fetchResult.result;
|
||||
actualFetcher = fetchResult.fetcher;
|
||||
@@ -847,6 +848,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
useFetcher?: FetcherId,
|
||||
canRetryOnce?: boolean,
|
||||
requestKindOptions?: IBackgroundRequestOptions | ISubagentRequestOptions,
|
||||
summarizedAtRoundId?: string,
|
||||
): Promise<{ result: ChatResults | ChatRequestFailed | ChatRequestCanceled; fetcher?: FetcherId; bytesReceived?: number; statusCode?: number; suspendEventSeen?: boolean; resumeEventSeen?: boolean; otelSpan?: ISpanHandle }> {
|
||||
const isPowerSaveBlockerEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.TeamInternal.ChatRequestPowerSaveBlocker, this._experimentationService);
|
||||
const blockerHandle = isPowerSaveBlockerEnabled && location !== ChatLocation.Other ? this._powerService.acquirePowerSaveBlocker() : undefined;
|
||||
@@ -885,6 +887,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
useFetcher,
|
||||
canRetryOnce,
|
||||
requestKindOptions,
|
||||
summarizedAtRoundId,
|
||||
);
|
||||
return { ...fetchResult, suspendEventSeen: suspendEventSeen || undefined, resumeEventSeen: resumeEventSeen || undefined };
|
||||
} catch (err) {
|
||||
@@ -922,6 +925,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
useFetcher?: FetcherId,
|
||||
canRetryOnce?: boolean,
|
||||
requestKindOptions?: IBackgroundRequestOptions | ISubagentRequestOptions,
|
||||
summarizedAtRoundId?: string,
|
||||
): Promise<{ result: ChatResults | ChatRequestFailed | ChatRequestCanceled; fetcher?: FetcherId; bytesReceived?: number; statusCode?: number; otelSpan?: ISpanHandle }> {
|
||||
|
||||
if (cancellationToken.isCancellationRequested) {
|
||||
@@ -994,6 +998,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
userInitiatedRequest,
|
||||
telemetryProperties,
|
||||
requestKindOptions,
|
||||
summarizedAtRoundId,
|
||||
);
|
||||
return { ...wsResult, otelSpan };
|
||||
}
|
||||
@@ -1051,6 +1056,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
userInitiatedRequest: boolean | undefined,
|
||||
telemetryProperties: TelemetryProperties | undefined,
|
||||
requestKindOptions: IBackgroundRequestOptions | ISubagentRequestOptions | undefined,
|
||||
summarizedAtRoundId: string | undefined,
|
||||
): Promise<{ result: ChatResults | ChatRequestFailed | ChatRequestCanceled }> {
|
||||
const intent = locationToIntent(location);
|
||||
const agentInteractionType = requestKindOptions?.kind === 'subagent' ?
|
||||
@@ -1108,7 +1114,7 @@ export class ChatMLFetcherImpl extends AbstractChatMLFetcher {
|
||||
this._telemetryService.sendGHTelemetryEvent('request.sent', telemetryData.properties, telemetryData.measurements);
|
||||
|
||||
const requestStart = Date.now();
|
||||
const handle = connection.sendRequest(request, { userInitiated: !!userInitiatedRequest, turnId, requestId: ourRequestId, countTokens, tokenCountMax: chatEndpointInfo.maxOutputTokens }, cancellationToken);
|
||||
const handle = connection.sendRequest(request, { userInitiated: !!userInitiatedRequest, turnId, requestId: ourRequestId, countTokens, tokenCountMax: chatEndpointInfo.maxOutputTokens, summarizedAtRoundId }, cancellationToken);
|
||||
|
||||
const extendedBaseTelemetryData = baseTelemetryData.extendedBy({ modelCallId });
|
||||
const processor = this._instantiationService.createInstance(OpenAIResponsesProcessor, extendedBaseTelemetryData, this._telemetryService, modelRequestId.headerRequestId, modelRequestId.gitHubRequestId, getResponsesApiCompactionThresholdFromBody(request));
|
||||
|
||||
@@ -48,9 +48,15 @@ export function createResponsesRequestBody(accessor: ServicesAccessor, options:
|
||||
const compactThreshold = getResponsesApiCompactionThreshold(configService, expService, endpoint);
|
||||
// compaction supported for all the models but works well for codex models and any future models after 5.3
|
||||
|
||||
const webSocketStatefulMarker = resolveWebSocketStatefulMarker(accessor, options);
|
||||
// When WebSocket is in use, always defer to the WebSocket marker (which may be
|
||||
// undefined if the connection is new or the summary state changed). Never fall
|
||||
// back to the HTTP marker lookup in that case.
|
||||
const ignoreStatefulMarker = !!options.ignoreStatefulMarker || !!options.useWebSocket;
|
||||
|
||||
const body: IEndpointBody = {
|
||||
model,
|
||||
...rawMessagesToResponseAPI(model, options.messages, !!options.ignoreStatefulMarker, resolveWebSocketStatefulMarker(accessor, options)),
|
||||
...rawMessagesToResponseAPI(model, options.messages, ignoreStatefulMarker, webSocketStatefulMarker),
|
||||
stream: true,
|
||||
tools: options.requestOptions?.tools?.map((tool): OpenAI.Responses.FunctionTool & OpenAiResponsesFunctionTool => ({
|
||||
...tool.function,
|
||||
@@ -136,7 +142,15 @@ function resolveWebSocketStatefulMarker(accessor: ServicesAccessor, options: ICr
|
||||
if (options.ignoreStatefulMarker || !options.useWebSocket || !options.conversationId) {
|
||||
return undefined;
|
||||
}
|
||||
return accessor.get(IChatWebSocketManager).getStatefulMarker(options.conversationId);
|
||||
const wsManager = accessor.get(IChatWebSocketManager);
|
||||
// If client-side summarization state changed since the stateful marker
|
||||
// was stored (new summary, or rollback removing a summary), the server's
|
||||
// state no longer matches. Skip the marker so the full history is sent.
|
||||
const connSummarizedAt = wsManager.getSummarizedAtRoundId(options.conversationId);
|
||||
if (options.summarizedAtRoundId !== connSummarizedAt) {
|
||||
return undefined;
|
||||
}
|
||||
return wsManager.getStatefulMarker(options.conversationId);
|
||||
}
|
||||
|
||||
function rawMessagesToResponseAPI(modelId: string, messages: readonly Raw.ChatMessage[], ignoreStatefulMarker: boolean, webSocketStatefulMarker: string | undefined): { input: OpenAI.Responses.ResponseInputItem[]; previous_response_id?: string } {
|
||||
|
||||
@@ -813,3 +813,146 @@ describe('processResponseFromChatEndpoint telemetry', () => {
|
||||
services.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('summarizedAtRoundId and stateful marker interaction', () => {
|
||||
it('skips stateful marker when summarizedAtRoundId differs from connection', () => {
|
||||
const services = createPlatformServices();
|
||||
const wsManager: IChatWebSocketManager = {
|
||||
_serviceBrand: undefined,
|
||||
getOrCreateConnection: () => { throw new Error('not implemented'); },
|
||||
hasActiveConnection: () => false,
|
||||
getStatefulMarker: () => 'resp-prev',
|
||||
getSummarizedAtRoundId: () => 'round-old',
|
||||
closeConnection: () => { },
|
||||
closeAll: () => { },
|
||||
};
|
||||
services.set(IChatWebSocketManager, wsManager);
|
||||
const accessor = services.createTestingAccessor();
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const messages: Raw.ChatMessage[] = [
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'summarized history' }] },
|
||||
createStatefulMarkerMessage(testEndpoint.model, 'resp-prev'),
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'after marker' }] },
|
||||
];
|
||||
|
||||
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(
|
||||
servicesAccessor,
|
||||
{ ...createRequestOptions(messages, true), conversationId: 'conv-1', summarizedAtRoundId: 'round-new' },
|
||||
testEndpoint.model, testEndpoint,
|
||||
));
|
||||
|
||||
expect(body.previous_response_id).toBeUndefined();
|
||||
expect(body.input).toHaveLength(2);
|
||||
|
||||
accessor.dispose();
|
||||
services.dispose();
|
||||
});
|
||||
|
||||
it('uses stateful marker when summarizedAtRoundId matches connection', () => {
|
||||
const services = createPlatformServices();
|
||||
const wsManager = new NullChatWebSocketManager();
|
||||
wsManager.getStatefulMarker = () => 'resp-prev';
|
||||
wsManager.getSummarizedAtRoundId = () => 'round-5';
|
||||
services.set(IChatWebSocketManager, wsManager);
|
||||
const accessor = services.createTestingAccessor();
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const messages: Raw.ChatMessage[] = [
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'summarized history' }] },
|
||||
createStatefulMarkerMessage(testEndpoint.model, 'resp-prev'),
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'after marker' }] },
|
||||
];
|
||||
|
||||
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(
|
||||
servicesAccessor,
|
||||
{ ...createRequestOptions(messages, true), conversationId: 'conv-1', summarizedAtRoundId: 'round-5' },
|
||||
testEndpoint.model, testEndpoint,
|
||||
));
|
||||
|
||||
expect(body.previous_response_id).toBe('resp-prev');
|
||||
expect(body.input).toHaveLength(1);
|
||||
|
||||
accessor.dispose();
|
||||
services.dispose();
|
||||
});
|
||||
|
||||
it('uses stateful marker when both sides have no summary', () => {
|
||||
const services = createPlatformServices();
|
||||
const wsManager = new NullChatWebSocketManager();
|
||||
wsManager.getStatefulMarker = () => 'resp-prev';
|
||||
wsManager.getSummarizedAtRoundId = () => undefined;
|
||||
services.set(IChatWebSocketManager, wsManager);
|
||||
const accessor = services.createTestingAccessor();
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const messages: Raw.ChatMessage[] = [
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'first message' }] },
|
||||
createStatefulMarkerMessage(testEndpoint.model, 'resp-prev'),
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'second message' }] },
|
||||
];
|
||||
|
||||
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(
|
||||
servicesAccessor,
|
||||
{ ...createRequestOptions(messages, true), conversationId: 'conv-1' },
|
||||
testEndpoint.model, testEndpoint,
|
||||
));
|
||||
|
||||
expect(body.previous_response_id).toBe('resp-prev');
|
||||
expect(body.input).toHaveLength(1);
|
||||
|
||||
accessor.dispose();
|
||||
services.dispose();
|
||||
});
|
||||
|
||||
it('skips stateful marker when conversation is rolled back past summary', () => {
|
||||
const services = createPlatformServices();
|
||||
const wsManager = new NullChatWebSocketManager();
|
||||
wsManager.getStatefulMarker = () => 'resp-prev';
|
||||
wsManager.getSummarizedAtRoundId = () => 'round-5';
|
||||
services.set(IChatWebSocketManager, wsManager);
|
||||
const accessor = services.createTestingAccessor();
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const messages: Raw.ChatMessage[] = [
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'first message' }] },
|
||||
createStatefulMarkerMessage(testEndpoint.model, 'resp-prev'),
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'second message' }] },
|
||||
];
|
||||
|
||||
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(
|
||||
servicesAccessor,
|
||||
{ ...createRequestOptions(messages, true), conversationId: 'conv-1', summarizedAtRoundId: undefined },
|
||||
testEndpoint.model, testEndpoint,
|
||||
));
|
||||
|
||||
expect(body.previous_response_id).toBeUndefined();
|
||||
expect(body.input).toHaveLength(2);
|
||||
|
||||
accessor.dispose();
|
||||
services.dispose();
|
||||
});
|
||||
|
||||
it('skips stateful marker on first request after new summarization', () => {
|
||||
const services = createPlatformServices();
|
||||
const wsManager = new NullChatWebSocketManager();
|
||||
wsManager.getStatefulMarker = () => 'resp-prev';
|
||||
wsManager.getSummarizedAtRoundId = () => undefined;
|
||||
services.set(IChatWebSocketManager, wsManager);
|
||||
const accessor = services.createTestingAccessor();
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const messages: Raw.ChatMessage[] = [
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'summarized history' }] },
|
||||
createStatefulMarkerMessage(testEndpoint.model, 'resp-prev'),
|
||||
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'after marker' }] },
|
||||
];
|
||||
|
||||
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(
|
||||
servicesAccessor,
|
||||
{ ...createRequestOptions(messages, true), conversationId: 'conv-1', summarizedAtRoundId: 'round-new' },
|
||||
testEndpoint.model, testEndpoint,
|
||||
));
|
||||
|
||||
expect(body.previous_response_id).toBeUndefined();
|
||||
expect(body.input).toHaveLength(2);
|
||||
|
||||
accessor.dispose();
|
||||
services.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -198,6 +198,11 @@ export interface IMakeChatRequestOptions {
|
||||
useFetcher?: FetcherId;
|
||||
/** Per-request model capability opt-ins (thinking, tool search, context editing). */
|
||||
modelCapabilities?: IModelCapabilityOptions;
|
||||
/**
|
||||
* The round ID at which the most recent client-side summarization occurred.
|
||||
* Used to detect when the WebSocket stateful marker predates a summary.
|
||||
*/
|
||||
summarizedAtRoundId?: string;
|
||||
/** Enable retrying once on simple network errors like ECONNRESET. */
|
||||
canRetryOnceWithoutRollback?: boolean;
|
||||
/** Custom metadata to be displayed in the log document */
|
||||
|
||||
@@ -45,6 +45,12 @@ export interface IChatWebSocketManager {
|
||||
*/
|
||||
getStatefulMarker(conversationId: string): string | undefined;
|
||||
|
||||
/**
|
||||
* Returns the round ID at which the last client-side summarization
|
||||
* occurred for this connection, or undefined if none.
|
||||
*/
|
||||
getSummarizedAtRoundId(conversationId: string): string | undefined;
|
||||
|
||||
/**
|
||||
* Closes and removes the connection for a specific conversation.
|
||||
*/
|
||||
@@ -66,6 +72,7 @@ export class NullChatWebSocketManager implements IChatWebSocketManager {
|
||||
}
|
||||
hasActiveConnection(_conversationId: string): boolean { return false; }
|
||||
getStatefulMarker(_conversationId: string): string | undefined { return undefined; }
|
||||
getSummarizedAtRoundId(_conversationId: string): string | undefined { return undefined; }
|
||||
closeConnection(_conversationId: string): void { }
|
||||
closeAll(): void { }
|
||||
}
|
||||
@@ -76,6 +83,7 @@ export interface IChatWebSocketRequestOptions {
|
||||
requestId: string;
|
||||
countTokens: () => Promise<number>;
|
||||
tokenCountMax: number;
|
||||
summarizedAtRoundId?: string;
|
||||
}
|
||||
|
||||
export interface IChatWebSocketConnection extends IDisposable {
|
||||
@@ -216,6 +224,11 @@ export class ChatWebSocketManager extends Disposable implements IChatWebSocketMa
|
||||
return connection?.isOpen ? connection.statefulMarker : undefined;
|
||||
}
|
||||
|
||||
getSummarizedAtRoundId(conversationId: string): string | undefined {
|
||||
const connection = this._connections.get(conversationId);
|
||||
return connection?.isOpen ? connection.summarizedAtRoundId : undefined;
|
||||
}
|
||||
|
||||
closeConnection(conversationId: string): void {
|
||||
const connection = this._connections.get(conversationId);
|
||||
if (connection) {
|
||||
@@ -274,6 +287,7 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
private _state: ConnectionState = ConnectionState.Closed;
|
||||
private _activeRequest: ChatWebSocketActiveRequest | undefined;
|
||||
private _statefulMarker: string | undefined;
|
||||
private _summarizedAtRoundId: string | undefined;
|
||||
|
||||
private readonly _onDidDispose = this._register(new Emitter<void>());
|
||||
readonly onDidDispose = this._onDidDispose.event;
|
||||
@@ -320,6 +334,10 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
return this._statefulMarker;
|
||||
}
|
||||
|
||||
get summarizedAtRoundId(): string | undefined {
|
||||
return this._summarizedAtRoundId;
|
||||
}
|
||||
|
||||
get responseHeaders(): IHeaders {
|
||||
return this._responseHeaders;
|
||||
}
|
||||
@@ -469,6 +487,7 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
|
||||
if (!isCAPIWebSocketError(parsed) && parsed.type === 'response.completed') {
|
||||
this._statefulMarker = parsed.response.id;
|
||||
this._summarizedAtRoundId = this._activeRequest?.summarizedAtRoundId;
|
||||
}
|
||||
|
||||
this._activeRequest?.handleEvent(parsed);
|
||||
@@ -536,12 +555,13 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
const statefulMarkerMatched = this._statefulMarker === body.previous_response_id;
|
||||
const previousResponseIdUnset = body.previous_response_id === undefined;
|
||||
const hasCompactionData = body.input?.some(item => item?.type === 'compaction') ?? false;
|
||||
const summarizedAtRoundIdMatched = options.summarizedAtRoundId === this._summarizedAtRoundId;
|
||||
const statefulMarkerPrefix = this._statefulMarker?.slice(0, 5).concat('...') ?? '<none>';
|
||||
const previousResponsePrefix = body.previous_response_id?.slice(0, 5).concat('...') ?? '<none>';
|
||||
if (statefulMarkerMatched) {
|
||||
this._logService.trace(`[ChatWebSocketManager] WebSocket stateful marker matches previous_response_id (${previousResponsePrefix})`);
|
||||
this._logService.trace(`[ChatWebSocketManager] WebSocket stateful marker matches previous_response_id (${previousResponsePrefix}), summarizedAtRoundIdMatched: ${summarizedAtRoundIdMatched}`);
|
||||
} else {
|
||||
this._logService.info(`[ChatWebSocketManager] WebSocket stateful marker (${statefulMarkerPrefix}) does not match previous_response_id (${previousResponsePrefix})`);
|
||||
this._logService.debug(`[ChatWebSocketManager] WebSocket stateful marker (${statefulMarkerPrefix}) does not match previous_response_id (${previousResponsePrefix}), summarizedAtRoundIdMatched: ${summarizedAtRoundIdMatched}`);
|
||||
}
|
||||
|
||||
// Supersede any in-flight request before updating turn state
|
||||
@@ -572,7 +592,7 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
const promptTokenCountPromise = options.countTokens();
|
||||
let promptTokenCount = -1;
|
||||
promptTokenCountPromise.then(count => { promptTokenCount = count; }, () => { promptTokenCount = -2; });
|
||||
const request = new ChatWebSocketActiveRequest(requestId, body.model, this._configurationService, this._logService);
|
||||
const request = new ChatWebSocketActiveRequest(requestId, body.model, options.summarizedAtRoundId, this._configurationService, this._logService);
|
||||
request.onDidSettle(({ outcome, closeCode, closeReason, serverErrorMessage, serverErrorCode }) => {
|
||||
if (this._activeRequest === request) {
|
||||
this._activeRequest = undefined;
|
||||
@@ -596,6 +616,7 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
statefulMarkerMatched,
|
||||
previousResponseIdUnset,
|
||||
hasCompactionData,
|
||||
summarizedAtRoundIdMatched,
|
||||
promptTokenCount,
|
||||
tokenCountMax: options.tokenCountMax,
|
||||
connectionDurationMs,
|
||||
@@ -650,6 +671,7 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
|
||||
statefulMarkerMatched,
|
||||
previousResponseIdUnset,
|
||||
hasCompactionData,
|
||||
summarizedAtRoundIdMatched,
|
||||
tokenCountMax: options.tokenCountMax,
|
||||
connectionDurationMs,
|
||||
totalSentMessageCount: this._totalSentMessageCount,
|
||||
@@ -702,6 +724,7 @@ class ChatWebSocketActiveRequest implements IChatWebSocketRequestHandle {
|
||||
constructor(
|
||||
readonly requestId: string,
|
||||
readonly modelId: string | undefined,
|
||||
readonly summarizedAtRoundId: string | undefined,
|
||||
private readonly _configurationService: IConfigurationService,
|
||||
private readonly _logService: ILogService,
|
||||
) {
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface IChatWebSocketRequestSentTelemetryProperties extends IChatWebSo
|
||||
statefulMarkerMatched: boolean;
|
||||
previousResponseIdUnset: boolean;
|
||||
hasCompactionData: boolean;
|
||||
summarizedAtRoundIdMatched: boolean;
|
||||
tokenCountMax: number;
|
||||
connectionDurationMs: number;
|
||||
totalSentMessageCount: number;
|
||||
@@ -90,6 +91,7 @@ export interface IChatWebSocketRequestOutcomeTelemetryProperties extends IChatWe
|
||||
statefulMarkerMatched: boolean;
|
||||
previousResponseIdUnset: boolean;
|
||||
hasCompactionData: boolean;
|
||||
summarizedAtRoundIdMatched: boolean;
|
||||
promptTokenCount: number;
|
||||
tokenCountMax: number;
|
||||
connectionDurationMs: number;
|
||||
@@ -306,6 +308,7 @@ export class ChatWebSocketTelemetrySender {
|
||||
"statefulMarkerMatched": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the connection stateful marker matched the previous_response_id sent in the request", "isMeasurement": true },
|
||||
"previousResponseIdUnset": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether previous_response_id was undefined in the request", "isMeasurement": true },
|
||||
"hasCompactionData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the request input contains compaction data", "isMeasurement": true },
|
||||
"summarizedAtRoundIdMatched": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the summarized round ID matches the one stored on the connection", "isMeasurement": true },
|
||||
"tokenCountMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum generated tokens", "isMeasurement": true },
|
||||
"totalSentMessageCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of messages sent over this connection", "isMeasurement": true },
|
||||
"totalReceivedMessageCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of messages received over this connection", "isMeasurement": true },
|
||||
@@ -328,6 +331,7 @@ export class ChatWebSocketTelemetrySender {
|
||||
statefulMarkerMatched: properties.statefulMarkerMatched ? 1 : 0,
|
||||
previousResponseIdUnset: properties.previousResponseIdUnset ? 1 : 0,
|
||||
hasCompactionData: properties.hasCompactionData ? 1 : 0,
|
||||
summarizedAtRoundIdMatched: properties.summarizedAtRoundIdMatched ? 1 : 0,
|
||||
tokenCountMax: properties.tokenCountMax,
|
||||
totalSentMessageCount: properties.totalSentMessageCount,
|
||||
totalReceivedMessageCount: properties.totalReceivedMessageCount,
|
||||
@@ -403,6 +407,7 @@ export class ChatWebSocketTelemetrySender {
|
||||
"statefulMarkerMatched": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the connection stateful marker matched the previous_response_id sent in the request", "isMeasurement": true },
|
||||
"previousResponseIdUnset": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether previous_response_id was undefined in the request", "isMeasurement": true },
|
||||
"hasCompactionData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the request input contains compaction data", "isMeasurement": true },
|
||||
"summarizedAtRoundIdMatched": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Whether the summarized round ID matches the one stored on the connection", "isMeasurement": true },
|
||||
"promptTokenCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of prompt tokens, locally counted", "isMeasurement": true },
|
||||
"tokenCountMax": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Maximum generated tokens", "isMeasurement": true },
|
||||
"totalSentMessageCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Number of messages sent over this connection", "isMeasurement": true },
|
||||
@@ -438,6 +443,7 @@ export class ChatWebSocketTelemetrySender {
|
||||
statefulMarkerMatched: properties.statefulMarkerMatched ? 1 : 0,
|
||||
previousResponseIdUnset: properties.previousResponseIdUnset ? 1 : 0,
|
||||
hasCompactionData: properties.hasCompactionData ? 1 : 0,
|
||||
summarizedAtRoundIdMatched: properties.summarizedAtRoundIdMatched ? 1 : 0,
|
||||
promptTokenCount: properties.promptTokenCount,
|
||||
tokenCountMax: properties.tokenCountMax,
|
||||
totalSentMessageCount: properties.totalSentMessageCount,
|
||||
|
||||
Reference in New Issue
Block a user