File size: 4,839 Bytes
06671aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations

import json
import sys
import unittest
from pathlib import Path
from unittest.mock import AsyncMock, patch

SERVICE_ROOT = Path(__file__).resolve().parents[1]
if str(SERVICE_ROOT) not in sys.path:
    sys.path.insert(0, str(SERVICE_ROOT))

from tabular_agent import run_chat, run_review  # noqa: E402


class PlaybookFlowTests(unittest.IsolatedAsyncioTestCase):
    async def test_run_review_builds_canonical_payload_and_normalizes_findings(
        self,
    ) -> None:
        raw_review = json.dumps(
            {
                "workflow_id": "buyer",
                "findings": [
                    {
                        "id": "finding_001",
                        "risk_level": "high",
                        "clause_type": "Payment",
                        "issue": "Payment period is too long.",
                        "comment": "Reduce the payment period.",
                        "target_text": "Payment is due in 90 days.",
                        "start_paragraph_id": "P_0001",
                        "end_paragraph_id": "P_0001",
                        "confidence": 0.9,
                    }
                ],
            }
        )
        mocked_post = AsyncMock(
            return_value=(
                raw_review,
                {"totalTokenCount": 42},
                {"candidates": [{"content": {"parts": [{"text": raw_review}]}}]},
            )
        )

        with patch("tabular_agent._post", mocked_post):
            result, usage, _trace_id = await run_review(
                review_text="[P_0001] Payment is due in 90 days.",
                document_text="Payment is due in 90 days.",
                document_name="contract.docx",
                playbook_id="buyer",
                playbook_name="Buyer Playbook",
                playbook_text="Payment must be due within 30 days.",
                max_findings=10,
            )

        self.assertEqual(result.workflow_id, "buyer")
        self.assertEqual(len(result.findings), 1)
        self.assertEqual(result.findings[0].issue, "Payment period is too long.")
        self.assertEqual(result.model, "TabularAgent")
        self.assertEqual(usage, {"totalTokenCount": 42})

        call = mocked_post.await_args
        self.assertEqual(call.kwargs["feature_id"], "python-review")
        payload_text = call.args[0]["messages"][1]["content"]
        self.assertIn("SYSTEM INSTRUCTIONS:", payload_text)
        self.assertIn("USER INPUT:", payload_text)
        self.assertIn("Buyer Playbook", payload_text)
        self.assertIn("Payment must be due within 30 days.", payload_text)
        self.assertIn("Payment is due in 90 days.", payload_text)

    async def test_run_chat_builds_canonical_payload_and_renders_sources(
        self,
    ) -> None:
        source_url = "https://example.test/payment-terms"
        raw_chat = json.dumps(
            {
                "answer_paragraphs": [
                    {
                        "text": "The clause exceeds the playbook position.",
                        "citation_urls": [source_url],
                    }
                ],
                "sources": [
                    {
                        "title": "Payment Terms Guidance",
                        "url": source_url,
                    }
                ],
            }
        )
        mocked_post = AsyncMock(
            return_value=(
                raw_chat,
                {"totalTokenCount": 24},
                {"candidates": [{"content": {"parts": [{"text": raw_chat}]}}]},
            )
        )

        with patch("tabular_agent._post", mocked_post):
            text, usage, _trace_id = await run_chat(
                message="Does this payment clause comply?",
                document_text="Payment is due in 90 days.",
                document_name="contract.docx",
                playbook_id="buyer",
                playbook_name="Buyer Playbook",
                role_brief="Protect the buyer.",
            )

        self.assertIn("The clause exceeds the playbook position.", text)
        self.assertIn(
            "[Payment Terms Guidance](https://example.test/payment-terms)",
            text,
        )
        self.assertIn("Sources:", text)
        self.assertEqual(usage, {"totalTokenCount": 24})

        call = mocked_post.await_args
        self.assertEqual(call.kwargs["feature_id"], "python-chat")
        payload_text = call.args[0]["messages"][1]["content"]
        self.assertIn("SYSTEM INSTRUCTIONS:", payload_text)
        self.assertIn("USER INPUT:", payload_text)
        self.assertIn("Protect the buyer.", payload_text)
        self.assertIn("Does this payment clause comply?", payload_text)
        self.assertIn("Payment is due in 90 days.", payload_text)


if __name__ == "__main__":
    unittest.main()