feat(v2): 完善配置中心与可替换轨迹质检并联动预审依据
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""Scoped configuration and read-only trajectory quality API (BR72–78)."""
|
||||
from typing import Literal
|
||||
from fastapi import APIRouter,Depends,Request,HTTPException
|
||||
from pydantic import BaseModel,ConfigDict,Field
|
||||
from api.risk_agent_access import authorize
|
||||
from api.project_assistant import enabled
|
||||
from api.lease_library import service,guard,project,card,translate as library_translate
|
||||
from fastapi.responses import JSONResponse
|
||||
from services import assistant_store as store,catalog_configuration as catalog,trajectory_quality as quality
|
||||
router=APIRouter(prefix='/api/xingbang/risk-agent/project-assistant/library',dependencies=[Depends(authorize),Depends(enabled)])
|
||||
def translate(call):
|
||||
def checked():
|
||||
try:return call()
|
||||
except ValueError as exc:
|
||||
text=str(exc)
|
||||
if 'COMPONENT_HIDDEN' in text:return JSONResponse(status_code=409,content={'error':{'code':'COMPONENT_HIDDEN','message':'评审方案存在隐藏组件,请更新配置后重试'}})
|
||||
return JSONResponse(status_code=422,content={'error':{'code':'CONFIGURATION_INVALID','message':'配置不合法,请检查必填项与受信模型地址'}})
|
||||
return library_translate(checked)
|
||||
|
||||
class Input(BaseModel):model_config=ConfigDict(extra='forbid')
|
||||
class Visibility(Input):hidden:bool
|
||||
class CardInput(Input):
|
||||
project_id:str=Field(min_length=1,max_length=180)
|
||||
kind:Literal['data_card','template']
|
||||
name:str=Field(min_length=1,max_length=120)
|
||||
content:dict
|
||||
class Query(Input):
|
||||
project_id:str=Field(min_length=1,max_length=180)
|
||||
message:str=Field(min_length=1,max_length=1000)
|
||||
class Settings(Input):
|
||||
provider:Literal['disabled','local','jev']
|
||||
endpoint:str=Field(default='',max_length=500)
|
||||
model:str=Field(default='',max_length=160)
|
||||
api_key:str|None=Field(default=None,max_length=4000)
|
||||
allow_external:bool=False
|
||||
class RunInput(Input):run_id:str=Field(min_length=1,max_length=180)
|
||||
@router.patch('/cards/{card_id}/visibility')
|
||||
def visibility(card_id:str,payload:Visibility,request:Request):
|
||||
actor=guard(request,True)
|
||||
def action():
|
||||
with service().connect() as db:return catalog.set_visibility(db,card(db,request,card_id),actor,payload.hidden)
|
||||
return translate(action)
|
||||
@router.post('/configuration/cards',status_code=201)
|
||||
def create_card(payload:CardInput,request:Request):
|
||||
actor=guard(request,True)
|
||||
def action():
|
||||
with service().connect() as db:return catalog.create(db,project(db,request,payload.project_id),actor,payload.kind,payload.name,payload.content)
|
||||
return translate(action)
|
||||
@router.post('/inventory')
|
||||
def inventory(payload:Query,request:Request):
|
||||
guard(request)
|
||||
def action():
|
||||
with service().connect() as db:
|
||||
context=project(db,request,payload.project_id)
|
||||
return catalog.answer(db,context['id'],context['data_domain'],request.state.identity['role'],payload.message)
|
||||
return translate(action)
|
||||
@router.get('/quality/settings')
|
||||
def settings(request:Request):
|
||||
guard(request,True);return quality.public()
|
||||
@router.put('/quality/settings')
|
||||
def save_settings(payload:Settings,request:Request):
|
||||
actor=guard(request,True)
|
||||
def action():
|
||||
result=quality.save(**payload.model_dump())
|
||||
with service().connect() as db:store.event(db,'','quality.settings_updated',actor,{'provider':result['provider'],'revision':result['revision']})
|
||||
return result
|
||||
return translate(action)
|
||||
@router.post('/quality/probe')
|
||||
def probe(request:Request):
|
||||
guard(request,True);return quality.probe()
|
||||
@router.get('/projects/{project_id}/quality')
|
||||
def history(project_id:str,request:Request):
|
||||
guard(request)
|
||||
def action():
|
||||
with service().connect() as db:
|
||||
project(db,request,project_id)
|
||||
return {'items':store.listing(db,'quality_review',project_id)}
|
||||
return translate(action)
|
||||
@router.post('/projects/{project_id}/quality',status_code=201)
|
||||
def inspect(project_id:str,payload:RunInput,request:Request):
|
||||
actor=guard(request)
|
||||
def action():
|
||||
svc=service()
|
||||
with svc.connect() as db:
|
||||
project(db,request,project_id);run=store.get(db,'run',payload.run_id)
|
||||
if run['project_id']!=project_id:raise KeyError('运行不可访问')
|
||||
result=quality.inspect(run)
|
||||
with svc.connect() as db:
|
||||
store.put(db,'quality_review',result,immutable=True)
|
||||
store.event(db,project_id,'trajectory.quality',actor,{'review_id':result['id'],'run_id':payload.run_id,'status':result['judgment']['status']})
|
||||
return result
|
||||
return translate(action)
|
||||
|
||||
@router.get('/configuration/sources')
|
||||
def sources(project_id:str,request:Request):
|
||||
guard(request,True)
|
||||
def action():
|
||||
from services import component_registry as registry
|
||||
with service().connect() as db:
|
||||
context=project(db,request,project_id)
|
||||
rows=[registry.resolve(db,p['id']) for p in store.listing(db,'release')]
|
||||
return {'items':[{'id':r['id'],'name':r['name'],'type':r['type']} for r in rows if r.get('kind')=='data_source' and r.get('scope')==context['data_domain'] and r.get('project_id') in (None,'',project_id) and not catalog.hidden(db,r['id'])]}
|
||||
return translate(action)
|
||||
|
||||
@router.get('/projects/{project_id}/runs/{run_id}/influences')
|
||||
def influences(project_id:str,run_id:str,request:Request):
|
||||
guard(request)
|
||||
def action():
|
||||
from services.prescreen_explanation import explain
|
||||
with service().connect() as db:
|
||||
project(db,request,project_id);run=store.get(db,'run',run_id)
|
||||
if run.get('project_id')!=project_id:raise KeyError('运行不可访问')
|
||||
view=store.get(db,'prescreen_run',run_id)
|
||||
return explain(run,view)
|
||||
return translate(action)
|
||||
Reference in New Issue
Block a user