115 lines
7.9 KiB
Python
115 lines
7.9 KiB
Python
"""Synthetic, disposable UI fixture; never uses model or database providers."""
|
||
import json
|
||
import os
|
||
import tempfile
|
||
from pathlib import Path
|
||
|
||
import uvicorn
|
||
from viano_bug_analysis.api import APIConfig, Client, Project
|
||
from viano_bug_analysis.api.app import create_app
|
||
from viano_bug_analysis.lifecycle import Workspace, CheckpointStore
|
||
from viano_bug_analysis.lifecycle.job import timestamp
|
||
from viano_bug_analysis.lifecycle.storage import atomic_json, json_bytes
|
||
from viano_bug_analysis.package.factory import PackageFactory
|
||
from viano_bug_analysis.case import CaseBuilder
|
||
import viano_bug_analysis.api.dashboard as dashboard
|
||
import viano_bug_analysis.api.operations as operations
|
||
|
||
TOKEN = 'ui_fixture_' + 'x' * 40
|
||
|
||
|
||
def seed(root, case_id, title, completed=False):
|
||
workspace = Workspace(root)
|
||
store = CheckpointStore(workspace)
|
||
now = timestamp()
|
||
manifest = {'format':'viano-bug-case-package','format_version':1,'case_id':case_id,
|
||
'package_id':'preview-'+case_id,'package_generation':0,'analysis_revision':0,
|
||
'project':{'id':'viano-core','type':'wow','profile':'vcore'},'created_at':now,'updated_at':now,
|
||
'state':'NEW','routing':{'destination':'inbox','reason':None,'retry_count':0,'retry_not_before':None},
|
||
'artifacts':{'case':None,'diagnosis':None,'change_request':None,'case_inputs':'inputs.json'},
|
||
'history':{'analysis_revisions':0,'reproductions':0,'evidence':0,'handoffs':0,'feedback':0,'events_ref':None},'integrity':{}}
|
||
contents={'inputs.json':json_bytes({'format':'viano-case-inputs','format_version':1,'reports':['report.json']}),
|
||
'report.json':json_bytes({'format':'viano-bug-report','format_version':1,'title':title,
|
||
'observations':[{'text':'Synthetic UI fixture observation'}]})}
|
||
path=workspace.inbox/(case_id+'.zip');PackageFactory().create(path,manifest,contents)
|
||
store.intake(path);path.unlink()
|
||
identity=case_id,manifest['package_id'];CaseBuilder(store).build(*identity)
|
||
for state,pointer in [('INTERPRETING_CASE','case_interpretation'),('GENERATING_HYPOTHESES','hypotheses')]:
|
||
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')
|
||
hypothesis={'id':'H1','proposal':{'title':'Synthetische Arbeitshypothese','mechanism':'Die erwartete Aktion bleibt aus.'},
|
||
'confidence':'supported','disposition':'open','iterations':2,'evidence_refs':['E000001'],'working_brief_ref':'brief.json'}
|
||
store.advance(*identity,'INVESTIGATING_HYPOTHESES',step_id='fixture-state',
|
||
artifacts={'investigation_state':'state.json'},details={'operation':'investigation-state','state_ref':'state.json'},
|
||
additions={'state.json':json_bytes({'analysis_revision':1,'hypotheses':{'H1':hypothesis}}),
|
||
'brief.json':json_bytes({'analysis_revision':1,'hypothesis_id':'H1','brief':{'findings':[
|
||
{'direction':'support','text':'Die Beobachtung passt zur Hypothese.','evidence_refs':['E000001']},
|
||
{'direction':'against','text':'Der vorhandene Handler spricht dagegen.','evidence_refs':['E000002']}],
|
||
'uncertainties':['Die Laufzeitkonfiguration ist noch ungeklärt.']}})})
|
||
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':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':prompt})})
|
||
if completed:
|
||
store.advance(*identity,'BUILDING_DIAGNOSIS',step_id='diagnosis')
|
||
documents={
|
||
'diagnosis.json':{'analysis_revision':1,'diagnosis':{'status':'confirmed','root_cause':'Synthetische Diagnose für die Oberflächenprüfung','confidence':'strongly_supported'}},
|
||
'coding.json':{'analysis_revision':1,'assignment':'assigned','human_dev_assigned':True,
|
||
'request':{'required_change':'SQL-Vorlage mit Platzhaltern erstellen.','placeholders':['Passende Koordinaten ergänzen.']}},
|
||
'decision.json':{'human_dev':{'status':'assigned','summary':'Passende Spawndaten ermitteln und prüfen.'}},
|
||
}
|
||
store.advance(*identity,'BUILDING_DIAGNOSIS',step_id='diagnosis-document',
|
||
details={'operation':'diagnosis-complete','diagnosis_ref':'diagnosis.json'},
|
||
artifacts={'diagnosis':'diagnosis.json'},additions={ref:json_bytes(doc) for ref,doc in documents.items()})
|
||
store.advance(*identity,'BUILDING_DIAGNOSIS',step_id='handoff-document',
|
||
details={'operation':'change-request-complete','change_request_ref':'coding.json','decision_ref':'decision.json'},
|
||
artifacts={'change_request':'coding.json'})
|
||
store.advance(*identity,'OUTPUT_READY',step_id='done')
|
||
return identity
|
||
|
||
|
||
if __name__ == '__main__':
|
||
with tempfile.TemporaryDirectory(prefix='vba-dashboard-', dir=os.environ.get('VBA_UI_TEST_WORKSPACE')) as temporary:
|
||
root=Path(temporary)
|
||
identity=seed(root,'BUG-000020','Wolpertinger fehlen in Dun Morogh')
|
||
seed(root,'BUG-000017','Leuchtbrille – Kontrolllauf',completed=True)
|
||
# Display-only test request fixture; real feedback/budget binding is tested
|
||
# independently through the actual coordinator in test_human_web.py.
|
||
store=CheckpointStore(Workspace(root));ref='ui-test-request.json'
|
||
request={'case_id':identity[0],'package_id':identity[1],'analysis_revision':1,'request_id':'ui-test',
|
||
'executor':'human','hypothesis_id':'H1','question':'Ist die Kreatur nach Questannahme sichtbar?',
|
||
'environment':'Lokaler Testserver','prerequisites':['Quest noch nicht angenommen'],
|
||
'steps':['Quest annehmen','Umgebung beobachten'],'requested_observations':['Sichtbarkeit und Build notieren'],
|
||
'deadline':'2099-01-01T00:00:00Z'}
|
||
store.advance(*identity,'BLOCKED',step_id='ui-test-wait',reason='AWAITING_TEST_FEEDBACK',
|
||
retry_not_before=request['deadline'],artifacts={'async_test_request':ref},
|
||
details={'operation':'async-test-wait','request_ref':ref},additions={ref:json_bytes(request)})
|
||
config=APIConfig(root,(Project('viano-core','wow','vcore'),),
|
||
(Client('preview','VBA_UI_TEST_TOKEN',('viano-core',),operator=True),))
|
||
from viano_bug_analysis.api.users import Users
|
||
Users(root/'auth/users.sqlite3').manage('create_user','preview','ui-fixture-password')
|
||
os.environ['VBA_UI_TEST_TOKEN']=TOKEN
|
||
dashboard.read_status=lambda _: {'observer':'LIVE','service_id':'fixture-worker'}
|
||
operations.read_status=dashboard.read_status
|
||
folder=root/'runtime/worker';folder.mkdir(exist_ok=True)
|
||
atomic_json(folder/'status.json',{'status':'ANALYZING','service_id':'fixture-worker','updated_at':timestamp(),
|
||
'case':{'case_id':identity[0],'package_id':identity[1]}})
|
||
uvicorn.run(create_app(config),host='127.0.0.1',port=int(os.environ.get('VBA_UI_TEST_PORT','8765')),access_log=False)
|