diff --git a/backend/app/api/access_keys.py b/backend/app/api/access_keys.py new file mode 100644 index 00000000..eea2b9a0 --- /dev/null +++ b/backend/app/api/access_keys.py @@ -0,0 +1,9 @@ +"""访问密钥与服务账号(docs/design/next-round/04-对外集成与触达.md A1–A7)。 + +main.py 挂登录即可(require_role("user")):本人的个人密钥本人管,服务账号和全部密钥在接口里要管理员。第 2 波 W2-d 填。 +""" +from __future__ import annotations + +from fastapi import APIRouter + +router = APIRouter(prefix="/api/access-keys", tags=["access-keys"]) diff --git a/backend/app/api/evals.py b/backend/app/api/evals.py new file mode 100644 index 00000000..21238230 --- /dev/null +++ b/backend/app/api/evals.py @@ -0,0 +1,9 @@ +"""评测接口(docs/design/next-round/01-效果度量与回归评测.md):题集、题目、期望答案试算、评测、结果、对比。 + +整组只给搭建者及以上(main.py 挂 require_role("builder"))。第 2 波 W2-c 起填。 +""" +from __future__ import annotations + +from fastapi import APIRouter + +router = APIRouter(prefix="/api/evals", tags=["evals"]) diff --git a/backend/app/api/ledger.py b/backend/app/api/ledger.py index 7bd78ea4..10bc2165 100644 --- a/backend/app/api/ledger.py +++ b/backend/app/api/ledger.py @@ -16,6 +16,9 @@ from app.db.models import Workflow from app.db.models_process import ActionRecord router = APIRouter(prefix="/api/ledger", tags=["ledger"]) +#: 口径账本里按运行读的那几个(一个答案里每个数的口径标签等,docs/design/next-round/02-口径账本.md):挂在 +#: /api/runs/{run_id}/ledger… 下,和证据接口同一道门(main.py 挂 evidence_scope:使用者只读、而且要能读这次运行) +runs_router = APIRouter(prefix="/api/runs", tags=["ledger"]) @router.get("/workflow-versions/{workflow_id}/{version}") diff --git a/backend/app/db/base.py b/backend/app/db/base.py index 40204684..b45bbade 100644 --- a/backend/app/db/base.py +++ b/backend/app/db/base.py @@ -245,6 +245,9 @@ _COLUMN_MIGRATIONS: list[tuple[str, str, str]] = [ ("schedules", "target_kind", "VARCHAR(20) DEFAULT 'workflow'"), ("schedules", "target_id", "VARCHAR(32)"), ("schedule_fires", "eval_run_id", "VARCHAR(32)"), + # 第 2 波:服务账号(users.kind);剖析上传表格源时的快照 + ("users", "kind", "VARCHAR(20) DEFAULT 'person'"), + ("catalog_profile_tasks", "snapshot_id", "VARCHAR(32)"), ] #: 迁移补上的列要建的索引:(索引名, 表, 列或多列)。create_all 不碰已有的表,表上新列的索引也就不会建。 @@ -259,6 +262,8 @@ _INDEX_MIGRATIONS: list[tuple[str, str, str | tuple[str, ...]]] = [ ("ix_runs_wf_ver_status", "runs", ("workflow_id", "version", "status")), ("ix_conversations_app_id", "conversations", "app_id"), ("ix_conversations_created_by", "conversations", "created_by"), + # 口径账本按种类反查工件(kind=caliber_match,next-round 02 做法 2) + ("ix_artifacts_kind", "artifacts", "kind"), ] diff --git a/backend/app/db/models.py b/backend/app/db/models.py index 6c11c6bf..8232536d 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -521,6 +521,8 @@ class CatalogProfileTask(Base): version: Mapped[int] = mapped_column(Integer, default=0) # 整张表的原因:表结构里没有、视图、空表、没有可剖析的项、写入一直冲突、停止 / 中断 / 止损 error: Mapped[str | None] = mapped_column(Text, default=None) + # 剖析上传表格源时那一刻的快照(数据库源为空):上传表格换了版本之后,旧版本的剖析事实不再交给 look(next-round 遗留 e) + snapshot_id: Mapped[str | None] = mapped_column(String(32), default=None) started_at: Mapped[datetime | None] = mapped_column(UTCDateTime, default=None) finished_at: Mapped[datetime | None] = mapped_column(UTCDateTime, default=None) @@ -1081,3 +1083,6 @@ from app.db.models_process import ( # noqa: E402,F401 from app.db.models_blueprints import ( # noqa: E402,F401 Blueprint, BlueprintEvent, PublishPlan, RecordsTable, StoredFile, ) +# 下一轮第 2 波(docs/design/next-round):数据源对象索引与后台探查(models_sources)、评测(models_eval)。整模块导入, +# 各自的模型类由那两个文件自己登记,这里不逐个列名 +from app.db import models_eval, models_sources # noqa: E402,F401 diff --git a/backend/app/db/models_eval.py b/backend/app/db/models_eval.py new file mode 100644 index 00000000..e06dd7a9 --- /dev/null +++ b/backend/app/db/models_eval.py @@ -0,0 +1,5 @@ +"""评测:题集、题目、评测、结果(docs/design/next-round/01-效果度量与回归评测.md 做法 7)。 + +第 2 波 W2-c 填。由 app/db/models.py 末尾整模块导入,create_all 建表。 +""" +from __future__ import annotations diff --git a/backend/app/db/models_identity.py b/backend/app/db/models_identity.py index 67a7638c..1495e138 100644 --- a/backend/app/db/models_identity.py +++ b/backend/app/db/models_identity.py @@ -28,6 +28,8 @@ class User(Base, TimestampMixin): display_name: Mapped[str] = mapped_column(String(100), default="") email: Mapped[str | None] = mapped_column(String(200), default=None) role: Mapped[str] = mapped_column(String(20), default="user") + # person 人;service 服务账号(next-round 04 A1):管理员建,没有口令、不能登录,只持有访问密钥 + kind: Mapped[str] = mapped_column(String(20), default="person") # local 模式:hashlib.scrypt 的结果(含盐和参数);OIDC 用户为空 password_hash: Mapped[str | None] = mapped_column(Text, default=None) oidc_issuer: Mapped[str | None] = mapped_column(String(300), default=None) diff --git a/backend/app/db/models_sources.py b/backend/app/db/models_sources.py new file mode 100644 index 00000000..949d5ed1 --- /dev/null +++ b/backend/app/db/models_sources.py @@ -0,0 +1,5 @@ +"""数据源的对象索引、探查范围、后台探查任务(docs/design/next-round/03-数据接入与目录维护.md A1)。 + +第 2 波 W2-a 填。由 app/db/models.py 末尾整模块导入,create_all 建表。 +""" +from __future__ import annotations diff --git a/backend/app/main.py b/backend/app/main.py index 045cb390..88f6946d 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -18,6 +18,8 @@ from app.api import ( actions as actions_api, apps as apps_api, auth as auth_api, capabilities as capabilities_api, hooks as hooks_api, identity as identity_api, ledger as ledger_api, notify as notify_api, tasks as tasks_api, ) +# 下一轮第 2 波(docs/design/next-round):评测、访问密钥 +from app.api import access_keys as access_keys_api, evals as evals_api # 普通用户建应用(docs/design/normal-user-apps.md §3.10):蓝图、模板、文件、个人应用。权限在接口里按蓝图的范围和拥有者判 from app.api import ( blueprints as blueprints_api, copilot_blueprint, files as files_api, personal as personal_api, @@ -214,6 +216,8 @@ for router, guard in ( (bundle.router, [*_BUILDER, private_run_scope]), (inquiry.router, [*_BUILDER, private_run_scope]), (whatif.router, [*_BUILDER, private_run_scope]), + # 口径账本按运行读的接口(/api/runs/{run_id}/ledger…):和证据同一道门 + (ledger_api.runs_router, [evidence_scope]), (runs.router, [run_scope]), (runs.approvals_router, _BUILDER), (evidence.router, [evidence_scope]), @@ -262,6 +266,9 @@ for router, guard in ( (hooks_api.router, []), (notify_api.router, _USER), (ledger_api.router, _BUILDER), + # 下一轮第 2 波:评测只给搭建者;访问密钥登录即可,接口里再按本人 / 管理员判 + (evals_api.router, _BUILDER), + (access_keys_api.router, _USER), ): app.include_router(router, dependencies=guard)