feat(v2): 实现SQLite对公租赁经验加工与卡片预审轨迹闭环
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""Observable trajectory details and immutable run comparisons (AC-77)."""
|
||||
from services import assistant_store as store
|
||||
|
||||
|
||||
def step_details(run):
|
||||
components=run.get('components') or {}
|
||||
methods={c['id']:c for c in components.get('skills',[])+components.get('rules',[])}
|
||||
evidence={e['id']:e for e in run.get('evidence',[]) if e.get('id')}
|
||||
details={}
|
||||
for raw in run.get('steps',[]):
|
||||
key=raw['step_id']; component=methods.get(key,{})
|
||||
checks=[{k:c.get(k) for k in ('code','title','result','fields','evidence_ids','suggested_action','basis_ids')}
|
||||
for c in raw.get('findings',[])]
|
||||
ids={e for c in checks for e in c.get('evidence_ids') or []}
|
||||
details[key]={'name':raw.get('name',key), 'action':component.get('instructions') or component.get('description') or raw.get('name',key),
|
||||
'declared_inputs':component.get('inputs',[]), 'conditions':component.get('conditions',[]),
|
||||
'exceptions':component.get('exception_note') or component.get('exceptions',[]), 'checks':checks,
|
||||
'evidence':[{k:evidence[e].get(k) for k in ('id','field','value','source_id','locator','document_id')} for e in ids if e in evidence],
|
||||
'missing_inputs':raw.get('missing_inputs',[])}
|
||||
return details
|
||||
|
||||
|
||||
def compare(db,project_id,left_id,right_id):
|
||||
left=store.get(db,'run',left_id);right=store.get(db,'run',right_id)
|
||||
if left.get('project_id')!=project_id or right.get('project_id')!=project_id:
|
||||
raise KeyError('运行不存在或不属于该项目')
|
||||
def summary(run):
|
||||
snapshot=store.get(db,'input_snapshot',run['input_snapshot_id'])
|
||||
components=run.get('components') or {}
|
||||
return {'id':run['id'],'status':run['status'],'created_at':run['created_at'],
|
||||
'snapshot_id':run['input_snapshot_id'],'instruction':run.get('requirement',''),
|
||||
'documents':[{k:d.get(k) for k in ('id','name','version','content_hash')} for d in snapshot.get('documents',[])],
|
||||
'plan':{k:(components.get('plan') or {}).get(k) for k in ('id','version')},
|
||||
'components':[{'id':c['id'],'version':c.get('version'),'content_hash':c.get('content_hash')} for c in components.get('skills',[])+components.get('rules',[])],
|
||||
'model':{k:(run.get('model_config') or {}).get(k) for k in ('available','name')},
|
||||
'checks':[{k:c.get(k) for k in ('code','title','result','suggested_action','evidence_ids')} for c in run.get('checks',[])]}
|
||||
a,b=summary(left),summary(right)
|
||||
old={c['code']:c for c in a['checks']};new={c['code']:c for c in b['checks']}
|
||||
open_results=('issue','unknown','error')
|
||||
delta={'resolved':[new[k] for k in new if k in old and old[k]['result'] in open_results and new[k]['result']=='pass'],
|
||||
'new':[new[k] for k in new if k not in old and new[k]['result'] in open_results],
|
||||
'remaining':[new[k] for k in new if k in old and new[k]['result'] in open_results],
|
||||
'unexecuted':[old[k] for k in old if k not in new]}
|
||||
config_changed=a['plan']!=b['plan'] or a['components']!=b['components'] or a['model']!=b['model'] or a['instruction']!=b['instruction']
|
||||
return {'left':a,'right':b,'delta':delta,'configuration_changed':config_changed,'read_only':True,
|
||||
'note':'两次配置、模型或指令有变化,差异不能全部归因于补件。' if config_changed else '仅展示两次冻结结果差异;相关变化不能单独证明补件因果效应。'}
|
||||
Reference in New Issue
Block a user