mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 10:21:59 +00:00
1a6082f033
为什么:几千张表的库以前用不了——探查每个源最多取 200 个对象、按数据库列出的顺序截断,同步请求、整份替换、不排队; 落在窗口外的表在目录、look、剖析里被说成「表已不在」。 做了什么: - db/models_sources.py:source_objects(对象索引一行一个对象:范围、原因、结构指纹与 struct_version、first_seen / last_seen / removed_at、reflected_at、按需加入的留痕)、introspect_jobs / introspect_tasks(后台探查任务与逐表状态)。 - data/source_objects.py:每种方言一条字典查询建对象索引(失败退回 Inspector,上限 5 万,截断时不判删除);探查范围规则 (options.introspect_scope:通配、按 name_key、排除优先、显式列出 / 移出;有目录的表、已发布模板写死 SQL 用到的表 pinned; 命中超上限拒绝并说出命中数);唯一的 rebuild_schema_cache 从范围内已反射的行物化 schema_cache(形状不变,另加 scope 摘要); 三种「不在」分开说(removed / missing / out_of_scope / reflect_failed);按需加入(当场只读反射、落盘、留痕、进程内按源加锁)。 - data/introspect_jobs.py:探查改成后台任务(core/job_runtime,lane kind introspect,和同源起草、剖析串行;按批反射、逐表记状态、 可停止、读完的照样物化;重启标中断不续跑)。 - api/datasources.py:POST /introspect 返回 202 和任务,dry_run 同步只刷新索引、返回命中数;/introspect-jobs 读任务、停止; /introspect-scope 读、预览、保存规则;/schema?table= 带剖析事实、说清三种「不在」;DataSourceOut 加 schema_scope、introspect_job。 - schema_view:search_tables(BM25 倒排,遮罩列只用列名,范围外只按名字和注释、权重低一档)、profile_lines(剖析事实一节, 附「只供写查询参考」,遮罩按渲染那一刻重滤,上传表格快照不一致时不给)、absent_detail。 - look:候选超过 PREFILTER_LIMIT 时只给预筛结果 + 已用到的表 + 一跳关联;点名或挑中范围外的表按需加入(每次查看最多 4 张), look 记录加 added_to_scope、prefiltered;表详情带剖析事实。 - tools/datasource:db_schema__ 加 keyword、不传 table 时只列前 50 张;点名范围外的表按需加入(每次运行最多 8 张);查询结果 照实列「未按目录检查的表」(unchecked_tables,不新增检查规则);建工具时读剖析事实。 - 剖析:_relation_ends 分清「范围外,可以加入」和「表结构里没有」;任务记上传表格的 snapshot_id;last_facts。表助手、/schema 用同一段剖析事实。 - 目录接口:清单只列范围内,absent_reason 分开说;GET /catalog/search 查对象索引,POST /catalog/scope/add 加入范围。 - 第 1 波留下的一处:check_valid_filter 和 stored_valid_filter 拼有效记录条件时按 quote_name 强制加引号。 - 工具参数纠错:空着的可选参数不止一个时,只认拼写明显接近的那一个(db_schema 多了 keyword 以后 tabel 仍能纠成 table)。 - 测试:3,000 张表的合成库(对象索引、缺省范围超上限拒绝、规则圈 400 张后台读完、停止、重启标中断、和剖析排队、同步起草 409)、删除与结构版本、Inspector 回退、截断不判删除、范围规则、pinned、方言金样;改了同步探查的老测试改为等任务做完。
109 lines
6.8 KiB
Python
109 lines
6.8 KiB
Python
"""带机器码的 HTTP 错误。
|
||
|
||
detail 仍是给人看的那一句,老前端照旧直接显示;code 是给程序认的稳定标识。
|
||
以前前端只能按 detail 的开头几个字认出某一类错误,文案一改就悄悄失灵。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from fastapi import HTTPException, Request
|
||
from fastapi.responses import JSONResponse
|
||
|
||
#: 限定的数据源一个都不在了(删了或停用了),见 copilot._sources
|
||
DATASOURCE_SCOPE_EMPTY = "datasource_scope_empty"
|
||
|
||
#: 保存数据源时剖析设置不合规(422):field 是出错的那一项(table_max_queries……),说不清是哪一项时没有 field
|
||
PROFILE_SETTINGS_INVALID = "profile_settings_invalid"
|
||
#: 剖析任务开始不了(409,api/catalog.create_profile_job):未开启、数据源停用、没有表结构、快照被改动、这个源已有排队或
|
||
#: 进行中的剖析任务(导入目录、同步起草遇到剖析任务也是它)、按范围挑出 0 张表
|
||
PROFILE_DISABLED = "profile_disabled"
|
||
DATASOURCE_INACTIVE = "datasource_inactive"
|
||
SCHEMA_MISSING = "schema_missing"
|
||
PROFILE_BUSY = "profile_busy"
|
||
SNAPSHOT_TAMPERED = "snapshot_tampered"
|
||
PROFILE_NOTHING = "profile_nothing"
|
||
#: 要停止的剖析任务已经结束了(409)
|
||
CATALOG_PROFILE_FINISHED = "catalog_profile_finished"
|
||
#: 数据目录的后台起草任务(api/catalog.py 的 /catalog/draft-jobs):同一个源已有排队或进行中的任务(409,界面改去
|
||
#: GET /draft-jobs/active 显示那一个)、起草模型用不了(409,不建任务)、要停止的任务已经结束了(409)
|
||
CATALOG_DRAFT_BUSY = "catalog_draft_busy"
|
||
CATALOG_DRAFT_MODEL_UNAVAILABLE = "catalog_draft_model_unavailable"
|
||
CATALOG_DRAFT_FINISHED = "catalog_draft_finished"
|
||
#: 后台探查(api/datasources.py 的 /introspect,next-round 03 A1):同一个源已有排队或进行中的探查任务(409,界面改去
|
||
#: GET /introspect-jobs/active 显示那一个);探查范围的规则命中超过上限(409,另带 hits、cap、pinned,不建任务、不截断);
|
||
#: 要停止的探查任务已经结束了(409);对象清单都拿不到(400,连不上、schema 不对:失败照实记进了表结构缓存)
|
||
INTROSPECT_BUSY = "introspect_busy"
|
||
SCOPE_TOO_LARGE = "scope_too_large"
|
||
INTROSPECT_FINISHED = "introspect_finished"
|
||
INTROSPECT_FAILED = "introspect_failed"
|
||
#: 探查范围的规则不合规(422)
|
||
SCOPE_INVALID = "scope_invalid"
|
||
|
||
# ---------------------------------------------------------------- 集成接口(04 细案 A7)
|
||
# 外部系统用访问密钥调的应用、待办、工件接口,以及角色、运行读权限的拒绝:按 code 认是哪种错,detail 照旧是给人看的那句
|
||
|
||
#: 身份:没登录(401,中间件、auth.require 都回它)、角色不够(403,路由级依赖、接口内部的 ensure_role、开登录时的 /docs)
|
||
AUTH_REQUIRED = "auth_required"
|
||
ROLE_REQUIRED = "role_required"
|
||
#: 运行:这次运行读不了(403,api/deps 的证据、运行接口);别人的个人应用刷新、个人蓝图试运行只给拥有者(403)
|
||
RUN_FORBIDDEN = "run_forbidden"
|
||
RUN_PRIVATE = "run_private"
|
||
#: 问数据和助手:使用者没开问数据,或者这个接口不在问数据的范围里(403)
|
||
CHAT_FORBIDDEN = "chat_forbidden"
|
||
|
||
#: 应用(api/apps.py)。app_not_found 同入口发起时的 apps/entries.APP_GONE;没有任何授权的人也回它(不该知道应用在不在)
|
||
APP_NOT_FOUND = "app_not_found"
|
||
APP_VERSION_NOT_FOUND = "app_version_not_found"
|
||
APP_INSTANCE_NOT_FOUND = "app_instance_not_found"
|
||
#: 草稿里、当前发布版里都没有声明这个钩子(404);声明了、还没生成密钥(404)
|
||
APP_HOOK_NOT_DECLARED = "app_hook_not_declared"
|
||
APP_HOOK_NOT_FOUND = "app_hook_not_found"
|
||
#: 改草稿、授权时写进来的不合规(422):岗位绑定、个人应用的草稿(只许改呈现)、授权的人和组
|
||
APP_BINDINGS_INVALID = "app_bindings_invalid"
|
||
APP_DRAFT_INVALID = "app_draft_invalid"
|
||
APP_GRANTS_INVALID = "app_grants_invalid"
|
||
#: 页面参数不是 JSON 对象(422)
|
||
APP_PARAMS_INVALID = "app_params_invalid"
|
||
#: 入口表单、按需视图的参数没通过校验(422,另带 errors:[{path, code, message}])。同待办回复、入站钩子的表单错误
|
||
FORM_INVALID = "form_invalid"
|
||
#: 发起实例、刷新视图、补算时引擎不发起运行,又没有更细的码(engine/launch.LaunchRefused 没带 code 的那几种)
|
||
RUN_LAUNCH_REFUSED = "run_launch_refused"
|
||
# 派生应用草稿时工作流、版本不存在(404)的码在 apps/derive.py(WORKFLOW_GONE、VERSION_GONE);入口、视图、补算的码
|
||
# 在 apps/entries.py、apps/resolve.py、apps/backfill.py,同这里一样都是 app_ 开头
|
||
|
||
#: 待办(api/tasks.py)。服务层(process/tasks.py 的 TaskRefused)没带码的拒绝按状态补上同一类的码:404 不存在;403
|
||
#: 不能这样做(不是管理员也不是应用的管理者,不能改派、催办);409 待办已经处理过、关闭了、刚被别人回复,或者所属的
|
||
#: 运行不再等这条回复——刷新看最新状态;422 回复的内容不对。带了码的照用自己的(task_not_assigned、form_invalid……)
|
||
TASK_NOT_FOUND = "task_not_found"
|
||
TASK_FORBIDDEN = "task_forbidden"
|
||
TASK_CLOSED = "task_closed"
|
||
TASK_INVALID = "task_invalid"
|
||
|
||
#: 工件(api/artifacts.py):没有权限(403)、这类工件的原文只给搭建者(403)、不存在(404)、内容和哈希对不上(409)
|
||
ARTIFACT_FORBIDDEN = "artifact_forbidden"
|
||
ARTIFACT_KIND_FORBIDDEN = "artifact_kind_forbidden"
|
||
ARTIFACT_NOT_FOUND = "artifact_not_found"
|
||
ARTIFACT_TAMPERED = "artifact_tampered"
|
||
|
||
#: 设置(api/settings.py):对外访问地址由环境变量 AGENTLAB_PUBLIC_URL 指定时,设置里那一项不能改成别的值(409)
|
||
PUBLIC_URL_ENV_LOCKED = "public_url_env_locked"
|
||
|
||
|
||
class CodedHTTPException(HTTPException):
|
||
"""field:出错的是哪一项(表单据此把报错落到那一格)。可选,没有就不出现在返回里。extra:给程序读的几个数(探查范围
|
||
超上限时的命中数、上限),并进返回体;不能盖掉 detail、code、field。"""
|
||
|
||
def __init__(self, status_code: int, detail: str, code: str, *, field: str | None = None,
|
||
extra: dict[str, object] | None = None) -> None:
|
||
super().__init__(status_code, detail)
|
||
self.code = code
|
||
self.field = field
|
||
self.extra = {k: v for k, v in (extra or {}).items() if k not in ("detail", "code", "field")}
|
||
|
||
|
||
async def coded_handler(_: Request, exc: Exception) -> JSONResponse:
|
||
assert isinstance(exc, CodedHTTPException)
|
||
content: dict[str, object] = {**getattr(exc, "extra", {}), "detail": exc.detail, "code": exc.code}
|
||
if exc.field:
|
||
content["field"] = exc.field
|
||
return JSONResponse(status_code=exc.status_code, content=content, headers=exc.headers)
|