Spaces:
Running
Running
File size: 13,581 Bytes
4052d84 ce6728e 898bc18 bf8f1ff 4052d84 2bc545f 4052d84 ce6728e a085ad1 bf8f1ff 4052d84 6218d9a 4052d84 aa1acaa | 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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | """
UndertriAI — Pydantic Models
Defines all Action and Observation types for the bail assessment environment.
"""
from __future__ import annotations
from typing import Any, Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field
# ---------------------------------------------------------------------------
# OpenEnv base stubs (avoids hard import dependency when running locally)
# ---------------------------------------------------------------------------
try:
from openenv.core.models import Action, Observation, State, StepResult # type: ignore
except ImportError:
class Action(BaseModel):
pass
class Observation(BaseModel):
pass
class State(BaseModel):
episode_id: str = ""
step_count: int = 0
class StepResult(BaseModel):
observation: Any
reward: float = 0.0
done: bool = False
info: Dict[str, Any] = Field(default_factory=dict)
# ---------------------------------------------------------------------------
# ACTIONS — tool calls the agent can make before submitting the final memo
# ---------------------------------------------------------------------------
class RequestDocumentAction(Action):
"""Request a missing document (surety affidavit, prior judgment, etc.)"""
tool_name: Literal["request_document"] = "request_document"
document_type: str = Field(
...,
description="Type of document to request: surety_affidavit | prior_judgment | fir_copy | medical_report | employment_proof",
)
justification: str = Field(..., description="Why this document is needed")
class FlagInconsistencyAction(Action):
"""Flag a legal inconsistency in the charge or prosecution argument."""
tool_name: Literal["flag_inconsistency"] = "flag_inconsistency"
inconsistency: str = Field(..., description="Description of the inconsistency found")
severity: Literal["minor", "major", "fatal"] = Field(
..., description="Severity: minor=procedural, major=affects merits, fatal=vitiates proceedings"
)
location: str = Field(..., description="Where in the record the inconsistency appears")
class CrossReferencePrecedentAction(Action):
"""Retrieve and cite a relevant precedent from the case database."""
tool_name: Literal["cross_reference_precedent"] = "cross_reference_precedent"
query: str = Field(..., description="Legal principle or scenario to search for")
jurisdiction: Optional[str] = Field(None, description="Preferred jurisdiction (e.g., 'Supreme Court', 'Delhi HC')")
crime_category: Optional[str] = Field(None, description="Narrow search by crime category")
class ComputeStatutoryEligibilityAction(Action):
"""Check if accused has served half the maximum sentence (default bail eligibility)."""
tool_name: Literal["compute_statutory_eligibility"] = "compute_statutory_eligibility"
sections_invoked: List[str] = Field(..., description="IPC/BNSS sections charged under")
max_sentence_years: float = Field(..., description="Maximum sentence for the most serious charge in years")
custody_months: float = Field(..., description="Months in custody to date")
special_law_applicable: bool = Field(False, description="Whether NDPS/UAPA/PMLA or similar special law applies")
class AssessSuretyAction(Action):
"""Evaluate financial viability of the proposed surety."""
tool_name: Literal["assess_surety"] = "assess_surety"
proposed_amount: int = Field(..., description="Proposed surety amount in INR")
accused_occupation: str = Field(..., description="Occupation of accused")
income_estimate: Optional[int] = Field(None, description="Estimated monthly income in INR")
surety_relation: Optional[str] = Field(None, description="Relation of surety to accused")
class ClassifyBailTypeAction(Action):
"""Determine whether grounds support conditional bail, absolute bail, or denial."""
tool_name: Literal["classify_bail_type"] = "classify_bail_type"
grounds_for: List[str] = Field(..., description="List of grounds supporting bail")
grounds_against: List[str] = Field(..., description="List of grounds opposing bail")
accused_category: Optional[str] = Field(None, description="First-time offender | repeat | undertrial | convict")
class ReadSubmissionsAction(Action):
"""Read and summarise prosecution or defence submissions on record."""
tool_name: Literal["read_submissions"] = "read_submissions"
party: Literal["prosecution", "defence", "both"] = Field(
..., description="Which party's submissions to read"
)
focus: Optional[str] = Field(
None, description="Specific legal issue to focus on (e.g. 'flight risk', 'BNSS 479')"
)
class AssessFlightRiskAction(Action):
"""Systematically assess the accused's flight risk based on case factors."""
tool_name: Literal["assess_flight_risk"] = "assess_flight_risk"
roots_in_community: Optional[str] = Field(
None, description="Evidence of local ties: family, employment, property"
)
prior_absconding: bool = Field(False, description="Has the accused ever absconded before?")
passport_status: Optional[str] = Field(
None, description="surrendered | impounded | at-large | unknown"
)
severity_of_offence: Literal["minor", "moderate", "serious", "heinous"] = Field(
..., description="Gravity of the offence (determines flight incentive)"
)
class CheckCaseFactorsAction(Action):
"""Examine specific case factors relevant to bail determination."""
tool_name: Literal["check_case_factors"] = "check_case_factors"
factors_to_check: List[str] = Field(
...,
description="Factors to examine, e.g.: 'nature_of_offence', 'victim_vulnerability', "
"'evidence_tampering_risk', 'co_accused_bail_status', 'recovery_of_property'"
)
class ApplyProportionalityAction(Action):
"""Apply proportionality principle: custody duration vs. maximum sentence vs. trial timeline."""
tool_name: Literal["apply_proportionality"] = "apply_proportionality"
custody_months: float = Field(..., description="Months in custody to date")
max_sentence_years: float = Field(..., description="Maximum sentence for the most serious charge")
expected_trial_months: Optional[float] = Field(
None, description="Estimated months until trial completion (if known)"
)
class PullCriminalHistoryAction(Action):
"""Pull the accused's prior criminal record, bail history, and conviction status."""
tool_name: Literal["pull_criminal_history"] = "pull_criminal_history"
include_bail_history: bool = Field(
default=True, description="Whether to include prior bail applications and outcomes"
)
class IssueOrderAction(Action):
"""
TERMINAL ACTION — Issue a bail order (Block 4.3 spec alias for submit_memo).
Maps order_type to recommended_outcome:
grant → Bail Granted
deny → Bail Denied
conditional → Bail Granted (conditions must be provided)
This is the short-form action compatible with the OpenEnv compliance
checklist spec (`issue_order(grant | deny | conditional)`). Use
submit_memo for the full structured memo form.
"""
tool_name: Literal["issue_order"] = "issue_order"
order_type: Literal["grant", "deny", "conditional"] = Field(
..., description="Type of bail order: grant | deny | conditional"
)
flight_risk: Literal["Low", "Medium", "High"] = Field(
..., description="Flight risk classification"
)
flight_risk_justification: str = Field(
..., description="Justification for flight risk assessment referencing case facts"
)
statutory_eligible: bool = Field(
..., description="Whether accused qualifies for default bail under statute"
)
statutory_computation: str = Field(
..., description="Computation: section → max sentence → threshold → custody served"
)
grounds_for_bail: List[str] = Field(..., description="Grounds supporting bail")
grounds_against_bail: List[str] = Field(..., description="Grounds opposing bail")
recommended_conditions: Optional[List[str]] = Field(
None, description="Bail conditions (required when order_type='conditional')"
)
confidence: Literal["High", "Medium", "Low"] = "Medium"
class SubmitMemoAction(Action):
"""
TERMINAL ACTION — Submit the structured bail assessment memo.
This triggers reward computation against the ground truth.
"""
tool_name: Literal["submit_memo"] = "submit_memo"
# Flight risk assessment
flight_risk: Literal["Low", "Medium", "High"] = Field(
..., description="Flight risk classification"
)
flight_risk_justification: str = Field(
..., description="Justification for flight risk score with reference to case facts"
)
# Statutory eligibility
statutory_eligible: bool = Field(
..., description="Whether accused is eligible for bail under the statute"
)
statutory_computation: str = Field(
..., description="Show the computation: sections, max sentence, time served, threshold"
)
# Balanced assessment
grounds_for_bail: List[str] = Field(
..., description="Specific grounds from case facts supporting bail"
)
grounds_against_bail: List[str] = Field(
..., description="Specific grounds from prosecution / case facts opposing bail"
)
# Recommendation
recommended_outcome: Literal["Bail Granted", "Bail Denied"] = Field(
..., description="Final recommendation: Bail Granted | Bail Denied"
)
recommended_conditions: Optional[List[str]] = Field(
None,
description="Conditions if bail granted: surety amount, travel restrictions, reporting, etc."
)
# Confidence
confidence: Literal["High", "Medium", "Low"] = Field(
"Medium", description="Confidence in the recommendation"
)
# Union of all valid agent actions
BailAction = Union[
RequestDocumentAction,
FlagInconsistencyAction,
CrossReferencePrecedentAction,
ComputeStatutoryEligibilityAction,
AssessSuretyAction,
ClassifyBailTypeAction,
ReadSubmissionsAction,
AssessFlightRiskAction,
CheckCaseFactorsAction,
ApplyProportionalityAction,
PullCriminalHistoryAction,
IssueOrderAction, # Block 4.3: spec-compliant alias for submit_memo
SubmitMemoAction,
]
# ---------------------------------------------------------------------------
# OBSERVATION — what the agent sees at each step
# ---------------------------------------------------------------------------
class AccusedProfile(BaseModel):
name: str
gender: str
occupation: Optional[str] = None
region: Optional[str] = None
prior_cases: Optional[str] = None
bail_type: Optional[str] = None
class CaseObservation(Observation):
"""Full state the agent observes at each step of an episode."""
case_id: str
case_title: str
# Case materials
charge_sheet: str = Field(..., description="Facts and FIR summary")
ipc_sections: List[str] = Field(..., description="Sections invoked (IPC or BNSS)")
crime_type: str
court: str
date: str
# Accused
accused_profile: AccusedProfile
# Arguments
prosecution_arguments: List[str]
defence_arguments: List[str]
legal_issues: List[str]
# Context
cited_precedents: List[str] = Field(default_factory=list)
documents_available: List[str] = Field(default_factory=list)
# Episode state
action_result: Optional[str] = None
action_history: List[str] = Field(
default_factory=list,
description="Ordered log of all tool results seen so far this episode",
)
flags_raised: List[str] = Field(default_factory=list)
precedents_retrieved: List[str] = Field(default_factory=list)
memo_submitted: bool = False
step_count: int = 0
# Schema drift indicator (Patronus AI bonus track)
schema_variant: str = "standard" # "standard" | "bnss" | "regional_<state>"
# ---------------------------------------------------------------------------
# REWARD BREAKDOWN — returned in StepResult.info when memo is submitted
# ---------------------------------------------------------------------------
class RewardBreakdown(BaseModel):
outcome_match: float # 0.0 – 1.0
flight_risk_accuracy: float # 0.0 – 1.0
statutory_accuracy: float # 0.0 – 1.0
condition_appropriateness: float # 0.0 – 1.0
bias_penalty: float # 0.0 – 1.0 (subtracted)
total_reward: float # final R
ground_truth_outcome: str
agent_outcome: str
explanation: str
# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------
__all__ = [
# Base types
"Action", "Observation", "State", "StepResult",
# Actions (12 tool types + 1 terminal alias)
"RequestDocumentAction",
"FlagInconsistencyAction",
"CrossReferencePrecedentAction",
"ComputeStatutoryEligibilityAction",
"AssessSuretyAction",
"ClassifyBailTypeAction",
"ReadSubmissionsAction",
"AssessFlightRiskAction",
"CheckCaseFactorsAction",
"ApplyProportionalityAction",
"PullCriminalHistoryAction",
"IssueOrderAction",
"SubmitMemoAction",
# Union type
"BailAction",
# Observation / state
"AccusedProfile",
"CaseObservation",
"RewardBreakdown",
]
|