116 lines
5.6 KiB
Python
116 lines
5.6 KiB
Python
"""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)
|