Enable persistent CoT for hidden models (#320333)

This commit is contained in:
dileepyavan
2026-06-08 08:45:04 -07:00
committed by GitHub
parent 65ee38a7b2
commit ffe46771e8
6 changed files with 73 additions and 4 deletions
+9
View File
@@ -4045,6 +4045,15 @@
"onExp"
]
},
"github.copilot.chat.responsesApi.persistentCoT.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "%github.copilot.config.responsesApi.persistentCoT.enabled%",
"tags": [
"experimental",
"onExp"
]
},
"github.copilot.chat.updated53CodexPrompt.enabled": {
"type": "boolean",
"default": true,
+1
View File
@@ -345,6 +345,7 @@
"github.copilot.config.responsesApiReasoningSummary": "Sets the reasoning summary style used for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
"github.copilot.config.responsesApiContextManagement.enabled": "Enables context management for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
"github.copilot.config.responsesApi.promptCacheKey.enabled": "Enables prompt cache key being set for the Responses API.",
"github.copilot.config.responsesApi.persistentCoT.enabled": "Enables persistent chain of thought for supported Responses API models.",
"github.copilot.config.updated53CodexPrompt.enabled": "Enables the updated prompt for gpt-5.3-codex model.",
"github.copilot.config.claude47OpusPrompt.enabled": "Enables the updated system prompt tuned for the Claude Opus 4.7 model.",
"github.copilot.config.gpt55GetChangedFilesTool.enabled": "Enables the Get Changed Files tool for gpt-5.5 models.",
@@ -968,6 +968,8 @@ export namespace ConfigKey {
export const ResponsesApiContextManagementEnabled = defineSetting<boolean>('chat.responsesApiContextManagement.enabled', ConfigType.ExperimentBased, false);
/** Enable client-side prompt_cache_key (conversationId:modelFamily) sent to Responses API */
export const ResponsesApiPromptCacheKeyEnabled = defineSetting<boolean>('chat.responsesApi.promptCacheKey.enabled', ConfigType.ExperimentBased, false);
/** Enable persistent chain of thought for supported Responses API model families */
export const ResponsesApiPersistentCoTEnabled = defineSetting<boolean>('chat.responsesApi.persistentCoT.enabled', ConfigType.ExperimentBased, false);
/** Enable updated prompt for 5.3Codex model */
export const Updated53CodexPromptEnabled = defineSetting<boolean>('chat.updated53CodexPrompt.enabled', ConfigType.ExperimentBased, true);
/** Enable updated prompt for Claude Opus 4.7 model */
@@ -27,7 +27,7 @@ import { IChatWebSocketManager } from '../../networking/node/chatWebSocketManage
import { IExperimentationService } from '../../telemetry/common/nullExperimentationService';
import { ITelemetryService } from '../../telemetry/common/telemetry';
import { TelemetryData } from '../../telemetry/common/telemetryData';
import { getVerbosityForModelSync } from '../common/chatModelCapabilities';
import { getVerbosityForModelSync, isHiddenModelM } from '../common/chatModelCapabilities';
import { rawPartAsCompactionData } from '../common/compactionDataContainer';
import { rawPartAsPhaseData } from '../common/phaseDataContainer';
import { getIndexOfStatefulMarker, getStatefulMarkerAndIndex } from '../common/statefulMarkerContainer';
@@ -163,10 +163,13 @@ export function createResponsesRequestBody(accessor: ServicesAccessor, options:
? (effortFromSetting || options.modelCapabilities?.reasoningEffort || 'medium')
: undefined;
const summary = summaryConfig === 'off' || shouldDisableReasoningSummary ? undefined : summaryConfig;
if (effort || summary) {
const persistentCoTEnabled = configService.getExperimentBasedConfig(ConfigKey.ResponsesApiPersistentCoTEnabled, expService)
&& isHiddenModelM(endpoint);
if (effort || summary || persistentCoTEnabled) {
body.reasoning = {
...(effort ? { effort } : {}),
...(summary ? { summary } : {})
...(summary ? { summary } : {}),
...(persistentCoTEnabled ? { context: 'all_turns' } : {})
};
}
@@ -9,6 +9,7 @@ import { describe, expect, it } from 'vitest';
import { TokenizerType } from '../../../../util/common/tokenizer';
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
import { ChatLocation } from '../../../chat/common/commonTypes';
import { ConfigKey, IConfigurationService } from '../../../configuration/common/configurationService';
import { ILogService } from '../../../log/common/logService';
import { isOpenAIContextManagementResponse } from '../../../networking/common/fetch';
import { IChatEndpoint, ICreateEndpointBodyOptions } from '../../../networking/common/networking';
@@ -368,6 +369,59 @@ describe('responseApiInputToRawMessagesForLogging', () => {
});
describe('createResponsesRequestBody', () => {
it('enables persistent CoT on initial requests for hidden model M when the experiment is enabled', () => {
const services = createPlatformServices();
const accessor = services.createTestingAccessor();
const instantiationService = accessor.get(IInstantiationService);
accessor.get(IConfigurationService).setConfig(ConfigKey.ResponsesApiPersistentCoTEnabled, true);
const endpoint = { ...testEndpoint, family: 'ember-alpha', supportsReasoningEffort: ['low', 'medium', 'high'] };
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(servicesAccessor, createRequestOptions([], false), endpoint.model, endpoint));
expect(body.reasoning).toEqual({ effort: 'medium', summary: 'detailed', context: 'all_turns' });
accessor.dispose();
services.dispose();
});
it('does not enable persistent CoT when the experiment is disabled or the family is unsupported', () => {
const services = createPlatformServices();
const accessor = services.createTestingAccessor();
const instantiationService = accessor.get(IInstantiationService);
const emberEndpoint = { ...testEndpoint, family: 'ember-alpha' };
const unsupportedEndpoint = { ...testEndpoint, model: 'ember-alpha', family: 'other-family' };
const disabledBody = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(servicesAccessor, createRequestOptions([], false), emberEndpoint.model, emberEndpoint));
accessor.get(IConfigurationService).setConfig(ConfigKey.ResponsesApiPersistentCoTEnabled, true);
const unsupportedBody = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(servicesAccessor, createRequestOptions([], false), unsupportedEndpoint.model, unsupportedEndpoint));
expect(disabledBody.reasoning?.context).toBeUndefined();
expect(unsupportedBody.reasoning?.context).toBeUndefined();
accessor.dispose();
services.dispose();
});
it('keeps persistent CoT enabled when continuing from a previous response', () => {
const services = createPlatformServices();
const accessor = services.createTestingAccessor();
const instantiationService = accessor.get(IInstantiationService);
accessor.get(IConfigurationService).setConfig(ConfigKey.ResponsesApiPersistentCoTEnabled, true);
const endpoint = { ...testEndpoint, family: 'ember-alpha' };
const messages: Raw.ChatMessage[] = [
createStatefulMarkerMessage(endpoint.model, 'resp-prev'),
{ role: Raw.ChatRole.User, content: [{ type: Raw.ChatCompletionContentPartKind.Text, text: 'continue' }] },
];
const body = instantiationService.invokeFunction(servicesAccessor => createResponsesRequestBody(servicesAccessor, createRequestOptions(messages, false), endpoint.model, endpoint));
expect(body.previous_response_id).toBe('resp-prev');
expect(body.reasoning?.context).toBe('all_turns');
accessor.dispose();
services.dispose();
});
it('extracts compaction threshold from request body context management', () => {
expect(getResponsesApiCompactionThresholdFromBody({
context_management: [{
@@ -75,7 +75,7 @@ export interface IEndpointBody {
prediction?: Prediction;
messages?: any[];
n?: number;
reasoning?: { effort?: string; summary?: string };
reasoning?: { effort?: string; summary?: string; context?: 'current_turn' | 'all_turns' };
tool_choice?: OptionalChatRequestParams['tool_choice'] | { type: 'function'; name: string } | string;
top_logprobs?: number;
intent?: boolean;