Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/openrouter/reviewer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ describe('summarizeToolUsage', () => {
// ─── extractUserQuestion ────────────────────────────────────────────────────

describe('extractUserQuestion', () => {
it('extracts the first real user message', () => {
it('extracts the most recent user message', () => {
const messages: ChatMessage[] = [
{ role: 'system', content: 'You are an assistant' },
{ role: 'user', content: 'What is the weather in Milan?' },
Expand Down Expand Up @@ -229,6 +229,26 @@ describe('extractUserQuestion', () => {
it('returns fallback for empty messages', () => {
expect(extractUserQuestion([])).toBe('(Unknown question)');
});

it('picks the latest user question in multi-turn conversations', () => {
const messages: ChatMessage[] = [
{ role: 'system', content: 'You are an assistant' },
{ role: 'user', content: 'What is the capital of France?' },
{ role: 'assistant', content: 'Paris.' },
{ role: 'user', content: 'Now read the README.md from my repo and summarize it' },
{ role: 'assistant', content: null, tool_calls: [{ id: 'tc1', type: 'function' as const, function: { name: 'github_read_file', arguments: '{}' } }] },
{ role: 'tool', content: '# README content...', tool_call_id: 'tc1' },
];
expect(extractUserQuestion(messages)).toBe('Now read the README.md from my repo and summarize it');
});

it('skips file injection blocks from Phase 7B.4', () => {
const messages: ChatMessage[] = [
{ role: 'user', content: 'Read and summarize the project' },
{ role: 'user', content: '[FILE: owner/repo/README.md]\n# Contents here...' },
];
expect(extractUserQuestion(messages)).toBe('Read and summarize the project');
});
});

// ─── buildReviewMessages ────────────────────────────────────────────────────
Expand Down
9 changes: 6 additions & 3 deletions src/openrouter/reviewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,16 +170,19 @@ export function summarizeToolUsage(messages: readonly ChatMessage[]): string {
}

/**
* Extract the original user question from the conversation messages.
* Skips system messages and planning prompts.
* Extract the most recent user question from the conversation messages.
* Iterates backwards to find the latest user message, skipping injected phase prompts.
*/
export function extractUserQuestion(messages: readonly ChatMessage[]): string {
for (const msg of messages) {
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
if (msg.role !== 'user') continue;
const text = typeof msg.content === 'string' ? msg.content : '';
// Skip injected phase prompts
if (text.includes('[PLANNING PHASE]') || text.includes('[REVIEW PHASE]')) continue;
if (text.includes('STRUCTURED_PLAN_PROMPT') || text.startsWith('Before starting,')) continue;
// Skip file injection blocks (Phase 7B.4)
if (text.startsWith('[FILE:') || text.startsWith('Pre-loaded file contents')) continue;
if (text.length > 10) return text;
}
return '(Unknown question)';
Expand Down
5 changes: 4 additions & 1 deletion src/telegram/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2222,8 +2222,11 @@ export class TelegramHandler {
const complexity = classifyTaskComplexity(messageText, fullHistory.length);

// Route simple queries to fast models when user is on default 'auto' (Phase 7B.2)
// Use message-only complexity (ignoring conversation length) so that simple messages
// in long conversations still get routed to fast models.
const autoRouteEnabled = await this.storage.getUserAutoRoute(userId);
const routing = routeByComplexity(modelAlias, complexity, autoRouteEnabled);
const routingComplexity = classifyTaskComplexity(messageText, 0);
const routing = routeByComplexity(modelAlias, routingComplexity, autoRouteEnabled);
if (routing.wasRouted) {
console.log(`[ModelRouter] ${routing.reason} (user=${userId})`);
modelAlias = routing.modelAlias;
Expand Down
Loading