Show all analysis phases in token chart legend and preview
This commit is contained in:
1 parent
58fc00601e
commit
cbb69a2115
6 files changed
+70
-7
No files matched your search
@@ -0,0 +1,19 @@
|
||||
# Kreisdiagramm über alle Phasen – 2026-10-21
|
||||
|
||||
Ausgangspunkt `58fc006`. Die tatsächliche Phasenzuordnung unterstützte bereits den
|
||||
gesamten Ablauf, aber die erste Browservorschau enthielt ausschließlich synthetische
|
||||
Untersuchungs-/Korrekturaufrufe. Die Vorschau wurde deshalb um Fallaufbau, Planung,
|
||||
Hypothesen, Diagnose und Codingauftrag ergänzt. Die Legende zeigt diese Phasen nun
|
||||
auch dann an, wenn noch kein Verbrauch gemessen wurde; Nullwerte erhalten keinen
|
||||
erfundenen Flächenanteil.
|
||||
|
||||
Der neue Regressionstest prüft die Zuordnung aller sechs LLM-Phasenkategorien sowie
|
||||
die zusätzliche, disjunkte Korrekturkategorie. Ein gemeinsamer Planungsaufruf wird
|
||||
weiterhin nicht doppelt gezählt. Die Browserprüfung verlangt jede Phasenkategorie
|
||||
in der Legende und prüft weiterhin Projektfilter, Mobilansicht und Abmeldung.
|
||||
|
||||
Die neue Vorschau enthält 35.300 synthetische Tokens und 22,1 % Korrekturanteil. Ein
|
||||
abgeschlossener Testfall steuert auch Diagnose und Codingauftrag bei; der noch
|
||||
laufende Testfall enthält keine erfundenen künftigen Abschlussaufrufe. Dies sind
|
||||
ausschließlich Testdaten, keine tatsächlichen Providerkosten. Der vorherige
|
||||
Prüfbericht bleibt als eigener Snapshot erhalten.
|
||||
@@ -38,6 +38,9 @@ Telemetrie-Heartbeat wird nicht als aktiver Worker dargestellt.
|
||||
|
||||
- Kreisdiagramm: bekannte Eingabe- und Ausgabetokens je Phase; zusätzliche
|
||||
Korrektur-/Reparatur-/Wiederholungsaufrufe haben ein eigenes orangefarbenes Segment.
|
||||
Die Legende zeigt Fallaufbau, Fallplanung, Hypothesen, Untersuchung, Diagnose und
|
||||
Codingauftrag auch bei null gemessenen Tokens. Nullwerte erzeugen keinen
|
||||
künstlichen Tortenanteil. Gemeinsame Planungsaufrufe bleiben einmal gezählt.
|
||||
- Balkendiagramm: Eingabe und Ausgabe einschließlich Denken je Kategorie.
|
||||
- Der erste reguläre Aufruf bleibt bei seiner Phase, auch wenn sein Ergebnis später
|
||||
zurückgewiesen wurde. Seine Korrektur wird ausschließlich dem Korrektursegment
|
||||
|
||||
@@ -57,7 +57,8 @@ function renderPie(buckets) {
|
||||
const legend=$('legend'); legend.replaceChildren(); let angle=-Math.PI/2;
|
||||
if(!total) {ctx.beginPath();ctx.arc(180,180,140,0,Math.PI*2);ctx.fillStyle='#edf0f7';ctx.fill();}
|
||||
buckets.forEach((b,i)=>{
|
||||
const count=b.input_tokens+b.output_tokens; if(!count&&!b.calls)return;
|
||||
const count=b.input_tokens+b.output_tokens;
|
||||
if(b.id==='unknown'&&!count&&!b.calls)return;
|
||||
const color=colors[i%colors.length]; if(total&&count) {const end=angle+count/total*Math.PI*2;
|
||||
ctx.beginPath();ctx.moveTo(180,180);ctx.arc(180,180,140,angle,end);ctx.closePath();ctx.fillStyle=color;ctx.fill();angle=end;}
|
||||
const item=document.createElement('li'), swatch=text('span','','swatch');swatch.style.background=color;
|
||||
|
||||
@@ -30,15 +30,19 @@ try {
|
||||
await send('Page.navigate',{url:`http://127.0.0.1:${port}/`});
|
||||
await until(()=>evaluate("document.readyState === 'complete' && typeof render === 'function'"));
|
||||
await evaluate("document.getElementById('token').value='ui_fixture_"+'x'.repeat(40)+"';document.getElementById('login-form').dispatchEvent(new Event('submit',{bubbles:true,cancelable:true}));");
|
||||
await until(()=>evaluate("document.getElementById('token-count').textContent==='37.800'"));
|
||||
await until(()=>evaluate("document.getElementById('token-count').textContent==='35.300'"));
|
||||
const state=await evaluate("({workers:document.querySelectorAll('.worker').length,cases:document.querySelectorAll('#case-rows tr').length,active:document.getElementById('active-count').textContent,correction:document.getElementById('correction-count').textContent,overflow:document.documentElement.scrollWidth>innerWidth,stored:localStorage.length,progress:document.querySelector('.worker [role=progressbar]').getAttribute('aria-valuenow')})");
|
||||
if(state.workers!==1||state.cases!==1||state.active!=='1'||state.stored!==0||state.overflow||state.progress!=='57')throw new Error(JSON.stringify(state));
|
||||
const categories=await evaluate("Array.from(document.querySelectorAll('#legend li')).map(e=>e.textContent)");
|
||||
for(const name of ['Fallaufbau','Fallplanung','Hypothesen','Untersuchung','Diagnose','Codingauftrag','Korrekturen']) {
|
||||
if(!categories.some(c=>c.includes(name)))throw new Error(`Missing phase in pie legend: ${name}`);
|
||||
}
|
||||
await writeFile(path.join(output,'desktop.png'),Buffer.from((await send('Page.captureScreenshot',{format:'png'})).data,'base64'));
|
||||
await send('Emulation.setDeviceMetricsOverride',{width:390,height:1200,deviceScaleFactor:1,mobile:true});await pause(200);
|
||||
if(await evaluate('document.documentElement.scrollWidth>innerWidth'))throw new Error('Mobile horizontal overflow');
|
||||
await writeFile(path.join(output,'mobile.png'),Buffer.from((await send('Page.captureScreenshot',{format:'png'})).data,'base64'));
|
||||
await evaluate("document.getElementById('project-filter').value='viano-core';document.getElementById('project-filter').dispatchEvent(new Event('change')); ");
|
||||
if(await evaluate("document.getElementById('token-count').textContent")!=='37.800')throw new Error('Project aggregation differs');
|
||||
if(await evaluate("document.getElementById('token-count').textContent")!=='35.300')throw new Error('Project aggregation differs');
|
||||
await evaluate("document.getElementById('kind').value='case_return';document.getElementById('kind').dispatchEvent(new Event('change'));");
|
||||
if(!await evaluate("document.getElementById('new-files').hidden&&!document.getElementById('return-files').hidden"))throw new Error('Return form not selected');
|
||||
await evaluate("document.getElementById('logout').click()");
|
||||
|
||||
+25
-1
@@ -9,7 +9,7 @@ from starlette.testclient import TestClient
|
||||
|
||||
from viano_bug_analysis.api import APIConfig, Client, Project
|
||||
from viano_bug_analysis.api.app import create_app
|
||||
from viano_bug_analysis.api.dashboard import aggregate, case_view
|
||||
from viano_bug_analysis.api.dashboard import aggregate, case_view, phase
|
||||
from viano_bug_analysis.llm.accounting import classification
|
||||
from viano_bug_analysis.lifecycle.storage import atomic_json
|
||||
from test_case_builder import case_setup
|
||||
@@ -75,6 +75,30 @@ def test_unknown_usage_is_not_reported_as_complete():
|
||||
assert next(b for b in result['buckets'] if b['id']=='unknown')['output_tokens']==5
|
||||
|
||||
|
||||
def test_pie_accounts_for_whole_pipeline_and_separates_corrections():
|
||||
stages = [
|
||||
('BUILDING_CASE', 'entity_resolution_plan_v4', 'building'),
|
||||
('INTERPRETING_CASE', 'case_plan_v2', 'planning'),
|
||||
('GENERATING_HYPOTHESES', 'hypotheses_v4', 'hypotheses'),
|
||||
('INVESTIGATING_HYPOTHESES', 'investigation_lean_v21', 'investigation'),
|
||||
('BUILDING_DIAGNOSIS', 'diagnosis_v8', 'diagnosis'),
|
||||
('BUILDING_DIAGNOSIS', 'change_request_v9', 'coding'),
|
||||
]
|
||||
calls = []
|
||||
for state, prompt, expected in stages:
|
||||
assert phase({'phase': state}, prompt) == expected
|
||||
calls.append({'bucket': expected, 'category':'regular', 'phase':expected,
|
||||
'day':'2026-10-21', 'input_tokens':10, 'output_tokens':2,
|
||||
'reasoning_tokens':1, 'cached_input_tokens':0})
|
||||
calls.append(calls[3] | {'bucket':'correction','category':'correction'})
|
||||
buckets = {b['id']:b for b in aggregate([{'calls':calls}])['buckets']}
|
||||
assert all(buckets[expected]['calls']==1 for _, _, expected in stages)
|
||||
assert buckets['correction']['calls']==1
|
||||
assert sum(b['input_tokens']+b['output_tokens'] for b in buckets.values())==84
|
||||
empty = {b['id']:b for b in aggregate([])['buckets']}
|
||||
assert all(expected in empty for _, _, expected in stages)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def project_manifest(manifest):
|
||||
manifest['project']={'id':'p','type':'wow','profile':'vcore'}
|
||||
|
||||
@@ -37,14 +37,26 @@ def seed(root, case_id, title, completed=False):
|
||||
ref=pointer+'.json';store.advance(*identity,state,step_id=state,
|
||||
artifacts={pointer:ref},additions={ref:json_bytes({'analysis_revision':1})})
|
||||
store.advance(*identity,'INVESTIGATING_HYPOTHESES',step_id='investigate')
|
||||
for i,(category,inp,out) in enumerate([('regular',12000,3000),('correction',3000,900)]):
|
||||
measured = [
|
||||
('BUILDING_CASE','entity_resolution_plan_v4','regular',2500,500),
|
||||
('INTERPRETING_CASE','case_interpretation_v1','regular',2000,500),
|
||||
('GENERATING_HYPOTHESES','hypotheses_v4','regular',1500,500),
|
||||
('INVESTIGATING_HYPOTHESES','investigation_lean_v21','regular',4000,1000),
|
||||
]
|
||||
if completed:
|
||||
measured += [
|
||||
('BUILDING_DIAGNOSIS','diagnosis_v8','regular',1500,500),
|
||||
('BUILDING_DIAGNOSIS','change_request_v9','regular',500,0),
|
||||
]
|
||||
measured.append(('INVESTIGATING_HYPOTHESES','investigation_format_repair_v1','correction',3000,900))
|
||||
for i,(phase,prompt,category,inp,out) in enumerate(measured):
|
||||
ref=f'call{i}.json';request=f'request{i}.json'
|
||||
call={'rounds':1,'phase':'INVESTIGATING_HYPOTHESES','request_ref':request,'outcome':{
|
||||
call={'rounds':1,'phase':phase,'request_ref':request,'outcome':{
|
||||
'usage_classification':category,'metadata':{'input_tokens':inp,'output_tokens':out,
|
||||
'reasoning_tokens':out//2,'cached_input_tokens':1000}}}
|
||||
store.advance(*identity,'INVESTIGATING_HYPOTHESES',step_id=f'call{i}',
|
||||
details={'operation':'llm-round','result_ref':ref},
|
||||
additions={ref:json_bytes(call),request:json_bytes({'prompt_version':'investigation_lean_v21'})})
|
||||
additions={ref:json_bytes(call),request:json_bytes({'prompt_version':prompt})})
|
||||
if completed:
|
||||
store.advance(*identity,'BUILDING_DIAGNOSIS',step_id='diagnosis')
|
||||
store.advance(*identity,'OUTPUT_READY',step_id='done')
|
||||
|
||||
Reference in new issue
Block a user