File size: 3,041 Bytes
7ff7119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""DDState — the DD assistant multi-agent supervisor graph state.

Topology: contract_filter → per_contract_summary → supervisor (LLM router) →
specialist agents (audit/legal/compliance/financial) → dd_synthesizer.

Specialist outputs (Pydantic structs) accumulate in the state; the supervisor
decides the next specialist or routes to the synthesizer based on them.
"""

from __future__ import annotations

from operator import add
from typing import Annotated, TypedDict

from pydantic import BaseModel, Field

from graph.states.pipeline_state import (
    DDPortfolioReport,
    ProcessedDocument,
)


class DDContractSummary(BaseModel):
    """Per-contract Python-computed summary (input to the specialist agents)."""

    file_name: str
    contract_type: str = "unknown"
    parties: list[str] = Field(default_factory=list)
    effective_date: str | None = None
    expiry_date: str | None = None
    total_value: float | None = None
    currency: str = "USD"
    monthly_fee: float | None = None
    monthly_fee_currency: str = "USD"
    risk_level: str = "low"  # low | medium | high
    risk_elements: list[str] = Field(default_factory=list)
    red_flags: list[str] = Field(default_factory=list)


class AuditFindings(BaseModel):
    """Audit specialist output."""
    pricing_anomalies: list[str] = Field(default_factory=list)
    overcharging: list[str] = Field(default_factory=list)
    note: str = ""


class LegalFindings(BaseModel):
    """Legal specialist output."""
    red_flags: list[str] = Field(default_factory=list)
    change_of_control: list[str] = Field(default_factory=list)
    non_compete: list[str] = Field(default_factory=list)
    note: str = ""


class ComplianceFindings(BaseModel):
    """Compliance specialist output."""
    gdpr_issues: list[str] = Field(default_factory=list)
    aml_alerts: list[str] = Field(default_factory=list)
    note: str = ""


class FinancialFindings(BaseModel):
    """Financial specialist output."""
    monthly_obligations: dict[str, float] = Field(default_factory=dict)
    expiring_soon: list[str] = Field(default_factory=list)
    high_value_contracts: list[str] = Field(default_factory=list)
    note: str = ""


class DDState(TypedDict, total=False):
    """The dd_graph state."""

    documents: list[ProcessedDocument]
    """Input: the full pipeline_graph documents list. contract_filter narrows it."""

    contracts: list[DDContractSummary]
    """Output of per_contract_summary (Python-deterministic)."""

    # Specialist outputs
    audit_findings: AuditFindings | None
    legal_findings: LegalFindings | None
    compliance_findings: ComplianceFindings | None
    financial_findings: FinancialFindings | None

    # Supervisor state
    call_history: Annotated[list[str], add]
    """Specialists already invoked (string list, append-only)."""

    next_specialist: str | None
    """Supervisor decision: 'audit' | 'legal' | 'compliance' | 'financial' | 'DONE'."""

    iteration_count: int

    # Final result
    dd_report: DDPortfolioReport | None