feat(ai): add personal expense application memory

This commit is contained in:
caoxiaozhu
2026-07-14 17:16:23 +08:00
parent 211f85d981
commit 54754b5502
36 changed files with 3579 additions and 74 deletions

View File

@@ -19,7 +19,7 @@ from app.db.schema_ownership import MIGRATION_OWNED_TABLES, create_legacy_schema
MIGRATION_TEST_DATABASE_URL = os.getenv("MIGRATION_TEST_DATABASE_URL", "").strip()
LEGACY_PROBE_TABLE = "legacy_migration_probe_records"
HEAD_REVISION = "20260714_0004"
HEAD_REVISION = "20260714_0005"
SERVER_DIR = Path(__file__).resolve().parents[1]
ALEMBIC_INI_PATH = SERVER_DIR / "alembic.ini"
@@ -132,6 +132,18 @@ def _assert_indexes(
assert indexes.get(index_name) == expected_columns
def _assert_check_constraint(
engine: Engine,
table_name: str,
constraint_name: str,
) -> None:
constraints = {
str(item["name"])
for item in inspect(engine).get_check_constraints(table_name, schema="public")
}
assert constraint_name in constraints
def _assert_cascade_foreign_key(engine: Engine, table_name: str) -> None:
foreign_keys = inspect(engine).get_foreign_keys(table_name, schema="public")
matching = [
@@ -226,18 +238,47 @@ def _assert_head_schema(engine: Engine) -> None:
"uq_ai_application_preview_decisions_issue_request",
("tenant_id", "actor_id", "auth_session_id", "issue_request_id"),
)
_assert_unique_constraint(
engine,
"ai_decision_feedback",
"uq_ai_decision_feedback_tenant_id",
("tenant_id", "id"),
)
_assert_unique_constraint(
engine,
"ai_decision_feedback",
"uq_ai_decision_feedback_tenant_idempotency",
("tenant_id", "idempotency_key"),
)
_assert_unique_constraint(
engine,
"workflow_outcomes",
"uq_workflow_outcomes_tenant_id",
("tenant_id", "id"),
)
_assert_unique_constraint(
engine,
"workflow_outcomes",
"uq_workflow_outcomes_tenant_idempotency",
("tenant_id", "idempotency_key"),
)
_assert_unique_constraint(
engine,
"memory_entries",
"uq_memory_entries_generation",
("tenant_id", "scope_type", "scope_id", "scene", "field_key", "generation"),
)
_assert_unique_constraint(
engine,
"memory_evidence_links",
"uq_memory_evidence_links_entry_case",
("tenant_id", "memory_entry_id", "expense_case_id"),
)
_assert_check_constraint(
engine,
"memory_entries",
"ck_memory_entries_expired_fields",
)
_assert_indexes(
engine,
@@ -332,6 +373,31 @@ def _assert_head_schema(engine: Engine) -> None:
),
},
)
_assert_indexes(
engine,
"memory_entries",
{
"ix_memory_entries_scope_lookup": (
"tenant_id",
"scope_type",
"scope_id",
"scene",
"field_key",
"status",
),
},
)
_assert_indexes(
engine,
"memory_evidence_links",
{
"ix_memory_evidence_links_entry_time": (
"tenant_id",
"memory_entry_id",
"created_at",
),
},
)
_assert_cascade_foreign_key(engine, "expense_case_links")
_assert_cascade_foreign_key(engine, "business_events")
_assert_composite_foreign_key(
@@ -379,6 +445,42 @@ def _assert_head_schema(engine: Engine) -> None:
"business_events",
("tenant_id", "expense_case_id", "id"),
)
_assert_composite_foreign_key(
engine,
"memory_entries",
("tenant_id", "superseded_by_id"),
"memory_entries",
)
_assert_composite_foreign_key(
engine,
"memory_evidence_links",
("tenant_id", "memory_entry_id"),
"memory_entries",
)
_assert_composite_foreign_key(
engine,
"memory_evidence_links",
("tenant_id", "expense_case_id"),
"expense_cases",
)
_assert_composite_foreign_key(
engine,
"memory_evidence_links",
("tenant_id", "decision_id"),
"ai_decisions",
)
_assert_composite_foreign_key(
engine,
"memory_evidence_links",
("tenant_id", "feedback_id"),
"ai_decision_feedback",
)
_assert_composite_foreign_key(
engine,
"memory_evidence_links",
("tenant_id", "outcome_id"),
"workflow_outcomes",
)
def _assert_runtime_cascade(engine: Engine) -> None: