feat(analytics): implement logarithmic scale for response length histogram
This commit is contained in:
parent
48455d94e8
commit
bed925ef4c
6 changed files with 18 additions and 7 deletions
|
|
@ -288,6 +288,7 @@ export const Analytics: Component = () => {
|
|||
<MetaCluster
|
||||
items={[
|
||||
{ key: 'Metric', value: 'completion_tokens' },
|
||||
{ key: 'Scale', value: 'Log' },
|
||||
]}
|
||||
/>
|
||||
<HistogramChart data={histogram() ?? []} />
|
||||
|
|
@ -296,6 +297,7 @@ export const Analytics: Component = () => {
|
|||
<Panel title="Daily Response Length Spread" description="Completion token box plot by day using min / q1 / median / q3 / max summary.">
|
||||
<MetaCluster
|
||||
items={[
|
||||
{ key: 'Scale', value: 'Log' },
|
||||
{ key: 'Outliers', value: 'Hidden in this view' },
|
||||
]}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@ function formatPercent(value: number): string {
|
|||
return `${value.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
function getSymlogMax(value: number): number {
|
||||
return value <= 0 ? 1 : value * 1.1;
|
||||
}
|
||||
|
||||
function getDateTicks(values: Date[], width: number): Date[] {
|
||||
if (values.length <= 7) {
|
||||
return values;
|
||||
|
|
@ -645,11 +649,11 @@ export function HistogramChart(props: HistogramChartProps) {
|
|||
const xScale = createMemo(() => {
|
||||
const min = d3.min(props.data, (bin: { bin_start: number; bin_end: number; count: number }) => bin.bin_start) ?? 0;
|
||||
const max = d3.max(props.data, (bin: { bin_start: number; bin_end: number; count: number }) => bin.bin_end) ?? 1;
|
||||
return d3.scaleLinear().domain([min, max]).range([dimensions().marginLeft, dimensions().marginLeft + getInnerWidth(dimensions())]);
|
||||
return d3.scaleSymlog().domain([Math.max(0, min), getSymlogMax(max)]).range([dimensions().marginLeft, dimensions().marginLeft + getInnerWidth(dimensions())]);
|
||||
});
|
||||
const yScale = createMemo(() => {
|
||||
const max = d3.max(props.data, (bin: { bin_start: number; bin_end: number; count: number }) => bin.count) ?? 0;
|
||||
return d3.scaleLinear().domain([0, max === 0 ? 1 : max * 1.1]).range([dimensions().marginTop + getInnerHeight(dimensions()), dimensions().marginTop]);
|
||||
return d3.scaleSymlog().domain([0, getSymlogMax(max)]).range([dimensions().marginTop + getInnerHeight(dimensions()), dimensions().marginTop]);
|
||||
});
|
||||
const xTicks = createMemo(() => xScale().ticks(Math.max(2, Math.floor(getInnerWidth(dimensions()) / 100))));
|
||||
|
||||
|
|
@ -734,7 +738,7 @@ export function BoxPlotChart(props: BoxPlotChartProps) {
|
|||
});
|
||||
const yScale = createMemo(() => {
|
||||
const max = d3.max(points(), (point: ParsedBoxPlotDatum) => point.max) ?? 0;
|
||||
return d3.scaleLinear().domain([0, max === 0 ? 1 : max * 1.1]).range([dimensions().marginTop + getInnerHeight(dimensions()), dimensions().marginTop]);
|
||||
return d3.scaleSymlog().domain([0, getSymlogMax(max)]).range([dimensions().marginTop + getInnerHeight(dimensions()), dimensions().marginTop]);
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -27,4 +27,5 @@
|
|||
|
||||
- 모델 추이의 모델 키는 `response_model -> routed_model -> request_model -> unknown` 순서로 결정한다.
|
||||
- response length 계열 시각화는 `completion_tokens` 값이 있는 요청만 집계한다.
|
||||
- response length 계열 시각화는 긴 꼬리 분포를 읽기 쉽도록 로그 계열 스케일을 사용한다.
|
||||
- 상세 요청 단위의 latency/body 확인은 계속 `DetailLogs` 화면에서 담당한다.
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@
|
|||
|
||||
- `model-trends` 는 `response_model -> routed_model -> request_model -> unknown` 순서로 모델 키를 결정한다.
|
||||
- response length 계열 endpoint는 `completion_tokens` 가 있는 요청만 집계한다.
|
||||
- `response-length-histogram` 은 긴 꼬리 분포를 읽기 쉽도록 로그 간격 bin을 반환한다.
|
||||
- 자세한 내용은 [docs/analytics.md](./analytics.md) 참고.
|
||||
|
||||
### Dashboard Summary
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ server/src/
|
|||
- `AnalyticsService` 는 `analytics.db` 의 일별 집계와 `request_logs_YYYY-MM.db` 의 범위 조회를 함께 사용해 시계열/분포 데이터를 만든다.
|
||||
- 모델 추이 키는 `response_model -> routed_model -> request_model -> unknown` 순서로 결정한다.
|
||||
- response length 계열 집계는 `completion_tokens` 가 있는 요청만 포함한다.
|
||||
- response length histogram은 긴 꼬리 분포를 위해 로그 간격 bin을 사용한다.
|
||||
- 자세한 화면/API 설명은 [docs/analytics.md](./analytics.md) 참고.
|
||||
|
||||
## Deployment Notes
|
||||
|
|
|
|||
|
|
@ -338,15 +338,17 @@ export class AnalyticsService {
|
|||
return [{ bin_start: min, bin_end: max, count: values.length }];
|
||||
}
|
||||
|
||||
const width = (max - min) / safeBinCount;
|
||||
const transformedMin = Math.log1p(min);
|
||||
const transformedMax = Math.log1p(max);
|
||||
const width = (transformedMax - transformedMin) / safeBinCount;
|
||||
const histogram = Array.from({ length: safeBinCount }, (_, index) => ({
|
||||
bin_start: min + width * index,
|
||||
bin_end: index === safeBinCount - 1 ? max : min + width * (index + 1),
|
||||
bin_start: Math.expm1(transformedMin + width * index),
|
||||
bin_end: index === safeBinCount - 1 ? max : Math.expm1(transformedMin + width * (index + 1)),
|
||||
count: 0,
|
||||
}));
|
||||
|
||||
for (const value of values) {
|
||||
const index = Math.min(safeBinCount - 1, Math.floor((value - min) / width));
|
||||
const index = Math.min(safeBinCount - 1, Math.floor((Math.log1p(value) - transformedMin) / width));
|
||||
histogram[index].count += 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue