-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathengine.py
More file actions
132 lines (114 loc) · 4.75 KB
/
Copy pathengine.py
File metadata and controls
132 lines (114 loc) · 4.75 KB
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
"""
Pipeline orchestrator.
Stages:
1. extract (LLM) transcript -> structured extraction
2. synthesize (LLM) extraction + business_context -> claim objects
3. audit (LLM) synthesis -> mutated synthesis (5 ops only)
4. render (Python) audited synthesis -> prose-ready sections
Returns a result dict with all intermediate artifacts so downstream code
(markdown export, eval harness, debugging) can inspect any stage.
"""
import os
import json
import hashlib
from datetime import datetime
from typing import Optional
from agents import extract, synthesize, audit, parse_llm_json
from renderer import render
def run_pipeline(
transcript: str,
business_context: Optional[str] = None,
transcript_context: Optional[str] = None,
skip_audit: bool = False,
verbose: bool = True,
) -> dict:
"""
Run the full pipeline on a transcript.
Args:
transcript: the raw meeting transcript (required).
business_context: the business context packet for Business Read.
If None or empty, Business Read will be sparse.
transcript_context: optional additional context about the meeting
itself (e.g., "this was a follow-up to last
week's roadmap planning"). Distinct from
business_context.
skip_audit: if True, skips stage 3 and renders raw synthesis.
Useful for debugging or comparing audited vs raw.
verbose: print stage progress to stdout.
Returns:
{
"metadata": {...},
"extraction": {...},
"synthesis_raw": {...},
"audit": {...} or None,
"audited_synthesis": {...},
"rendered": {...}
}
"""
if not transcript or not isinstance(transcript, str):
raise ValueError("Transcript must be a non-empty string")
def _log(msg: str):
if verbose:
print(msg)
# ----- Metadata for logging and audit trail -----
business_context = business_context or ""
bc_hash = hashlib.sha256(business_context.encode("utf-8")).hexdigest()[:12]
bc_chars = len(business_context)
metadata = {
"run_at": datetime.utcnow().isoformat() + "Z",
"transcript_chars": len(transcript),
"business_context_hash": bc_hash,
"business_context_chars": bc_chars,
"extract_model": os.getenv("EXTRACT_MODEL", "default"),
"synthesize_model": os.getenv("SYNTHESIZE_MODEL", "default"),
"audit_model": os.getenv("AUDIT_MODEL", "default"),
"skip_audit": skip_audit,
}
_log(f"\n=== Pipeline run started at {metadata['run_at']} ===")
_log(f" transcript: {len(transcript)} chars")
_log(f" business_context: {bc_chars} chars (hash={bc_hash})")
# ----- Stage 1: EXTRACT -----
_log("\n[Stage 1] Extract")
extraction_raw = extract(transcript, transcript_context)
extraction = parse_llm_json(extraction_raw)
_log(f" extracted: meeting_type={extraction.get('meeting_type')}, "
f"decision_state={extraction.get('decision_state')}")
# ----- Stage 2: SYNTHESIZE -----
_log("\n[Stage 2] Synthesize")
synthesis_raw_str = synthesize(
extraction_json=json.dumps(extraction, indent=2),
business_context=business_context,
)
synthesis_raw = parse_llm_json(synthesis_raw_str)
_log(" synthesis produced")
# ----- Stage 3: AUDIT (optional) -----
audit_result = None
audited_synthesis = synthesis_raw
if not skip_audit:
_log("\n[Stage 3] Audit")
audit_raw_str = audit(
extraction_json=json.dumps(extraction, indent=2),
synthesis_json=json.dumps(synthesis_raw, indent=2),
business_context=business_context,
)
audit_result = parse_llm_json(audit_raw_str)
audited_synthesis = audit_result.get("audited_synthesis", synthesis_raw)
verdict = audit_result.get("overall_verdict", "unknown")
action = audit_result.get("recommended_action", "unknown")
n_ops = len(audit_result.get("audit_log", []))
_log(f" audit verdict: {verdict}, action: {action}, ops: {n_ops}")
else:
_log("\n[Stage 3] Audit SKIPPED (skip_audit=True)")
# ----- Stage 4: RENDER (deterministic) -----
_log("\n[Stage 4] Render")
rendered = render(audited_synthesis)
_log(" rendered into prose sections")
_log("\n=== Pipeline complete ===\n")
return {
"metadata": metadata,
"extraction": extraction,
"synthesis_raw": synthesis_raw,
"audit": audit_result,
"audited_synthesis": audited_synthesis,
"rendered": rendered,
}