doatlas-2 / lib /db /src /schema /capability.ts
Iostream-Li's picture
Add files using upload-large-folder tool
9c12e58 verified
import {
pgTable,
text,
timestamp,
jsonb,
integer,
index,
uniqueIndex,
} from "drizzle-orm/pg-core";
/**
* Task #242 (B1) — Capability 通用框架的核心表(只登记不调度)。
*
* 设计语义:
* - `capabilities` 一行 = 一个 capability(ligand_screening / target_discovery / ...)
* - `capability_lifecycle_events` 状态转换审计(B5 才真正驱动 evolution)
* - `capability_inner_loop_runs` 内循环执行轨迹(只记录,B5 才调度)
* - `capability_data_foundation` 自产数据基座(B2 已建,本任务保留)
* - `capability_external_knowledge_calls` 6 类适配器调用 telemetry(B2 已建)
* - `network_dispatch_violations` input_contract ajv 校验未通过的行(本任务漏洞 2)
*
* 重要:本任务 5 个抽象骨架模块(registry/lifecycle/inner-loop/data-foundation/
* external-knowledge)只接口不实现;真实现在 B2/B5/B7。这些表在 B1 落地是
* 为了让"接口骨架返回值"有地方写,避免后续骨架接口改动连带 schema 漂移。
*/
// -------------------------------------------------------- capabilities
export const capabilities = pgTable(
"capabilities",
{
id: text("id").primaryKey(),
/** 稳定 handle,例如 "ligand_screening"。UNIQUE。 */
name: text("name").notNull().unique(),
/** 业务域,例如 "drug_discovery" / "biology" / "ml_systems"。 */
domain: text("domain").notNull(),
description: text("description").notNull().default(""),
/** Lifecycle:'created' | 'cold_start' | 'graduated' | 'live' | 'degraded'。 */
currentLifecycleState: text("current_lifecycle_state")
.notNull()
.default("created"),
/** 关联到 tool_networks.name(向后兼容期 capability 复用旧 network 执行)。 */
boundNetworkName: text("bound_network_name"),
/** Free-form 元数据(B2/B5/B7 各自往里塞)。 */
meta: jsonb("meta").notNull().default({}),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(t) => ({
byDomain: index("capabilities_domain_idx").on(t.domain),
byState: index("capabilities_state_idx").on(t.currentLifecycleState),
}),
);
export type CapabilityRow = typeof capabilities.$inferSelect;
export type InsertCapabilityRow = typeof capabilities.$inferInsert;
// -------------------------------------------------------- lifecycle events
export const capabilityLifecycleEvents = pgTable(
"capability_lifecycle_events",
{
id: text("id").primaryKey(),
capabilityId: text("capability_id").notNull(),
fromState: text("from_state"),
toState: text("to_state").notNull(),
/** 'manual' | 'auto_promote' | 'auto_rollback' | 'cold_start_complete' | ... */
reason: text("reason").notNull(),
payload: jsonb("payload").notNull().default({}),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(t) => ({
byCapability: index("capability_lifecycle_events_cap_idx").on(
t.capabilityId,
),
byCreatedAt: index("capability_lifecycle_events_created_at_idx").on(
t.createdAt,
),
}),
);
export type CapabilityLifecycleEventRow =
typeof capabilityLifecycleEvents.$inferSelect;
export type InsertCapabilityLifecycleEventRow =
typeof capabilityLifecycleEvents.$inferInsert;
// -------------------------------------------------------- inner-loop runs
export const capabilityInnerLoopRuns = pgTable(
"capability_inner_loop_runs",
{
id: text("id").primaryKey(),
capabilityId: text("capability_id").notNull(),
/**
* `[{ step: 'observe' | 'diagnose' | 'plan' | 'search' | 'code' | 'act' |
* 'judge' | 'fix', input, output, durationMs, llmRole? }]`。
* 真 schema 在 B5 落 ddl 注释 + 类型(本任务不约束)。
*/
stepsJson: jsonb("steps_json").notNull(),
sourceRunId: text("source_run_id"),
durationMs: integer("duration_ms"),
/** 'ok' | 'aborted' | 'failed' */
status: text("status").notNull().default("ok"),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(t) => ({
byCapability: index("capability_inner_loop_runs_cap_idx").on(
t.capabilityId,
),
byCreatedAt: index("capability_inner_loop_runs_created_at_idx").on(
t.createdAt,
),
}),
);
export type CapabilityInnerLoopRunRow =
typeof capabilityInnerLoopRuns.$inferSelect;
export type InsertCapabilityInnerLoopRunRow =
typeof capabilityInnerLoopRuns.$inferInsert;
// -------------------------------------------------------- data foundation
//
// 已在 B2 (#255) 建表;保留原 schema 不动以维持向后兼容。本任务的
// `capability/data-foundation/index.ts` 抽象层会读这张表。
export const capabilityDataFoundation = pgTable(
"capability_data_foundation",
{
id: text("id").primaryKey(),
capabilityId: text("capability_id").notNull(),
kind: text("kind").notNull(),
payload: jsonb("payload").notNull(),
sourceRunId: text("source_run_id"),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(t) => ({
byCapability: index("capability_data_foundation_capability_idx").on(
t.capabilityId,
),
byKind: index("capability_data_foundation_kind_idx").on(t.kind),
byCapabilityKind: index("capability_data_foundation_cap_kind_idx").on(
t.capabilityId,
t.kind,
),
}),
);
export type CapabilityDataFoundationRow =
typeof capabilityDataFoundation.$inferSelect;
export type InsertCapabilityDataFoundationRow =
typeof capabilityDataFoundation.$inferInsert;
// -------------------------------------------------------- external-knowledge calls
//
// 已在 B2 (#255) 建表;保留原 schema 不动。
export const capabilityExternalKnowledgeCalls = pgTable(
"capability_external_knowledge_calls",
{
id: text("id").primaryKey(),
adapter: text("adapter").notNull(),
capabilityId: text("capability_id"),
latencyMs: integer("latency_ms").notNull(),
hitCount: integer("hit_count").notNull().default(0),
cacheHit: text("cache_hit"),
error: text("error"),
paramsFingerprint: text("params_fingerprint"),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(t) => ({
byAdapter: index("capability_ek_calls_adapter_idx").on(t.adapter),
byCapability: index("capability_ek_calls_capability_idx").on(
t.capabilityId,
),
byCreatedAt: index("capability_ek_calls_created_at_idx").on(t.createdAt),
}),
);
export type CapabilityExternalKnowledgeCallRow =
typeof capabilityExternalKnowledgeCalls.$inferSelect;
export type InsertCapabilityExternalKnowledgeCallRow =
typeof capabilityExternalKnowledgeCalls.$inferInsert;
// -------------------------------------------------------- dispatch violations
export const networkDispatchViolations = pgTable(
"network_dispatch_violations",
{
id: text("id").primaryKey(),
networkName: text("network_name").notNull(),
toolName: text("tool_name").notNull(),
/** SHA256 前 16 hex of canonical(args) — 不存原始 args 防隐私泄漏。 */
argsHash: text("args_hash").notNull(),
/** ajv `errors[]` 全量 JSON。 */
detailsJson: jsonb("details_json").notNull(),
/** 'enforced' | 'observed_only'(feature flag 关时只记录不阻断)。 */
mode: text("mode").notNull().default("enforced"),
/** 关联会话/用户(诊断用)。 */
conversationId: text("conversation_id"),
ownerUserId: text("owner_user_id"),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),
},
(t) => ({
byNetwork: index("network_dispatch_violations_net_idx").on(t.networkName),
byCreatedAt: index("network_dispatch_violations_created_at_idx").on(
t.createdAt,
),
uniqArgsHashRecent: uniqueIndex(
"network_dispatch_violations_uniq_recent",
).on(t.networkName, t.argsHash, t.createdAt),
}),
);
export type NetworkDispatchViolationRow =
typeof networkDispatchViolations.$inferSelect;
export type InsertNetworkDispatchViolationRow =
typeof networkDispatchViolations.$inferInsert;