@cat-factory/app 0.178.2 → 0.179.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/panels/ObservabilityPanel.vue +105 -1
- package/app/types/execution.ts +1 -0
- package/app/utils/observability.spec.ts +60 -1
- package/app/utils/observability.ts +56 -1
- package/i18n/locales/de.json +13 -0
- package/i18n/locales/en.json +13 -0
- package/i18n/locales/es.json +13 -0
- package/i18n/locales/fr.json +13 -0
- package/i18n/locales/he.json +13 -0
- package/i18n/locales/it.json +13 -0
- package/i18n/locales/ja.json +13 -0
- package/i18n/locales/pl.json +13 -0
- package/i18n/locales/tr.json +13 -0
- package/i18n/locales/uk.json +13 -0
- package/package.json +2 -2
|
@@ -8,7 +8,13 @@ import type {
|
|
|
8
8
|
WebSearchProvider,
|
|
9
9
|
} from '~/types/execution'
|
|
10
10
|
import { agentKindMeta } from '~/utils/catalog'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
foldRunPhaseMetrics,
|
|
13
|
+
formatMs,
|
|
14
|
+
formatTokens,
|
|
15
|
+
pct,
|
|
16
|
+
totalInputTokens,
|
|
17
|
+
} from '~/utils/observability'
|
|
12
18
|
|
|
13
19
|
// Drill-down overlay for a run's LLM activity. Opened via
|
|
14
20
|
// `ui.openObservability(instanceId)` from a step surface; loads the full per-call
|
|
@@ -151,6 +157,26 @@ const totals = computed(() => {
|
|
|
151
157
|
function sum(items: LlmCallMetric[], pick: (m: LlmCallMetric) => number): number {
|
|
152
158
|
return items.reduce((acc, m) => acc + pick(m), 0)
|
|
153
159
|
}
|
|
160
|
+
|
|
161
|
+
// Where the run's tokens went, by PHASE. Unlike the totals above (derived from the capped call
|
|
162
|
+
// list), this reads the engine's SQL rollup off the steps, so it stays honest on a long run.
|
|
163
|
+
const phaseRows = computed(() => foldRunPhaseMetrics(instance.value?.steps ?? []))
|
|
164
|
+
const phaseCarryTotal = computed(() =>
|
|
165
|
+
phaseRows.value.reduce((acc, p) => acc + p.carryCostTokens, 0),
|
|
166
|
+
)
|
|
167
|
+
/** Share of the run's carry cost a phase accounts for (0..100), or null when nothing carried. */
|
|
168
|
+
function carryShare(carryCostTokens: number): number | null {
|
|
169
|
+
return phaseCarryTotal.value > 0 ? pct(carryCostTokens / phaseCarryTotal.value) : null
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The phase label as shown. The vocabulary belongs to the HARNESS — it is whatever its handlers
|
|
173
|
+
* pass to `onPhase`, so there is deliberately no closed union to translate against; a newer
|
|
174
|
+
* image's phase must render verbatim rather than disappear. The one label the platform owns is
|
|
175
|
+
* the empty string, which means "nothing could attribute this call" and needs saying in words.
|
|
176
|
+
*/
|
|
177
|
+
function phaseLabel(phase: string): string {
|
|
178
|
+
return phase || t('observability.phase.unattributed')
|
|
179
|
+
}
|
|
154
180
|
function isWarning(finishReason: string | null): boolean {
|
|
155
181
|
return finishReason === 'length' || finishReason === 'content_filter'
|
|
156
182
|
}
|
|
@@ -384,6 +410,84 @@ function exportJson() {
|
|
|
384
410
|
</div>
|
|
385
411
|
</section>
|
|
386
412
|
|
|
413
|
+
<!-- where the run's tokens went, by phase (the engine's SQL rollup, not the
|
|
414
|
+
capped call list) -->
|
|
415
|
+
<section
|
|
416
|
+
v-if="phaseRows.length"
|
|
417
|
+
class="rounded-xl border border-slate-800 bg-slate-900/50 p-4"
|
|
418
|
+
>
|
|
419
|
+
<div class="flex items-baseline gap-2">
|
|
420
|
+
<h2 class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
421
|
+
{{ t('observability.phase.title') }}
|
|
422
|
+
</h2>
|
|
423
|
+
<span class="text-[11px] text-slate-600">
|
|
424
|
+
{{ t('observability.phase.subtitle') }}
|
|
425
|
+
</span>
|
|
426
|
+
</div>
|
|
427
|
+
<div class="mt-3 overflow-x-auto">
|
|
428
|
+
<table class="w-full min-w-[32rem] text-[12px]">
|
|
429
|
+
<thead>
|
|
430
|
+
<tr class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
431
|
+
<th class="py-1 pe-3 text-start font-normal">
|
|
432
|
+
{{ t('observability.phase.columns.phase') }}
|
|
433
|
+
</th>
|
|
434
|
+
<th class="py-1 px-3 text-end font-normal">
|
|
435
|
+
{{ t('observability.phase.columns.turns') }}
|
|
436
|
+
</th>
|
|
437
|
+
<th class="py-1 px-3 text-end font-normal">
|
|
438
|
+
{{ t('observability.phase.columns.tokensInOut') }}
|
|
439
|
+
</th>
|
|
440
|
+
<!-- The sort key, MARKED as one. Rows lead with carry cost rather than
|
|
441
|
+
with tokens, and the two orders genuinely differ: a phase that runs
|
|
442
|
+
late carries almost nothing however much it spent (nothing after it
|
|
443
|
+
re-sends its context). Leaving that implicit invites reading row 1
|
|
444
|
+
as "the phase that burned the most", which is the neighbouring
|
|
445
|
+
column. -->
|
|
446
|
+
<th aria-sort="descending" class="py-1 ps-3 text-end font-normal">
|
|
447
|
+
<span :title="t('observability.phase.carryCostHint')">
|
|
448
|
+
{{ t('observability.phase.columns.carryCost') }} ↓
|
|
449
|
+
</span>
|
|
450
|
+
</th>
|
|
451
|
+
</tr>
|
|
452
|
+
</thead>
|
|
453
|
+
<tbody>
|
|
454
|
+
<tr v-for="p in phaseRows" :key="p.phase" class="border-t border-slate-800/70">
|
|
455
|
+
<td class="py-1.5 pe-3 text-slate-200">
|
|
456
|
+
<span :class="p.phase ? '' : 'text-slate-400 italic'">
|
|
457
|
+
{{ phaseLabel(p.phase) }}
|
|
458
|
+
</span>
|
|
459
|
+
<span v-if="!p.phase" class="ms-1.5 text-[11px] text-slate-600">
|
|
460
|
+
{{ t('observability.phase.unattributedHint') }}
|
|
461
|
+
</span>
|
|
462
|
+
<UBadge
|
|
463
|
+
v-if="p.errors"
|
|
464
|
+
color="error"
|
|
465
|
+
variant="subtle"
|
|
466
|
+
size="sm"
|
|
467
|
+
class="ms-2"
|
|
468
|
+
>
|
|
469
|
+
{{ t('observability.metricsBar.errors', { count: p.errors }, p.errors) }}
|
|
470
|
+
</UBadge>
|
|
471
|
+
</td>
|
|
472
|
+
<td class="py-1.5 px-3 text-end tabular-nums text-slate-300">
|
|
473
|
+
{{ p.calls }}
|
|
474
|
+
</td>
|
|
475
|
+
<td class="py-1.5 px-3 text-end tabular-nums text-slate-300">
|
|
476
|
+
{{ formatTokens(totalInputTokens(p)) }}↑
|
|
477
|
+
{{ formatTokens(p.completionTokens) }}↓
|
|
478
|
+
</td>
|
|
479
|
+
<td class="py-1.5 ps-3 text-end tabular-nums text-slate-300">
|
|
480
|
+
{{ formatTokens(p.carryCostTokens) }}
|
|
481
|
+
<span v-if="carryShare(p.carryCostTokens) !== null" class="text-slate-600">
|
|
482
|
+
· {{ carryShare(p.carryCostTokens) }}%
|
|
483
|
+
</span>
|
|
484
|
+
</td>
|
|
485
|
+
</tr>
|
|
486
|
+
</tbody>
|
|
487
|
+
</table>
|
|
488
|
+
</div>
|
|
489
|
+
</section>
|
|
490
|
+
|
|
387
491
|
<!-- states -->
|
|
388
492
|
<p
|
|
389
493
|
v-if="loading && !calls.length"
|
package/app/types/execution.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import type { PipelineStep, StepPhaseMetrics } from '~/types/execution'
|
|
3
|
+
import { foldRunPhaseMetrics, totalInputTokens } from './observability'
|
|
3
4
|
|
|
4
5
|
describe('totalInputTokens', () => {
|
|
5
6
|
it('sums all three input classes, so the headline matches Claude Code’s context gauge', () => {
|
|
@@ -26,3 +27,61 @@ describe('totalInputTokens', () => {
|
|
|
26
27
|
expect(totalInputTokens({ promptTokens: 500 })).toBe(500)
|
|
27
28
|
})
|
|
28
29
|
})
|
|
30
|
+
|
|
31
|
+
describe('foldRunPhaseMetrics', () => {
|
|
32
|
+
const phase = (over: Partial<StepPhaseMetrics> & Pick<StepPhaseMetrics, 'phase'>) => ({
|
|
33
|
+
calls: 1,
|
|
34
|
+
promptTokens: 10,
|
|
35
|
+
cacheReadTokens: 0,
|
|
36
|
+
cacheWriteTokens: 0,
|
|
37
|
+
completionTokens: 5,
|
|
38
|
+
carryCostTokens: 0,
|
|
39
|
+
errors: 0,
|
|
40
|
+
...over,
|
|
41
|
+
})
|
|
42
|
+
const step = (agentKind: string, byPhase: StepPhaseMetrics[]) =>
|
|
43
|
+
({ agentKind, metrics: { byPhase } }) as unknown as PipelineStep
|
|
44
|
+
|
|
45
|
+
it('does NOT double-count two steps that share an agent kind', () => {
|
|
46
|
+
// A step's rollup covers its agent KIND across the whole run (the proxy keys a conversation
|
|
47
|
+
// by `(execution, agentKind)`), so two tester steps carry identical numbers. Summing them
|
|
48
|
+
// would report twice the tokens the run actually spent.
|
|
49
|
+
const rows = [phase({ phase: 'agent', calls: 3, carryCostTokens: 90 })]
|
|
50
|
+
const folded = foldRunPhaseMetrics([step('tester', rows), step('tester', rows)])
|
|
51
|
+
expect(folded).toHaveLength(1)
|
|
52
|
+
expect(folded[0]).toMatchObject({ phase: 'agent', calls: 3, carryCostTokens: 90 })
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('merges a phase across different agent kinds and sorts costliest first', () => {
|
|
56
|
+
const folded = foldRunPhaseMetrics([
|
|
57
|
+
step('coder', [
|
|
58
|
+
phase({ phase: 'agent', calls: 2, carryCostTokens: 10 }),
|
|
59
|
+
phase({ phase: 'validation-repair', calls: 1, carryCostTokens: 500 }),
|
|
60
|
+
]),
|
|
61
|
+
step('reviewer', [phase({ phase: 'agent', calls: 4, carryCostTokens: 40 })]),
|
|
62
|
+
])
|
|
63
|
+
expect(folded.map((p) => [p.phase, p.calls, p.carryCostTokens])).toEqual([
|
|
64
|
+
['validation-repair', 1, 500],
|
|
65
|
+
['agent', 6, 50],
|
|
66
|
+
])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it("keeps the unattributed '' phase rather than hiding it", () => {
|
|
70
|
+
const folded = foldRunPhaseMetrics([step('coder', [phase({ phase: '', calls: 7 })])])
|
|
71
|
+
expect(folded.map((p) => p.phase)).toEqual([''])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('is empty when no step carries a rollup, so the section simply does not render', () => {
|
|
75
|
+
expect(foldRunPhaseMetrics([{ agentKind: 'coder' } as unknown as PipelineStep])).toEqual([])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('returns fresh rows rather than aliasing the store objects it folded', () => {
|
|
79
|
+
// The single-kind case is the one that used to pass a `step.metrics.byPhase` row straight
|
|
80
|
+
// through: a caller mutating what a fold handed it would have written into the store.
|
|
81
|
+
const row = phase({ phase: 'agent', calls: 3, carryCostTokens: 90 })
|
|
82
|
+
const folded = foldRunPhaseMetrics([step('coder', [row])])
|
|
83
|
+
expect(folded[0]).not.toBe(row)
|
|
84
|
+
folded[0]!.calls = 999
|
|
85
|
+
expect(row.calls).toBe(3)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// rollups + the drill-down panel). Kept here so the components stay declarative and
|
|
3
3
|
// the number-crunching is unit-testable.
|
|
4
4
|
|
|
5
|
-
import type { StepMetrics } from '~/types/execution'
|
|
5
|
+
import type { PipelineStep, StepMetrics, StepPhaseMetrics } from '~/types/execution'
|
|
6
6
|
|
|
7
7
|
/** Compact token count: 1234 → "1.2k", 980 → "980", 2_500_000 → "2.5M". */
|
|
8
8
|
export function formatTokens(n: number): string {
|
|
@@ -69,6 +69,61 @@ export function transportRatio(m: Pick<StepMetrics, 'upstreamMs' | 'overheadMs'>
|
|
|
69
69
|
return total > 0 ? m.overheadMs / total : null
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** Zero cell, so the fold below has one accumulator shape and never aliases a store row. */
|
|
73
|
+
const EMPTY_PHASE: Omit<StepPhaseMetrics, 'phase'> = {
|
|
74
|
+
calls: 0,
|
|
75
|
+
promptTokens: 0,
|
|
76
|
+
cacheReadTokens: 0,
|
|
77
|
+
cacheWriteTokens: 0,
|
|
78
|
+
completionTokens: 0,
|
|
79
|
+
carryCostTokens: 0,
|
|
80
|
+
errors: 0,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The run's model spend split by the PHASE that spent it, folded from the per-step rollups the
|
|
85
|
+
* engine already pushes. Rows come back costliest-carry-cost first — the slice worth attacking.
|
|
86
|
+
*
|
|
87
|
+
* Two things make this correct rather than a naive sum over `steps`:
|
|
88
|
+
*
|
|
89
|
+
* 1. **Deduplicate by agent kind.** A step's `metrics` is the rollup for its AGENT KIND across
|
|
90
|
+
* the whole run (the proxy keys a conversation by `(execution, agentKind)`, not by step
|
|
91
|
+
* index), so two steps of the same kind carry the SAME numbers. Adding them would double
|
|
92
|
+
* every figure on any pipeline with, say, two tester steps.
|
|
93
|
+
* 2. **Read it off the rollup, not off the loaded calls.** The panel's call list is capped, so
|
|
94
|
+
* folding phases client-side from it would silently under-report exactly the long runs this
|
|
95
|
+
* breakdown exists for. The rollup is a SQL aggregate over every row.
|
|
96
|
+
*
|
|
97
|
+
* Every returned row is a FRESH object, never a row of `step.metrics.byPhase` passed through:
|
|
98
|
+
* those belong to the store, and a fold whose output aliases its input is a trap for the next
|
|
99
|
+
* caller that reasonably assumes it may mutate what a fold handed it.
|
|
100
|
+
*/
|
|
101
|
+
export function foldRunPhaseMetrics(steps: readonly PipelineStep[]): StepPhaseMetrics[] {
|
|
102
|
+
const seenKinds = new Set<string>()
|
|
103
|
+
const byPhase = new Map<string, StepPhaseMetrics>()
|
|
104
|
+
for (const step of steps) {
|
|
105
|
+
const rows = step.metrics?.byPhase
|
|
106
|
+
if (!rows?.length || seenKinds.has(step.agentKind)) continue
|
|
107
|
+
seenKinds.add(step.agentKind)
|
|
108
|
+
for (const row of rows) {
|
|
109
|
+
const prev = byPhase.get(row.phase) ?? EMPTY_PHASE
|
|
110
|
+
byPhase.set(row.phase, {
|
|
111
|
+
phase: row.phase,
|
|
112
|
+
calls: prev.calls + row.calls,
|
|
113
|
+
promptTokens: prev.promptTokens + row.promptTokens,
|
|
114
|
+
cacheReadTokens: prev.cacheReadTokens + row.cacheReadTokens,
|
|
115
|
+
cacheWriteTokens: prev.cacheWriteTokens + row.cacheWriteTokens,
|
|
116
|
+
completionTokens: prev.completionTokens + row.completionTokens,
|
|
117
|
+
carryCostTokens: prev.carryCostTokens + row.carryCostTokens,
|
|
118
|
+
errors: prev.errors + row.errors,
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return [...byPhase.values()].sort(
|
|
123
|
+
(a, b) => b.carryCostTokens - a.carryCostTokens || b.calls - a.calls,
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
72
127
|
/** Tailwind text/bg colour for an output-headroom level (green → amber → red). */
|
|
73
128
|
export function headroomColor(ratio: number | null, truncated: boolean): string {
|
|
74
129
|
if (truncated || (ratio != null && ratio >= 0.98)) return 'text-rose-400'
|
package/i18n/locales/de.json
CHANGED
|
@@ -3010,6 +3010,19 @@
|
|
|
3010
3010
|
"cacheWrite": "{tokens} in den Cache geschrieben",
|
|
3011
3011
|
"cacheWriteHint": "Eingabe-Tokens, die in den Cache des Providers geschrieben wurden (1,25x bis 2x der Preis frischer Eingabe-Tokens)"
|
|
3012
3012
|
},
|
|
3013
|
+
"phase": {
|
|
3014
|
+
"title": "Wohin die Tokens geflossen sind",
|
|
3015
|
+
"subtitle": "nach Phase des Laufs, sortiert nach Mitschleppkosten",
|
|
3016
|
+
"columns": {
|
|
3017
|
+
"phase": "Phase",
|
|
3018
|
+
"turns": "Runden",
|
|
3019
|
+
"tokensInOut": "Tokens (ein / aus)",
|
|
3020
|
+
"carryCost": "Mitschleppkosten"
|
|
3021
|
+
},
|
|
3022
|
+
"carryCostHint": "Wie stark jede Phase die nachfolgenden Runden belastet hat: ihr Kontext einmal für jede spätere Runde gezählt, die ihn erneut senden musste. Vergleichen Sie die Phasen eines Laufs miteinander; für sich allein sagt die Zahl nichts aus. Eine spät laufende Phase schleppt wenig mit, wie viel sie auch verbraucht hat; lesen Sie diese Spalte daher zusammen mit den Tokens daneben.",
|
|
3023
|
+
"unattributed": "Nicht zugeordnet",
|
|
3024
|
+
"unattributedHint": "von einem Kanal erfasst, der keine Phase meldet"
|
|
3025
|
+
},
|
|
3013
3026
|
"metricsBar": {
|
|
3014
3027
|
"calls": "{count} Aufruf | {count} Aufrufe",
|
|
3015
3028
|
"inputCompletionTokens": "Eingabe- / Ausgabe-Tokens insgesamt. Die Eingabe zählt auch gecachte Tokens mit: Sie belegen weiterhin das Kontextfenster, genau wie es die Kontextanzeige von Claude Code zählt.",
|
package/i18n/locales/en.json
CHANGED
|
@@ -1474,6 +1474,19 @@
|
|
|
1474
1474
|
"cacheWrite": "{tokens} cache write",
|
|
1475
1475
|
"cacheWriteHint": "Input tokens written into the provider's cache (1.25x to 2x the price of fresh input)"
|
|
1476
1476
|
},
|
|
1477
|
+
"phase": {
|
|
1478
|
+
"title": "Where the tokens went",
|
|
1479
|
+
"subtitle": "by phase of the run, ordered by carry cost",
|
|
1480
|
+
"columns": {
|
|
1481
|
+
"phase": "Phase",
|
|
1482
|
+
"turns": "Turns",
|
|
1483
|
+
"tokensInOut": "Tokens (in / out)",
|
|
1484
|
+
"carryCost": "Carry cost"
|
|
1485
|
+
},
|
|
1486
|
+
"carryCostHint": "How much each phase burdened the turns that came after it: its context counted once for every later turn that had to re-send it. Compare a run's phases with each other; on its own the number means nothing. A phase that runs late carries little however much it spent, so read this column alongside the tokens beside it.",
|
|
1487
|
+
"unattributed": "Unattributed",
|
|
1488
|
+
"unattributedHint": "recorded by a channel that reports no phase"
|
|
1489
|
+
},
|
|
1477
1490
|
"metricsBar": {
|
|
1478
1491
|
"calls": "{count} call | {count} calls",
|
|
1479
1492
|
"inputCompletionTokens": "Total input / completion tokens. Input counts cached tokens too: they still occupy the context window, exactly as Claude Code's own context gauge counts them.",
|
package/i18n/locales/es.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} escritos en caché",
|
|
1409
1409
|
"cacheWriteHint": "Tokens de entrada escritos en la caché del proveedor (de 1,25 a 2 veces el precio de la entrada fresca)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "Adónde fueron los tokens",
|
|
1413
|
+
"subtitle": "por fase de la ejecución, ordenado por coste de arrastre",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "Fase",
|
|
1416
|
+
"turns": "Turnos",
|
|
1417
|
+
"tokensInOut": "Tokens (entrada / salida)",
|
|
1418
|
+
"carryCost": "Coste de arrastre"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "Cuánto cargó cada fase sobre los turnos posteriores: su contexto contado una vez por cada turno que tuvo que reenviarlo. Compara entre sí las fases de una ejecución; por sí sola la cifra no significa nada. Una fase que se ejecuta al final arrastra poco por mucho que haya gastado, así que lee esta columna junto a los tokens de al lado.",
|
|
1421
|
+
"unattributed": "Sin atribuir",
|
|
1422
|
+
"unattributedHint": "registrado por un canal que no informa de la fase"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} llamada | {count} llamadas",
|
|
1413
1426
|
"inputCompletionTokens": "Tokens totales de entrada / salida. La entrada tambien cuenta los tokens en caché: siguen ocupando la ventana de contexto, igual que los cuenta el medidor de contexto de Claude Code.",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} écrits dans le cache",
|
|
1409
1409
|
"cacheWriteHint": "Tokens d'entrée écrits dans le cache du fournisseur (1,25 à 2 fois le prix d'une entrée fraîche)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "Où sont passés les tokens",
|
|
1413
|
+
"subtitle": "par phase de l'exécution, trié par coût de report",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "Phase",
|
|
1416
|
+
"turns": "Tours",
|
|
1417
|
+
"tokensInOut": "Tokens (entrée / sortie)",
|
|
1418
|
+
"carryCost": "Coût de report"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "Ce que chaque phase a fait peser sur les tours suivants : son contexte compté une fois pour chaque tour ultérieur ayant dû le renvoyer. Comparez les phases d'une exécution entre elles ; isolée, la valeur ne signifie rien. Une phase qui s'exécute en dernier reporte peu, quoi qu'elle ait dépensé : lisez donc cette colonne avec les tokens voisins.",
|
|
1421
|
+
"unattributed": "Non attribué",
|
|
1422
|
+
"unattributedHint": "enregistré par un canal qui ne rapporte aucune phase"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} appel | {count} appels",
|
|
1413
1426
|
"inputCompletionTokens": "Total des tokens d'entrée / de sortie. L'entree compte aussi les tokens en cache : ils occupent toujours la fenêtre de contexte, exactement comme les compte la jauge de contexte de Claude Code.",
|
package/i18n/locales/he.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} נכתבו למטמון",
|
|
1409
1409
|
"cacheWriteHint": "טוקני קלט שנכתבו למטמון הספק (פי 1.25 עד 2 ממחיר קלט חדש)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "לאן הלכו הטוקנים",
|
|
1413
|
+
"subtitle": "לפי שלב בהרצה, ממוין לפי עלות גרירה",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "שלב",
|
|
1416
|
+
"turns": "תורים",
|
|
1417
|
+
"tokensInOut": "טוקנים (נכנס / יוצא)",
|
|
1418
|
+
"carryCost": "עלות גרירה"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "כמה כל שלב העמיס על התורים שבאו אחריו: ההקשר שלו נספר פעם אחת עבור כל תור מאוחר יותר שנאלץ לשלוח אותו שוב. השוו בין שלבי ההרצה זה לזה; בפני עצמו המספר אינו אומר דבר. שלב שרץ בסוף גורר מעט גם אם צרך הרבה, ולכן קראו את העמודה הזאת יחד עם הטוקנים שלצדה.",
|
|
1421
|
+
"unattributed": "ללא שיוך",
|
|
1422
|
+
"unattributedHint": "נרשם על ידי ערוץ שאינו מדווח על שלב"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} קריאה | {count} קריאות",
|
|
1413
1426
|
"inputCompletionTokens": "סך טוקני הקלט / הפלט. הקלט סופר גם טוקנים מהמטמון: הם עדיין תופסים את חלון ההקשר, בדיוק כפי שמד ההקשר של Claude Code סופר אותם.",
|
package/i18n/locales/it.json
CHANGED
|
@@ -3010,6 +3010,19 @@
|
|
|
3010
3010
|
"cacheWrite": "{tokens} scritti nella cache",
|
|
3011
3011
|
"cacheWriteHint": "Token di input scritti nella cache del provider (da 1,25 a 2 volte il prezzo dell'input fresco)"
|
|
3012
3012
|
},
|
|
3013
|
+
"phase": {
|
|
3014
|
+
"title": "Dove sono finiti i token",
|
|
3015
|
+
"subtitle": "per fase dell'esecuzione, ordinato per costo di trascinamento",
|
|
3016
|
+
"columns": {
|
|
3017
|
+
"phase": "Fase",
|
|
3018
|
+
"turns": "Turni",
|
|
3019
|
+
"tokensInOut": "Token (in / out)",
|
|
3020
|
+
"carryCost": "Costo di trascinamento"
|
|
3021
|
+
},
|
|
3022
|
+
"carryCostHint": "Quanto ogni fase ha gravato sui turni successivi: il suo contesto contato una volta per ogni turno che ha dovuto rinviarlo. Confronta tra loro le fasi di un'esecuzione; da solo il numero non significa nulla. Una fase che viene eseguita per ultima trascina poco per quanto abbia speso, quindi leggi questa colonna insieme ai token accanto.",
|
|
3023
|
+
"unattributed": "Non attribuito",
|
|
3024
|
+
"unattributedHint": "registrato da un canale che non riporta la fase"
|
|
3025
|
+
},
|
|
3013
3026
|
"metricsBar": {
|
|
3014
3027
|
"calls": "{count} chiamata | {count} chiamate",
|
|
3015
3028
|
"inputCompletionTokens": "Token totali di input / output. L'input conta anche i token in cache: occupano comunque la finestra di contesto, esattamente come li conta l'indicatore di contesto di Claude Code.",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} キャッシュ書き込み",
|
|
1409
1409
|
"cacheWriteHint": "プロバイダーのキャッシュに書き込まれた入力トークン (新規入力の 1.25 倍から 2 倍の価格)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "トークンの使われ先",
|
|
1413
|
+
"subtitle": "実行のフェーズ別、持ち越しコスト順",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "フェーズ",
|
|
1416
|
+
"turns": "ターン数",
|
|
1417
|
+
"tokensInOut": "トークン (入力 / 出力)",
|
|
1418
|
+
"carryCost": "持ち越しコスト"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "各フェーズが後続のターンにどれだけ負担をかけたか。そのコンテキストを、再送が必要になった後続ターンの数だけ数えた値です。1 回の実行内でフェーズ同士を比較してください。単独の数値には意味がありません。最後に実行されるフェーズは、どれだけ消費しても持ち越しはわずかです。この列は隣のトークン数と併せて読んでください。",
|
|
1421
|
+
"unattributed": "未特定",
|
|
1422
|
+
"unattributedHint": "フェーズを報告しないチャネルで記録"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} 件の呼び出し | {count} 件の呼び出し",
|
|
1413
1426
|
"inputCompletionTokens": "入力 / 出力トークンの合計。入力にはキャッシュされたトークンも含まれます。それらも依然としてコンテキストウィンドウを占有するため、Claude Code のコンテキスト表示と同じ数え方です。",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} zapisane do pamięci podręcznej",
|
|
1409
1409
|
"cacheWriteHint": "Tokeny wejściowe zapisane do pamięci podręcznej dostawcy (od 1,25 do 2 razy cena świeżego wejścia)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "Na co poszły tokeny",
|
|
1413
|
+
"subtitle": "według fazy przebiegu, posortowane według kosztu przenoszenia",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "Faza",
|
|
1416
|
+
"turns": "Tury",
|
|
1417
|
+
"tokensInOut": "Tokeny (we / wy)",
|
|
1418
|
+
"carryCost": "Koszt przenoszenia"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "Jak bardzo każda faza obciążyła kolejne tury: jej kontekst policzony raz za każdą późniejszą turę, która musiała go wysłać ponownie. Porównuj fazy jednego przebiegu ze sobą; sama liczba nic nie znaczy. Faza wykonywana na końcu przenosi niewiele, choć by zużyła dużo, dlatego czytaj tę kolumnę razem z tokenami obok.",
|
|
1421
|
+
"unattributed": "Bez przypisania",
|
|
1422
|
+
"unattributedHint": "zarejestrowane przez kanał, który nie podaje fazy"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} wywołanie | {count} wywołania | {count} wywołań",
|
|
1413
1426
|
"inputCompletionTokens": "Łączna liczba tokenów wejścia / wyjscia. Wejście liczy takze tokeny z pamięci podręcznej: nadal zajmują okno kontekstu, dokładnie tak, jak liczy je wskaźnik kontekstu w Claude Code.",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} önbelleğe yazıldı",
|
|
1409
1409
|
"cacheWriteHint": "Sağlayıcının önbelleğine yazılan giriş token'ları (yeni girişin 1,25 ila 2 katı fiyat)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "Token'lar nereye gitti",
|
|
1413
|
+
"subtitle": "çalıştırmanın aşamasına göre, taşıma maliyetine göre sıralanır",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "Aşama",
|
|
1416
|
+
"turns": "Tur",
|
|
1417
|
+
"tokensInOut": "Token (giriş / çıkış)",
|
|
1418
|
+
"carryCost": "Taşıma maliyeti"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "Her aşamanın kendisinden sonraki turlara ne kadar yük bindirdiği: bağlamı, onu yeniden göndermek zorunda kalan her sonraki tur için bir kez sayılır. Bir çalıştırmanın aşamalarını birbiriyle karşılaştırın; tek başına bu sayı bir şey ifade etmez. En sonda çalışan bir aşama ne kadar harcarsa harcasın az taşır; bu sütunu yanındaki token sayılarıyla birlikte okuyun.",
|
|
1421
|
+
"unattributed": "Atanmamış",
|
|
1422
|
+
"unattributedHint": "aşama bildirmeyen bir kanal tarafından kaydedildi"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} çağrı | {count} çağrı",
|
|
1413
1426
|
"inputCompletionTokens": "Toplam giriş / çıkış token'i. Giriş, önbelleğe alınmış token'ları da sayar: bunlar hâlâ bağlam penceresini kaplar, tıpkı Claude Code'un kendi bağlam göstergesinin saydığı gibi.",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -1408,6 +1408,19 @@
|
|
|
1408
1408
|
"cacheWrite": "{tokens} записано в кеш",
|
|
1409
1409
|
"cacheWriteHint": "Вхідні токени, записані в кеш провайдера (від 1,25 до 2 разів ціна нових вхідних токенів)"
|
|
1410
1410
|
},
|
|
1411
|
+
"phase": {
|
|
1412
|
+
"title": "Куди пішли токени",
|
|
1413
|
+
"subtitle": "за фазою запуску, упорядковано за вартістю перенесення",
|
|
1414
|
+
"columns": {
|
|
1415
|
+
"phase": "Фаза",
|
|
1416
|
+
"turns": "Ходи",
|
|
1417
|
+
"tokensInOut": "Токени (вхід / вихід)",
|
|
1418
|
+
"carryCost": "Вартість перенесення"
|
|
1419
|
+
},
|
|
1420
|
+
"carryCostHint": "Наскільки кожна фаза обтяжила наступні ходи: її контекст пораховано один раз за кожен пізніший хід, який мусив надіслати його знову. Порівнюйте фази одного запуску між собою; сама по собі ця величина нічого не означає. Фаза, що виконується останньою, переносить мало, скільки б не витратила, тож читайте цей стовпець разом із токенами поруч.",
|
|
1421
|
+
"unattributed": "Без атрибуції",
|
|
1422
|
+
"unattributedHint": "записано каналом, який не повідомляє фазу"
|
|
1423
|
+
},
|
|
1411
1424
|
"metricsBar": {
|
|
1412
1425
|
"calls": "{count} виклик | {count} виклики | {count} викликів",
|
|
1413
1426
|
"inputCompletionTokens": "Загальна кількість токенів входу / виходу. Вхід враховує й токени з кешу: вони так само займають вікно контексту, точно як їх рахує індикатор контексту в Claude Code.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.179.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"valibot": "^1.4.2",
|
|
41
41
|
"vue": "3.5.40",
|
|
42
42
|
"wretch": "^3.0.9",
|
|
43
|
-
"@cat-factory/contracts": "0.
|
|
43
|
+
"@cat-factory/contracts": "0.189.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|