File size: 6,338 Bytes
f109bf8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | #!/usr/bin/env python3
"""Harness: COBOL evidence fixtures → evidence with deterministic 1536-d vectors.
Mirrors sql/003_evidence_seed_cobol.sql IDs/content; fills embedding (003 leaves NULL).
Reuse embed/fmt_vector from load_agent_registry (smoke hashed bag-of-tokens only).
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from load_agent_registry import embed, fmt_vector
try:
import psycopg2
except ImportError:
psycopg2 = None
# Same fixtures as sql/003_evidence_seed_cobol.sql
FIXTURES = [
{
"evidence_id": "ev_copybook_01",
"source_id": "src_copybook_acct",
"source_type": "copybook",
"document_id": "ACCTREC.cpy",
"location": "01-ACCOUNT-BALANCE",
"content": (
"01 ACCOUNT-RECORD. 05 ACCOUNT-BALANCE PIC S9(9)V99. "
"05 PAYMENT-AMOUNT PIC S9(7)V99. 05 LAST-PAYMENT-DATE PIC X(8)."
),
"content_hash": "seed_copybook_01",
"source_authority": 0.95,
"parent_evidence": ["ev_provenance_root"],
},
{
"evidence_id": "ev_cobol_src_01",
"source_id": "src_cobol_pay",
"source_type": "cobol_source",
"document_id": "PAYROLL.cbl",
"location": "PROCEDURE DIVISION / CALC-BALANCE",
"content": (
"COMPUTE ACCOUNT-BALANCE = ACCOUNT-BALANCE - PAYMENT-AMOUNT. "
"IF ACCOUNT-BALANCE < ZERO THEN PERFORM OVERDRAFT-CHECK. "
"MOVE PAYMENT-AMOUNT TO WS-LAST-PAYMENT."
),
"content_hash": "seed_cobol_src_01",
"source_authority": 0.90,
"parent_evidence": ["ev_provenance_root"],
},
{
"evidence_id": "ev_db2_schema_01",
"source_id": "src_db2_acct",
"source_type": "db2_schema",
"document_id": "ACCT.DDL",
"location": "TABLE ACCOUNT_BALANCES",
"content": (
"CREATE TABLE ACCOUNT_BALANCES (ACCT_ID CHAR(12), BALANCE DECIMAL(11,2), "
"LAST_PAYMENT DECIMAL(9,2), UPDATED_TS TIMESTAMP)."
),
"content_hash": "seed_db2_schema_01",
"source_authority": 0.85,
"parent_evidence": ["ev_provenance_root"],
},
{
"evidence_id": "ev_quant_pricing_01",
"source_id": "src_quant_report",
"source_type": "quant_report",
"document_id": "PRICING_Q3.pdf",
"location": "section:monte_carlo",
"content": (
"Monte Carlo pricing of exotic options. Implied volatility surface and "
"greek sensitivities. No COBOL or account balance fields."
),
"content_hash": "seed_quant_pricing_01",
"source_authority": 0.80,
"parent_evidence": ["ev_provenance_root"],
},
{
"evidence_id": "ev_lexical_lift_01",
"source_id": "src_glossary",
"source_type": "glossary",
"document_id": "LEGACY-GLOSSARY.txt",
"location": "entry:PAYMENT-BALANCE",
"content": (
"cobol payment balance glossary entry: PAYMENT and BALANCE fields on "
"ACCOUNT records in legacy batch."
),
"content_hash": "seed_lexical_lift_01",
"source_authority": 0.70,
"parent_evidence": ["ev_provenance_root"],
},
{
"evidence_id": "ev_junk_low_auth",
"source_id": "src_web_scrape",
"source_type": "web_scrape",
"document_id": "random-blog.html",
"location": "body",
"content": (
"Someone said COBOL payment balance might work like a spreadsheet. "
"Unverified forum post."
),
"content_hash": "seed_junk_low_auth",
"source_authority": 0.10,
"parent_evidence": [],
},
]
def normalize_for_embed(text: str) -> str:
"""Split identifiers on - _ / . so ACCOUNT_BALANCES → account balances tokens."""
return re.sub(r"[-_./]+", " ", text.lower())
def embed_text_of(f: dict) -> str:
"""content + source_type + document_id + location for dense path."""
parts = [
f.get("source_type") or "",
f.get("document_id") or "",
f.get("location") or "",
f["content"],
]
return " ".join(p for p in parts if p)
def main() -> int:
dsn = sys.argv[1] if len(sys.argv) > 1 else "dbname=david user=david host=127.0.0.1"
if psycopg2 is None:
print("psycopg2 missing", file=sys.stderr)
return 2
conn = psycopg2.connect(dsn)
conn.autocommit = True
with conn.cursor() as cur:
for f in FIXTURES:
et = normalize_for_embed(embed_text_of(f))
emb = fmt_vector(embed(et))
cur.execute(
"""
INSERT INTO evidence (
evidence_id, source_id, source_type, document_id, location,
content, content_hash, embedding, source_authority, parent_evidence
) VALUES (
%s, %s, %s, %s, %s,
%s, %s, %s::vector, %s, %s::text[]
)
ON CONFLICT (evidence_id) DO UPDATE SET
source_id = EXCLUDED.source_id,
source_type = EXCLUDED.source_type,
document_id = EXCLUDED.document_id,
location = EXCLUDED.location,
content = EXCLUDED.content,
content_hash = EXCLUDED.content_hash,
embedding = EXCLUDED.embedding,
source_authority = EXCLUDED.source_authority,
parent_evidence = EXCLUDED.parent_evidence
""",
(
f["evidence_id"],
f["source_id"],
f["source_type"],
f["document_id"],
f["location"],
f["content"],
f["content_hash"],
emb,
f["source_authority"],
f["parent_evidence"],
),
)
cur.execute(
"SELECT count(*) FILTER (WHERE embedding IS NOT NULL), count(*) FROM evidence"
)
with_emb, total = cur.fetchone()
conn.close()
print(f"loaded_evidence={total} with_embedding={with_emb}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
|