File size: 5,606 Bytes
18e58fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import re
import os
import urllib.parse


# ─── Jurisdiction Detection ───────────────────────────────────────────────────
INDIA_KEYWORDS = [
    "gst", "cgst", "sgst", "igst", "tds", "tcs", "itc", "sebi", "rbi", "india", "indian",
    "rupee", "inr", "crore", "lakh", "section 194", "companies act", "ipc", "ibc",
    "income tax", "delhi", "mumbai", "bombay", "chennai", "bangalore", "hyderabad",
    "goods and services tax", "finance act", "cbdt", "gstr", "pan", "tan",
    "fema", "rera", "msme", "nri", "oci", "itat", "nclt", "nclat"
]
UAE_KEYWORDS = [
    "vat", "uae vat", "fta", "uae", "dubai", "abu dhabi", "sharjah", "ajman",
    "dirham", "aed", "free zone", "difc", "adgm", "excise tax",
    "federal tax authority", "corporate tax", "uae corporate", "ministry of finance",
    "cabinet decision", "federal decree", "mainland uae", "freezone"
]

def detect_jurisdiction(query: str) -> str:
    q = query.lower()
    india = sum(1 for kw in INDIA_KEYWORDS if kw in q)
    uae   = sum(1 for kw in UAE_KEYWORDS   if kw in q)
    if india > 0 and uae == 0: return "India"
    if uae   > 0 and india == 0: return "UAE"
    return "Both"

# ─── Auto Context Depth ───────────────────────────────────────────────────────
COMPLEX_TERMS = [
    "section", "act", "regulation", "notification", "circular", "judgment",
    "case", "versus", "liability", "exemption", "penalty", "compliance",
    "provision", "clause", "amendment", "appeal", "tribunal", "writ",
    "holding", "ratio", "precedent", "article", "schedule"
]

def auto_context_depth(query: str) -> int:
    wc   = len(query.split())
    hits = sum(1 for t in COMPLEX_TERMS if t in query.lower())
    if wc < 12 and hits < 2: return 5
    if wc < 30 and hits < 4: return 8
    if wc < 60 or hits < 6:  return 12
    return 15

# ─── Source Confidence Tiering ────────────────────────────────────────────────
def tier_sources(docs: list) -> str:
    if not docs: return "SYNTHESIZED"
    max_score = max(d.get("rerank_score", d.get("score", 0)) for d in docs)
    if max_score > 0.4:  return "GROUNDED"
    if max_score > 0.12: return "PARTIAL"
    return "SYNTHESIZED"

# ─── Think-Tag State Machine ──────────────────────────────────────────────────
def strip_think_tags(token: str, in_think: bool) -> tuple[str, bool]:
    output = ""
    remaining = token
    while remaining:
        if in_think:
            end_idx = remaining.find("</think>")
            if end_idx == -1:
                remaining = ""
            else:
                in_think  = False
                remaining = remaining[end_idx + len("</think>"):]
        else:
            start_idx = remaining.find("<think>")
            if start_idx == -1:
                output   += remaining
                remaining = ""
            else:
                output   += remaining[:start_idx]
                in_think  = True
                remaining = remaining[start_idx + len("<think>"):]
    return output, in_think

# ─── Citation patterns for UAE and India ─────────────────────────────────────
UAE_LAW_PATTERN = r"((?:Federal\s+)?(?:Decree-)?Law\s+No\.\s*(?:\(?\d+\)?)\s+of\s+\d{4})"
INDIA_LAW_PATTERN = r"((?:Income[\s-]?Tax\s+Act|Companies\s+Act|GST\s+Act|Income[\s-]?Tax\s+Rules?|Indian\s+Penal\s+Code|Insolvency\s+and\s+Bankruptcy\s+Code),?\s+\d{4})"
INDIA_SECTION_PATTERN = r"(Section\s+\d+[A-Z]?(?:-\s*[A-Z]+)?)"

def parse_citations(text: str) -> str:
    """
    Scans text for legal citations and wraps them in professional tagging or markdown links.
    Avoids double wrapping already-linked text.
    """
    # UAE Law linking (Search on UAE Legislation Portal - ignores existing markdown links)
    uae_combined = r"(\[[^\]]+\]\([^\)]+\))|" + UAE_LAW_PATTERN
    def uae_repl(match):
        if match.group(1):
            return match.group(1)
        val = match.group(2)
        encoded = urllib.parse.quote(val)
        return f"[{val}](https://elaws.moj.gov.ae/UAE-Legislations-Search-en.aspx?query={encoded})"
    text = re.sub(uae_combined, uae_repl, text)
    
    # India Law linking (Search on Indian Kanoon)
    india_combined = r"(\[[^\]]+\]\([^\)]+\))|" + INDIA_LAW_PATTERN
    def india_repl(match):
        if match.group(1):
            return match.group(1)
        val = match.group(2)
        encoded = urllib.parse.quote(val)
        return f"[{val}](https://indiankanoon.org/search/?formInput={encoded})"
    text = re.sub(india_combined, india_repl, text)
    
    # Section linking (Contextual search on Indian Kanoon)
    section_combined = r"(\[[^\]]+\]\([^\)]+\))|" + INDIA_SECTION_PATTERN
    def section_repl(match):
        if match.group(1):
            return match.group(1)
        val = match.group(2)
        encoded = urllib.parse.quote(val)
        return f"[{val}](https://indiankanoon.org/search/?formInput={encoded})"
    text = re.sub(section_combined, section_repl, text)
    
    return text

def format_score(score: float) -> str:
    """Formats a search score into a user-friendly percentage or indicator."""
    if score > 0.8: return "High"
    if score > 0.5: return "Medium"
    return "Low"