Skip to content

Commit 1e0d5df

Browse files
authored
perf(auto-routing): slim classifier prompt input (#3964)
* perf(auto-routing): slim classifier prompt input * test(auto-routing): clarify latest prompt routing * perf(auto-routing): tune classifier long-session summary * feat(auto-routing): show body size in admin analytics * fix(auto-routing): ignore tool results for latest prompt
1 parent 35b08e2 commit 1e0d5df

13 files changed

Lines changed: 556 additions & 147 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from 'react';
2+
import { renderToStaticMarkup } from 'react-dom/server';
3+
import { describe, expect, it } from '@jest/globals';
4+
import type { AutoRoutingClassifierAnalyticsResponse } from '@kilocode/auto-routing-contracts';
5+
import { AutoRoutingBreakdownTables } from './AutoRoutingAdminContent';
6+
7+
const analytics: AutoRoutingClassifierAnalyticsResponse = {
8+
period: '24h',
9+
summary: {
10+
totalRequests: 10,
11+
classifiedRequests: 8,
12+
classifierErrors: 1,
13+
invalidRequests: 1,
14+
totalCostCredits: 0.0000123,
15+
avgDurationMs: 123.4,
16+
p95DurationMs: 456.7,
17+
avgConfidence: 0.82,
18+
withSessionId: 9,
19+
uniqueSessions: 7,
20+
requiresTools: 5,
21+
mirroredHasTools: 6,
22+
avgBodyBytes: 2048,
23+
},
24+
statusBreakdown: [{ status: 'classified', requests: 8 }],
25+
taskTypeBreakdown: [{ taskType: 'implementation', requests: 5, avgConfidence: 0.9 }],
26+
taskSubtypeBreakdown: [
27+
{
28+
taskType: 'implementation',
29+
subtaskType: 'feature_development',
30+
requests: 4,
31+
avgConfidence: 0.88,
32+
},
33+
],
34+
classifierModelBreakdown: [{ classifierModel: 'google/gemini-2.5-flash-lite', requests: 10 }],
35+
};
36+
37+
describe('AutoRoutingBreakdownTables', () => {
38+
it('renders task type tables in a separate row after status and model tables', () => {
39+
const html = renderToStaticMarkup(
40+
React.createElement(AutoRoutingBreakdownTables, {
41+
analytics,
42+
loading: false,
43+
})
44+
);
45+
46+
const statusIndex = html.indexOf('Status');
47+
const modelIndex = html.indexOf('Classifier Models');
48+
const taskTypeIndex = html.indexOf('Task Types');
49+
const taskSubtypeIndex = html.indexOf('Task Subtypes');
50+
51+
expect(statusIndex).toBeGreaterThan(-1);
52+
expect(modelIndex).toBeGreaterThan(statusIndex);
53+
expect(taskTypeIndex).toBeGreaterThan(modelIndex);
54+
expect(taskSubtypeIndex).toBeGreaterThan(taskTypeIndex);
55+
});
56+
});

apps/web/src/app/admin/auto-routing/AutoRoutingAdminContent.tsx

Lines changed: 151 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type AutoRoutingClassifierAnalyticsResponse,
88
type AutoRoutingClassifierModelResponse,
99
} from '@kilocode/auto-routing-contracts';
10-
import { useEffect, useMemo, useState, type ReactNode } from 'react';
10+
import React, { useEffect, useMemo, useState, type ReactNode } from 'react';
1111
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
1212
import { toast } from 'sonner';
1313
import { BarChart3, Clock3, DollarSign, HelpCircle, RefreshCw, Route, Save } from 'lucide-react';
@@ -31,6 +31,7 @@ import {
3131
type OpenRouterModelsResponse,
3232
} from '@/lib/organizations/organization-types';
3333
import { cn } from '@/lib/utils';
34+
import { formatBodySizeKilobytes } from './auto-routing-format';
3435

3536
const periods: Array<{ value: AutoRoutingAnalyticsPeriod; label: string }> = [
3637
{ value: '1h', label: '1h' },
@@ -208,150 +209,156 @@ function EmptyTableRow({ colSpan }: { colSpan: number }) {
208209
);
209210
}
210211

211-
function BreakdownTables({
212+
export function AutoRoutingBreakdownTables({
212213
analytics,
213214
loading,
214215
}: {
215216
analytics?: AutoRoutingClassifierAnalyticsResponse;
216217
loading: boolean;
217218
}) {
218219
return (
219-
<div className="grid gap-4 xl:grid-cols-4">
220-
<BreakdownCard
221-
title="Status"
222-
help="Breakdown by raw classifier status, including classifier_error:<subtype> rows for classifier failures."
223-
loading={loading}
224-
>
225-
<Table>
226-
<TableHeader>
227-
<TableRow>
228-
<TableHead>Status</TableHead>
229-
<TableHead className="text-right">Requests</TableHead>
230-
</TableRow>
231-
</TableHeader>
232-
<TableBody>
233-
{analytics?.statusBreakdown.length ? (
234-
analytics.statusBreakdown.map(row => (
235-
<TableRow key={row.status}>
236-
<TableCell>
237-
<Badge variant="outline" className="max-w-56 truncate">
238-
{row.status || 'unknown'}
239-
</Badge>
240-
</TableCell>
241-
<TableCell className="text-right tabular-nums">
242-
{formatNumber(row.requests)}
243-
</TableCell>
244-
</TableRow>
245-
))
246-
) : (
247-
<EmptyTableRow colSpan={2} />
248-
)}
249-
</TableBody>
250-
</Table>
251-
</BreakdownCard>
252-
253-
<BreakdownCard
254-
title="Task Types"
255-
help="Successful classifier task categories and their average classifier confidence."
256-
loading={loading}
257-
>
258-
<Table>
259-
<TableHeader>
260-
<TableRow>
261-
<TableHead>Task</TableHead>
262-
<TableHead className="text-right">Requests</TableHead>
263-
<TableHead className="text-right">Confidence</TableHead>
264-
</TableRow>
265-
</TableHeader>
266-
<TableBody>
267-
{analytics?.taskTypeBreakdown.length ? (
268-
analytics.taskTypeBreakdown.map(row => (
269-
<TableRow key={row.taskType}>
270-
<TableCell className="capitalize">{row.taskType.replaceAll('_', ' ')}</TableCell>
271-
<TableCell className="text-right tabular-nums">
272-
{formatNumber(row.requests)}
273-
</TableCell>
274-
<TableCell className="text-right tabular-nums">
275-
{formatPercent(row.avgConfidence)}
276-
</TableCell>
277-
</TableRow>
278-
))
279-
) : (
280-
<EmptyTableRow colSpan={3} />
281-
)}
282-
</TableBody>
283-
</Table>
284-
</BreakdownCard>
285-
286-
<BreakdownCard
287-
title="Task Subtypes"
288-
help="Successful classifier task and subtask pairs with their average classifier confidence."
289-
loading={loading}
290-
>
291-
<Table>
292-
<TableHeader>
293-
<TableRow>
294-
<TableHead>Type</TableHead>
295-
<TableHead>Subtype</TableHead>
296-
<TableHead className="text-right">Requests</TableHead>
297-
<TableHead className="text-right">Confidence</TableHead>
298-
</TableRow>
299-
</TableHeader>
300-
<TableBody>
301-
{analytics?.taskSubtypeBreakdown.length ? (
302-
analytics.taskSubtypeBreakdown.map(row => (
303-
<TableRow key={`${row.taskType}:${row.subtaskType}`}>
304-
<TableCell className="max-w-32 truncate capitalize">
305-
{row.taskType.replaceAll('_', ' ')}
306-
</TableCell>
307-
<TableCell className="max-w-40 truncate capitalize">
308-
{row.subtaskType.replaceAll('_', ' ')}
309-
</TableCell>
310-
<TableCell className="text-right tabular-nums">
311-
{formatNumber(row.requests)}
312-
</TableCell>
313-
<TableCell className="text-right tabular-nums">
314-
{formatPercent(row.avgConfidence)}
315-
</TableCell>
316-
</TableRow>
317-
))
318-
) : (
319-
<EmptyTableRow colSpan={4} />
320-
)}
321-
</TableBody>
322-
</Table>
323-
</BreakdownCard>
324-
325-
<BreakdownCard
326-
title="Classifier Models"
327-
help="Classifier model used for each request, or unknown when no classifier call happened."
328-
loading={loading}
329-
>
330-
<Table>
331-
<TableHeader>
332-
<TableRow>
333-
<TableHead>Model</TableHead>
334-
<TableHead className="text-right">Requests</TableHead>
335-
</TableRow>
336-
</TableHeader>
337-
<TableBody>
338-
{analytics?.classifierModelBreakdown.length ? (
339-
analytics.classifierModelBreakdown.map(row => (
340-
<TableRow key={row.classifierModel}>
341-
<TableCell className="max-w-64 truncate font-mono text-xs">
342-
{row.classifierModel || 'unknown'}
343-
</TableCell>
344-
<TableCell className="text-right tabular-nums">
345-
{formatNumber(row.requests)}
346-
</TableCell>
347-
</TableRow>
348-
))
349-
) : (
350-
<EmptyTableRow colSpan={2} />
351-
)}
352-
</TableBody>
353-
</Table>
354-
</BreakdownCard>
220+
<div className="flex flex-col gap-4">
221+
<div className="grid gap-4 xl:grid-cols-2">
222+
<BreakdownCard
223+
title="Status"
224+
help="Breakdown by raw classifier status, including classifier_error:<subtype> rows for classifier failures."
225+
loading={loading}
226+
>
227+
<Table>
228+
<TableHeader>
229+
<TableRow>
230+
<TableHead>Status</TableHead>
231+
<TableHead className="text-right">Requests</TableHead>
232+
</TableRow>
233+
</TableHeader>
234+
<TableBody>
235+
{analytics?.statusBreakdown.length ? (
236+
analytics.statusBreakdown.map(row => (
237+
<TableRow key={row.status}>
238+
<TableCell>
239+
<Badge variant="outline" className="max-w-56 truncate">
240+
{row.status || 'unknown'}
241+
</Badge>
242+
</TableCell>
243+
<TableCell className="text-right tabular-nums">
244+
{formatNumber(row.requests)}
245+
</TableCell>
246+
</TableRow>
247+
))
248+
) : (
249+
<EmptyTableRow colSpan={2} />
250+
)}
251+
</TableBody>
252+
</Table>
253+
</BreakdownCard>
254+
255+
<BreakdownCard
256+
title="Classifier Models"
257+
help="Classifier model used for each request, or unknown when no classifier call happened."
258+
loading={loading}
259+
>
260+
<Table>
261+
<TableHeader>
262+
<TableRow>
263+
<TableHead>Model</TableHead>
264+
<TableHead className="text-right">Requests</TableHead>
265+
</TableRow>
266+
</TableHeader>
267+
<TableBody>
268+
{analytics?.classifierModelBreakdown.length ? (
269+
analytics.classifierModelBreakdown.map(row => (
270+
<TableRow key={row.classifierModel}>
271+
<TableCell className="max-w-64 truncate font-mono text-xs">
272+
{row.classifierModel || 'unknown'}
273+
</TableCell>
274+
<TableCell className="text-right tabular-nums">
275+
{formatNumber(row.requests)}
276+
</TableCell>
277+
</TableRow>
278+
))
279+
) : (
280+
<EmptyTableRow colSpan={2} />
281+
)}
282+
</TableBody>
283+
</Table>
284+
</BreakdownCard>
285+
</div>
286+
287+
<div className="grid gap-4 xl:grid-cols-2">
288+
<BreakdownCard
289+
title="Task Types"
290+
help="Successful classifier task categories and their average classifier confidence."
291+
loading={loading}
292+
>
293+
<Table>
294+
<TableHeader>
295+
<TableRow>
296+
<TableHead>Task</TableHead>
297+
<TableHead className="text-right">Requests</TableHead>
298+
<TableHead className="text-right">Confidence</TableHead>
299+
</TableRow>
300+
</TableHeader>
301+
<TableBody>
302+
{analytics?.taskTypeBreakdown.length ? (
303+
analytics.taskTypeBreakdown.map(row => (
304+
<TableRow key={row.taskType}>
305+
<TableCell className="capitalize">
306+
{row.taskType.replaceAll('_', ' ')}
307+
</TableCell>
308+
<TableCell className="text-right tabular-nums">
309+
{formatNumber(row.requests)}
310+
</TableCell>
311+
<TableCell className="text-right tabular-nums">
312+
{formatPercent(row.avgConfidence)}
313+
</TableCell>
314+
</TableRow>
315+
))
316+
) : (
317+
<EmptyTableRow colSpan={3} />
318+
)}
319+
</TableBody>
320+
</Table>
321+
</BreakdownCard>
322+
323+
<BreakdownCard
324+
title="Task Subtypes"
325+
help="Successful classifier task and subtask pairs with their average classifier confidence."
326+
loading={loading}
327+
>
328+
<Table>
329+
<TableHeader>
330+
<TableRow>
331+
<TableHead>Type</TableHead>
332+
<TableHead>Subtype</TableHead>
333+
<TableHead className="text-right">Requests</TableHead>
334+
<TableHead className="text-right">Confidence</TableHead>
335+
</TableRow>
336+
</TableHeader>
337+
<TableBody>
338+
{analytics?.taskSubtypeBreakdown.length ? (
339+
analytics.taskSubtypeBreakdown.map(row => (
340+
<TableRow key={`${row.taskType}:${row.subtaskType}`}>
341+
<TableCell className="max-w-32 truncate capitalize">
342+
{row.taskType.replaceAll('_', ' ')}
343+
</TableCell>
344+
<TableCell className="max-w-40 truncate capitalize">
345+
{row.subtaskType.replaceAll('_', ' ')}
346+
</TableCell>
347+
<TableCell className="text-right tabular-nums">
348+
{formatNumber(row.requests)}
349+
</TableCell>
350+
<TableCell className="text-right tabular-nums">
351+
{formatPercent(row.avgConfidence)}
352+
</TableCell>
353+
</TableRow>
354+
))
355+
) : (
356+
<EmptyTableRow colSpan={4} />
357+
)}
358+
</TableBody>
359+
</Table>
360+
</BreakdownCard>
361+
</div>
355362
</div>
356363
);
357364
}
@@ -576,15 +583,18 @@ export function AutoRoutingAdminContent() {
576583
/>
577584
<MetricCard
578585
title="Body Size"
579-
value={`${formatDecimal(summary?.avgBodyBytes ?? 0)} B`}
586+
value={formatBodySizeKilobytes(summary?.avgBodyBytes ?? 0)}
580587
detail={`${formatPercent(summary?.avgConfidence ?? 0)} avg confidence`}
581588
icon={BarChart3}
582589
loading={analyticsQuery.isLoading}
583-
help="Average mirrored body size in bytes. The detail shows average classifier confidence for successful classifications."
590+
help="Average mirrored body size in KB. The detail shows average classifier confidence for successful classifications."
584591
/>
585592
</div>
586593

587-
<BreakdownTables analytics={analyticsQuery.data} loading={analyticsQuery.isLoading} />
594+
<AutoRoutingBreakdownTables
595+
analytics={analyticsQuery.data}
596+
loading={analyticsQuery.isLoading}
597+
/>
588598
</>
589599
)}
590600
</div>

0 commit comments

Comments
 (0)