Extract chat session routing heuristics (#329522)

* Extract chat session routing heuristics

* Export routing enrichment limit

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Megan Rogge
2026-08-07 08:48:13 +00:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent 9950fa3b6c
commit ae1d0bab11
2 changed files with 272 additions and 0 deletions
@@ -0,0 +1,159 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from '../../../../../base/common/uri.js';
import { IWorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js';
import { IRoutableSession, isHighConfidenceSessionRoute, ISessionRouteResult } from '../../common/sessionRouter.js';
/** Number of top-ranked candidates whose conversation content should be resolved. */
export const ROUTE_ENRICH_MAX_CANDIDATES = 12;
const RELATED_SESSION_FOLDER_CONFIDENCE = 0.35;
/**
* Extracts the task from an explicit request to start a new session.
* Requests that merely mention creating another resource are deliberately ignored.
*/
export function parseExplicitNewSessionRequest(utterance: string): string | undefined {
const match = /^(?:please\s+)?(?:create|start|open)\s+(?:a\s+)?new\s+(?:chat\s+)?session(?:\s+(?:to|for|and)\s+|\s*[:,-]\s*)(.+)$/i.exec(utterance.trim());
const task = match?.[1]?.trim();
return task || undefined;
}
/**
* Chooses the workspace folder for a newly routed session. Explicit folder
* mentions win, followed by a sufficiently related session's folder and then
* the caller's default.
*/
export function resolveNewSessionWorkspaceFolder(
utterance: string,
folders: readonly IWorkspaceFolder[],
results: readonly ISessionRouteResult[],
candidates: readonly IRoutableSession[],
defaultFolder: URI | undefined,
): URI | undefined {
return folderMentionedInUtterance(utterance, folders)
?? folderFromRelatedSession(results, candidates, folders)
?? defaultFolder
?? folders[0]?.uri;
}
/**
* Bounds transcript enrichment while preserving the model's preliminary order.
* Any remaining slots favor active and recently updated sessions.
*/
export function selectRouterShortlist(
candidates: readonly IRoutableSession[],
preliminaryResults: readonly ISessionRouteResult[],
limit: number = ROUTE_ENRICH_MAX_CANDIDATES,
): IRoutableSession[] {
if (candidates.length <= limit) {
return [...candidates];
}
const candidatesById = new Map(candidates.map(candidate => [candidate.sessionId, candidate]));
const selectedIds = new Set<string>();
const shortlist: IRoutableSession[] = [];
for (const result of preliminaryResults) {
const candidate = candidatesById.get(result.sessionId);
if (candidate && !selectedIds.has(candidate.sessionId)) {
selectedIds.add(candidate.sessionId);
shortlist.push(candidate);
if (shortlist.length === limit) {
return shortlist;
}
}
}
const fallback = candidates
.filter(candidate => !selectedIds.has(candidate.sessionId))
.sort((a, b) =>
sessionStatusPriority(b.status) - sessionStatusPriority(a.status)
|| (b.lastActivity ?? 0) - (a.lastActivity ?? 0)
|| a.label.localeCompare(b.label)
|| a.sessionId.localeCompare(b.sessionId));
shortlist.push(...fallback.slice(0, limit - shortlist.length));
return shortlist;
}
/** Selects the top result only when it clears the shared confidence threshold. */
export function selectBestSessionRoute(results: readonly ISessionRouteResult[]): ISessionRouteResult | undefined {
const top = results[0];
return top && isHighConfidenceSessionRoute(top) ? top : undefined;
}
function sessionStatusPriority(status: string | undefined): number {
return status === 'working' ? 2 : status === 'idle' ? 1 : 0;
}
function folderMentionedInUtterance(utterance: string, folders: readonly IWorkspaceFolder[]): URI | undefined {
const normalizedUtterance = utterance.toLocaleLowerCase();
let best: { folder: IWorkspaceFolder; length: number } | undefined;
for (const folder of folders) {
const names = new Set([folder.name, folder.uri.path.split('/').filter(Boolean).at(-1)]);
for (const name of names) {
if (!name || name.length < 3) {
continue;
}
const normalizedName = name.toLocaleLowerCase();
let start = normalizedUtterance.indexOf(normalizedName);
while (start >= 0) {
if (isWordBoundary(normalizedUtterance[start - 1])
&& isWordBoundary(normalizedUtterance[start + normalizedName.length])) {
if (!best || normalizedName.length > best.length) {
best = { folder, length: normalizedName.length };
}
break;
}
start = normalizedUtterance.indexOf(normalizedName, start + normalizedName.length);
}
}
}
return best?.folder.uri;
}
function folderFromRelatedSession(
results: readonly ISessionRouteResult[],
candidates: readonly IRoutableSession[],
folders: readonly IWorkspaceFolder[],
): URI | undefined {
const candidateById = new Map(candidates.map(candidate => [candidate.sessionId, candidate]));
for (const result of results) {
if (result.confidence < RELATED_SESSION_FOLDER_CONFIDENCE) {
continue;
}
const candidate = candidateById.get(result.sessionId);
const folder = candidate && folderForSessionMetadata(candidate, folders);
if (folder) {
return folder.uri;
}
}
return undefined;
}
function folderForSessionMetadata(candidate: IRoutableSession, folders: readonly IWorkspaceFolder[]): IWorkspaceFolder | undefined {
for (const path of [candidate.cwd, candidate.repo]) {
if (!path) {
continue;
}
const normalizedPath = path.replaceAll('\\', '/').replace(/\/+$/, '').replace(/^([a-zA-Z]:\/)/, '/$1').toLocaleLowerCase();
const match = folders
.filter(folder => {
const folderPath = folder.uri.path.replace(/\/+$/, '').toLocaleLowerCase();
return normalizedPath === folderPath
|| normalizedPath.startsWith(`${folderPath}/`)
|| normalizedPath.endsWith(`/${folder.name.toLocaleLowerCase()}`)
|| normalizedPath.endsWith(`/${folder.name.toLocaleLowerCase()}.git`);
})
.sort((a, b) => b.uri.path.length - a.uri.path.length)[0];
if (match) {
return match;
}
}
return undefined;
}
function isWordBoundary(value: string | undefined): boolean {
return value === undefined || !/[\p{L}\p{N}_-]/u.test(value);
}
@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import assert from 'assert';
import { URI } from '../../../../../../base/common/uri.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
import { IWorkspaceFolder } from '../../../../../../platform/workspace/common/workspace.js';
import { parseExplicitNewSessionRequest, resolveNewSessionWorkspaceFolder, selectBestSessionRoute, selectRouterShortlist } from '../../../browser/sessionRouter/chatSessionRoutingHelpers.js';
suite('Chat session routing helpers', () => {
ensureNoDisposablesAreLeakedInTestSuite();
const vscode = folder('vscode', '/work/vscode', 0);
const docs = folder('vscode-docs', '/work/vscode-docs', 1);
test('chooses an explicitly mentioned workspace folder', () => {
const result = resolveNewSessionWorkspaceFolder(
'update the vscode-docs API reference',
[vscode, docs],
[],
[],
vscode.uri,
);
assert.strictEqual(result?.toString(), docs.uri.toString());
});
test('uses a related session working directory when starting a new session', () => {
const result = resolveNewSessionWorkspaceFolder(
'continue the authentication cleanup',
[vscode, docs],
[{ sessionId: 'related', confidence: 0.5 }],
[{ sessionId: 'related', label: 'Authentication cleanup', cwd: '/work/vscode-docs/src' }],
vscode.uri,
);
assert.strictEqual(result?.toString(), docs.uri.toString());
});
test('explicit folder mention overrides a related session in another folder', () => {
const result = resolveNewSessionWorkspaceFolder(
'update the vscode-docs API reference',
[vscode, docs],
[{ sessionId: 'related', confidence: 0.9 }],
[{ sessionId: 'related', label: 'Related work', cwd: '/work/vscode/src' }],
vscode.uri,
);
assert.strictEqual(result?.toString(), docs.uri.toString());
});
test('bounds transcript enrichment after every candidate receives model scoring', () => {
const candidates = Array.from({ length: 13 }, (_, index) => ({
sessionId: `s${index}`,
label: `Session ${index}`,
status: index === 12 ? 'working' : 'idle',
lastActivity: index,
}));
const shortlist = selectRouterShortlist(candidates, [
{ sessionId: 's0', confidence: 0.9 },
{ sessionId: 's3', confidence: 0.8 },
]);
assert.deepStrictEqual({
length: shortlist.length,
first: shortlist[0].sessionId,
second: shortlist[1].sessionId,
third: shortlist[2].sessionId,
excluded: candidates.filter(candidate => !shortlist.includes(candidate)).map(candidate => candidate.sessionId),
}, {
length: 12,
first: 's0',
second: 's3',
third: 's12',
excluded: ['s1'],
});
});
test('selects only a high-confidence route', () => {
assert.deepStrictEqual(selectBestSessionRoute([
{ sessionId: 'best', confidence: 0.9 },
{ sessionId: 'previous', confidence: 0.86 },
]), { sessionId: 'best', confidence: 0.9 });
assert.strictEqual(selectBestSessionRoute([{ sessionId: 'weak', confidence: 0.8 }]), undefined);
});
test('keeps the default folder for a weak related-session match', () => {
const result = resolveNewSessionWorkspaceFolder(
'start something new',
[vscode, docs],
[{ sessionId: 'weak', confidence: 0.1 }],
[{ sessionId: 'weak', label: 'Unrelated docs work', cwd: '/work/vscode-docs' }],
vscode.uri,
);
assert.strictEqual(result?.toString(), vscode.uri.toString());
});
test('extracts only explicit new-session tasks', () => {
assert.strictEqual(parseExplicitNewSessionRequest('Create a new session to update the chocolate file'), 'update the chocolate file');
assert.strictEqual(parseExplicitNewSessionRequest('Please start a new chat session for fixing tests'), 'fixing tests');
assert.strictEqual(parseExplicitNewSessionRequest('Create a new session'), undefined);
assert.strictEqual(parseExplicitNewSessionRequest('Create a file in the current session'), undefined);
});
});
function folder(name: string, path: string, index: number): IWorkspaceFolder {
const uri = URI.file(path);
return { uri, name, index, toResource: relativePath => URI.joinPath(uri, relativePath) };
}