mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-10 16:58:55 -05:00
nes: make tryRebase conflict limit exp-configurable (#312708)
Add `maxImperfectAgreementLength` to `NesRebaseConfigs` as a required field (no fallback needed at call sites). Wire it up through a new experiment-based `ConfigKey.TeamInternal.InlineEditsMaxImperfectAgreementLength` setting (default 5, key `chat.advanced.inlineEdits.maxImperfectAgreementLength`). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
8d0b49d0cf
commit
e46ea1e654
@@ -14,6 +14,9 @@ import { ILinesDiffComputerOptions } from '../../../util/vs/editor/common/diff/l
|
||||
|
||||
const TROUBLESHOOT_EDIT_CONSISTENCY = false;
|
||||
|
||||
export const maxAgreementOffset = 10; // If the user's typing is more than this into the suggestion we consider it a miss.
|
||||
export const maxImperfectAgreementLength = 5; // If the user's typing is longer than this and the suggestion is not a perfect match we consider it a miss.
|
||||
|
||||
export interface NesRebaseConfigs {
|
||||
/**
|
||||
* When enabled, if the user's typed text is an editor auto-close pair
|
||||
@@ -30,6 +33,15 @@ export interface NesRebaseConfigs {
|
||||
* rebased suggestion.
|
||||
*/
|
||||
readonly reverseAgreement?: boolean;
|
||||
/**
|
||||
* Maximum length (in characters) of an imperfect agreement that is still
|
||||
* accepted during a strict rebase. When the base new-text is longer than
|
||||
* this value and it does not start at the exact predicted offset, the
|
||||
* rebase is considered a miss. Helper functions such as {@link tryRebase}
|
||||
* use {@link maxImperfectAgreementLength} when `nesConfigs` is omitted,
|
||||
* but explicit {@link NesRebaseConfigs} objects must provide this value.
|
||||
*/
|
||||
readonly maxImperfectAgreementLength: number;
|
||||
}
|
||||
|
||||
export class EditDataWithIndex implements IEditData<EditDataWithIndex> {
|
||||
@@ -45,7 +57,7 @@ export class EditDataWithIndex implements IEditData<EditDataWithIndex> {
|
||||
}
|
||||
}
|
||||
|
||||
export function tryRebase(originalDocument: string, editWindow: OffsetRange | undefined, originalEdits: readonly StringReplacement[], detailedEdits: AnnotatedStringReplacement<EditDataWithIndex>[][], userEditSince: StringEdit, currentDocumentContent: string, currentSelection: readonly OffsetRange[], resolution: 'strict' | 'lenient', logger: ILogger, nesConfigs: NesRebaseConfigs = {}): { rebasedEdit: StringReplacement; rebasedEditIndex: number }[] | 'outsideEditWindow' | 'rebaseFailed' | 'error' | 'inconsistentEdits' {
|
||||
export function tryRebase(originalDocument: string, editWindow: OffsetRange | undefined, originalEdits: readonly StringReplacement[], detailedEdits: AnnotatedStringReplacement<EditDataWithIndex>[][], userEditSince: StringEdit, currentDocumentContent: string, currentSelection: readonly OffsetRange[], resolution: 'strict' | 'lenient', logger: ILogger, nesConfigs: NesRebaseConfigs = { maxImperfectAgreementLength }): { rebasedEdit: StringReplacement; rebasedEditIndex: number }[] | 'outsideEditWindow' | 'rebaseFailed' | 'error' | 'inconsistentEdits' {
|
||||
const start = Date.now();
|
||||
try {
|
||||
return _tryRebase(originalDocument, editWindow, originalEdits, detailedEdits, userEditSince, currentDocumentContent, currentSelection, resolution, logger, nesConfigs);
|
||||
@@ -133,7 +145,7 @@ export function checkEditConsistency(original: string, edit: StringEdit, current
|
||||
return consistent;
|
||||
}
|
||||
|
||||
export function tryRebaseStringEdits<T extends IEditData<T>>(content: string, ours: StringEdit, base: StringEdit, resolution: 'strict' | 'lenient', nesConfigs: NesRebaseConfigs = {}): StringEdit | undefined {
|
||||
export function tryRebaseStringEdits<T extends IEditData<T>>(content: string, ours: StringEdit, base: StringEdit, resolution: 'strict' | 'lenient', nesConfigs: NesRebaseConfigs = { maxImperfectAgreementLength }): StringEdit | undefined {
|
||||
return tryRebaseEdits(content, ours.mapData(r => new VoidEditData()), base, resolution, nesConfigs)?.toStringEdit();
|
||||
}
|
||||
|
||||
@@ -237,7 +249,7 @@ function tryRebaseEdits<T extends IEditData<T>>(content: string, ours: Annotated
|
||||
const j = baseEdit.newText.indexOf(effectiveText, baseNewTextOffset);
|
||||
const strictRejected = j !== -1 && resolution === 'strict' && (
|
||||
j - baseNewTextOffset > maxAgreementOffset ||
|
||||
(j - baseNewTextOffset > 0 && effectiveText.length > maxImperfectAgreementLength)
|
||||
(j - baseNewTextOffset > 0 && effectiveText.length > nesConfigs.maxImperfectAgreementLength)
|
||||
);
|
||||
|
||||
if (j !== -1 && !strictRejected) {
|
||||
@@ -312,9 +324,6 @@ function tryRebaseEdits<T extends IEditData<T>>(content: string, ours: Annotated
|
||||
return AnnotatedStringEdit.create(newEdits);
|
||||
}
|
||||
|
||||
export const maxAgreementOffset = 10; // If the user's typing is more than this into the suggestion we consider it a miss.
|
||||
export const maxImperfectAgreementLength = 5; // If the user's typing is longer than this and the suggestion is not a perfect match we consider it a miss.
|
||||
|
||||
/** Returns true if every character of `typed` appears in `suggestion` in order (subsequence match). */
|
||||
function isSubsequenceOf(typed: string, suggestion: string): boolean {
|
||||
let si = 0;
|
||||
@@ -347,7 +356,7 @@ function agreementIndexOf<T extends IEditData<T>>(content: string, ourE: Annotat
|
||||
const j = ourE.newText.indexOf(baseE.newText, ourNewTextOffset);
|
||||
const strictRejected = j !== -1 && resolution === 'strict' && (
|
||||
j > maxAgreementOffset ||
|
||||
(j > 0 && baseE.newText.length > maxImperfectAgreementLength)
|
||||
(j > 0 && baseE.newText.length > nesConfigs.maxImperfectAgreementLength)
|
||||
);
|
||||
if (j !== -1 && !strictRejected) {
|
||||
return j + baseE.newText.length;
|
||||
|
||||
@@ -125,9 +125,12 @@ export class NextEditCache extends Disposable {
|
||||
}
|
||||
|
||||
private _getNesRebaseConfigs(): NesRebaseConfigs {
|
||||
const maxImperfectAgreementLength = this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsMaxImperfectAgreementLength, this._expService);
|
||||
|
||||
return {
|
||||
absorbSubsequenceTyping: this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsAbsorbSubsequenceTyping, this._expService),
|
||||
reverseAgreement: this._configService.getExperimentBasedConfig(ConfigKey.TeamInternal.InlineEditsReverseAgreement, this._expService),
|
||||
maxImperfectAgreementLength: typeof maxImperfectAgreementLength === 'number' ? Math.max(0, maxImperfectAgreementLength) : maxImperfectAgreementLength,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -114,22 +114,21 @@ export class RebaseFailureInfo implements MarkdownLoggable {
|
||||
|
||||
lines.push(`\tconst currentSelection = [${this.currentSelection.map(s => `new OffsetRange(${s.start}, ${s.endExclusive})`).join(', ')}];`);
|
||||
|
||||
if (this.nesRebaseConfigs.absorbSubsequenceTyping || this.nesRebaseConfigs.reverseAgreement) {
|
||||
const configEntries: string[] = [];
|
||||
if (this.nesRebaseConfigs.absorbSubsequenceTyping) {
|
||||
configEntries.push(`absorbSubsequenceTyping: ${this.nesRebaseConfigs.absorbSubsequenceTyping}`);
|
||||
}
|
||||
if (this.nesRebaseConfigs.reverseAgreement) {
|
||||
configEntries.push(`reverseAgreement: ${this.nesRebaseConfigs.reverseAgreement}`);
|
||||
}
|
||||
lines.push(`\tconst nesConfigs = { ${configEntries.join(', ')} };`);
|
||||
const configEntries: string[] = [];
|
||||
if (this.nesRebaseConfigs.absorbSubsequenceTyping) {
|
||||
configEntries.push(`absorbSubsequenceTyping: ${this.nesRebaseConfigs.absorbSubsequenceTyping}`);
|
||||
}
|
||||
if (this.nesRebaseConfigs.reverseAgreement) {
|
||||
configEntries.push(`reverseAgreement: ${this.nesRebaseConfigs.reverseAgreement}`);
|
||||
}
|
||||
configEntries.push(`maxImperfectAgreementLength: ${this.nesRebaseConfigs.maxImperfectAgreementLength}`);
|
||||
lines.push(`\tconst nesConfigs = { ${configEntries.join(', ')} };`);
|
||||
|
||||
lines.push('');
|
||||
lines.push('\tconst logger = new TestLogService();');
|
||||
lines.push('\texpect(userEditSince.apply(originalDocument)).toBe(currentDocumentContent);');
|
||||
|
||||
const configsArg = (this.nesRebaseConfigs.absorbSubsequenceTyping || this.nesRebaseConfigs.reverseAgreement) ? ', nesConfigs' : '';
|
||||
const configsArg = ', nesConfigs';
|
||||
lines.push(`\texpect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger${configsArg})).toMatchInlineSnapshot();`);
|
||||
|
||||
lines.push('});');
|
||||
|
||||
@@ -279,7 +279,7 @@ int main()
|
||||
const currentDocumentContent = 'function fib()\n';
|
||||
const editWindow = new OffsetRange(0, 13);
|
||||
const currentSelection = [new OffsetRange(13, 13)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
const final = 'function fib(n: number): number {\n if (n <= 1) return n;\n return fib(n - 1) + fib(n - 2);\n}\n';
|
||||
@@ -313,7 +313,7 @@ int main()
|
||||
const currentDocumentContent = 'function fib(n: )\n';
|
||||
const editWindow = new OffsetRange(0, 13);
|
||||
const currentSelection = [new OffsetRange(16, 16)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger, nesConfigs)).toBe('rebaseFailed');
|
||||
@@ -333,7 +333,7 @@ int main()
|
||||
const currentDocumentContent = 'const x;\n';
|
||||
const editWindow = new OffsetRange(0, 8);
|
||||
const currentSelection = [new OffsetRange(8, 8)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger, nesConfigs)).toBe('rebaseFailed');
|
||||
@@ -354,7 +354,7 @@ int main()
|
||||
const currentDocumentContent = 'const x;\n';
|
||||
const editWindow = new OffsetRange(0, 8);
|
||||
const currentSelection = [new OffsetRange(8, 8)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
// Strict rejects the exact match (offset 25 > maxAgreementOffset) and absorption
|
||||
@@ -375,7 +375,7 @@ int main()
|
||||
const currentDocumentContent = 'function fibabc\n';
|
||||
const editWindow = new OffsetRange(0, 13);
|
||||
const currentSelection = [new OffsetRange(15, 15)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger, nesConfigs)).toBe('rebaseFailed');
|
||||
@@ -395,7 +395,7 @@ int main()
|
||||
const currentDocumentContent = 'function fib(a\n';
|
||||
const editWindow = new OffsetRange(0, 13);
|
||||
const currentSelection = [new OffsetRange(14, 14)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger, nesConfigs)).toBe('rebaseFailed');
|
||||
@@ -418,7 +418,7 @@ int main()
|
||||
const logger = new TestLogService();
|
||||
|
||||
// Explicitly disabled
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger, { absorbSubsequenceTyping: false })).toBe('rebaseFailed');
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger, { absorbSubsequenceTyping: false, maxImperfectAgreementLength })).toBe('rebaseFailed');
|
||||
// Default (no config)
|
||||
expect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger)).toBe('rebaseFailed');
|
||||
});
|
||||
@@ -436,7 +436,7 @@ int main()
|
||||
const currentDocument = userEdit.apply(originalDocument);
|
||||
expect(currentDocument).toBe('function fib(n\n');
|
||||
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const res = tryRebaseStringEdits(originalDocument, suggestedEdit, userEdit, 'strict', nesConfigs);
|
||||
expect(res).toBeDefined();
|
||||
expect(res!.apply(currentDocument)).toBe(suggestedEdit.apply(originalDocument));
|
||||
@@ -457,7 +457,7 @@ int main()
|
||||
expect(tryRebaseStringEdits(text, suggestion, userEdit, 'strict')).toBeUndefined();
|
||||
|
||||
// With config: still fails because a single "{" is not an auto-close pair
|
||||
expect(tryRebaseStringEdits(text, suggestion, userEdit, 'strict', { absorbSubsequenceTyping: true })).toBeUndefined();
|
||||
expect(tryRebaseStringEdits(text, suggestion, userEdit, 'strict', { absorbSubsequenceTyping: true, maxImperfectAgreementLength })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('absorbSubsequenceTyping: "{}" NOT absorbed when suggestion only has opening brace', () => {
|
||||
@@ -473,7 +473,7 @@ int main()
|
||||
const current = userEdit.apply(text);
|
||||
expect(current).toBe('if (true){}\n');
|
||||
|
||||
expect(tryRebaseStringEdits(text, suggestion, userEdit, 'strict', { absorbSubsequenceTyping: true })).toBeUndefined();
|
||||
expect(tryRebaseStringEdits(text, suggestion, userEdit, 'strict', { absorbSubsequenceTyping: true, maxImperfectAgreementLength })).toBeUndefined();
|
||||
});
|
||||
|
||||
test('absorbSubsequenceTyping: "{}" absorbed when suggestion has both braces', () => {
|
||||
@@ -490,7 +490,7 @@ int main()
|
||||
const final = suggestion.apply(text);
|
||||
expect(final).toBe('if (true) {\n console.log("yes");\n}\n');
|
||||
|
||||
const result = tryRebaseStringEdits(text, suggestion, userEdit, 'strict', { absorbSubsequenceTyping: true });
|
||||
const result = tryRebaseStringEdits(text, suggestion, userEdit, 'strict', { absorbSubsequenceTyping: true, maxImperfectAgreementLength });
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.apply(current)).toBe(final);
|
||||
});
|
||||
@@ -509,7 +509,7 @@ int main()
|
||||
const currentDocumentContent = 'function fib(n: number) {}\n';
|
||||
const editWindow = new OffsetRange(0, 25);
|
||||
const currentSelection = [new OffsetRange(26, 26)];
|
||||
const nesConfigs = { absorbSubsequenceTyping: true };
|
||||
const nesConfigs = { absorbSubsequenceTyping: true, maxImperfectAgreementLength };
|
||||
const logger = new TestLogService();
|
||||
|
||||
const final = 'function fib(n: number) {\n if (n <= 1) return 1;\n return n * factorial(n - 1);\n}\n';
|
||||
@@ -1105,7 +1105,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 16), 'class Fibonacci {\n\t'),
|
||||
]);
|
||||
const currentDocumentContent = 'class Fibonacci {\n\t\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
// Without flag: rebase fails
|
||||
@@ -1157,7 +1157,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 10), 'class Foo XYZ'),
|
||||
]);
|
||||
const currentDocumentContent = 'class Foo XYZ\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
expect(tryRebase(originalDocument, undefined, originalEdits, [], userEditSince, currentDocumentContent, [], 'strict', logger, nesConfigs)).toBe('rebaseFailed');
|
||||
@@ -1180,7 +1180,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 5), 'helloXX{YY'),
|
||||
]);
|
||||
const currentDocumentContent = 'helloXX{YY\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
// Fails because user's remaining text "YY" doesn't match model's second edit
|
||||
@@ -1203,7 +1203,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 1), 'a' + pad + '{'),
|
||||
]);
|
||||
const currentDocumentContent = 'a' + pad + '{\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
expect(tryRebase(originalDocument, undefined, originalEdits, [], userEditSince, currentDocumentContent, [], 'strict', logger, nesConfigs)).toBe('rebaseFailed');
|
||||
@@ -1224,7 +1224,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 1), 'aX' + longText),
|
||||
]);
|
||||
const currentDocumentContent = 'aX' + longText + '\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
// offset = 1 > 0, effectiveText.length = longText.length > maxImperfectAgreementLength
|
||||
@@ -1244,7 +1244,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 3), 'fn {\n\tfoo\n}'),
|
||||
]);
|
||||
const currentDocumentContent = 'fn {\n\tfoo\n}\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
// Without flag: rebase fails
|
||||
@@ -1270,7 +1270,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 16), 'class Fibonacci {\n\t'),
|
||||
]);
|
||||
const currentDocumentContent = 'class Fibonacci {\n\t\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
// Expected final: apply both model edits in sequence to original
|
||||
const expectedFinal = new StringEdit([originalEdits[0]]).apply(originalDocument);
|
||||
@@ -1308,7 +1308,7 @@ class Point3D {
|
||||
// Without flag: rebase fails
|
||||
expect(tryRebaseStringEdits(originalDocument, suggestedEdit, userEdit, 'strict')).toBeUndefined();
|
||||
// With flag: model edit fully consumed → empty result
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
const res = tryRebaseStringEdits(originalDocument, suggestedEdit, userEdit, 'strict', nesConfigs);
|
||||
expect(res).toBeDefined();
|
||||
expect(res!.replacements.length).toBe(0);
|
||||
@@ -1326,7 +1326,7 @@ class Point3D {
|
||||
StringReplacement.replace(new OffsetRange(0, 5), 'XYZWV'),
|
||||
]);
|
||||
const currentDocumentContent = 'XYZWV\n';
|
||||
const nesConfigs = { reverseAgreement: true };
|
||||
const nesConfigs = { reverseAgreement: true, maxImperfectAgreementLength };
|
||||
|
||||
const logger = new TestLogService();
|
||||
// The ranges don't match after removeCommonSuffixAndPrefix, so this conflicts
|
||||
|
||||
@@ -785,6 +785,7 @@ export namespace ConfigKey {
|
||||
export const InlineEditsRebasedCacheDelay = defineTeamInternalSetting<number | undefined>('chat.advanced.inlineEdits.rebasedCacheDelay', ConfigType.ExperimentBased, 0);
|
||||
export const InlineEditsAbsorbSubsequenceTyping = defineTeamInternalSetting<boolean>('chat.advanced.inlineEdits.absorbSubsequenceTyping', ConfigType.ExperimentBased, false);
|
||||
export const InlineEditsReverseAgreement = defineTeamInternalSetting<boolean>('chat.advanced.inlineEdits.reverseAgreement', ConfigType.ExperimentBased, false);
|
||||
export const InlineEditsMaxImperfectAgreementLength = defineTeamInternalSetting<number>('chat.advanced.inlineEdits.maxImperfectAgreementLength', ConfigType.ExperimentBased, 5, vNumber());
|
||||
export const InlineEditsBackoffDebounceEnabled = defineTeamInternalSetting<boolean>('chat.advanced.inlineEdits.backoffDebounceEnabled', ConfigType.ExperimentBased, true);
|
||||
export const InlineEditsExtraDebounceEndOfLine = defineTeamInternalSetting<number>('chat.advanced.inlineEdits.extraDebounceEndOfLine', ConfigType.ExperimentBased, 2000);
|
||||
export const InlineEditsSpeculativeRequests = defineTeamInternalSetting<SpeculativeRequestsEnablement>('chat.advanced.inlineEdits.speculativeRequests', ConfigType.ExperimentBased, SpeculativeRequestsEnablement.Off, SpeculativeRequestsEnablement.VALIDATOR);
|
||||
|
||||
Reference in New Issue
Block a user