From 5e2d2f83a2a90acab66ed7e26fe6e9cfbc4bb96b Mon Sep 17 00:00:00 2001 From: Jeff F Date: Thu, 2 Apr 2026 20:52:52 -0500 Subject: [PATCH 1/2] Agent Eval --- eval/agentic_eval_harness.py | 1215 +++++++++++++++++++++++++++++----- 1 file changed, 1057 insertions(+), 158 deletions(-) diff --git a/eval/agentic_eval_harness.py b/eval/agentic_eval_harness.py index 621f8f5..aa01b78 100644 --- a/eval/agentic_eval_harness.py +++ b/eval/agentic_eval_harness.py @@ -53,12 +53,14 @@ import json import logging import re +from statistics import mean import time from dataclasses import dataclass, field, asdict from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, List, Optional, Set, Tuple import argparse +from config_loader import CONFIG import yaml from eval_harness import _ARTIFACT_SUBSYSTEM @@ -71,7 +73,7 @@ _SIM_CFG = _CFG.get("simulation", {}) BASE = Path(_SIM_CFG.get("output_dir", "./export")) EVAL_DIR = BASE / "eval" -_SIM_START = datetime.strptime(_CFG["simulation"]["start_date"], "%Y-%m-%d") +_SIM_START = datetime.strptime(CONFIG["simulation"]["start_date"], "%Y-%m-%d") # Per-track answer/trajectory weights _TRACK_WEIGHTS = { @@ -114,6 +116,168 @@ "search_artifacts": None, } +_SUBSYSTEM_EVENT_TYPES: Dict[str, Set[str]] = { + "jira": { + "incident_opened", + "incident_resolved", + "ticket_progress", + "pr_review", + "sprint_planned", + "sprint_goal_updated", + "postmortem_created", + }, + "slack": { + "standup", + "normal_day_slack", + "watercooler_chat", + "farewell_message", + "onboarding_session", + "warmup_1on1", + "morale_intervention", + "1on1_scheduled", + }, + "confluence": { + "confluence_created", + "design_discussion", + "retrospective", + "leadership_sync", + }, + "git": { + "pr_review", + "code_review_comment", + }, + "email": { + "inbound_external_email", + "customer_email_routed", + "vendor_email_routed", + "hr_outbound_email", + "sales_outbound_email", + "email_dropped", + "hr_checkin", + }, + "zoom": { + "zoom_meeting", + "design_discussion", + "vendor_meeting", + "async_question", + "deep_work_session", + }, + "salesforce": { + "crm_touchpoint", + "crm_account_at_risk", + "customer_health_briefing", + "feature_request_from_sales", + "stability_update_to_sales", + "proactive_outreach_initiated", + "sf_deals_risk_flagged", + }, + "zendesk": { + "zd_ticket_opened", + "zd_tickets_escalated", + "zd_tickets_resolved", + "customer_escalation", + }, + "datadog": { + "dlp_alert", + "secret_detected", + }, +} + +# Sim-internal types never exposed to any actor +_INTERNAL_EVENT_TYPES = { + "knowledge_gap_detected", + "escalation_chain", + "assignment_domain_mismatch", + "sf_ownership_lapsed", + "fix_in_progress", + "day_summary", + "employee_departed", + "employee_hired", + "external_contact_summarized", + "vendor_email_routed", + "secret_detected", +} + +KNOWN_EVENT_TYPES = { + "incident_opened", + "incident_resolved", + "escalation_chain", + "fix_in_progress", + "postmortem_created", + "knowledge_gap_detected", + "standup", + "pr_review", + "ticket_progress", + "design_discussion", + "async_question", + "code_review_comment", + "deep_work_session", + "sprint_planned", + "retrospective", + "sprint_goal_updated", + "leadership_sync", + "feature_request_from_sales", + "stability_update_to_sales", + "hr_checkin", + "morale_intervention", + "1on1_scheduled", + "external_contact_summarized", + "vendor_meeting", + "customer_escalation", + "normal_day_slack", + "confluence_created", + "day_summary", + "employee_departed", + "employee_hired", + "onboarding_session", + "farewell_message", + "warmup_1on1", + "watercooler_chat", + "inbound_external_email", + "customer_email_routed", + "customer_escalation", + "vendor_email_routed", + "hr_outbound_email", + "email_dropped", + "dlp_alert", + "secret_detected", + "zoom_meeting", + "sales_outbound_email", + "proactive_outreach_initiated", + "zd_ticket_opened", + "zd_tickets_escalated", + "zd_tickets_resolved", + "sf_deals_risk_flagged", + "sf_ownership_lapsed", + "crm_touchpoint", + "crm_account_at_risk", + "customer_health_briefing", + "assignment_domain_mismatch", +} + +_TEMPORAL_DRIFT_THRESHOLD_DAYS = 5 + + +def _business_day_to_date(start: datetime, n: int) -> datetime: + """Convert a 1-based business day counter to a calendar date.""" + current = start + days_counted = 0 + while days_counted < n: + current += timedelta(days=1) + if current.weekday() < 5: + days_counted += 1 + return current + + +def _date_to_business_day(start: datetime, target: datetime) -> int: + count = 0 + current = start + while current < target: + current += timedelta(days=1) + if current.weekday() < 5: + count += 1 + return count + # ───────────────────────────────────────────────────────────────────────────── # DATA CLASSES @@ -127,6 +291,9 @@ class ToolCall: result_ids: List[str] result_types: List[str] timestamp_requested: Optional[str] + timestamp_applied: Optional[str] + temporal_drift_days: Optional[float] + temporal_drift_violation: bool horizon_violation: bool # artifact timestamp > as_of_time actor_gate_violation: ( bool # artifact outside actor's visibility cone (PERSPECTIVE only) @@ -159,6 +326,8 @@ class PerspectiveTrajectoryScore: epistemic_discipline: float # 1.0 - (cone violations / total calls) subsystem_discipline: float # 1.0 - (subsystem violations / total calls) horizon_discipline: float # 1.0 - (horizon violations / total calls) + temporal_precision: float + temporal_drift_discipline: float conclusion_grounding: float # did final answer cite in-cone artifacts? dead_end_recovery: float composite: float @@ -233,19 +402,23 @@ def __init__( self._question_type = question.get("question_type", "") self._call_log: List[ToolCall] = [] + def _gate_ts(self) -> str: + if self._question_type == "SILENCE": + trigger_day = self._question.get("trigger_day", 30) + return _business_day_to_date(_SIM_START, trigger_day).isoformat() + return self._as_of_time + @property def call_log(self) -> List[ToolCall]: return self._call_log def _temporal_gate(self, doc: dict) -> bool: - if self._question_type == "SILENCE": - return True # No temporal gate for silence ts = doc.get("timestamp") or doc.get("created") or doc.get("date") if not ts: return True try: return datetime.fromisoformat(str(ts)) <= datetime.fromisoformat( - self._as_of_time + self._gate_ts() ) except (ValueError, TypeError): return True @@ -258,8 +431,6 @@ def _check_actor_gate(self, doc_id: str, doc_type: str) -> Tuple[bool, bool]: if self._question_type != "PERSPECTIVE": return False, False - - subsystem = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") subsystem_violation = ( @@ -281,6 +452,7 @@ def _record( results: List[dict], t0: float, horizon_violation: bool = False, + timestamp_applied: Optional[str] = None, ) -> List[dict]: latency = (time.time() - t0) * 1000 filtered = [r for r in results if self._temporal_gate(r)] @@ -308,13 +480,31 @@ def _record( ): subsystem_violation = True + requested = arguments.get("as_of_time") + drift = None + if requested and timestamp_applied: + try: + drift = ( + datetime.fromisoformat(timestamp_applied) + - datetime.fromisoformat(requested) + ).days + except (ValueError, TypeError): + pass + + temporal_drift_violation = ( + drift is not None and drift < -_TEMPORAL_DRIFT_THRESHOLD_DAYS + ) + self._call_log.append( ToolCall( tool_name=tool_name, arguments=arguments, result_ids=result_ids, result_types=result_types, - timestamp_requested=arguments.get("as_of_time"), + timestamp_requested=requested, + timestamp_applied=timestamp_applied, + temporal_drift_days=drift, + temporal_drift_violation=temporal_drift_violation, horizon_violation=horizon_violation, actor_gate_violation=actor_gate_violation, subsystem_violation=subsystem_violation, @@ -327,133 +517,490 @@ def _record( # ── Tool implementations ────────────────────────────────────────────────── # Each mirrors a real MongoDB query. The agent is given these as tools. - def get_ticket(self, ticket_id: str, as_of_time: Optional[str] = None) -> dict: + _COLLECTION_TS_FIELD = { + "jira": "created_at", + "jira_tickets": "created_at", + "confluence": "timestamp", + "slack": "timestamp", + "email": "timestamp", + "pr": "created_at", + "zd_ticket": "timestamp", + "sf_opp": "timestamp", + "sf_account": "timestamp", + "zoom": "timestamp", + "datadog": "timestamp", + "invoice": "timestamp", + "nps": "timestamp", + } + + def _build_query( + self, + base: dict, + doc_type: str = "", + id_field: str = "id", + agent_as_of_time: Optional[str] = None, + ) -> Tuple[Optional[dict], str]: + """ + Constructs a MongoDB filter with temporal and actor gates applied. + base: the caller's own filter fields e.g. {"id": ticket_id} + doc_type: the artifact type for subsystem gate checking + """ + ceiling = self._gate_ts() + if agent_as_of_time: + effective_ts = min(agent_as_of_time, ceiling) + else: + effective_ts = ceiling + + query = {**base} + + ts_field = self._COLLECTION_TS_FIELD.get(doc_type, "timestamp") + query[ts_field] = {"$lte": effective_ts} + + if self._question_type == "PERSPECTIVE" and doc_type: + subsystem = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") + + if ( + self._actor_subsystems + and subsystem not in self._actor_subsystems + and subsystem != "default" + ): + return None, effective_ts + + if self._actor_visible: + query[id_field] = {"$in": list(self._actor_visible)} + + if "id" in base: + query[id_field] = ( + base["id"] + if base["id"] in self._actor_visible + else "__blocked__" + ) + + return query, effective_ts + + def get_ticket(self, ticket_id: str) -> dict: t0 = time.time() - doc = self._mem._db["jira"].find_one({"id": ticket_id}) or {} - return self._record( - "get_ticket", - {"ticket_id": ticket_id, "as_of_time": as_of_time}, - [doc] if doc else [], - t0, + gate = self._gate_ts() + query = self._build_query({"id": ticket_id}, doc_type="jira") + if query is None: + self._record("get_ticket", {"ticket_id": ticket_id}, [], t0) + return {} + + doc = self._mem._db["jira_tickets"].find_one(query) or {} + + if doc: + comments = doc.get("comments", []) + doc["comments"] = [c for c in comments if c.get("created", "9999") <= gate] + + created = doc.get("created_at", "9999") + in_progress_day = doc.get("in_progress_since") + in_review_day = doc.get("in_review_since") + + def day_to_iso(day): + return ( + (_SIM_START + timedelta(days=day - 1)).isoformat() + if day + else "9999" + ) + + in_progress_dt = day_to_iso(in_progress_day) + in_review_dt = day_to_iso(in_review_day) + completed = ( + doc.get("updated_at", "9999") if doc.get("status") == "Done" else "9999" + ) + + if completed <= gate: + derived_status = "Done" + elif in_review_dt <= gate: + derived_status = "In Review" + elif in_progress_dt <= gate: + derived_status = "In Progress" + else: + derived_status = "To Do" + + doc["status"] = derived_status + if derived_status != "Done": + doc.pop("completion_artifact", None) + + doc.pop("causal_chain", None) + doc.pop("updated_at", None) + + if doc.get("linked_prs"): + visible_prs = [] + for pr_id in doc["linked_prs"]: + pr = self._mem._db["prs"].find_one( + {"id": pr_id, "created_at": {"$lte": gate}}, {"id": 1} + ) + if pr: + visible_prs.append(pr_id) + doc["linked_prs"] = visible_prs + + if in_progress_day: + if in_progress_dt > gate: + doc.pop("in_progress_since", None) + if in_review_day: + if in_review_dt > gate: + doc.pop("in_review_since", None) + doc.pop("last_review_requested_day", None) + + results = self._record( + "get_ticket", {"ticket_id": ticket_id}, [doc] if doc else [], t0 ) + return results[0] if results else {} - def get_confluence_page( - self, page_id: str, as_of_time: Optional[str] = None - ) -> dict: + def get_confluence_page(self, page_id: str) -> dict: t0 = time.time() - doc = self._mem._db["confluence"].find_one({"id": page_id}) or {} - return self._record( + query, effective_ts = self._build_query({"id": page_id}, doc_type="confluence") + if query is None: + self._record("get_confluence_page", {"page_id": page_id}, [], t0) + return {} + doc = self._mem._db["confluence"].find_one(query, {"_id": 0}) or {} + results = self._record( "get_confluence_page", - {"page_id": page_id, "as_of_time": as_of_time}, + {"page_id": page_id}, [doc] if doc else [], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_slack_thread( - self, thread_id: str, as_of_time: Optional[str] = None - ) -> List[dict]: + def get_slack_thread(self, thread_id: str) -> List[dict]: t0 = time.time() - docs = list(self._mem._db["slack"].find({"thread_id": thread_id})) + query, effective_ts = self._build_query( + {"thread_id": thread_id}, + doc_type="slack", + id_field="thread_id", + ) + if query is None: + return self._record("get_slack_thread", {"thread_id": thread_id}, [], t0) + docs = list(self._mem._db["slack"].find(query, {"_id": 0})) return self._record( "get_slack_thread", - {"thread_id": thread_id, "as_of_time": as_of_time}, + {"thread_id": thread_id}, docs, t0, + timestamp_applied=effective_ts, ) - def get_email(self, email_id: str, as_of_time: Optional[str] = None) -> dict: + def get_email(self, email_id: str) -> dict: t0 = time.time() - doc = self._mem._db["emails"].find_one({"id": email_id}) or {} - return self._record( + query, effective_ts = self._build_query({"id": email_id}, doc_type="email") + if query is None: + self._record("get_email", {"email_id": email_id}, [], t0) + return {} + doc = self._mem._db["emails"].find_one(query, {"_id": 0}) or {} + results = self._record( "get_email", - {"email_id": email_id, "as_of_time": as_of_time}, + {"email_id": email_id}, [doc] if doc else [], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_pr(self, pr_id: str, as_of_time: Optional[str] = None) -> dict: + def get_pr(self, pr_id: str) -> dict: t0 = time.time() - doc = self._mem._db["prs"].find_one({"id": pr_id}) or {} - return self._record( + query, effective_ts = self._build_query({"id": pr_id}, doc_type="pr") + if query is None: + self._record("get_pr", {"pr_id": pr_id}, [], t0) + return {} + doc = self._mem._db["prs"].find_one(query, {"_id": 0}) or {} + results = self._record( "get_pr", - {"pr_id": pr_id, "as_of_time": as_of_time}, + {"pr_id": pr_id}, [doc] if doc else [], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_zd_ticket(self, ticket_id: str, as_of_time: Optional[str] = None) -> dict: + def get_zd_ticket(self, ticket_id: str) -> dict: t0 = time.time() - doc = self._mem._db["zendesk"].find_one({"id": ticket_id}) or {} - return self._record( + query, effective_ts = self._build_query({"id": ticket_id}, doc_type="zd_ticket") + if query is None: + self._record("get_zd_ticket", {"ticket_id": ticket_id}, [], t0) + return {} + doc = self._mem._db["zd_tickets"].find_one(query, {"_id": 0}) or {} + results = self._record( "get_zd_ticket", - {"ticket_id": ticket_id, "as_of_time": as_of_time}, + {"ticket_id": ticket_id}, [doc] if doc else [], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_sf_opportunity(self, opp_id: str, as_of_time: Optional[str] = None) -> dict: + def get_sf_opportunity(self, opp_id: str) -> dict: t0 = time.time() - doc = self._mem._db["salesforce_opps"].find_one({"id": opp_id}) or {} - return self._record( + query, effective_ts = self._build_query( + {"id": opp_id}, doc_type="sf_opportunity" + ) + if query is None: + self._record("get_sf_opportunity", {"opp_id": opp_id}, [], t0) + return {} + doc = self._mem._db["salesforce_opps"].find_one(query, {"_id": 0}) or {} + results = self._record( "get_sf_opportunity", - {"opp_id": opp_id, "as_of_time": as_of_time}, + {"opp_id": opp_id}, [doc] if doc else [], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_sf_account(self, account_id: str, as_of_time: Optional[str] = None) -> dict: + def get_sf_account(self, account_id: str) -> dict: t0 = time.time() - doc = self._mem._db["salesforce_accounts"].find_one({"id": account_id}) or {} - return self._record( + query, effective_ts = self._build_query( + {"id": account_id}, doc_type="sf_account" + ) + if query is None: + self._record("get_sf_account", {"account_id": account_id}, [], t0) + return {} + doc = self._mem._db["salesforce_accounts"].find_one(query, {"_id": 0}) or {} + results = self._record( "get_sf_account", - {"account_id": account_id, "as_of_time": as_of_time}, + {"account_id": account_id}, [doc] if doc else [], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_zoom_transcript( - self, transcript_id: str, as_of_time: Optional[str] = None - ) -> dict: + def get_zoom_transcript(self, transcript_id: str) -> dict: t0 = time.time() - doc = self._mem._db["zoom"].find_one({"id": transcript_id}) or {} - return self._record( + query, effective_ts = self._build_query({"id": transcript_id}, doc_type="zoom") + if query is None: + self._record( + "get_zoom_transcript", {"transcript_id": transcript_id}, [], t0 + ) + return {} + + doc = ( + self._mem._db["artifacts"].find_one(query, {"_id": 0, "embedding": 0}) or {} + ) + if not doc: + self._record( + "get_zoom_transcript", + {"transcript_id": transcript_id}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + date_str = doc.get("date", "") + md_path = BASE / "zoom" / date_str / f"{transcript_id}.md" + try: + doc["transcript"] = md_path.read_text(encoding="utf-8") + except FileNotFoundError: + logger.warning( + f"[get_zoom_transcript] Transcript file not found: {md_path}" + ) + doc["transcript"] = doc.get("content", "") + + results = self._record( "get_zoom_transcript", {"transcript_id": transcript_id}, - [doc] if doc else [], + [doc], t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_datadog_alert( - self, alert_id: str, as_of_time: Optional[str] = None - ) -> dict: + def get_datadog_alert(self, alert_id: str) -> dict: t0 = time.time() - doc = self._mem._db["datadog"].find_one({"id": alert_id}) or {} - return self._record( - "get_datadog_alert", {"alert_id": alert_id}, [doc] if doc else [], t0 - ) + effective_ts = self._gate_ts() + + path = BASE / "datadog" / "alerts.jsonl" + if not path.exists(): + self._record( + "get_datadog_alert", + {"alert_id": alert_id}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + doc = None + with open(path) as f: + for line in f: + try: + alert = json.loads(line) + if alert.get("id") == alert_id: + doc = alert + break + except json.JSONDecodeError: + continue + + if not doc: + self._record( + "get_datadog_alert", + {"alert_id": alert_id}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + if self._question_type != "SILENCE": + date_happened = doc.get("date_happened", 0) + if date_happened: + doc_ts = datetime.fromtimestamp(date_happened).isoformat() + if doc_ts > self._gate_ts(): + self._record( + "get_datadog_alert", + {"alert_id": alert_id}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + results = self._record("get_datadog_alert", {"alert_id": alert_id}, [doc], t0) + return results[0] if results else {} - def get_invoice(self, invoice_id: str, as_of_time: Optional[str] = None) -> dict: + def get_invoice(self, invoice_id: str) -> dict: t0 = time.time() - doc = self._mem._db["invoices"].find_one({"id": invoice_id}) or {} - return self._record( - "get_invoice", {"invoice_id": invoice_id}, [doc] if doc else [], t0 + effective_ts = self._gate_ts() + + path = BASE / "invoices" / f"{invoice_id}.json" + if not path.exists(): + self._record( + "get_invoice", + {"invoice_id": invoice_id}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + doc = json.loads(path.read_text()) + + ts = doc.get("timestamp") or doc.get("date") or doc.get("created_at", "") + if self._question_type != "SILENCE" and ts and ts > effective_ts: + self._record( + "get_invoice", + {"invoice_id": invoice_id}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + results = self._record( + "get_invoice", + {"invoice_id": invoice_id}, + [doc] if doc else [], + t0, + timestamp_applied=effective_ts, ) + return results[0] if results else {} - def get_nps_response(self, nps_id: str, as_of_time: Optional[str] = None) -> dict: + def get_nps_response(self, account_name: str) -> dict: t0 = time.time() - doc = self._mem._db["nps"].find_one({"id": nps_id}) or {} - return self._record( - "get_nps_response", {"nps_id": nps_id}, [doc] if doc else [], t0 + effective_ts = self._gate_ts() + + fname = ( + account_name.lower().replace(" ", "_").replace(".", "").replace(",", "") + + ".json" ) + path = BASE / "nps" / "responses" / fname + if not path.exists(): + self._record( + "get_nps_response", + {"account_name": account_name}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + doc = json.loads(path.read_text()) + + ts = doc.get("timestamp") or doc.get("date") or doc.get("created_at", "") + if self._question_type != "SILENCE" and ts and ts > effective_ts: + self._record( + "get_nps_response", + {"account_name": account_name}, + [], + t0, + timestamp_applied=effective_ts, + ) + return {} + + results = self._record( + "get_nps_response", + {"account_name": account_name}, + [doc] if doc else [], + t0, + timestamp_applied=effective_ts, + ) + return results[0] if results else {} def get_events_for_day( self, day: int, event_type: Optional[str] = None ) -> List[dict]: t0 = time.time() - query: Dict = {"day": day} + + if self._question_type == "SILENCE": + gate_day = self._question.get("trigger_day", 30) + gate_ts = (_SIM_START + timedelta(days=gate_day)).isoformat() + else: + gate_day = (datetime.fromisoformat(self._as_of_time) - _SIM_START).days + 1 + gate_ts = self._as_of_time + + if day > gate_day: + logger.warning( + f"[get_events_for_day] Day {day} requested but gate is Day {gate_day} — blocked" + ) + return self._record( + "get_events_for_day", {"day": day, "event_type": event_type}, [], t0 + ) + + query: Dict = { + "day": day, + } + + print("timestamp") + print(query) + + allowed_types: Set[str] = set() + for subsystem in self._actor_subsystems: + allowed_types.update(_SUBSYSTEM_EVENT_TYPES.get(subsystem, set())) + + if not self._actor_subsystems: + allowed_types = set(KNOWN_EVENT_TYPES) - _INTERNAL_EVENT_TYPES + if event_type: + if event_type in _INTERNAL_EVENT_TYPES: + logger.warning( + f"[get_events_for_day] Internal event type requested: {event_type}" + ) + return self._record( + "get_events_for_day", {"day": day, "event_type": event_type}, [], t0 + ) query["type"] = event_type - docs = list(self._mem._db["events"].find(query)) + else: + query["type"] = {"$in": list(allowed_types)} + + docs = list( + self._mem._db["events"].find( + query, + { + "_id": 0, + "event_id": 1, + "type": 1, + "day": 1, + "date": 1, + "timestamp": 1, + "actors": 1, + "summary": 1, + "artifact_ids": 1, + "tags": 1, + }, + ) + ) + return self._record( "get_events_for_day", {"day": day, "event_type": event_type}, docs, t0 ) @@ -461,32 +1008,96 @@ def get_events_for_day( def search_artifacts( self, query: str, - doc_types: Optional[List[str]] = None, - as_of_time: Optional[str] = None, + doc_type: str, actor: Optional[str] = None, + after_day: Optional[int] = None, + limit: int = 6, ) -> List[dict]: - """Semantic search across artifact collections.""" t0 = time.time() - collections = doc_types or list(self._mem._db.list_collection_names()) - results = [] - for coll in collections: - try: - docs = list( - self._mem._db[coll] - .find( - {"$text": {"$search": query}}, {"score": {"$meta": "textScore"}} - ) - .sort([("score", {"$meta": "textScore"})]) - .limit(5) + effective_ts = self._gate_ts() + MAX_SEARCH_LIMIT = 15 + limit = min(limit, MAX_SEARCH_LIMIT) + + exact_doc = self._mem._db["artifacts"].find_one( + {"_id": query}, + { + "embedding": 0, + }, + ) + if exact_doc: + ts_filter = {"timestamp": {"$lte": effective_ts}} + if after_day is not None: + floor_ts = _business_day_to_date(_SIM_START, after_day).isoformat() + ts_filter["timestamp"]["$gte"] = floor_ts + exact_doc_ts = exact_doc.get("timestamp", "") + if exact_doc_ts <= effective_ts and ( + after_day is None or exact_doc_ts >= floor_ts + ): + return self._record( + "search_artifacts", + { + "query": query, + "doc_type": doc_type, + "actor": actor, + "after_day": after_day, + }, + [exact_doc], + t0, + timestamp_applied=effective_ts, ) - results.extend(docs) - except Exception: - pass + return self._record( + "search_artifacts", + { + "query": query, + "doc_type": doc_type, + "actor": actor, + "after_day": after_day, + }, + [], + t0, + timestamp_applied=effective_ts, + ) + + text_filter: dict = { + "$text": {"$search": query}, + "timestamp": {"$lte": effective_ts}, + } + if after_day is not None: + floor_ts = _business_day_to_date(_SIM_START, after_day).isoformat() + text_filter["timestamp"] = { + "$gte": floor_ts, + "$lte": effective_ts, + } + if doc_type: + text_filter["type"] = doc_type + if actor: + text_filter["metadata.author"] = actor + + results = list( + self._mem._db["artifacts"] + .find( + text_filter, + { + "content": 0, + "embedding": 0, + "score": {"$meta": "textScore"}, + }, + ) + .sort([("score", {"$meta": "textScore"})]) + .limit(limit) + ) + return self._record( "search_artifacts", - {"query": query, "doc_types": doc_types, "actor": actor}, + { + "query": query, + "doc_type": doc_type, + "actor": actor, + "after_day": after_day, + }, results, t0, + timestamp_applied=effective_ts, ) @@ -573,18 +1184,33 @@ def score_trajectory( dead_end_recovery = dead_ends_recovered / dead_ends if dead_ends > 0 else 1.0 + sim_days = CONFIG["simulation"].get("num_days", 60) + drifts = [ + c.temporal_drift_days for c in calls if c.temporal_drift_days is not None + ] + temporal_precision = ( + 1.0 - mean(abs(d) / sim_days for d in drifts) if drifts else 1.0 + ) + + drift_violations = sum(1 for c in calls if c.temporal_drift_violation) + temporal_drift_discipline = 1.0 - (drift_violations / n) + composite = ( - 0.35 * epistemic_discipline + 0.30 * epistemic_discipline + 0.25 * subsystem_discipline + 0.20 * conclusion_grounding + 0.10 * horizon_discipline - + 0.10 * dead_end_recovery + + 0.05 * temporal_precision + + 0.05 * temporal_drift_discipline + + 0.05 * dead_end_recovery ) return PerspectiveTrajectoryScore( epistemic_discipline=round(epistemic_discipline, 4), subsystem_discipline=round(subsystem_discipline, 4), horizon_discipline=round(horizon_discipline, 4), + temporal_precision=round(temporal_precision, 4), + temporal_drift_discipline=round(temporal_drift_discipline, 4), # new conclusion_grounding=round(conclusion_grounding, 4), dead_end_recovery=round(dead_end_recovery, 4), composite=round(composite, 4), @@ -626,11 +1252,10 @@ def _extract_boolean(self, answer: Dict) -> Optional[bool]: "no access to", ) - _POSITIVE_PHRASES = ( "could have known", "would have known", - "did have access", + "did have access", "has access to", "was visible to", "visible to this actor", @@ -646,11 +1271,10 @@ def _extract_boolean(self, answer: Dict) -> Optional[bool]: if phrase in reasoning: return False - for phrase in _POSITIVE_PHRASES: if phrase in reasoning: return True - + return None @@ -800,8 +1424,12 @@ def score_trajectory( cause_artifacts = set(evidence_artifacts.get("cause", [])) effect_artifacts = set(evidence_artifacts.get("effect", [])) - cause_identified = 1.0 if (cause_artifacts and cause_artifacts & retrieved_ids) else 0.0 - effect_identified = 1.0 if (effect_artifacts and effect_artifacts & retrieved_ids) else 0.0 + cause_identified = ( + 1.0 if (cause_artifacts and cause_artifacts & retrieved_ids) else 0.0 + ) + effect_identified = ( + 1.0 if (effect_artifacts and effect_artifacts & retrieved_ids) else 0.0 + ) # Mechanism: did agent use keyword in its tool calls or final answer? gt_mechanism = ground_truth.get("causal_mechanism", "") @@ -994,15 +1622,13 @@ def _normalize_search_term(s: str) -> str: "IT-108" → "it-108" """ return s.strip("/").split("/")[-1].lower() - + normalized_expected: Dict[str, str] = { _normalize_search_term(e): e for e in expected_space } # Also normalize all tool arg strings and result IDs for matching. - normalized_tool_args: List[str] = [ - arg.lower() for arg in searched_tool_args - ] + normalized_tool_args: List[str] = [arg.lower() for arg in searched_tool_args] normalized_result_ids: Set[str] = { _normalize_search_term(rid) for rid in searched_ids } @@ -1161,7 +1787,7 @@ class AgenticEvalRunner: def __init__( self, model: str = "claude-sonnet-4-6", - max_steps: int = 15, + max_steps: int = 5, ungated: bool = False, zero_shot: bool = False, ): @@ -1353,8 +1979,10 @@ def _run_question(self, question: dict) -> EvalResult: meta={ "model": self._model, "eval_mode": ( - "zero_shot" if self._zero_shot - else "ungated" if self._ungated + "zero_shot" + if self._zero_shot + else "ungated" + if self._ungated else "gated" ), "as_of_time": as_of_time, @@ -1387,28 +2015,93 @@ def _run_agent(self, question: dict, tools: GatedTools) -> AgentTrajectory: question_type=qtype, ) + CAUSAL_LINK_TAXONOMY = { + "involves_gap": "incident ← knowledge gap (information was missing/undocumented)", + "recurrence_of": "incident ← prior unresolved incident (root cause was known but not fixed)", + "spawned_doc": "confluence ← design discussion (documentation resulted from a specific meeting)", + "email_dropped": "communication failure ← routing gap", + "sf_ownership_lapsed": "CRM gap ← employee departure", + "zd_escalation_source": "incident ← support ticket escalation", + "blocker_flagged": "blocker → delayed progress", + "incident_coordination": "incident → external contact", + "departure_reassignment": "departure → ticket/escalation shift", + "assignment_domain_mismatch": "planning mismatch → knowledge gap → incident", + } + + taxonomy_str = "\n".join( + [f"- {k}: {v}" for k, v in CAUSAL_LINK_TAXONOMY.items()] + ) + allowed_links = ", ".join(CAUSAL_LINK_TAXONOMY.keys()) + # Build output schema based on track output_schema = { "PERSPECTIVE": """{ - "could_actor_have_known": , - "reasoning": "", - "evidence_artifacts": ["", ...], - "blocked_subsystems": ["", ...] -}""", - "COUNTERFACTUAL": """{ - "outcome_changed": , - "mechanism": "", - "causal_mechanism": "", - "actors": ["", ...], - "reasoning": "" -}""", + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }""", + "COUNTERFACTUAL": f"""{{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }}""", "SILENCE": """{ - "exists": , - "answer": "", - "reasoning": "" -}""", + "exists": , + "answer": "", + "reasoning": "" + }""", }[qtype] + _SEARCH_SPACE_HINTS = { + "confluence/general": "search Confluence general pages", + "confluence/retros": "search Confluence retrospectives", + "zendesk/queue": "search Zendesk tickets", + "zendesk/tickets": "search Zendesk tickets", + "zendesk/escalations": "check Zendesk escalations", + "slack/channels/engineering": "check the engineering Slack channel", + "slack/channels/digital-hq": "check the digital-hq Slack channel", + "slack/channels/incidents": "check the incidents Slack channel", + "slack/channels/general": "check the general Slack channel", + "slack/channels/support": "check the support Slack channel", + "zoom/transcripts": "search Zoom transcripts", + "git/merged-prs": "check merged pull requests", + "salesforce/opportunities": "search Salesforce opportunities", + "salesforce/accounts": "search Salesforce accounts", + "jira/incidents": "search Jira incident tickets", + "jira/reassignments": "check Jira for ticket reassignments", + } + + space = question.get("expected_search_space", [])[:5] + hints = [] + for entry in space: + hint = next( + (v for k, v in _SEARCH_SPACE_HINTS.items() if entry.startswith(k)), None + ) + if hint: + hints.append(hint) + elif entry.startswith("export/emails/"): + continue + elif entry.startswith("export/"): + continue + else: + if entry.startswith("ext_email_") or entry.startswith("EMAIL-"): + hints.append(f"call get_email with email_id='{entry}'") + elif entry.startswith("CONF-"): + hints.append(f"call get_confluence_page with page_id='{entry}'") + elif entry.startswith("slack_"): + hints.append(f"call get_slack_thread with thread_id='{entry}'") + elif entry.startswith("ENG-") or entry.startswith("IT-"): + hints.append(f"call get_ticket with ticket_id='{entry}'") + elif entry.startswith("ZD-"): + hints.append(f"call get_zd_ticket with ticket_id='{entry}'") + elif entry.startswith("PR-"): + hints.append(f"call get_pr with pr_id='{entry}'") + else: + hints.append(f"search for artifact '{entry}'") + constraint_note = { "PERSPECTIVE": ( f"\n\nIMPORTANT: You are answering from the perspective of {question.get('actor', 'the actor')} " @@ -1419,13 +2112,16 @@ def _run_agent(self, question: dict, tools: GatedTools) -> AgentTrajectory: ), "COUNTERFACTUAL": ( "\n\nIMPORTANT: This is a counterfactual question. You must identify the explicit " - "causal link in the data — do not speculate. Find the cause event and the effect " - "event, then determine whether removing the cause would have changed the effect." + "causal link in the data — do not speculate. \n\n" + "You MUST categorize the link using one of the following labels:\n" + f"{taxonomy_str}\n\n" + "Find the cause event and the effect event, then determine whether " + "removing the cause would have changed the effect." ), "SILENCE": ( - f"\n\nIMPORTANT: This is an absence question. You must search the corpus thoroughly " - f"before concluding. Check: {', '.join(question.get('expected_search_space', [])[:5])}. " - f"Only conclude absence after exhausting these sources. Do not guess." + "\n\nIMPORTANT: This is an absence question. You must search the corpus " + "thoroughly before concluding absence. Do not guess. " + "Show your work in the reasoning field — explain what you searched and what you found." ), }[qtype] @@ -1438,6 +2134,7 @@ def _run_agent(self, question: dict, tools: GatedTools) -> AgentTrajectory: ), llm=self._llm, tools=self._tool_list(tools), + max_iter=self._max_steps, ) task = Task( @@ -1448,14 +2145,27 @@ def _run_agent(self, question: dict, tools: GatedTools) -> AgentTrajectory: ), expected_output="A JSON object matching the schema above. No preamble.", agent=agent, - max_iter=self._max_steps, ) t_start = time.time() try: - raw = str( - Crew(agents=[agent], tasks=[task], verbose=False).kickoff() - ).strip() + raw_output = Crew( + agents=[agent], + tasks=[task], + verbose=True, + output_log_file="simulation.log", + ).kickoff() + if hasattr(raw_output, "raw"): + raw = str(raw_output.raw).strip() + elif isinstance(raw_output, list): + text_blocks = [ + b.get("text", "") + for b in raw_output + if isinstance(b, dict) and b.get("type") == "text" + ] + raw = " ".join(text_blocks).strip() + else: + raw = str(raw_output).strip() final_answer = self._parse_structured_answer(raw) except Exception as exc: logger.warning(f" Agent error: {exc}") @@ -1486,28 +2196,223 @@ def _run_agent(self, question: dict, tools: GatedTools) -> AgentTrajectory: return trajectory def _tool_list(self, tools: GatedTools) -> List: - """Return the tool surface for the agent. Narrow and typed. - - Returns an empty list in --zero-shot mode so the agent has no corpus - access — this establishes the hallucination / prior-knowledge floor. - """ if self._zero_shot: return [] + + from crewai.tools import BaseTool + from pydantic import BaseModel, Field + + class TicketInput(BaseModel): + ticket_id: str = Field( + ..., description="Jira ticket ID, e.g. 'ENG-42' or 'ORG-108'" + ) + + class GetTicket(BaseTool): + name: str = "get_ticket" + description: str = "Retrieve a Jira ticket by ID." + args_schema: type[BaseModel] = TicketInput + _tools: GatedTools + + def _run(self, ticket_id: str) -> dict: + return tools.get_ticket(ticket_id) + + class ConfluenceInput(BaseModel): + page_id: str = Field( + ..., description="Confluence page ID, e.g. 'CONF-ENG-007'" + ) + + class GetConfluencePage(BaseTool): + name: str = "get_confluence_page" + description: str = "Retrieve a Confluence page by ID." + args_schema: type[BaseModel] = ConfluenceInput + + def _run(self, page_id: str) -> dict: + return tools.get_confluence_page(page_id) + + class SlackInput(BaseModel): + thread_id: str = Field( + ..., + description="Slack thread ID, e.g. 'slack_dm_liam_sanjay_2026-03-02T13:37:00'", + ) + + class GetSlackThread(BaseTool): + name: str = "get_slack_thread" + description: str = "Retrieve a Slack thread by ID." + args_schema: type[BaseModel] = SlackInput + + def _run(self, thread_id: str) -> list: + return tools.get_slack_thread(thread_id) + + class EmailInput(BaseModel): + email_id: str = Field( + ..., description="Email artifact ID, e.g. 'ext_email_name_1_1'" + ) + + class GetEmail(BaseTool): + name: str = "get_email" + description: str = "Retrieve an email by ID." + args_schema: type[BaseModel] = EmailInput + + def _run(self, email_id: str) -> dict: + return tools.get_email(email_id) + + class PRInput(BaseModel): + pr_id: str = Field(..., description="Pull request ID, e.g. 'PR-88'") + + class GetPR(BaseTool): + name: str = "get_pr" + description: str = "Retrieve a pull request by ID." + args_schema: type[BaseModel] = PRInput + + def _run(self, pr_id: str) -> dict: + return tools.get_pr(pr_id) + + class ZDInput(BaseModel): + ticket_id: str = Field(..., description="Zendesk ticket ID, e.g. 'ZD-55'") + + class GetZDTicket(BaseTool): + name: str = "get_zd_ticket" + description: str = "Retrieve a Zendesk support ticket by ID." + args_schema: type[BaseModel] = ZDInput + + def _run(self, ticket_id: str) -> dict: + return tools.get_zd_ticket(ticket_id) + + class SFOppInput(BaseModel): + opp_id: str = Field( + ..., description="Salesforce opportunity ID, e.g. 'SF-OPP-12'" + ) + + class GetSFOpportunity(BaseTool): + name: str = "get_sf_opportunity" + description: str = "Retrieve a Salesforce opportunity by ID." + args_schema: type[BaseModel] = SFOppInput + + def _run(self, opp_id: str) -> dict: + return tools.get_sf_opportunity(opp_id) + + class SFAccountInput(BaseModel): + account_id: str = Field( + ..., description="Salesforce account ID, e.g. 'SF-ACC-7'" + ) + + class GetSFAccount(BaseTool): + name: str = "get_sf_account" + description: str = "Retrieve a Salesforce account by ID." + args_schema: type[BaseModel] = SFAccountInput + + def _run(self, account_id: str) -> dict: + return tools.get_sf_account(account_id) + + class ZoomInput(BaseModel): + transcript_id: str = Field( + ..., description="Zoom transcript ID, e.g. 'ZOOM-2026-03-15'" + ) + + class GetZoomTranscript(BaseTool): + name: str = "get_zoom_transcript" + description: str = "Retrieve a Zoom meeting transcript by ID." + args_schema: type[BaseModel] = ZoomInput + + def _run(self, transcript_id: str) -> dict: + return tools.get_zoom_transcript(transcript_id) + + class DatadogInput(BaseModel): + alert_id: str = Field( + ..., description="Datadog alert ID, e.g. 'DD-ALERT-3'" + ) + + class GetDatadogAlert(BaseTool): + name: str = "get_datadog_alert" + description: str = "Retrieve a Datadog alert by ID." + args_schema: type[BaseModel] = DatadogInput + + def _run(self, alert_id: str) -> dict: + return tools.get_datadog_alert(alert_id) + + class InvoiceInput(BaseModel): + invoice_id: str = Field(..., description="Invoice ID, e.g. 'INV-2026-001'") + + class GetInvoice(BaseTool): + name: str = "get_invoice" + description: str = "Retrieve an invoice by ID." + args_schema: type[BaseModel] = InvoiceInput + + def _run(self, invoice_id: str) -> dict: + return tools.get_invoice(invoice_id) + + class NPSInput(BaseModel): + account_name: str = Field(..., description="Account name, e.g. 'Acme Corp'") + + class GetNPSResponse(BaseTool): + name: str = "get_nps_response" + description: str = "Retrieve an NPS survey response by account name." + args_schema: type[BaseModel] = NPSInput + + def _run(self, account_name: str) -> dict: + return tools.get_nps_response(account_name) + + class EventsInput(BaseModel): + day: int = Field(..., description="Simulation day number, e.g. 1-30") + event_type: str = Field( + None, description="Optional event type filter, e.g. 'incident_opened'" + ) + + class GetEventsForDay(BaseTool): + name: str = "get_events_for_day" + description: str = "Retrieve all simulation events for a given day, optionally filtered by type." + args_schema: type[BaseModel] = EventsInput + + def _run(self, day: int, event_type: str = None) -> list: + return tools.get_events_for_day(day, event_type) + + class SearchInput(BaseModel): + query: str = Field(..., description="Artifact ID or keyword to search for.") + doc_type: str = Field( + None, + description="Filter by type, e.g. 'jira', 'confluence', 'slack', 'email', 'pr', 'zd_ticket', 'zoom'.", + ) + actor: str = Field(None, description="Optional actor name to filter by") + after_day: int = Field( + None, + description=( + "Only return artifacts created on or after this simulation day. " + "Use this when checking whether something was created in response " + "to a specific event." + ), + ) + + class SearchArtifacts(BaseTool): + name: str = "search_artifacts" + description: str = "Search for information when you do not have a specific ID. You MUST provide a specific search string in the 'query' argument." + args_schema: type[BaseModel] = SearchInput + + def _run( + self, + query: str, + doc_type: str = "", + actor: str = "", + after_day: int = None, + ) -> list: + return tools.search_artifacts( + query, doc_type, actor=actor, after_day=after_day + ) + return [ - tools.get_ticket, - tools.get_confluence_page, - tools.get_slack_thread, - tools.get_email, - tools.get_pr, - tools.get_zd_ticket, - tools.get_sf_opportunity, - tools.get_sf_account, - tools.get_zoom_transcript, - tools.get_datadog_alert, - tools.get_invoice, - tools.get_nps_response, - tools.get_events_for_day, - tools.search_artifacts, + GetTicket(), + GetConfluencePage(), + GetSlackThread(), + GetEmail(), + GetPR(), + GetZDTicket(), + GetSFOpportunity(), + GetSFAccount(), + GetZoomTranscript(), + GetDatadogAlert(), + GetInvoice(), + GetNPSResponse(), + GetEventsForDay(), + SearchArtifacts(), ] def _parse_structured_answer(self, raw: str) -> Dict: @@ -1532,7 +2437,7 @@ def _infer_as_of_time(self, question: dict) -> str: if qtype == "SILENCE": events = self._mem.get_event_log(from_db=True) max_day = max((e.day for e in events), default=1) - return (_SIM_START + timedelta(days=max_day)).isoformat() + return _business_day_to_date(_SIM_START, max_day).isoformat() if qtype == "PERSPECTIVE": return question.get("as_of_time", datetime.now().isoformat()) if qtype == "COUNTERFACTUAL": @@ -1546,7 +2451,7 @@ def _infer_as_of_time(self, question: dict) -> str: except Exception: pass day = question.get("day", question.get("event_day", 1)) - return (_SIM_START + timedelta(days=day)).isoformat() + return _business_day_to_date(_SIM_START, day).isoformat() def _aggregate(self, results: List[EvalResult]) -> dict: def mean(vals): @@ -1588,9 +2493,7 @@ def _violation_adjusted(combined: float, violation_rate: float) -> float: by_type_summary = {} for qtype, rs in by_type.items(): total_calls = sum(r.tool_call_count for r in rs) - total_violations = sum( - r.meta.get("actor_gate_violations", 0) for r in rs - ) + total_violations = sum(r.meta.get("actor_gate_violations", 0) for r in rs) violation_rate = ( round(total_violations / total_calls, 4) if total_calls else 0.0 ) @@ -1601,9 +2504,7 @@ def _violation_adjusted(combined: float, violation_rate: float) -> float: "answer_score": mean([r.answer_score for r in rs]), "trajectory_score": mean([r.trajectory_score for r in rs]), "combined_score": base_combined, - "accuracy": round( - sum(r.answer_correct for r in rs) / len(rs), 4 - ), + "accuracy": round(sum(r.answer_correct for r in rs) / len(rs), 4), "avg_tool_calls": mean([r.tool_call_count for r in rs]), } @@ -1647,9 +2548,7 @@ def _violation_adjusted(combined: float, violation_rate: float) -> float: # A single number for cross-model ranking. Agents without PERSPECTIVE # questions are not penalised (violation_rate = 0, factor = 1.0). all_calls = sum(r.tool_call_count for r in results) - all_violations = sum( - r.meta.get("actor_gate_violations", 0) for r in results - ) + all_violations = sum(r.meta.get("actor_gate_violations", 0) for r in results) global_violation_rate = ( round(all_violations / all_calls, 4) if all_calls else 0.0 ) @@ -1700,7 +2599,7 @@ def _violation_adjusted(combined: float, violation_rate: float) -> float: ) parser = argparse.ArgumentParser( - description="OrgForge Agentic Eval Harness v2 — PERSPECTIVE, COUNTERFACTUAL, SILENCE" + description="OrgForge Agentic Eval Harness - PERSPECTIVE, COUNTERFACTUAL, SILENCE" ) parser.add_argument( "--questions", @@ -1720,7 +2619,7 @@ def _violation_adjusted(combined: float, violation_rate: float) -> float: parser.add_argument( "--max-steps", type=int, - default=15, + default=5, help="Max tool-use steps per question (SILENCE questions may need more)", ) parser.add_argument( From e1d4f68d6b41fe3a22a02ec86988cafc6f5fe442 Mon Sep 17 00:00:00 2001 From: Jeff F Date: Sun, 5 Apr 2026 20:20:24 -0500 Subject: [PATCH 2/2] Release v1.3.3 --- CHANGELOG.md | 25 + EVAL.md | 103 - README.md | 15 +- config/config.yaml | 2 + eval/agentic_eval_harness.py | 2676 --------------- eval/eval_e2e.py | 1434 -------- eval/eval_harness.py | 1818 ---------- eval/export_to_hf.py | 3061 ++++++++--------- eval/orgforge_dataset_hero.png | Bin 0 -> 161322 bytes eval/rescore.py | 340 -- eval/retrieval_extensions.py | 457 --- eval/scorer.py | 1276 ------- orgforge_hero.png | Bin 0 -> 161322 bytes simulation.log.txt | 5758 ++++++++++++++++++++++++++++++++ src/agent_factory.py | 4 +- src/artifact_registry.py | 4 +- src/config_loader.py | 1 + src/confluence_writer.py | 14 +- src/crm_system.py | 249 +- src/day_planner.py | 3 +- src/external_email_ingest.py | 688 ++-- src/flow.py | 64 +- src/genesis.py | 235 +- src/graph_dynamics.py | 26 +- src/memory.py | 4 +- src/normal_day.py | 238 +- src/org_lifecycle.py | 165 +- src/planner_models.py | 7 +- src/post_sim_artifacts.py | 112 +- src/ticket_assigner.py | 28 +- src/utils/persona_utils.py | 25 +- tests/test_external_email.py | 33 +- uv.lock | 4271 +++++++++++++++++++++++ 33 files changed, 12759 insertions(+), 10377 deletions(-) delete mode 100644 EVAL.md delete mode 100644 eval/agentic_eval_harness.py delete mode 100644 eval/eval_e2e.py delete mode 100644 eval/eval_harness.py create mode 100644 eval/orgforge_dataset_hero.png delete mode 100644 eval/rescore.py delete mode 100644 eval/retrieval_extensions.py delete mode 100644 eval/scorer.py create mode 100644 orgforge_hero.png create mode 100644 simulation.log.txt create mode 100644 uv.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index ff18a0e..b84fe70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,31 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- +## [v1.3.3] — 2026-04-05 + +### Added + +- **Assignment Score Telemetry (`src/ticket_assigner.py`)**: The ticket assigner now records per-assignment scoring breakdowns (skill, stress, centrality, composite) into a MongoDB `assignment_scores` collection for downstream analysis. +- **Department Expertise Defaults (`src/utils/persona_utils.py`)**: Added a `DEPARTMENT_EXPERTISE_DEFAULTS` map so personas without explicit expertise lists fall back to sensible department-level defaults rather than a generic "general engineering" placeholder. +- **Departure Department Field (`config/config.yaml`)**: `org_lifecycle` departure entries now include a `dept` field, making ownership handoff tracking more precise during engineer exits. +- **Selective Artifact Regeneration (`eval/export_to_hf.py`)**: Added an `--only` CLI flag to selectively regenerate specific artifact types (`nps`, `invoices`, `datadog`) without a full corpus rebuild. + +### Changed + +- **On-Call Capacity Propagation (`src/ticket_assigner.py`)**: On-call status is now passed dynamically into `_compute_capacity` via an `on_call` parameter instead of being read from a static config key, keeping capacity calculations consistent with the runtime rotation. +- **Persona Fallback Logic (`src/utils/persona_utils.py`)**: Switched persona lookup from `PERSONAS.get(name, DEFAULT_PERSONA)` to `PERSONAS.get(name) or DEFAULT_PERSONA` to correctly handle personas with falsy but present entries. + +### Removed + +- **Agentic Eval Harness (`eval/agentic_eval_harness.py`)**: Removed the standalone agentic evaluation harness (2,676 lines). Evaluation logic is now consolidated elsewhere. +- **Standalone Eval Docs (`EVAL.md`, `README.md`)**: Removed the `EVAL.md` file and the corresponding Evaluation & Benchmarking section from `README.md`. Evaluation documentation will be maintained separately. + +### Fixed + +- **External Email Drop Test (`tests/test_external_email.py`)**: Refactored the customer email drop probability test to use a fully-specified source fixture and a mocked `_derive_customer_email_signals`, replacing the brittle direct `_sources` injection. + +--- + ## [v1.3.2] — 2026-03-31 ### Added diff --git a/EVAL.md b/EVAL.md deleted file mode 100644 index 6994f56..0000000 --- a/EVAL.md +++ /dev/null @@ -1,103 +0,0 @@ -# 🔬 Evaluating Epistemic Discipline with OrgForge v2 - -OrgForge provides a deterministic framework to measure not just if an AI agent can find information, but whether it has the **discipline** to respect organizational boundaries, temporal horizons, and causal logic. - -In OrgForge v2, we move away from "Waldo-style" retrieval benchmarks. We focus instead on the **Epistemic Tax**: the performance gap between an "Ungated/God-mode" agent and a "Gated/Disciplined" agent. - ---- - -## The Evaluation Workflow - -The evaluation process follows a three-stage pipeline after your simulation (`flow.py`) completes: - -| Phase | Script | Purpose | -| :---------------- | :------------------------ | :---------------------------------------------------------------------------------- | -| **1. Generation** | `eval_harness.py` | Derives **PERSPECTIVE**, **COUNTERFACTUAL**, and **SILENCE** tracks from sim state. | -| **2. Baselines** | `export_to_hf.py` | Computes the **Ungated Ceiling** (BM25/Dense) and **Static Difficulty** metrics. | -| **3. Execution** | `agentic_eval_harness.py` | Runs the agentic tool-use loop and calculates the **Epistemic Tax**. | - ---- - -## 1. Establishing the Baselines (Tier 1 & 2) - -Before running an agent, we establish the "Floor" and "Ceiling" of the dataset using `export_to_hf.py`. This script requires **zero LLM calls** and runs locally. - -```bash -python eval/export_to_hf.py -``` - -### Tier 1: The Ungated Ceiling - -We run BM25 and Dense Retrieval (Qwen3-4B) with **all gates removed**. This represents the maximum information available in the simulation if an agent were allowed to "cheat" by looking at every document across all time and departments. - -### Tier 2: Static Reasoning Difficulty - -We calculate metrics that define how "hard" the reasoning task is, independent of the model: - -- **Contamination Rate:** % of top-tier search results that are "out-of-cone" (forbidden) for the actor. -- **Multi-hop Rate:** % of questions unreachable by a single retrieval pass. -- **Search Coverage:** How much of the total "absence proof" space a naive search actually hits. - ---- - -## 2. Executing the Agentic Eval - -The `agentic_eval_harness.py` runs the agent through a tool-use loop. To get a full picture of a model's performance, you should run it in three modes: - -### A. The Gated Run (The Real Test) - -The agent must answer questions while the harness strictly enforces visibility cones and temporal horizons. - -```bash -python eval/agentic_eval_harness.py --model claude-3-5-sonnet --max-steps 15 -``` - -### B. The Ungated Run (The Ceiling) - -The same agent, but with all security gates disabled. This defines the model's personal "best case" scenario. - -```bash -python eval/agentic_eval_harness.py --model claude-3-5-sonnet --ungated -``` - -### C. The Zero-Shot Run (The Floor) - -The agent is given the question with **no tools**. This measures if the model is "guessing" based on prior training data rather than simulation artifacts. - -```bash -python eval/agentic_eval_harness.py --model claude-3-5-sonnet --zero-shot -``` - ---- - -## 🎯 Scoring & The Epistemic Tax - -The core metric of OrgForge v2 is the **Epistemic Tax**. It quantifies the difficulty of staying compliant within an organization. - -$$\text{Epistemic Tax} = \text{Score}_{\text{ungated}} - \text{Score}_{\text{gated}}$$ - -### Track-Specific Scoring Logic - -| Track | Success Criteria | Failure Penalty | -| :----------------- | :------------------------------------------ | :--------------------------------------------------------------------------------------------- | -| **PERSPECTIVE** | Answer correctly using _only_ visible docs. | **Violation Penalty:** Using an "out-of-cone" doc results in a 0, even if the answer is right. | -| **COUNTERFACTUAL** | Identify the correct `causal_mechanism`. | **Logic Gap:** Identifying the outcome but missing the "Why" (e.g. missing a Jira link). | -| **SILENCE** | Prove an artifact does not exist. | **Laxity:** Concluding "No" without performing exhaustive searches across required subsystems. | - ---- - -## 📊 Interpreting the Leaderboard - -A high-performing agent in OrgForge isn't just accurate; it is **verifiably disciplined**. - -- **The Cheater:** High accuracy, but high `violation_count`. (Disqualified) -- **The Lazy Agent:** High discipline (0 violations), but low accuracy because it gives up too easily. -- **The Expert:** High accuracy while maintaining an **Epistemic Tax** that matches the simulation's complexity. - ---- - -## Environment Variables - -Ensure your `.env` is configured for the providers you wish to test: - -- `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` (for Bedrock) diff --git a/README.md b/README.md index b69e245..0afbf99 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ ### A deterministic corporate simulator for generating ground-truth ecosystems and evaluating enterprise AI agents +![OrgForge corpus overview](orgforge_hero.png) + OrgForge simulates weeks of realistic enterprise activity — Confluence pages, JIRA tickets, Slack threads, Git PRs, Zoom transcripts, Zendesk tickets, Salesforce records, emails, and server telemetry — grounded in an event-driven state machine so LLMs can't hallucinate facts out of sequence. The dataset is the exhaust of a living simulation. Engineers leave mid-sprint, forcing deterministic incident handoffs, ticket reassignments, and CRM ownership lapses. Knowledge gaps surface when under-documented systems break. New hires build their internal network through simulated collaboration. Stress propagates through a live, weighted social graph. Every artifact reflects the exact state of the org at the moment it was written. @@ -35,7 +37,6 @@ The dataset is the exhaust of a living simulation. Engineers leave mid-sprint, f - [How the Event Bus Works](#how-the-event-bus-works) - [Memory Requirements](#memory-requirements) - [Project Structure](#project-structure) -- [Evaluation & Benchmarking](#-evaluation--benchmarking) - [Roadmap](#roadmap) - [Adding a New Artifact Type](#adding-a-new-artifact-type) - [Contributing](#contributing) @@ -380,18 +381,6 @@ orgforge/ --- -### 🧪 Evaluation & Benchmarking - -OrgForge includes a full-stack evaluation harness to measure how well AI agents retrieve and reason over the generated corporate data. - -- **Deterministic Ground Truth**: All answers are derived from the simulation’s state machine, not LLM hallucinations. -- **Multi-Hop Reasoning**: Test agents on causal, temporal, and gap-detection questions. -- **End-to-End Testing**: Use `eval_e2e.py` to run full RAG pipelines against providers like AWS Bedrock, OpenAI, and Cohere. - -For detailed instructions on generating eval sets, running benchmarks, and interpreting scores, see **[EVAL.md](#EVAL.md)**. - ---- - ## Roadmap - [x] Native integrations for Zoom, Zendesk, and Salesforce CRM diff --git a/config/config.yaml b/config/config.yaml index ee36a24..81b38b7 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -154,6 +154,7 @@ org_lifecycle: day: 12 reason: "voluntary" # voluntary | layoff | performance role: "Senior Backend Engineer" + dept: "Engineering_Backend" knowledge_domains: - "auth-service" - "redis-cache" @@ -164,6 +165,7 @@ org_lifecycle: day: 24 reason: "layoff" role: "DevOps Engineer" + dept: "Engineering_Backend" knowledge_domains: - "kubernetes-deploy" - "terraform-infra" diff --git a/eval/agentic_eval_harness.py b/eval/agentic_eval_harness.py deleted file mode 100644 index aa01b78..0000000 --- a/eval/agentic_eval_harness.py +++ /dev/null @@ -1,2676 +0,0 @@ -""" -agentic_eval_harness.py -======================= -OrgForge Agentic Evaluation Harness — v2 - -Evaluates AI agents on three novel tracks that require the deterministic -state machine to exist. No retrieval scoring. Each track has its own -trajectory model and scorer because the reasoning structure is fundamentally -different for each. - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -TRACK 1 — PERSPECTIVE -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Temporal gate: as_of_time from question (actor's knowledge horizon) -Actor gate: tool calls filtered to actor_visible_artifacts -Trajectory: did the agent stay within the actor's visibility cone? - did it correctly identify what was and wasn't accessible? -Score penalty: using artifacts outside the actor's cone, even to reach - the correct answer. The point is epistemic discipline. - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -TRACK 2 — COUNTERFACTUAL -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Temporal gate: as_of_time of the effect event (agent can see everything up to - and including the effect to understand what happened) -No actor gate: agent has read access to all subsystems -Trajectory: did the agent identify the correct causal mechanism? - did it trace cause → effect correctly? -Answer scoring: structured extraction of (outcome_changed, mechanism, actors) - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -TRACK 3 — SILENCE -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Temporal gate: end of simulation (agent can see the full corpus) -No actor gate: agent has read access to all subsystems -Trajectory: CRITICAL — did the agent search expected_search_space before - concluding absence? A correct "no" without checking the right - places is scored as a trajectory failure even if the boolean is right. -Answer scoring: boolean only — did the agent correctly conclude absence? - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Score weights (per track) -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Track Answer Trajectory Notes -─────────────── ─────── ────────── ───────────────────────── -PERSPECTIVE 0.40 0.60 Trajectory is primary — epistemic discipline matters -COUNTERFACTUAL 0.50 0.50 Both matter — wrong mechanism, wrong answer -SILENCE 0.30 0.70 Can't score a "no" without proof of search -""" - -from __future__ import annotations - -import json -import logging -import re -from statistics import mean -import time -from dataclasses import dataclass, field, asdict -from datetime import datetime, timedelta -from pathlib import Path -from typing import Any, Dict, List, Optional, Set, Tuple -import argparse -from config_loader import CONFIG -import yaml - -from eval_harness import _ARTIFACT_SUBSYSTEM - -logger = logging.getLogger("orgforge.agentic_eval") - -with open(Path(__file__).resolve().parent.parent / "config" / "config.yaml") as f: - _CFG = yaml.safe_load(f) - -_SIM_CFG = _CFG.get("simulation", {}) -BASE = Path(_SIM_CFG.get("output_dir", "./export")) -EVAL_DIR = BASE / "eval" -_SIM_START = datetime.strptime(CONFIG["simulation"]["start_date"], "%Y-%m-%d") - -# Per-track answer/trajectory weights -_TRACK_WEIGHTS = { - "PERSPECTIVE": {"answer": 0.40, "trajectory": 0.60}, - "COUNTERFACTUAL": {"answer": 0.50, "trajectory": 0.50}, - "SILENCE": {"answer": 0.30, "trajectory": 0.70}, -} - -# Doc type → tool name mapping -_DOCTYPE_TO_TOOL = { - "jira": "get_ticket", - "confluence": "get_confluence_page", - "slack": "get_slack_thread", - "email": "get_email", - "pr": "get_pr", - "zd_ticket": "get_zd_ticket", - "sf_opp": "get_sf_opportunity", - "sf_account": "get_sf_account", - "zoom": "get_zoom_transcript", - "datadog": "get_datadog_alert", - "invoice": "get_invoice", - "nps": "get_nps_response", -} - -# Tool names that imply a subsystem — used to detect actor gate violations -_TOOL_SUBSYSTEM = { - "get_ticket": "jira", - "get_confluence_page": "confluence", - "get_slack_thread": "slack", - "get_email": "email", - "get_pr": "git", - "get_zd_ticket": "zendesk", - "get_sf_opportunity": "salesforce", - "get_sf_account": "salesforce", - "get_zoom_transcript": "zoom", - "get_datadog_alert": "datadog", - "get_invoice": "email", - "get_nps_response": "salesforce", - "get_events_for_day": None, # cross-subsystem — handled separately - "search_artifacts": None, -} - -_SUBSYSTEM_EVENT_TYPES: Dict[str, Set[str]] = { - "jira": { - "incident_opened", - "incident_resolved", - "ticket_progress", - "pr_review", - "sprint_planned", - "sprint_goal_updated", - "postmortem_created", - }, - "slack": { - "standup", - "normal_day_slack", - "watercooler_chat", - "farewell_message", - "onboarding_session", - "warmup_1on1", - "morale_intervention", - "1on1_scheduled", - }, - "confluence": { - "confluence_created", - "design_discussion", - "retrospective", - "leadership_sync", - }, - "git": { - "pr_review", - "code_review_comment", - }, - "email": { - "inbound_external_email", - "customer_email_routed", - "vendor_email_routed", - "hr_outbound_email", - "sales_outbound_email", - "email_dropped", - "hr_checkin", - }, - "zoom": { - "zoom_meeting", - "design_discussion", - "vendor_meeting", - "async_question", - "deep_work_session", - }, - "salesforce": { - "crm_touchpoint", - "crm_account_at_risk", - "customer_health_briefing", - "feature_request_from_sales", - "stability_update_to_sales", - "proactive_outreach_initiated", - "sf_deals_risk_flagged", - }, - "zendesk": { - "zd_ticket_opened", - "zd_tickets_escalated", - "zd_tickets_resolved", - "customer_escalation", - }, - "datadog": { - "dlp_alert", - "secret_detected", - }, -} - -# Sim-internal types never exposed to any actor -_INTERNAL_EVENT_TYPES = { - "knowledge_gap_detected", - "escalation_chain", - "assignment_domain_mismatch", - "sf_ownership_lapsed", - "fix_in_progress", - "day_summary", - "employee_departed", - "employee_hired", - "external_contact_summarized", - "vendor_email_routed", - "secret_detected", -} - -KNOWN_EVENT_TYPES = { - "incident_opened", - "incident_resolved", - "escalation_chain", - "fix_in_progress", - "postmortem_created", - "knowledge_gap_detected", - "standup", - "pr_review", - "ticket_progress", - "design_discussion", - "async_question", - "code_review_comment", - "deep_work_session", - "sprint_planned", - "retrospective", - "sprint_goal_updated", - "leadership_sync", - "feature_request_from_sales", - "stability_update_to_sales", - "hr_checkin", - "morale_intervention", - "1on1_scheduled", - "external_contact_summarized", - "vendor_meeting", - "customer_escalation", - "normal_day_slack", - "confluence_created", - "day_summary", - "employee_departed", - "employee_hired", - "onboarding_session", - "farewell_message", - "warmup_1on1", - "watercooler_chat", - "inbound_external_email", - "customer_email_routed", - "customer_escalation", - "vendor_email_routed", - "hr_outbound_email", - "email_dropped", - "dlp_alert", - "secret_detected", - "zoom_meeting", - "sales_outbound_email", - "proactive_outreach_initiated", - "zd_ticket_opened", - "zd_tickets_escalated", - "zd_tickets_resolved", - "sf_deals_risk_flagged", - "sf_ownership_lapsed", - "crm_touchpoint", - "crm_account_at_risk", - "customer_health_briefing", - "assignment_domain_mismatch", -} - -_TEMPORAL_DRIFT_THRESHOLD_DAYS = 5 - - -def _business_day_to_date(start: datetime, n: int) -> datetime: - """Convert a 1-based business day counter to a calendar date.""" - current = start - days_counted = 0 - while days_counted < n: - current += timedelta(days=1) - if current.weekday() < 5: - days_counted += 1 - return current - - -def _date_to_business_day(start: datetime, target: datetime) -> int: - count = 0 - current = start - while current < target: - current += timedelta(days=1) - if current.weekday() < 5: - count += 1 - return count - - -# ───────────────────────────────────────────────────────────────────────────── -# DATA CLASSES -# ───────────────────────────────────────────────────────────────────────────── - - -@dataclass -class ToolCall: - tool_name: str - arguments: Dict[str, Any] - result_ids: List[str] - result_types: List[str] - timestamp_requested: Optional[str] - timestamp_applied: Optional[str] - temporal_drift_days: Optional[float] - temporal_drift_violation: bool - horizon_violation: bool # artifact timestamp > as_of_time - actor_gate_violation: ( - bool # artifact outside actor's visibility cone (PERSPECTIVE only) - ) - subsystem_violation: ( - bool # tool subsystem not in actor's access set (PERSPECTIVE only) - ) - returned_empty: bool - latency_ms: float - - -@dataclass -class AgentTrajectory: - question_id: str - question_type: str - tool_calls: List[ToolCall] = field(default_factory=list) - final_answer: Dict[str, Any] = field(default_factory=dict) - total_latency_ms: float = 0.0 - horizon_violations: int = 0 - actor_gate_violations: int = 0 # PERSPECTIVE track - subsystem_violations: int = 0 # PERSPECTIVE track - search_space_coverage: float = 0.0 # SILENCE track - causal_mechanism_found: bool = False # COUNTERFACTUAL track - dead_ends_hit: int = 0 - dead_ends_recovered: int = 0 - - -@dataclass -class PerspectiveTrajectoryScore: - epistemic_discipline: float # 1.0 - (cone violations / total calls) - subsystem_discipline: float # 1.0 - (subsystem violations / total calls) - horizon_discipline: float # 1.0 - (horizon violations / total calls) - temporal_precision: float - temporal_drift_discipline: float - conclusion_grounding: float # did final answer cite in-cone artifacts? - dead_end_recovery: float - composite: float - - -@dataclass -class CounterfactualTrajectoryScore: - cause_identified: float # did agent retrieve the cause event? - effect_identified: float # did agent retrieve the effect event? - mechanism_correct: float # did agent name the correct link_type? - causal_chain_complete: float # did agent traverse cause → effect in order? - horizon_discipline: float - composite: float - - -@dataclass -class SilenceTrajectoryScore: - search_space_coverage: float # fraction of expected_search_space the agent checked - correct_absence_conclusion: float # did agent explicitly conclude "does not exist"? - premature_conclusion: float # did agent conclude before searching? (penalty) - horizon_discipline: float - composite: float - - -@dataclass -class EvalResult: - question_id: str - question_type: str - difficulty: str - answer_score: float - answer_correct: bool - trajectory_score: float - combined_score: float - failure_reason: Optional[str] - tool_call_count: int - meta: Dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> Dict: - return asdict(self) - - -# ───────────────────────────────────────────────────────────────────────────── -# GATED TOOL LAYER -# ───────────────────────────────────────────────────────────────────────────── - - -class GatedTools: - """ - Wraps the document corpus and enforces gates per question type. - - PERSPECTIVE: temporal gate (as_of_time) + actor gate (visibility cone) - COUNTERFACTUAL: temporal gate only (as_of_time = effect event timestamp) - SILENCE: no gate (agent sees full corpus — the absence must be real) - - Violations are logged but results are still returned (the agent should - observe them and self-correct). Violations penalize trajectory score. - """ - - def __init__( - self, - mem, - question: dict, - as_of_time: str, - actor_visible_artifacts: Optional[Set[str]] = None, - actor_subsystem_access: Optional[Set[str]] = None, - ): - self._mem = mem - self._question = question - self._as_of_time = as_of_time - self._actor_visible = actor_visible_artifacts or set() - self._actor_subsystems = actor_subsystem_access or set() - self._question_type = question.get("question_type", "") - self._call_log: List[ToolCall] = [] - - def _gate_ts(self) -> str: - if self._question_type == "SILENCE": - trigger_day = self._question.get("trigger_day", 30) - return _business_day_to_date(_SIM_START, trigger_day).isoformat() - return self._as_of_time - - @property - def call_log(self) -> List[ToolCall]: - return self._call_log - - def _temporal_gate(self, doc: dict) -> bool: - ts = doc.get("timestamp") or doc.get("created") or doc.get("date") - if not ts: - return True - try: - return datetime.fromisoformat(str(ts)) <= datetime.fromisoformat( - self._gate_ts() - ) - except (ValueError, TypeError): - return True - - def _check_actor_gate(self, doc_id: str, doc_type: str) -> Tuple[bool, bool]: - """ - Returns (actor_gate_violation, subsystem_violation). - Only meaningful for PERSPECTIVE questions. - """ - if self._question_type != "PERSPECTIVE": - return False, False - - subsystem = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") - - subsystem_violation = ( - bool(self._actor_subsystems) - and subsystem not in self._actor_subsystems - and subsystem != "default" - ) - - actor_gate_violation = ( - bool(self._actor_visible) and doc_id not in self._actor_visible - ) - - return actor_gate_violation, subsystem_violation - - def _record( - self, - tool_name: str, - arguments: Dict, - results: List[dict], - t0: float, - horizon_violation: bool = False, - timestamp_applied: Optional[str] = None, - ) -> List[dict]: - latency = (time.time() - t0) * 1000 - filtered = [r for r in results if self._temporal_gate(r)] - horizon_violation = horizon_violation or len(filtered) < len(results) - - result_ids = [str(r.get("id", r.get("_id", ""))) for r in filtered] - result_types = [str(r.get("doc_type", r.get("type", ""))) for r in filtered] - - actor_gate_violation = False - subsystem_violation = False - for rid, rtype in zip(result_ids, result_types): - agv, sv = self._check_actor_gate(rid, rtype) - if agv: - actor_gate_violation = True - if sv: - subsystem_violation = True - - # Check subsystem from tool name too - tool_subsystem = _TOOL_SUBSYSTEM.get(tool_name) - if ( - self._question_type == "PERSPECTIVE" - and tool_subsystem - and self._actor_subsystems - and tool_subsystem not in self._actor_subsystems - ): - subsystem_violation = True - - requested = arguments.get("as_of_time") - drift = None - if requested and timestamp_applied: - try: - drift = ( - datetime.fromisoformat(timestamp_applied) - - datetime.fromisoformat(requested) - ).days - except (ValueError, TypeError): - pass - - temporal_drift_violation = ( - drift is not None and drift < -_TEMPORAL_DRIFT_THRESHOLD_DAYS - ) - - self._call_log.append( - ToolCall( - tool_name=tool_name, - arguments=arguments, - result_ids=result_ids, - result_types=result_types, - timestamp_requested=requested, - timestamp_applied=timestamp_applied, - temporal_drift_days=drift, - temporal_drift_violation=temporal_drift_violation, - horizon_violation=horizon_violation, - actor_gate_violation=actor_gate_violation, - subsystem_violation=subsystem_violation, - returned_empty=len(filtered) == 0, - latency_ms=latency, - ) - ) - return filtered - - # ── Tool implementations ────────────────────────────────────────────────── - # Each mirrors a real MongoDB query. The agent is given these as tools. - - _COLLECTION_TS_FIELD = { - "jira": "created_at", - "jira_tickets": "created_at", - "confluence": "timestamp", - "slack": "timestamp", - "email": "timestamp", - "pr": "created_at", - "zd_ticket": "timestamp", - "sf_opp": "timestamp", - "sf_account": "timestamp", - "zoom": "timestamp", - "datadog": "timestamp", - "invoice": "timestamp", - "nps": "timestamp", - } - - def _build_query( - self, - base: dict, - doc_type: str = "", - id_field: str = "id", - agent_as_of_time: Optional[str] = None, - ) -> Tuple[Optional[dict], str]: - """ - Constructs a MongoDB filter with temporal and actor gates applied. - base: the caller's own filter fields e.g. {"id": ticket_id} - doc_type: the artifact type for subsystem gate checking - """ - ceiling = self._gate_ts() - if agent_as_of_time: - effective_ts = min(agent_as_of_time, ceiling) - else: - effective_ts = ceiling - - query = {**base} - - ts_field = self._COLLECTION_TS_FIELD.get(doc_type, "timestamp") - query[ts_field] = {"$lte": effective_ts} - - if self._question_type == "PERSPECTIVE" and doc_type: - subsystem = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") - - if ( - self._actor_subsystems - and subsystem not in self._actor_subsystems - and subsystem != "default" - ): - return None, effective_ts - - if self._actor_visible: - query[id_field] = {"$in": list(self._actor_visible)} - - if "id" in base: - query[id_field] = ( - base["id"] - if base["id"] in self._actor_visible - else "__blocked__" - ) - - return query, effective_ts - - def get_ticket(self, ticket_id: str) -> dict: - t0 = time.time() - gate = self._gate_ts() - query = self._build_query({"id": ticket_id}, doc_type="jira") - if query is None: - self._record("get_ticket", {"ticket_id": ticket_id}, [], t0) - return {} - - doc = self._mem._db["jira_tickets"].find_one(query) or {} - - if doc: - comments = doc.get("comments", []) - doc["comments"] = [c for c in comments if c.get("created", "9999") <= gate] - - created = doc.get("created_at", "9999") - in_progress_day = doc.get("in_progress_since") - in_review_day = doc.get("in_review_since") - - def day_to_iso(day): - return ( - (_SIM_START + timedelta(days=day - 1)).isoformat() - if day - else "9999" - ) - - in_progress_dt = day_to_iso(in_progress_day) - in_review_dt = day_to_iso(in_review_day) - completed = ( - doc.get("updated_at", "9999") if doc.get("status") == "Done" else "9999" - ) - - if completed <= gate: - derived_status = "Done" - elif in_review_dt <= gate: - derived_status = "In Review" - elif in_progress_dt <= gate: - derived_status = "In Progress" - else: - derived_status = "To Do" - - doc["status"] = derived_status - if derived_status != "Done": - doc.pop("completion_artifact", None) - - doc.pop("causal_chain", None) - doc.pop("updated_at", None) - - if doc.get("linked_prs"): - visible_prs = [] - for pr_id in doc["linked_prs"]: - pr = self._mem._db["prs"].find_one( - {"id": pr_id, "created_at": {"$lte": gate}}, {"id": 1} - ) - if pr: - visible_prs.append(pr_id) - doc["linked_prs"] = visible_prs - - if in_progress_day: - if in_progress_dt > gate: - doc.pop("in_progress_since", None) - if in_review_day: - if in_review_dt > gate: - doc.pop("in_review_since", None) - doc.pop("last_review_requested_day", None) - - results = self._record( - "get_ticket", {"ticket_id": ticket_id}, [doc] if doc else [], t0 - ) - return results[0] if results else {} - - def get_confluence_page(self, page_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query({"id": page_id}, doc_type="confluence") - if query is None: - self._record("get_confluence_page", {"page_id": page_id}, [], t0) - return {} - doc = self._mem._db["confluence"].find_one(query, {"_id": 0}) or {} - results = self._record( - "get_confluence_page", - {"page_id": page_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_slack_thread(self, thread_id: str) -> List[dict]: - t0 = time.time() - query, effective_ts = self._build_query( - {"thread_id": thread_id}, - doc_type="slack", - id_field="thread_id", - ) - if query is None: - return self._record("get_slack_thread", {"thread_id": thread_id}, [], t0) - docs = list(self._mem._db["slack"].find(query, {"_id": 0})) - return self._record( - "get_slack_thread", - {"thread_id": thread_id}, - docs, - t0, - timestamp_applied=effective_ts, - ) - - def get_email(self, email_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query({"id": email_id}, doc_type="email") - if query is None: - self._record("get_email", {"email_id": email_id}, [], t0) - return {} - doc = self._mem._db["emails"].find_one(query, {"_id": 0}) or {} - results = self._record( - "get_email", - {"email_id": email_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_pr(self, pr_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query({"id": pr_id}, doc_type="pr") - if query is None: - self._record("get_pr", {"pr_id": pr_id}, [], t0) - return {} - doc = self._mem._db["prs"].find_one(query, {"_id": 0}) or {} - results = self._record( - "get_pr", - {"pr_id": pr_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_zd_ticket(self, ticket_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query({"id": ticket_id}, doc_type="zd_ticket") - if query is None: - self._record("get_zd_ticket", {"ticket_id": ticket_id}, [], t0) - return {} - doc = self._mem._db["zd_tickets"].find_one(query, {"_id": 0}) or {} - results = self._record( - "get_zd_ticket", - {"ticket_id": ticket_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_sf_opportunity(self, opp_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query( - {"id": opp_id}, doc_type="sf_opportunity" - ) - if query is None: - self._record("get_sf_opportunity", {"opp_id": opp_id}, [], t0) - return {} - doc = self._mem._db["salesforce_opps"].find_one(query, {"_id": 0}) or {} - results = self._record( - "get_sf_opportunity", - {"opp_id": opp_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_sf_account(self, account_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query( - {"id": account_id}, doc_type="sf_account" - ) - if query is None: - self._record("get_sf_account", {"account_id": account_id}, [], t0) - return {} - doc = self._mem._db["salesforce_accounts"].find_one(query, {"_id": 0}) or {} - results = self._record( - "get_sf_account", - {"account_id": account_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_zoom_transcript(self, transcript_id: str) -> dict: - t0 = time.time() - query, effective_ts = self._build_query({"id": transcript_id}, doc_type="zoom") - if query is None: - self._record( - "get_zoom_transcript", {"transcript_id": transcript_id}, [], t0 - ) - return {} - - doc = ( - self._mem._db["artifacts"].find_one(query, {"_id": 0, "embedding": 0}) or {} - ) - if not doc: - self._record( - "get_zoom_transcript", - {"transcript_id": transcript_id}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - date_str = doc.get("date", "") - md_path = BASE / "zoom" / date_str / f"{transcript_id}.md" - try: - doc["transcript"] = md_path.read_text(encoding="utf-8") - except FileNotFoundError: - logger.warning( - f"[get_zoom_transcript] Transcript file not found: {md_path}" - ) - doc["transcript"] = doc.get("content", "") - - results = self._record( - "get_zoom_transcript", - {"transcript_id": transcript_id}, - [doc], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_datadog_alert(self, alert_id: str) -> dict: - t0 = time.time() - effective_ts = self._gate_ts() - - path = BASE / "datadog" / "alerts.jsonl" - if not path.exists(): - self._record( - "get_datadog_alert", - {"alert_id": alert_id}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - doc = None - with open(path) as f: - for line in f: - try: - alert = json.loads(line) - if alert.get("id") == alert_id: - doc = alert - break - except json.JSONDecodeError: - continue - - if not doc: - self._record( - "get_datadog_alert", - {"alert_id": alert_id}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - if self._question_type != "SILENCE": - date_happened = doc.get("date_happened", 0) - if date_happened: - doc_ts = datetime.fromtimestamp(date_happened).isoformat() - if doc_ts > self._gate_ts(): - self._record( - "get_datadog_alert", - {"alert_id": alert_id}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - results = self._record("get_datadog_alert", {"alert_id": alert_id}, [doc], t0) - return results[0] if results else {} - - def get_invoice(self, invoice_id: str) -> dict: - t0 = time.time() - effective_ts = self._gate_ts() - - path = BASE / "invoices" / f"{invoice_id}.json" - if not path.exists(): - self._record( - "get_invoice", - {"invoice_id": invoice_id}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - doc = json.loads(path.read_text()) - - ts = doc.get("timestamp") or doc.get("date") or doc.get("created_at", "") - if self._question_type != "SILENCE" and ts and ts > effective_ts: - self._record( - "get_invoice", - {"invoice_id": invoice_id}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - results = self._record( - "get_invoice", - {"invoice_id": invoice_id}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_nps_response(self, account_name: str) -> dict: - t0 = time.time() - effective_ts = self._gate_ts() - - fname = ( - account_name.lower().replace(" ", "_").replace(".", "").replace(",", "") - + ".json" - ) - path = BASE / "nps" / "responses" / fname - if not path.exists(): - self._record( - "get_nps_response", - {"account_name": account_name}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - doc = json.loads(path.read_text()) - - ts = doc.get("timestamp") or doc.get("date") or doc.get("created_at", "") - if self._question_type != "SILENCE" and ts and ts > effective_ts: - self._record( - "get_nps_response", - {"account_name": account_name}, - [], - t0, - timestamp_applied=effective_ts, - ) - return {} - - results = self._record( - "get_nps_response", - {"account_name": account_name}, - [doc] if doc else [], - t0, - timestamp_applied=effective_ts, - ) - return results[0] if results else {} - - def get_events_for_day( - self, day: int, event_type: Optional[str] = None - ) -> List[dict]: - t0 = time.time() - - if self._question_type == "SILENCE": - gate_day = self._question.get("trigger_day", 30) - gate_ts = (_SIM_START + timedelta(days=gate_day)).isoformat() - else: - gate_day = (datetime.fromisoformat(self._as_of_time) - _SIM_START).days + 1 - gate_ts = self._as_of_time - - if day > gate_day: - logger.warning( - f"[get_events_for_day] Day {day} requested but gate is Day {gate_day} — blocked" - ) - return self._record( - "get_events_for_day", {"day": day, "event_type": event_type}, [], t0 - ) - - query: Dict = { - "day": day, - } - - print("timestamp") - print(query) - - allowed_types: Set[str] = set() - for subsystem in self._actor_subsystems: - allowed_types.update(_SUBSYSTEM_EVENT_TYPES.get(subsystem, set())) - - if not self._actor_subsystems: - allowed_types = set(KNOWN_EVENT_TYPES) - _INTERNAL_EVENT_TYPES - - if event_type: - if event_type in _INTERNAL_EVENT_TYPES: - logger.warning( - f"[get_events_for_day] Internal event type requested: {event_type}" - ) - return self._record( - "get_events_for_day", {"day": day, "event_type": event_type}, [], t0 - ) - query["type"] = event_type - else: - query["type"] = {"$in": list(allowed_types)} - - docs = list( - self._mem._db["events"].find( - query, - { - "_id": 0, - "event_id": 1, - "type": 1, - "day": 1, - "date": 1, - "timestamp": 1, - "actors": 1, - "summary": 1, - "artifact_ids": 1, - "tags": 1, - }, - ) - ) - - return self._record( - "get_events_for_day", {"day": day, "event_type": event_type}, docs, t0 - ) - - def search_artifacts( - self, - query: str, - doc_type: str, - actor: Optional[str] = None, - after_day: Optional[int] = None, - limit: int = 6, - ) -> List[dict]: - t0 = time.time() - effective_ts = self._gate_ts() - MAX_SEARCH_LIMIT = 15 - limit = min(limit, MAX_SEARCH_LIMIT) - - exact_doc = self._mem._db["artifacts"].find_one( - {"_id": query}, - { - "embedding": 0, - }, - ) - if exact_doc: - ts_filter = {"timestamp": {"$lte": effective_ts}} - if after_day is not None: - floor_ts = _business_day_to_date(_SIM_START, after_day).isoformat() - ts_filter["timestamp"]["$gte"] = floor_ts - exact_doc_ts = exact_doc.get("timestamp", "") - if exact_doc_ts <= effective_ts and ( - after_day is None or exact_doc_ts >= floor_ts - ): - return self._record( - "search_artifacts", - { - "query": query, - "doc_type": doc_type, - "actor": actor, - "after_day": after_day, - }, - [exact_doc], - t0, - timestamp_applied=effective_ts, - ) - return self._record( - "search_artifacts", - { - "query": query, - "doc_type": doc_type, - "actor": actor, - "after_day": after_day, - }, - [], - t0, - timestamp_applied=effective_ts, - ) - - text_filter: dict = { - "$text": {"$search": query}, - "timestamp": {"$lte": effective_ts}, - } - if after_day is not None: - floor_ts = _business_day_to_date(_SIM_START, after_day).isoformat() - text_filter["timestamp"] = { - "$gte": floor_ts, - "$lte": effective_ts, - } - if doc_type: - text_filter["type"] = doc_type - if actor: - text_filter["metadata.author"] = actor - - results = list( - self._mem._db["artifacts"] - .find( - text_filter, - { - "content": 0, - "embedding": 0, - "score": {"$meta": "textScore"}, - }, - ) - .sort([("score", {"$meta": "textScore"})]) - .limit(limit) - ) - - return self._record( - "search_artifacts", - { - "query": query, - "doc_type": doc_type, - "actor": actor, - "after_day": after_day, - }, - results, - t0, - timestamp_applied=effective_ts, - ) - - -# ───────────────────────────────────────────────────────────────────────────── -# SCORERS -# ───────────────────────────────────────────────────────────────────────────── - - -class PerspectiveScorer: - """ - Scores a PERSPECTIVE trajectory. - - Answer scoring: - - Exact match on ground_truth.could_actor_have_known (boolean) - - Partial credit for correctly identifying blocked_subsystems - - Partial credit for citing in-cone evidence - - Trajectory scoring: - - Epistemic discipline: fraction of tool calls that stayed within cone - - Subsystem discipline: fraction of tool calls to accessible subsystems - - Conclusion grounding: did the final answer cite in-cone artifacts? - """ - - def score_answer( - self, final_answer: Dict, ground_truth: Dict - ) -> Tuple[float, bool]: - if not final_answer: - return 0.0, False - - gt_bool = ground_truth.get("could_actor_have_known", False) - - # Extract boolean from agent answer - agent_bool = self._extract_boolean(final_answer) - if agent_bool is None: - return 0.1, False - - correct = agent_bool == gt_bool - if not correct: - return 0.0, False - - # Partial credit for explaining the mechanism correctly - score = 0.6 # base for correct boolean - - gt_blocked = set(ground_truth.get("blocked_subsystems", [])) - agent_blocked = set(final_answer.get("blocked_subsystems", [])) - if gt_blocked and agent_blocked: - overlap = len(gt_blocked & agent_blocked) / len(gt_blocked) - score += 0.2 * overlap - - gt_evidence = set(ground_truth.get("evidence_artifacts", [])) - agent_evidence = set(final_answer.get("evidence_artifacts", [])) - if gt_evidence and agent_evidence: - overlap = len(gt_evidence & agent_evidence) / len(gt_evidence) - score += 0.2 * overlap - elif not gt_evidence: - score += 0.2 # no evidence required — agent doesn't need to cite any - - return min(score, 1.0), True - - def score_trajectory( - self, - trajectory: AgentTrajectory, - question: dict, - ) -> PerspectiveTrajectoryScore: - calls = trajectory.tool_calls - if not calls: - return PerspectiveTrajectoryScore(0, 0, 0, 0, 1.0, 0.0) - - n = len(calls) - actor_cone_violations = sum(1 for c in calls if c.actor_gate_violation) - subsystem_violations = sum(1 for c in calls if c.subsystem_violation) - horizon_violations = sum(1 for c in calls if c.horizon_violation) - dead_ends = sum(1 for c in calls if c.returned_empty) - dead_ends_recovered = trajectory.dead_ends_recovered - - epistemic_discipline = 1.0 - (actor_cone_violations / n) - subsystem_discipline = 1.0 - (subsystem_violations / n) - horizon_discipline = 1.0 - (horizon_violations / n) - - # Conclusion grounding: did agent cite any in-cone artifact in final answer? - actor_visible = set(question.get("actor_visible_artifacts", [])) - cited = set(trajectory.final_answer.get("evidence_artifacts", [])) - conclusion_grounding = 1.0 if (cited & actor_visible) else 0.5 if cited else 0.0 - - dead_end_recovery = dead_ends_recovered / dead_ends if dead_ends > 0 else 1.0 - - sim_days = CONFIG["simulation"].get("num_days", 60) - drifts = [ - c.temporal_drift_days for c in calls if c.temporal_drift_days is not None - ] - temporal_precision = ( - 1.0 - mean(abs(d) / sim_days for d in drifts) if drifts else 1.0 - ) - - drift_violations = sum(1 for c in calls if c.temporal_drift_violation) - temporal_drift_discipline = 1.0 - (drift_violations / n) - - composite = ( - 0.30 * epistemic_discipline - + 0.25 * subsystem_discipline - + 0.20 * conclusion_grounding - + 0.10 * horizon_discipline - + 0.05 * temporal_precision - + 0.05 * temporal_drift_discipline - + 0.05 * dead_end_recovery - ) - - return PerspectiveTrajectoryScore( - epistemic_discipline=round(epistemic_discipline, 4), - subsystem_discipline=round(subsystem_discipline, 4), - horizon_discipline=round(horizon_discipline, 4), - temporal_precision=round(temporal_precision, 4), - temporal_drift_discipline=round(temporal_drift_discipline, 4), # new - conclusion_grounding=round(conclusion_grounding, 4), - dead_end_recovery=round(dead_end_recovery, 4), - composite=round(composite, 4), - ) - - def _extract_boolean(self, answer: Dict) -> Optional[bool]: - for key in ("could_actor_have_known", "answer", "result", "known", "visible"): - val = answer.get(key) - if isinstance(val, bool): - return val - if isinstance(val, str): - if val.lower() in ("true", "yes", "1"): - return True - if val.lower() in ("false", "no", "0"): - return False - - # Try to find a boolean in free-text reasoning. - # IMPORTANT: check negative phrases FIRST and use full-phrase matching so - # that "did not have access" cannot shadow the later "had access" check — - # both would match under simple substring logic since "had access" is a - # substring of "did not have access". We resolve this by checking the - # negative patterns against the exact negated forms only, not as substrings - # of longer phrases. - reasoning = str(answer.get("reasoning", answer.get("explanation", ""))).lower() - - # Negative indicators — listed as complete phrases, no substring ambiguity - _NEGATIVE_PHRASES = ( - "could not have known", - "did not have access", - "does not have access", - "had no access", - "was not visible", - "not visible to", - "outside their visibility", - "outside their access", - "outside their cone", - "not in their subsystem", - "blocked from", - "no access to", - ) - - _POSITIVE_PHRASES = ( - "could have known", - "would have known", - "did have access", - "has access to", - "was visible to", - "visible to this actor", - "within their visibility", - "within their access", - "within their cone", - "in their subsystem", - "had direct access", - "had full access", - ) - - for phrase in _NEGATIVE_PHRASES: - if phrase in reasoning: - return False - - for phrase in _POSITIVE_PHRASES: - if phrase in reasoning: - return True - - return None - - -class CounterfactualScorer: - """ - Scores a COUNTERFACTUAL trajectory. - - Answer scoring: - - outcome_changed: boolean match (0.4) - - mechanism: correct link_type identified (0.35) - - actors: at least one correct actor identified (0.25) - - Trajectory scoring: - - cause_identified: agent retrieved the cause event - - effect_identified: agent retrieved the effect event - - mechanism_correct: agent named the right link_type - - causal_chain_complete: agent traversed cause → effect in order - """ - - _MECHANISM_ALIASES = { - "involves_gap": { - "knowledge gap", - "gap", - "undocumented", - "missing documentation", - "knowledge_gap", - }, - "recurrence_of": { - "recurrence", - "repeat incident", - "recurred", - "same issue", - "recurring", - }, - "spawned_doc": { - "spawned", - "documentation", - "confluence", - "design discussion", - "produced doc", - }, - "email_dropped": { - "dropped", - "unactioned", - "routing failure", - "missed email", - "no response", - }, - "sf_ownership_lapsed": { - "ownership lapsed", - "crm gap", - "salesforce", - "account owner", - "orphaned", - }, - "zd_escalation_source": { - "zendesk", - "support ticket", - "escalated from", - "zd escalation", - }, - "blocker_flagged": { - "blocker", - "blocked", - "delay", - "progress", - "technical blocker", - "blocker_flagged", - }, - "incident_coordination": { - "coordination", - "external contact", - "external party", - "incident_coordination", - "coordinated with", - }, - "departure_reassignment": { - "reassignment", - "departed", - "departure", - "reassigned", - "departure_reassignment", - "not reassigned", - }, - } - - def score_answer( - self, final_answer: Dict, ground_truth: Dict - ) -> Tuple[float, bool]: - if not final_answer: - return 0.0, False - - score = 0.0 - gt_outcome = ground_truth.get("outcome_changed", True) - agent_outcome = self._extract_boolean(final_answer, "outcome_changed") - - if agent_outcome is None: - return 0.0, False - if agent_outcome == gt_outcome: - score += 0.4 - - # Mechanism match - gt_mechanism = ground_truth.get("causal_mechanism", "") - agent_mechanism = str( - final_answer.get("mechanism", final_answer.get("causal_mechanism", "")) - ).lower() - aliases = self._MECHANISM_ALIASES.get(gt_mechanism, {gt_mechanism}) - if any(alias in agent_mechanism for alias in aliases): - score += 0.35 - - # Actor match - gt_actors = set(ground_truth.get("actors", [])) - agent_actors_raw = final_answer.get( - "actors", final_answer.get("involved_actors", []) - ) - agent_actors = ( - set(agent_actors_raw) if isinstance(agent_actors_raw, list) else set() - ) - if gt_actors and agent_actors and (gt_actors & agent_actors): - score += 0.25 - elif not gt_actors: - score += 0.25 - - is_correct = score >= 0.75 - return round(min(score, 1.0), 4), is_correct - - def score_trajectory( - self, - trajectory: AgentTrajectory, - question: dict, - ground_truth: Dict, - ) -> CounterfactualTrajectoryScore: - calls = trajectory.tool_calls - if not calls: - return CounterfactualTrajectoryScore(0, 0, 0, 0, 1.0, 0.0) - - n = len(calls) - retrieved_ids = set() - for call in calls: - retrieved_ids.update(call.result_ids) - - # Use artifact IDs from evidence_chain_artifacts, not synthetic event IDs. - # Synthetic event IDs (e.g. "evt_incident_opened_5_IT-108_alex") are internal - # keys that never appear in MongoDB documents. Agents retrieve documents by - # their actual artifact IDs (e.g. "IT-108"), so we must match on those instead. - evidence_artifacts = ground_truth.get("evidence_chain_artifacts", {}) - cause_artifacts = set(evidence_artifacts.get("cause", [])) - effect_artifacts = set(evidence_artifacts.get("effect", [])) - - cause_identified = ( - 1.0 if (cause_artifacts and cause_artifacts & retrieved_ids) else 0.0 - ) - effect_identified = ( - 1.0 if (effect_artifacts and effect_artifacts & retrieved_ids) else 0.0 - ) - - # Mechanism: did agent use keyword in its tool calls or final answer? - gt_mechanism = ground_truth.get("causal_mechanism", "") - aliases = self._MECHANISM_ALIASES.get(gt_mechanism, {gt_mechanism}) - agent_text = " ".join( - [str(c.arguments) for c in calls] + [str(trajectory.final_answer)] - ).lower() - mechanism_correct = ( - 1.0 if any(alias in agent_text for alias in aliases) else 0.0 - ) - - # Causal chain: did agent retrieve a cause artifact before an effect artifact? - # Since we no longer have single cause_id/effect_id to index into call.result_ids, - # we find the FIRST call that returned any cause artifact and the FIRST that - # returned any effect artifact, then check ordering. - cause_call_idx = next( - (i for i, c in enumerate(calls) if cause_artifacts & set(c.result_ids)), - None, - ) - effect_call_idx = next( - (i for i, c in enumerate(calls) if effect_artifacts & set(c.result_ids)), - None, - ) - causal_chain_complete = ( - 1.0 - if ( - cause_call_idx is not None - and effect_call_idx is not None - and cause_call_idx <= effect_call_idx - ) - else 0.5 - if (cause_call_idx is not None or effect_call_idx is not None) - else 0.0 - ) - - horizon_violations = sum(1 for c in calls if c.horizon_violation) - horizon_discipline = 1.0 - (horizon_violations / n) - - composite = ( - 0.25 * cause_identified - + 0.25 * effect_identified - + 0.25 * mechanism_correct - + 0.15 * causal_chain_complete - + 0.10 * horizon_discipline - ) - - return CounterfactualTrajectoryScore( - cause_identified=cause_identified, - effect_identified=effect_identified, - mechanism_correct=mechanism_correct, - causal_chain_complete=causal_chain_complete, - horizon_discipline=round(horizon_discipline, 4), - composite=round(composite, 4), - ) - - def _extract_boolean(self, answer: Dict, key: str) -> Optional[bool]: - val = answer.get(key) - if isinstance(val, bool): - return val - if isinstance(val, str): - if val.lower() in ("true", "yes"): - return True - if val.lower() in ("false", "no"): - return False - - # Inspect free-text reasoning for outcome_changed signal. - # - # The original had two bugs: - # - # 1. "would not" was mapped to True (outcome DID change — the thing would - # NOT have happened). This is semantically correct for counterfactuals - # ("the incident would not have occurred") but the bare phrase is too - # short — "this would not be my first choice" would also match. - # Replaced with longer, unambiguous anchors. - # - # 2. "would have prevented" → True is correct but collides with - # "nothing would have prevented" → should be False. - # Fixed by checking the negated form first. - # - # 3. "no change" → False is a two-word phrase that can appear in - # unrelated contexts ("no change in personnel"). Replaced with - # longer anchors. - # - # Strategy: check negated/False-indicating phrases FIRST (longer, more - # specific), then check True-indicating phrases that are phrased to not - # overlap with any negated form above. - - reasoning = str(answer.get("reasoning", "")).lower() - - # False indicators — outcome did NOT change (removing cause = no difference) - _OUTCOME_UNCHANGED = ( - "would still have occurred", - "would have happened regardless", - "outcome would not have changed", - "outcome would be the same", - "would not have been prevented", - "nothing would have prevented", - "no change in outcome", - "would have proceeded regardless", - "result would be unchanged", - "would still have taken place", - ) - - # True indicators — outcome WOULD change (removing cause = different result) - # Phrased to not be substrings of any _OUTCOME_UNCHANGED phrase above. - _OUTCOME_CHANGED = ( - "would not have occurred", - "would have been prevented", - "would have been avoided", - "outcome would have changed", - "would have changed the outcome", - "would have been diagnosed faster", - "would not have escalated", - "would have been resolved", - "would not have happened", - "would have been different", - "causal chain would have been broken", - ) - - # Check False indicators first — they are more specific and longer - for phrase in _OUTCOME_UNCHANGED: - if phrase in reasoning: - return False - - # Check True indicators second - for phrase in _OUTCOME_CHANGED: - if phrase in reasoning: - return True - - return None - - -class SilenceScorer: - """ - Scores a SILENCE trajectory. - - The key insight: absence is only meaningful if the agent searched the - right places. A lucky "no" without evidence of search is scored as a - trajectory failure. - - Answer scoring: - - Boolean correct (did agent conclude the artifact does not exist): 1.0 - - If agent concluded "yes" (artifact exists): 0.0 - - Trajectory scoring: - - search_space_coverage: fraction of expected_search_space the agent checked - - correct_absence_conclusion: did agent say "does not exist" explicitly? - - premature_conclusion: penalty if agent concluded before searching - """ - - def score_answer( - self, final_answer: Dict, ground_truth: Dict - ) -> Tuple[float, bool]: - if not final_answer: - return 0.0, False - - gt_answer = ground_truth.get("answer", False) # Always False for SILENCE - agent_answer = self._extract_absence_conclusion(final_answer) - - if agent_answer is None: - return 0.1, False - - correct = agent_answer == gt_answer - return (1.0, True) if correct else (0.0, False) - - def score_trajectory( - self, - trajectory: AgentTrajectory, - question: dict, - ) -> SilenceTrajectoryScore: - calls = trajectory.tool_calls - expected_space = set(question.get("expected_search_space", [])) - if not calls: - return SilenceTrajectoryScore(0.0, 0.0, 0.0, 1.0, 0.0) - - n = len(calls) - - # What did the agent search? - searched_ids: Set[str] = set() - searched_tool_args: List[str] = [] - for call in calls: - searched_ids.update(call.result_ids) - searched_tool_args.append(str(call.arguments).lower()) - - def _normalize_search_term(s: str) -> str: - """ - Extract the terminal component of a path-style search space entry. - e.g. "confluence/postmortems/IT-108" → "it-108" - "slack/channels/incidents" → "incidents" - "IT-108" → "it-108" - """ - return s.strip("/").split("/")[-1].lower() - - normalized_expected: Dict[str, str] = { - _normalize_search_term(e): e for e in expected_space - } - - # Also normalize all tool arg strings and result IDs for matching. - normalized_tool_args: List[str] = [arg.lower() for arg in searched_tool_args] - normalized_result_ids: Set[str] = { - _normalize_search_term(rid) for rid in searched_ids - } - - covered = set() - for norm_term, original in normalized_expected.items(): - # Primary match: terminal component appears anywhere in a tool arg string. - # This catches {"page_id": "IT-108"} matching "confluence/postmortems/IT-108" - # and {"query": "incidents"} matching "slack/channels/incidents". - if any(norm_term in arg for arg in normalized_tool_args): - covered.add(original) - - # Secondary match: terminal component matches a normalized result ID. - # This catches cases where the agent retrieved the document directly - # and its ID is the terminal path component. - elif norm_term in normalized_result_ids: - covered.add(original) - - # Tertiary match: the full original path appears verbatim in a tool arg. - # Preserves the original behaviour for agents that do pass full paths. - elif any(original.lower() in arg for arg in normalized_tool_args): - covered.add(original) - - search_space_coverage = ( - len(covered) / len(expected_space) if expected_space else 1.0 - ) - - conclusion_text = str(trajectory.final_answer.get("reasoning", "")).lower() - conclusion_text += str(trajectory.final_answer.get("answer", "")).lower() - explicit_negative = any( - phrase in conclusion_text - for phrase in ( - "does not exist", - "was not created", - "no postmortem", - "not found", - "could not find", - "no record", - "never created", - "not in the corpus", - "no evidence", - "not present", - "absent", - ) - ) - correct_absence_conclusion = 1.0 if explicit_negative else 0.3 - - # Premature conclusion: did agent conclude before searching? - # Heuristic: if the final answer came after fewer than 2 tool calls, penalize - premature_conclusion = 1.0 - max(0.0, min(1.0, (n - 1) / 3)) - - horizon_violations = sum(1 for c in calls if c.horizon_violation) - horizon_discipline = ( - 1.0 # SILENCE has no temporal gate, so no violations possible - ) - - composite = ( - 0.50 * search_space_coverage - + 0.30 * correct_absence_conclusion - - 0.10 * (1.0 - premature_conclusion) # penalty for rushing - + 0.10 * horizon_discipline - ) - - return SilenceTrajectoryScore( - search_space_coverage=round(search_space_coverage, 4), - correct_absence_conclusion=round(correct_absence_conclusion, 4), - premature_conclusion=round(premature_conclusion, 4), - horizon_discipline=round(horizon_discipline, 4), - composite=round(max(0.0, composite), 4), - ) - - def _extract_absence_conclusion(self, answer: Dict) -> Optional[bool]: - """True = artifact exists, False = artifact does not exist.""" - val = answer.get("exists", answer.get("found", answer.get("answer"))) - if isinstance(val, bool): - return val - if isinstance(val, str): - if val.lower() in ("true", "yes", "exists", "found"): - return True - if val.lower() in ("false", "no", "not found", "does not exist", "absent"): - return False - - reasoning = str(answer.get("reasoning", "")).lower() - - # False indicators — artifact does NOT exist - # These are checked first and are long enough to be unambiguous. - _ABSENCE_PHRASES = ( - "does not exist", - "did not exist", - "was not created", - "has not been created", - "no record exists", - "no record was found", - "could not be found", - "could not find", - "was not found", - "is not present", - "was not present", - "no evidence of", - "never created", - "not in the corpus", - "absent from", - "no postmortem", - "no ticket was", - "no confluence page", - ) - - # True indicators — artifact DOES exist - # Reworded so none are substrings of any _ABSENCE_PHRASES entry above. - _PRESENCE_PHRASES = ( - "artifact exists", - "document exists", - "ticket exists", - "page exists", - "record exists", - "was successfully created", - "has been created", - "is present in", - "appears in the corpus", - "was located", - "has been found", - "confirmed to exist", - "did find", - ) - - # Check absence first — these are longer and more specific - for phrase in _ABSENCE_PHRASES: - if phrase in reasoning: - return False - - # Check presence second — phrased to not overlap with any absence phrase - for phrase in _PRESENCE_PHRASES: - if phrase in reasoning: - return True - - return None - - -# ───────────────────────────────────────────────────────────────────────────── -# AGENT RUNNER -# ───────────────────────────────────────────────────────────────────────────── - - -class AgenticEvalRunner: - """ - Runs the agent on each question and scores the result. - - For each question: - 1. Sets up the gated tool layer appropriate for the track - 2. Runs the agent with the typed tool surface - 3. Collects the trajectory - 4. Scores answer + trajectory with the track-specific scorer - 5. Combines scores with track-specific weights - """ - - def __init__( - self, - model: str = "claude-sonnet-4-6", - max_steps: int = 5, - ungated: bool = False, - zero_shot: bool = False, - ): - self._model = model - self._max_steps = max_steps - - # --ungated: all actor/subsystem gates disabled regardless of question type. - # Establishes the "god-mode" information ceiling for the Epistemic Tax. - self._ungated = ungated - - # --zero-shot: agent receives no tools at all (no corpus access). - # Establishes the hallucination / prior-knowledge floor. - # Mutually exclusive with --ungated; zero_shot takes precedence if both set. - self._zero_shot = zero_shot - - from flow import build_llm - from memory import Memory - - self._mem = Memory() - self._llm = build_llm("worker") - - self._perspective_scorer = PerspectiveScorer() - self._counterfactual_scorer = CounterfactualScorer() - self._silence_scorer = SilenceScorer() - - def run( - self, - questions_path: Path, - out_path: Path, - question_types: Optional[List[str]] = None, - max_questions: Optional[int] = None, - ) -> None: - with open(questions_path) as f: - data = json.load(f) - - questions = data["questions"] - if question_types: - questions = [q for q in questions if q["question_type"] in question_types] - if max_questions: - questions = questions[:max_questions] - - logger.info( - f"Running agentic eval on {len(questions)} questions " - f"(model={self._model}, max_steps={self._max_steps})" - ) - - results: List[EvalResult] = [] - per_question: List[dict] = [] - - for i, question in enumerate(questions): - qtype = question["question_type"] - logger.info( - f"[{i + 1}/{len(questions)}] {qtype} — {question['question_id']}" - ) - - try: - result = self._run_question(question) - except Exception as exc: - logger.error(f" Failed: {exc}") - result = EvalResult( - question_id=question["question_id"], - question_type=qtype, - difficulty=question.get("difficulty", "unknown"), - answer_score=0.0, - answer_correct=False, - trajectory_score=0.0, - combined_score=0.0, - failure_reason=str(exc), - tool_call_count=0, - meta={"error": str(exc)}, - ) - - results.append(result) - per_question.append(result.to_dict()) - logger.info( - f" answer={result.answer_score:.3f} " - f"trajectory={result.trajectory_score:.3f} " - f"combined={result.combined_score:.3f} " - f"tools={result.tool_call_count}" - ) - - summary = self._aggregate(results) - out_path.parent.mkdir(parents=True, exist_ok=True) - output = { - "meta": { - "model": self._model, - "max_steps": self._max_steps, - "n_questions": len(results), - "track_weights": _TRACK_WEIGHTS, - }, - "summary": summary, - "per_question": per_question, - } - with open(out_path, "w") as f: - json.dump(output, f, indent=2, default=str) - - logger.info(f"Results written to {out_path}") - logger.info( - f"Overall — answer: {summary['overall']['answer_score']:.3f} " - f"trajectory: {summary['overall']['trajectory_score']:.3f} " - f"combined: {summary['overall']['combined_score']:.3f}" - + ( - f" | violation_adjusted: " - f"{summary['overall'].get('violation_adjusted_combined_score', 'n/a')}" - ) - ) - - def _run_question(self, question: dict) -> EvalResult: - qtype = question["question_type"] - ground_truth = question["ground_truth"] - - # Set up gated tools. - # --ungated: strip all actor/subsystem gates by passing None for both, - # regardless of question type. Temporal gate still applies. - # --zero-shot: GatedTools is still constructed (for consistent call - # logging infrastructure) but _tool_list() returns [] so the agent - # never actually invokes any tool. - as_of_time = self._infer_as_of_time(question) - - if self._ungated: - actor_visible = None - actor_subsystems = None - else: - actor_visible = ( - set(question.get("actor_visible_artifacts", [])) - if qtype == "PERSPECTIVE" - else None - ) - actor_subsystems = ( - set(question.get("subsystem_access", [])) - if qtype == "PERSPECTIVE" - else None - ) - - tools = GatedTools( - mem=self._mem, - question=question, - as_of_time=as_of_time, - actor_visible_artifacts=actor_visible, - actor_subsystem_access=actor_subsystems, - ) - - # Run agent - trajectory = self._run_agent(question, tools) - - # Score - if qtype == "PERSPECTIVE": - answer_score, answer_correct = self._perspective_scorer.score_answer( - trajectory.final_answer, ground_truth - ) - traj = self._perspective_scorer.score_trajectory(trajectory, question) - traj_score = traj.composite - traj_detail = asdict(traj) - - elif qtype == "COUNTERFACTUAL": - answer_score, answer_correct = self._counterfactual_scorer.score_answer( - trajectory.final_answer, ground_truth - ) - traj = self._counterfactual_scorer.score_trajectory( - trajectory, question, ground_truth - ) - traj_score = traj.composite - traj_detail = asdict(traj) - - elif qtype == "SILENCE": - answer_score, answer_correct = self._silence_scorer.score_answer( - trajectory.final_answer, ground_truth - ) - traj = self._silence_scorer.score_trajectory(trajectory, question) - traj_score = traj.composite - traj_detail = asdict(traj) - - else: - raise ValueError(f"Unknown question type: {qtype}") - - weights = _TRACK_WEIGHTS[qtype] - combined = weights["answer"] * answer_score + weights["trajectory"] * traj_score - - return EvalResult( - question_id=question["question_id"], - question_type=qtype, - difficulty=question.get("difficulty", "unknown"), - answer_score=round(answer_score, 4), - answer_correct=answer_correct, - trajectory_score=round(traj_score, 4), - combined_score=round(combined, 4), - failure_reason=None, - tool_call_count=len(trajectory.tool_calls), - meta={ - "model": self._model, - "eval_mode": ( - "zero_shot" - if self._zero_shot - else "ungated" - if self._ungated - else "gated" - ), - "as_of_time": as_of_time, - "trajectory_detail": traj_detail, - "horizon_violations": trajectory.horizon_violations, - "actor_gate_violations": trajectory.actor_gate_violations, - "subsystem_violations": trajectory.subsystem_violations, - "dead_ends_hit": trajectory.dead_ends_hit, - "dead_ends_recovered": trajectory.dead_ends_recovered, - "total_latency_ms": round(trajectory.total_latency_ms, 1), - "tool_calls": [asdict(tc) for tc in trajectory.tool_calls], - "final_answer": trajectory.final_answer, - }, - ) - - def _run_agent(self, question: dict, tools: GatedTools) -> AgentTrajectory: - """ - Runs the agent against the question using the gated tool surface. - Returns a populated AgentTrajectory. - - The agent is given a structured output format so answer extraction - is reliable across all three tracks. - """ - from agent_factory import make_agent - from crewai import Crew, Task - - qtype = question["question_type"] - trajectory = AgentTrajectory( - question_id=question["question_id"], - question_type=qtype, - ) - - CAUSAL_LINK_TAXONOMY = { - "involves_gap": "incident ← knowledge gap (information was missing/undocumented)", - "recurrence_of": "incident ← prior unresolved incident (root cause was known but not fixed)", - "spawned_doc": "confluence ← design discussion (documentation resulted from a specific meeting)", - "email_dropped": "communication failure ← routing gap", - "sf_ownership_lapsed": "CRM gap ← employee departure", - "zd_escalation_source": "incident ← support ticket escalation", - "blocker_flagged": "blocker → delayed progress", - "incident_coordination": "incident → external contact", - "departure_reassignment": "departure → ticket/escalation shift", - "assignment_domain_mismatch": "planning mismatch → knowledge gap → incident", - } - - taxonomy_str = "\n".join( - [f"- {k}: {v}" for k, v in CAUSAL_LINK_TAXONOMY.items()] - ) - allowed_links = ", ".join(CAUSAL_LINK_TAXONOMY.keys()) - - # Build output schema based on track - output_schema = { - "PERSPECTIVE": """{ - "could_actor_have_known": , - "reasoning": "", - "evidence_artifacts": ["", ...], - "blocked_subsystems": ["", ...] - }""", - "COUNTERFACTUAL": f"""{{ - "outcome_changed": , - "mechanism": "", - "causal_mechanism": "", - "actors": ["", ...], - "reasoning": "" - }}""", - "SILENCE": """{ - "exists": , - "answer": "", - "reasoning": "" - }""", - }[qtype] - - _SEARCH_SPACE_HINTS = { - "confluence/general": "search Confluence general pages", - "confluence/retros": "search Confluence retrospectives", - "zendesk/queue": "search Zendesk tickets", - "zendesk/tickets": "search Zendesk tickets", - "zendesk/escalations": "check Zendesk escalations", - "slack/channels/engineering": "check the engineering Slack channel", - "slack/channels/digital-hq": "check the digital-hq Slack channel", - "slack/channels/incidents": "check the incidents Slack channel", - "slack/channels/general": "check the general Slack channel", - "slack/channels/support": "check the support Slack channel", - "zoom/transcripts": "search Zoom transcripts", - "git/merged-prs": "check merged pull requests", - "salesforce/opportunities": "search Salesforce opportunities", - "salesforce/accounts": "search Salesforce accounts", - "jira/incidents": "search Jira incident tickets", - "jira/reassignments": "check Jira for ticket reassignments", - } - - space = question.get("expected_search_space", [])[:5] - hints = [] - for entry in space: - hint = next( - (v for k, v in _SEARCH_SPACE_HINTS.items() if entry.startswith(k)), None - ) - if hint: - hints.append(hint) - elif entry.startswith("export/emails/"): - continue - elif entry.startswith("export/"): - continue - else: - if entry.startswith("ext_email_") or entry.startswith("EMAIL-"): - hints.append(f"call get_email with email_id='{entry}'") - elif entry.startswith("CONF-"): - hints.append(f"call get_confluence_page with page_id='{entry}'") - elif entry.startswith("slack_"): - hints.append(f"call get_slack_thread with thread_id='{entry}'") - elif entry.startswith("ENG-") or entry.startswith("IT-"): - hints.append(f"call get_ticket with ticket_id='{entry}'") - elif entry.startswith("ZD-"): - hints.append(f"call get_zd_ticket with ticket_id='{entry}'") - elif entry.startswith("PR-"): - hints.append(f"call get_pr with pr_id='{entry}'") - else: - hints.append(f"search for artifact '{entry}'") - - constraint_note = { - "PERSPECTIVE": ( - f"\n\nIMPORTANT: You are answering from the perspective of {question.get('actor', 'the actor')} " - f"as of Day {question.get('as_of_day', '?')}. " - f"This actor only has access to: {', '.join(question.get('subsystem_access', []))}. " - f"You must not use information from systems outside this list. " - f"Accessing artifacts outside the actor's visibility cone is a violation." - ), - "COUNTERFACTUAL": ( - "\n\nIMPORTANT: This is a counterfactual question. You must identify the explicit " - "causal link in the data — do not speculate. \n\n" - "You MUST categorize the link using one of the following labels:\n" - f"{taxonomy_str}\n\n" - "Find the cause event and the effect event, then determine whether " - "removing the cause would have changed the effect." - ), - "SILENCE": ( - "\n\nIMPORTANT: This is an absence question. You must search the corpus " - "thoroughly before concluding absence. Do not guess. " - "Show your work in the reasoning field — explain what you searched and what you found." - ), - }[qtype] - - agent = make_agent( - role="Enterprise Knowledge Analyst", - goal="Reason carefully over corporate documents to answer complex questions.", - backstory=( - "You are an expert analyst evaluating enterprise AI systems. You reason " - "carefully, cite evidence, stay within stated constraints, and never guess." - ), - llm=self._llm, - tools=self._tool_list(tools), - max_iter=self._max_steps, - ) - - task = Task( - description=( - f"{question['question_text']}" - f"{constraint_note}" - f"\n\nRespond ONLY with a JSON object matching this schema:\n{output_schema}" - ), - expected_output="A JSON object matching the schema above. No preamble.", - agent=agent, - ) - - t_start = time.time() - try: - raw_output = Crew( - agents=[agent], - tasks=[task], - verbose=True, - output_log_file="simulation.log", - ).kickoff() - if hasattr(raw_output, "raw"): - raw = str(raw_output.raw).strip() - elif isinstance(raw_output, list): - text_blocks = [ - b.get("text", "") - for b in raw_output - if isinstance(b, dict) and b.get("type") == "text" - ] - raw = " ".join(text_blocks).strip() - else: - raw = str(raw_output).strip() - final_answer = self._parse_structured_answer(raw) - except Exception as exc: - logger.warning(f" Agent error: {exc}") - final_answer = {} - - trajectory.total_latency_ms = (time.time() - t_start) * 1000 - trajectory.tool_calls = list(tools.call_log) - trajectory.final_answer = final_answer - trajectory.horizon_violations = sum( - 1 for c in trajectory.tool_calls if c.horizon_violation - ) - trajectory.actor_gate_violations = sum( - 1 for c in trajectory.tool_calls if c.actor_gate_violation - ) - trajectory.subsystem_violations = sum( - 1 for c in trajectory.tool_calls if c.subsystem_violation - ) - trajectory.dead_ends_hit = sum( - 1 for c in trajectory.tool_calls if c.returned_empty - ) - - # Dead end recovery: count cases where agent made a successful call after a dead end - for i, call in enumerate(trajectory.tool_calls): - if call.returned_empty and i + 1 < len(trajectory.tool_calls): - if not trajectory.tool_calls[i + 1].returned_empty: - trajectory.dead_ends_recovered += 1 - - return trajectory - - def _tool_list(self, tools: GatedTools) -> List: - if self._zero_shot: - return [] - - from crewai.tools import BaseTool - from pydantic import BaseModel, Field - - class TicketInput(BaseModel): - ticket_id: str = Field( - ..., description="Jira ticket ID, e.g. 'ENG-42' or 'ORG-108'" - ) - - class GetTicket(BaseTool): - name: str = "get_ticket" - description: str = "Retrieve a Jira ticket by ID." - args_schema: type[BaseModel] = TicketInput - _tools: GatedTools - - def _run(self, ticket_id: str) -> dict: - return tools.get_ticket(ticket_id) - - class ConfluenceInput(BaseModel): - page_id: str = Field( - ..., description="Confluence page ID, e.g. 'CONF-ENG-007'" - ) - - class GetConfluencePage(BaseTool): - name: str = "get_confluence_page" - description: str = "Retrieve a Confluence page by ID." - args_schema: type[BaseModel] = ConfluenceInput - - def _run(self, page_id: str) -> dict: - return tools.get_confluence_page(page_id) - - class SlackInput(BaseModel): - thread_id: str = Field( - ..., - description="Slack thread ID, e.g. 'slack_dm_liam_sanjay_2026-03-02T13:37:00'", - ) - - class GetSlackThread(BaseTool): - name: str = "get_slack_thread" - description: str = "Retrieve a Slack thread by ID." - args_schema: type[BaseModel] = SlackInput - - def _run(self, thread_id: str) -> list: - return tools.get_slack_thread(thread_id) - - class EmailInput(BaseModel): - email_id: str = Field( - ..., description="Email artifact ID, e.g. 'ext_email_name_1_1'" - ) - - class GetEmail(BaseTool): - name: str = "get_email" - description: str = "Retrieve an email by ID." - args_schema: type[BaseModel] = EmailInput - - def _run(self, email_id: str) -> dict: - return tools.get_email(email_id) - - class PRInput(BaseModel): - pr_id: str = Field(..., description="Pull request ID, e.g. 'PR-88'") - - class GetPR(BaseTool): - name: str = "get_pr" - description: str = "Retrieve a pull request by ID." - args_schema: type[BaseModel] = PRInput - - def _run(self, pr_id: str) -> dict: - return tools.get_pr(pr_id) - - class ZDInput(BaseModel): - ticket_id: str = Field(..., description="Zendesk ticket ID, e.g. 'ZD-55'") - - class GetZDTicket(BaseTool): - name: str = "get_zd_ticket" - description: str = "Retrieve a Zendesk support ticket by ID." - args_schema: type[BaseModel] = ZDInput - - def _run(self, ticket_id: str) -> dict: - return tools.get_zd_ticket(ticket_id) - - class SFOppInput(BaseModel): - opp_id: str = Field( - ..., description="Salesforce opportunity ID, e.g. 'SF-OPP-12'" - ) - - class GetSFOpportunity(BaseTool): - name: str = "get_sf_opportunity" - description: str = "Retrieve a Salesforce opportunity by ID." - args_schema: type[BaseModel] = SFOppInput - - def _run(self, opp_id: str) -> dict: - return tools.get_sf_opportunity(opp_id) - - class SFAccountInput(BaseModel): - account_id: str = Field( - ..., description="Salesforce account ID, e.g. 'SF-ACC-7'" - ) - - class GetSFAccount(BaseTool): - name: str = "get_sf_account" - description: str = "Retrieve a Salesforce account by ID." - args_schema: type[BaseModel] = SFAccountInput - - def _run(self, account_id: str) -> dict: - return tools.get_sf_account(account_id) - - class ZoomInput(BaseModel): - transcript_id: str = Field( - ..., description="Zoom transcript ID, e.g. 'ZOOM-2026-03-15'" - ) - - class GetZoomTranscript(BaseTool): - name: str = "get_zoom_transcript" - description: str = "Retrieve a Zoom meeting transcript by ID." - args_schema: type[BaseModel] = ZoomInput - - def _run(self, transcript_id: str) -> dict: - return tools.get_zoom_transcript(transcript_id) - - class DatadogInput(BaseModel): - alert_id: str = Field( - ..., description="Datadog alert ID, e.g. 'DD-ALERT-3'" - ) - - class GetDatadogAlert(BaseTool): - name: str = "get_datadog_alert" - description: str = "Retrieve a Datadog alert by ID." - args_schema: type[BaseModel] = DatadogInput - - def _run(self, alert_id: str) -> dict: - return tools.get_datadog_alert(alert_id) - - class InvoiceInput(BaseModel): - invoice_id: str = Field(..., description="Invoice ID, e.g. 'INV-2026-001'") - - class GetInvoice(BaseTool): - name: str = "get_invoice" - description: str = "Retrieve an invoice by ID." - args_schema: type[BaseModel] = InvoiceInput - - def _run(self, invoice_id: str) -> dict: - return tools.get_invoice(invoice_id) - - class NPSInput(BaseModel): - account_name: str = Field(..., description="Account name, e.g. 'Acme Corp'") - - class GetNPSResponse(BaseTool): - name: str = "get_nps_response" - description: str = "Retrieve an NPS survey response by account name." - args_schema: type[BaseModel] = NPSInput - - def _run(self, account_name: str) -> dict: - return tools.get_nps_response(account_name) - - class EventsInput(BaseModel): - day: int = Field(..., description="Simulation day number, e.g. 1-30") - event_type: str = Field( - None, description="Optional event type filter, e.g. 'incident_opened'" - ) - - class GetEventsForDay(BaseTool): - name: str = "get_events_for_day" - description: str = "Retrieve all simulation events for a given day, optionally filtered by type." - args_schema: type[BaseModel] = EventsInput - - def _run(self, day: int, event_type: str = None) -> list: - return tools.get_events_for_day(day, event_type) - - class SearchInput(BaseModel): - query: str = Field(..., description="Artifact ID or keyword to search for.") - doc_type: str = Field( - None, - description="Filter by type, e.g. 'jira', 'confluence', 'slack', 'email', 'pr', 'zd_ticket', 'zoom'.", - ) - actor: str = Field(None, description="Optional actor name to filter by") - after_day: int = Field( - None, - description=( - "Only return artifacts created on or after this simulation day. " - "Use this when checking whether something was created in response " - "to a specific event." - ), - ) - - class SearchArtifacts(BaseTool): - name: str = "search_artifacts" - description: str = "Search for information when you do not have a specific ID. You MUST provide a specific search string in the 'query' argument." - args_schema: type[BaseModel] = SearchInput - - def _run( - self, - query: str, - doc_type: str = "", - actor: str = "", - after_day: int = None, - ) -> list: - return tools.search_artifacts( - query, doc_type, actor=actor, after_day=after_day - ) - - return [ - GetTicket(), - GetConfluencePage(), - GetSlackThread(), - GetEmail(), - GetPR(), - GetZDTicket(), - GetSFOpportunity(), - GetSFAccount(), - GetZoomTranscript(), - GetDatadogAlert(), - GetInvoice(), - GetNPSResponse(), - GetEventsForDay(), - SearchArtifacts(), - ] - - def _parse_structured_answer(self, raw: str) -> Dict: - """Extract JSON from agent response. Strips markdown fences.""" - text = raw.strip() - text = re.sub(r"^```(?:json)?\s*", "", text) - text = re.sub(r"\s*```$", "", text) - try: - return json.loads(text) - except json.JSONDecodeError: - # Try to find JSON object in response - match = re.search(r"\{.*\}", text, re.DOTALL) - if match: - try: - return json.loads(match.group()) - except json.JSONDecodeError: - pass - return {"raw_response": raw} - - def _infer_as_of_time(self, question: dict) -> str: - qtype = question.get("question_type", "") - if qtype == "SILENCE": - events = self._mem.get_event_log(from_db=True) - max_day = max((e.day for e in events), default=1) - return _business_day_to_date(_SIM_START, max_day).isoformat() - if qtype == "PERSPECTIVE": - return question.get("as_of_time", datetime.now().isoformat()) - if qtype == "COUNTERFACTUAL": - # Use effect event timestamp - effect_id = question.get("ground_truth", {}).get("effect_event_id") - if effect_id: - try: - ev = self._mem._db["events"].find_one({"event_id": effect_id}) - if ev and ev.get("timestamp"): - return str(ev["timestamp"]) - except Exception: - pass - day = question.get("day", question.get("event_day", 1)) - return _business_day_to_date(_SIM_START, day).isoformat() - - def _aggregate(self, results: List[EvalResult]) -> dict: - def mean(vals): - return round(sum(vals) / len(vals), 4) if vals else 0.0 - - # ── Violation-adjusted scoring ──────────────────────────────────────── - # violation_rate = total_actor_gate_violations / total_tool_calls - # compliance_factor = max(0, 1 − violation_rate) ** _VIOLATION_EXPONENT - # adjusted_score = combined_score × compliance_factor - # - # Quadratic exponent (2) means violations compound non-linearly: - # 0% violations → 1.00× multiplier (no penalty) - # 25% violations → 0.56× multiplier - # 50% violations → 0.25× multiplier (score quartered) - # 75% violations → 0.06× multiplier (effectively disqualified) - # - # This decouples compliance from trajectory scoring and makes it a - # multiplicative gate at the aggregate level — a cheating agent cannot - # overcome the penalty through high answer accuracy alone. - _VIOLATION_EXPONENT = 2 - - def _compliance_tier(rate: float) -> str: - if rate < 0.05: - return "compliant" - if rate < 0.20: - return "borderline" - return "non_compliant" - - def _violation_adjusted(combined: float, violation_rate: float) -> float: - factor = max(0.0, 1.0 - violation_rate) ** _VIOLATION_EXPONENT - return round(combined * factor, 4) - - by_type: Dict[str, List[EvalResult]] = {} - by_difficulty: Dict[str, List[EvalResult]] = {} - for r in results: - by_type.setdefault(r.question_type, []).append(r) - by_difficulty.setdefault(r.difficulty, []).append(r) - - by_type_summary = {} - for qtype, rs in by_type.items(): - total_calls = sum(r.tool_call_count for r in rs) - total_violations = sum(r.meta.get("actor_gate_violations", 0) for r in rs) - violation_rate = ( - round(total_violations / total_calls, 4) if total_calls else 0.0 - ) - base_combined = mean([r.combined_score for r in rs]) - - summary: Dict[str, Any] = { - "n": len(rs), - "answer_score": mean([r.answer_score for r in rs]), - "trajectory_score": mean([r.trajectory_score for r in rs]), - "combined_score": base_combined, - "accuracy": round(sum(r.answer_correct for r in rs) / len(rs), 4), - "avg_tool_calls": mean([r.tool_call_count for r in rs]), - } - - if qtype == "PERSPECTIVE": - compliance_factor = round( - max(0.0, 1.0 - violation_rate) ** _VIOLATION_EXPONENT, 4 - ) - summary.update( - { - "violation_rate": violation_rate, - "compliance_factor": compliance_factor, - "compliance_tier": _compliance_tier(violation_rate), - # Primary leaderboard axis — combined_score alone allows a - # cheating agent to rank above a disciplined one. This number - # prevents that by applying the compliance penalty independently - # of answer quality. - "violation_adjusted_combined_score": _violation_adjusted( - base_combined, violation_rate - ), - "avg_actor_gate_violations": mean( - [r.meta.get("actor_gate_violations", 0) for r in rs] - ), - "avg_subsystem_violations": mean( - [r.meta.get("subsystem_violations", 0) for r in rs] - ), - } - ) - elif qtype == "SILENCE": - summary["search_space_coverage"] = mean( - [ - r.meta.get("trajectory_detail", {}).get( - "search_space_coverage", 0 - ) - for r in rs - ] - ) - - by_type_summary[qtype] = summary - - # ── Global violation_adjusted_combined_score ────────────────────────── - # A single number for cross-model ranking. Agents without PERSPECTIVE - # questions are not penalised (violation_rate = 0, factor = 1.0). - all_calls = sum(r.tool_call_count for r in results) - all_violations = sum(r.meta.get("actor_gate_violations", 0) for r in results) - global_violation_rate = ( - round(all_violations / all_calls, 4) if all_calls else 0.0 - ) - overall_combined = mean([r.combined_score for r in results]) - - return { - "overall": { - "n": len(results), - "answer_score": mean([r.answer_score for r in results]), - "trajectory_score": mean([r.trajectory_score for r in results]), - "combined_score": overall_combined, - "accuracy": round( - sum(r.answer_correct for r in results) / len(results), 4 - ), - "avg_tool_calls": mean([r.tool_call_count for r in results]), - "global_violation_rate": global_violation_rate, - "global_compliance_factor": round( - max(0.0, 1.0 - global_violation_rate) ** _VIOLATION_EXPONENT, 4 - ), - "global_compliance_tier": _compliance_tier(global_violation_rate), - # Primary cross-track ranking number - "violation_adjusted_combined_score": _violation_adjusted( - overall_combined, global_violation_rate - ), - }, - "by_type": by_type_summary, - "by_difficulty": { - diff: { - "n": len(rs), - "answer_score": mean([r.answer_score for r in rs]), - "trajectory_score": mean([r.trajectory_score for r in rs]), - "combined_score": mean([r.combined_score for r in rs]), - } - for diff, rs in by_difficulty.items() - }, - } - - -# ───────────────────────────────────────────────────────────────────────────── -# ENTRYPOINT -# ───────────────────────────────────────────────────────────────────────────── - -if __name__ == "__main__": - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(levelname)s - %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - - parser = argparse.ArgumentParser( - description="OrgForge Agentic Eval Harness - PERSPECTIVE, COUNTERFACTUAL, SILENCE" - ) - parser.add_argument( - "--questions", - type=Path, - default=EVAL_DIR / "eval_questions.json", - ) - parser.add_argument( - "--out", - type=Path, - default=EVAL_DIR / "agentic_results.json", - ) - parser.add_argument( - "--model", - type=str, - default="claude-sonnet-4-6", - ) - parser.add_argument( - "--max-steps", - type=int, - default=5, - help="Max tool-use steps per question (SILENCE questions may need more)", - ) - parser.add_argument( - "--types", - nargs="+", - choices=["PERSPECTIVE", "COUNTERFACTUAL", "SILENCE"], - help="Run only specific tracks", - ) - parser.add_argument( - "--max-questions", - type=int, - default=None, - ) - parser.add_argument( - "--ungated", - action="store_true", - default=False, - help=( - "Disable all actor/subsystem gates — god-mode corpus access. " - "Establishes the Epistemic Tax ceiling. " - "Default output: export/eval/ungated_results.json" - ), - ) - parser.add_argument( - "--zero-shot", - action="store_true", - default=False, - help=( - "Provide no tools to the agent (no corpus access). " - "Establishes the hallucination / prior-knowledge floor. " - "Default output: export/eval/zero_shot_results.json" - ), - ) - args = parser.parse_args() - - # Default output paths differ by mode so runs don't clobber each other - if args.out == EVAL_DIR / "agentic_results.json": - if args.zero_shot: - args.out = EVAL_DIR / "zero_shot_results.json" - elif args.ungated: - args.out = EVAL_DIR / "ungated_results.json" - - runner = AgenticEvalRunner( - model=args.model, - max_steps=args.max_steps, - ungated=args.ungated, - zero_shot=args.zero_shot, - ) - runner.run( - questions_path=args.questions, - out_path=args.out, - question_types=args.types, - max_questions=args.max_questions, - ) diff --git a/eval/eval_e2e.py b/eval/eval_e2e.py deleted file mode 100644 index d40582c..0000000 --- a/eval/eval_e2e.py +++ /dev/null @@ -1,1434 +0,0 @@ -""" -eval_e2e.py -=========== -End-to-end evaluation harness for the OrgForge Enterprise RAG Benchmark. - -Runs a full retrieve → generate → score pipeline against any combination -of retriever and generation model, then writes results to -results// and appends a row to leaderboard.json. - -Supports: - Retrievers : bm25 | cohere | openai | sentence-transformers - Generators : claude | openai | cohere (Command R+) - -Usage ------ -# BM25 retrieval + Claude generation (uses HF dataset by default) -python eval_e2e.py --retriever bm25 --generator claude --model claude-sonnet-4-20250514 - -# Cohere Embed v4 retrieval + Claude generation -python eval_e2e.py --retriever cohere --generator claude --model claude-sonnet-4-20250514 - -# BM25 + GPT-4o -python eval_e2e.py --retriever bm25 --generator openai --model gpt-4o - -# Load from local parquets instead of HF -python eval_e2e.py --retriever bm25 --generator claude --local ./export/hf_dataset - -# Limit to N questions (useful for smoke-testing) -python eval_e2e.py --retriever bm25 --generator claude --limit 10 - -# Dry-run: retrieval only, no generation (just MRR@10 / Recall@10) -python eval_e2e.py --retriever cohere --generator none - -Environment variables ---------------------- - ANTHROPIC_API_KEY required for --generator claude - OPENAI_API_KEY required for --generator openai or --retriever openai - COHERE_API_KEY required for --retriever cohere or --generator cohere - -Output ------- -results// - per_question.json — full per-question results (retrieval + generation + score) - summary.json — aggregate metrics by question type and difficulty -leaderboard.json — append-only leaderboard table (one row per run) -""" - -from __future__ import annotations - -import argparse -import hashlib -import json -import logging -import os -import re -import time -from collections import defaultdict -from datetime import datetime, timezone -from pathlib import Path -from typing import Dict, List, Optional, Tuple -import numpy as np - -logger = logging.getLogger("orgforge.eval_e2e") - -# Lazy import so the module is optional when only using built-in retrievers. -try: - from retrieval_extensions import RRFRetriever, GraphAugmentedRetriever - - _EXTENSIONS_AVAILABLE = True -except ImportError: - _EXTENSIONS_AVAILABLE = False - -# ── Constants ───────────────────────────────────────────────────────────────── - -HF_DATASET_ID = os.environ.get("HF_DATASET_ID", "INSERT_ID_HERE") -TOP_K = 10 -RESULTS_DIR = Path("results") -LEADERBOARD_PATH = Path("leaderboard.json") - - -# ───────────────────────────────────────────────────────────────────────────── -# DATA LOADING -# ───────────────────────────────────────────────────────────────────────────── - - -def load_dataset(local_path: Optional[str] = None) -> Tuple[List[dict], List[dict]]: - """ - Returns (corpus, questions). - Loads from local Parquet files if local_path is given, - otherwise downloads from HuggingFace. - """ - if local_path: - return _load_local(Path(local_path)) - return _load_hf() - - -def _load_hf() -> Tuple[List[dict], List[dict]]: - try: - from datasets import load_dataset as hf_load - except ImportError: - raise SystemExit("pip install datasets (or pass --local path/to/hf_dataset)") - logger.info(f"Loading corpus from HuggingFace: {HF_DATASET_ID}") - corpus_ds = hf_load( - HF_DATASET_ID, data_files="corpus/corpus-00000.parquet", split="train" - ) - questions_ds = hf_load( - HF_DATASET_ID, data_files="questions/questions-00000.parquet", split="train" - ) - corpus = [dict(r) for r in corpus_ds] - questions = [dict(r) for r in questions_ds] - logger.info( - f" {len(corpus)} corpus docs, {len(questions)} questions loaded from HF" - ) - return corpus, questions - - -def _load_local(base: Path) -> Tuple[List[dict], List[dict]]: - try: - import pandas as pd - except ImportError: - raise SystemExit("pip install pandas pyarrow") - - corpus_path = base / "corpus" / "corpus-00000.parquet" - questions_path = base / "questions" / "questions-00000.parquet" - - if not corpus_path.exists(): - raise FileNotFoundError(f"Corpus not found: {corpus_path}") - if not questions_path.exists(): - raise FileNotFoundError(f"Questions not found: {questions_path}") - - corpus = pd.read_parquet(corpus_path).to_dict("records") - questions = pd.read_parquet(questions_path).to_dict("records") - logger.info( - f" {len(corpus)} corpus docs, {len(questions)} questions loaded from {base}" - ) - return corpus, questions - - -# ───────────────────────────────────────────────────────────────────────────── -# RETRIEVERS -# ───────────────────────────────────────────────────────────────────────────── - - -class Retriever: - """Base class — subclasses implement index() and retrieve().""" - - name: str = "base" - - def index(self, corpus: List[dict]) -> None: - raise NotImplementedError - - def retrieve(self, query: str, top_k: int = TOP_K) -> List[str]: - """Returns ordered list of doc_ids.""" - raise NotImplementedError - - -class BM25Retriever(Retriever): - name = "bm25" - - def index(self, corpus: List[dict]) -> None: - from rank_bm25 import BM25Okapi - - self._doc_ids = [r["doc_id"] for r in corpus] - tokenised = [ - self._tokenize(r.get("body") or r.get("content") or "") for r in corpus - ] - self._bm25 = BM25Okapi(tokenised) - logger.info(f" BM25 index built ({len(self._doc_ids)} docs)") - - def retrieve(self, query: str, top_k: int = TOP_K) -> List[str]: - scores = self._bm25.get_scores(self._tokenize(query)) - indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True) - return [self._doc_ids[i] for i in indices[:top_k]] - - @staticmethod - def _tokenize(text: str) -> List[str]: - return re.sub(r"[^\w\s]", " ", (text or "").lower()).split() - - -class CohereRetriever(Retriever): - """ - Cohere Embed v4 (embed-v4.0) with cosine similarity. - Uses the 'search_document' / 'search_query' input types. - Set COHERE_API_KEY in your environment. - """ - - name = "cohere-embed-v4" - - def __init__(self, model: str = "embed-v4.0", batch_size: int = 96): - self._model = model - self._batch_size = batch_size - - def index(self, corpus: List[dict]) -> None: - import cohere - - api_key = os.environ.get("COHERE_API_KEY") - if not api_key: - raise SystemExit("Set COHERE_API_KEY to use Cohere retriever") - - self._co = cohere.ClientV2(api_key=api_key) - self._doc_ids = [r["doc_id"] for r in corpus] - bodies = [r.get("body") or r.get("content") or "" for r in corpus] - - logger.info(f" Embedding {len(bodies)} docs with {self._model} ...") - embeddings = [] - for i in range(0, len(bodies), self._batch_size): - batch = bodies[i : i + self._batch_size] - resp = self._co.embed( - texts=batch, - model=self._model, - input_type="search_document", - embedding_types=["float"], - ) - embeddings.extend(resp.embeddings.float_) - logger.info( - f" embedded {min(i + self._batch_size, len(bodies))}/{len(bodies)}" - ) - - mat = np.array(embeddings, dtype=np.float32) - # Normalise for cosine similarity via dot product - norms = np.linalg.norm(mat, axis=1, keepdims=True) - self._matrix = mat / np.where(norms == 0, 1, norms) - logger.info(" Cohere index ready") - - def retrieve(self, query: str, top_k: int = TOP_K) -> List[str]: - - resp = self._co.embed( - texts=[query], - model=self._model, - input_type="search_query", - embedding_types=["float"], - ) - q_vec = np.array(resp.embeddings.float_[0], dtype=np.float32) - q_vec /= max(np.linalg.norm(q_vec), 1e-9) - scores = self._matrix @ q_vec - indices = scores.argsort()[::-1][:top_k] - return [self._doc_ids[int(i)] for i in indices] - - -class OpenAIRetriever(Retriever): - """ - OpenAI text-embedding-3-large. - Set OPENAI_API_KEY in your environment. - """ - - name = "openai-text-embedding-3-large" - - def __init__(self, model: str = "text-embedding-3-large", batch_size: int = 512): - self._model = model - self._batch_size = batch_size - - def index(self, corpus: List[dict]) -> None: - - from openai import OpenAI - - api_key = os.environ.get("OPENAI_API_KEY") - if not api_key: - raise SystemExit("Set OPENAI_API_KEY to use OpenAI retriever") - - self._client = OpenAI(api_key=api_key) - self._doc_ids = [r["doc_id"] for r in corpus] - bodies = [r.get("body", "") or "" for r in corpus] - - logger.info(f" Embedding {len(bodies)} docs with {self._model} ...") - embeddings = [] - for i in range(0, len(bodies), self._batch_size): - batch = bodies[i : i + self._batch_size] - resp = self._client.embeddings.create(model=self._model, input=batch) - embeddings.extend([e.embedding for e in resp.data]) - logger.info( - f" embedded {min(i + self._batch_size, len(bodies))}/{len(bodies)}" - ) - - mat = np.array(embeddings, dtype=np.float32) - norms = np.linalg.norm(mat, axis=1, keepdims=True) - self._matrix = mat / np.where(norms == 0, 1, norms) - logger.info(" OpenAI index ready") - - def retrieve(self, query: str, top_k: int = TOP_K) -> List[str]: - - resp = self._client.embeddings.create(model=self._model, input=[query]) - q_vec = np.array(resp.data[0].embedding, dtype=np.float32) - q_vec /= max(np.linalg.norm(q_vec), 1e-9) - scores = self._matrix @ q_vec - indices = scores.argsort()[::-1][:top_k] - return [self._doc_ids[int(i)] for i in indices] - - -class InfinityRetriever(Retriever): - """ - OpenAI-compatible retriever for a local/remote Infinity server. - Configurable via INFINITY_HOST environment variable (default: http://localhost:11434). - """ - - name = "infinity" - - _INSTRUCTIONS = { - "search_document": "", - "search_query": "query: ", - } - - def __init__( - self, - model: str = "Qwen/Qwen3-Embedding-4B", - batch_size: int = 2, - cache_dir: str = ".embed_cache", - ): - import requests - - self._model = os.environ.get("EMBED_MODEL", model) - self._host = os.environ.get("INFINITY_HOST", "http://localhost:11434") - self._batch_size = batch_size - self._session = requests.Session() - self._cache_dir = Path(cache_dir) - self._cache_dir.mkdir(exist_ok=True) - self._q_cache = {} - self._q_cache_path = None - - def _cache_key(self, corpus: List[dict]) -> str: - """Stable key based on model + corpus content.""" - corpus_fingerprint = hashlib.md5( - json.dumps([r["doc_id"] for r in corpus], sort_keys=True).encode() - ).hexdigest()[:12] - safe_model = self._model.replace("/", "_") - return f"{safe_model}__{corpus_fingerprint}" - - def index(self, corpus: List[dict]) -> None: - self._doc_ids = [r["doc_id"] for r in corpus] - key = self._cache_key(corpus) - cache_path = self._cache_dir / f"{self._cache_key(corpus)}.npz" - - self._q_cache_path = self._cache_dir / f"{key}_questions.json" - if self._q_cache_path.exists(): - with open(self._q_cache_path, "r") as f: - self._q_cache = json.load(f) - logger.info( - f" Loaded {len(self._q_cache)} cached questions from {self._q_cache_path}" - ) - - if cache_path.exists(): - logger.info(f" Loading Infinity embeddings from cache: {cache_path}") - data = np.load(cache_path, allow_pickle=True) - self._matrix = data["matrix"] - assert list(data["doc_ids"]) == self._doc_ids, "Cache doc_id mismatch!" - logger.info(" Infinity index ready (from cache)") - return - - bodies = [r.get("body", "") or "" for r in corpus] - - logger.info( - f" Embedding {len(bodies)} docs via Infinity ({self._model} at {self._host}) ..." - ) - - embeddings = [] - prefix = self._INSTRUCTIONS["search_document"] - - for i in range(0, len(bodies), self._batch_size): - batch = bodies[i : i + self._batch_size] - prefixed_batch = [prefix + text for text in batch] - - try: - resp = self._session.post( - f"{self._host}/embeddings", - json={"model": self._model, "input": prefixed_batch}, - timeout=300, - ) - resp.raise_for_status() - - batch_vecs = [d["embedding"] for d in resp.json()["data"]] - embeddings.extend(batch_vecs) - except Exception as e: - logger.error(f" Crash during corpus indexing at doc {i}: {e}") - raise - logger.info( - f" embedded {min(i + self._batch_size, len(bodies))}/{len(bodies)}" - ) - - mat = np.array(embeddings, dtype=np.float32) - norms = np.linalg.norm(mat, axis=1, keepdims=True) - self._matrix = mat / np.where(norms == 0, 1, norms) - np.savez_compressed(cache_path, matrix=self._matrix, doc_ids=self._doc_ids) - logger.info(f" Embeddings cached to {cache_path}") - - def retrieve(self, query: str, top_k: int = TOP_K) -> List[str]: - - if query in self._q_cache: - q_vec = np.array(self._q_cache[query], dtype=np.float32) - else: - prefix = self._INSTRUCTIONS["search_query"] - resp = self._session.post( - f"{self._host}/embeddings", - json={"model": self._model, "input": [prefix + query]}, - timeout=30, - ) - resp.raise_for_status() - - vec_list = resp.json()["data"][0]["embedding"] - self._q_cache[query] = vec_list - - if self._q_cache_path: - with open(self._q_cache_path, "w") as f: - json.dump(self._q_cache, f) - - q_vec = np.array(vec_list, dtype=np.float32) - - q_vec /= max(np.linalg.norm(q_vec), 1e-9) - scores = self._matrix @ q_vec - indices = scores.argsort()[::-1][:top_k] - return [self._doc_ids[int(i)] for i in indices] - - -class BedrockCohereRetriever(Retriever): - """ - Cohere Embed v4 via Amazon Bedrock (invoke_model). - - Uses the same AWS credential chain as BedrockGenerator — no separate - API key needed if you're already authenticated to Bedrock. - - Model ID : cohere.embed-v4:0 - Regions : us-east-1, eu-west-1, ap-northeast-1 - (cross-region inference also supported) - """ - - name = "bedrock-cohere-embed-v4" - - def __init__( - self, - model: str = "us.cohere.embed-v4:0", - region: str = "us-east-1", - batch_size: int = 96, - ): - import boto3 - - self._model = model - self._batch_size = batch_size - self._client = boto3.client("bedrock-runtime", region_name=region) - logger.info(f" BedrockCohereRetriever — model: {model}, region: {region}") - - def _embed(self, texts: List[str], input_type: str) -> "np.ndarray": - import json - - all_embeddings = [] - for i in range(0, len(texts), self._batch_size): - batch = texts[i : i + self._batch_size] - body = json.dumps( - { - "texts": batch, - "input_type": input_type, # "search_document" or "search_query" - "embedding_types": ["float"], - } - ) - resp = self._client.invoke_model( - modelId=self._model, - body=body, - accept="*/*", - contentType="application/json", - ) - result = json.loads(resp["body"].read()) - # Bedrock Cohere v4 returns {"embeddings": {"float": [[...], ...]}} - batch_vecs = result["embeddings"]["float"] - all_embeddings.extend(batch_vecs) - logger.info( - f" embedded {min(i + self._batch_size, len(texts))}/{len(texts)}" - ) - return np.array(all_embeddings, dtype=np.float32) - - def index(self, corpus: List[dict]) -> None: - - self._doc_ids = [r["doc_id"] for r in corpus] - bodies = [r.get("body", "") or "" for r in corpus] - - logger.info(f" Embedding {len(bodies)} docs via Bedrock Cohere Embed v4 ...") - mat = self._embed(bodies, input_type="search_document") - norms = np.linalg.norm(mat, axis=1, keepdims=True) - self._matrix = mat / np.where(norms == 0, 1, norms) - logger.info(" Bedrock Cohere index ready") - - def retrieve(self, query: str, top_k: int = TOP_K) -> List[str]: - - q_mat = self._embed([query], input_type="search_query") - q_vec = q_mat[0] - q_vec /= max(float(np.linalg.norm(q_vec)), 1e-9) - scores = self._matrix @ q_vec - indices = scores.argsort()[::-1][:top_k] - return [self._doc_ids[int(i)] for i in indices] - - -def build_retriever(name: str, region: str = "us-east-1") -> Retriever: - # ── original retrievers ──────────────────────────────────────────────────── - if name == "bm25": - return BM25Retriever() - if name == "cohere": - return CohereRetriever() - if name == "cohere-bedrock": - return BedrockCohereRetriever(region=region) - if name == "openai": - return OpenAIRetriever() - if name == "infinity": - return InfinityRetriever() - - # ── RRF and Graph retrievers (require retrieval_extensions.py) ───────────── - if not _EXTENSIONS_AVAILABLE: - raise SystemExit( - f"retriever={name!r} requires retrieval_extensions.py in the same " - "directory. Make sure that file is present and importable." - ) - - # RRF: fuse BM25 + dense retriever - if name == "rrf": - return RRFRetriever([BM25Retriever(), CohereRetriever()]) - if name == "rrf-openai": - return RRFRetriever([BM25Retriever(), OpenAIRetriever()]) - if name == "rrf-bedrock": - return RRFRetriever([BM25Retriever(), BedrockCohereRetriever(region=region)]) - if name == "rrf-infinity": - return RRFRetriever([BM25Retriever(), InfinityRetriever()]) - - # Graph-Augmented: expand any base retriever along the artifact graph - if name == "graph-bm25": - return GraphAugmentedRetriever(BM25Retriever()) - if name == "graph-cohere": - return GraphAugmentedRetriever(CohereRetriever()) - if name == "graph-rrf": - base = RRFRetriever([BM25Retriever(), CohereRetriever()]) - return GraphAugmentedRetriever(base) - if name == "graph-rrf": - base = RRFRetriever([BM25Retriever(), CohereRetriever()]) - return GraphAugmentedRetriever(base) - - raise ValueError( - f"Unknown retriever: {name!r}. " - "Choose bm25 | cohere | cohere-bedrock | openai | infinity | " - "rrf | rrf-openai | rrf-bedrock | rrf-infinity | " - "graph-bm25 | graph-cohere | graph-infinity | graph-rrf" - ) - - -# ───────────────────────────────────────────────────────────────────────────── -# GENERATORS -# ───────────────────────────────────────────────────────────────────────────── - - -SYSTEM_PROMPT = """\ -You are evaluating an enterprise knowledge base. You will be given a question -type, a question, and retrieved document excerpts. Answer using ONLY the -provided documents. Always respond with valid JSON matching the schema for -the question type — no markdown fences, no extra keys. - -─── RETRIEVAL ─────────────────────────────────────────────────────────────── -Which artifact first documented a specific fact? -{ - "artifact_id": "", - "artifact_type": "", - "timestamp": "", - "retrieved_artifact_ids": ["", ""] -} - -─── CAUSAL ────────────────────────────────────────────────────────────────── -What artifact or action directly followed event X? -{ - "artifact_id": "", - "event_type": "", - "actors": ["", ""], - "retrieved_artifact_ids": ["", ""] -} - -─── TEMPORAL ──────────────────────────────────────────────────────────────── -Did person P have access/knowledge of domain D before incident I? -{ - "had_knowledge": true, - "person": "", - "domain": "", - "departure_day": null, - "reasoning": "" -} - -─── GAP_DETECTION ─────────────────────────────────────────────────────────── -Was email/artifact E ever actioned? -{ - "was_actioned": false, - "artifact_id": "", - "downstream_artifacts": [], - "retrieved_artifact_ids": [""] -} - -─── ROUTING ───────────────────────────────────────────────────────────────── -Who was the first internal person to receive/see inbound artifact X? -{ - "first_recipient": "", - "was_escalated": true, - "retrieved_artifact_ids": [""] -} - -─── PLAN ──────────────────────────────────────────────────────────────────── -What was department X focused on during Day N? -{ - "dept": "", - "theme": "", - "retrieved_artifact_ids": [""] -} - -─── ESCALATION ────────────────────────────────────────────────────────────── -Who was involved in the escalation chain for incident X? -{ - "escalation_actors": ["", ""], - "retrieved_artifact_ids": ["", ""] -} - -─── KNOWLEDGE_GAP ─────────────────────────────────────────────────────────── -What domain was undocumented when incident X fired? -{ - "gap_areas": ["", ""], - "retrieved_artifact_ids": [""] -} - -If the documents contain insufficient evidence, still return the correct -schema with your best guess and add "insufficient_evidence": true. -""" - - -def _build_context(corpus_map: Dict[str, dict], doc_ids: List[str]) -> str: - parts = [] - for doc_id in doc_ids: - doc = corpus_map.get(doc_id) - if not doc: - continue - parts.append( - f"--- [{doc_id}] {doc.get('title', '')} ({doc.get('doc_type', '')}) ---\n" - f"{(doc.get('body', '') or '')[:1500]}" - ) - return "\n\n".join(parts) - - -class Generator: - name: str = "base" - - def generate(self, question: str, question_type: str, context: str) -> dict: - raise NotImplementedError - - -class NullGenerator(Generator): - """Used for retrieval-only runs (--generator none).""" - - name = "none" - - def generate(self, question: str, question_type: str, context: str) -> dict: - return {"answer": None, "artifact_ids": [], "reasoning": "retrieval-only run"} - - -class ClaudeGenerator(Generator): - def __init__(self, model: str = "claude-sonnet-4-20250514", max_tokens: int = 512): - import anthropic - - api_key = os.environ.get("ANTHROPIC_API_KEY") - if not api_key: - raise SystemExit("Set ANTHROPIC_API_KEY to use Claude generator") - self._client = anthropic.Anthropic(api_key=api_key) - self._model = model - self._max_tokens = max_tokens - self.name = f"claude/{model}" - - def generate(self, question: str, question_type: str, context: str) -> dict: - user_msg = ( - f"Question type: {question_type}\n\n" - f"Question: {question}\n\n" - f"Retrieved documents:\n{context}" - ) - resp = self._client.messages.create( - model=self._model, - max_tokens=self._max_tokens, - system=SYSTEM_PROMPT, - messages=[{"role": "user", "content": user_msg}], - ) - return _parse_json_response(resp.content[0].text) - - -class OpenAIGenerator(Generator): - def __init__(self, model: str = "gpt-4o", max_tokens: int = 512): - from openai import OpenAI - - api_key = os.environ.get("OPENAI_API_KEY") - if not api_key: - raise SystemExit("Set OPENAI_API_KEY to use OpenAI generator") - self._client = OpenAI(api_key=api_key) - self._model = model - self._max_tokens = max_tokens - self.name = f"openai/{model}" - - def generate(self, question: str, question_type: str, context: str) -> dict: - user_msg = ( - f"Question type: {question_type}\n\n" - f"Question: {question}\n\n" - f"Retrieved documents:\n{context}" - ) - resp = self._client.chat.completions.create( - model=self._model, - max_tokens=self._max_tokens, - messages=[ - {"role": "system", "content": SYSTEM_PROMPT}, - {"role": "user", "content": user_msg}, - ], - response_format={"type": "json_object"}, - ) - return _parse_json_response(resp.choices[0].message.content) - - -class CohereGenerator(Generator): - def __init__(self, model: str = "command-r-plus", max_tokens: int = 512): - import cohere - - api_key = os.environ.get("COHERE_API_KEY") - if not api_key: - raise SystemExit("Set COHERE_API_KEY to use Cohere generator") - self._co = cohere.ClientV2(api_key=api_key) - self._model = model - self._max_tokens = max_tokens - self.name = f"cohere/{model}" - - def generate(self, question: str, question_type: str, context: str) -> dict: - user_msg = ( - f"Question type: {question_type}\n\n" - f"Question: {question}\n\n" - f"Retrieved documents:\n{context}" - ) - resp = self._co.chat( - model=self._model, - messages=[ - {"role": "system", "content": SYSTEM_PROMPT}, - {"role": "user", "content": user_msg}, - ], - max_tokens=self._max_tokens, - ) - return _parse_json_response(resp.message.content[0].text) - - -class BedrockGenerator(Generator): - """ - Amazon Bedrock via boto3 converse() API. - - Works with any model Bedrock exposes through the Converse API: - Claude : anthropic.claude-3-5-sonnet-20241022-v2:0 - anthropic.claude-3-7-sonnet-20250219-v1:0 - Llama : meta.llama3-3-70b-instruct-v1:0 - Mistral : mistral.mistral-large-2402-v1:0 - Nova : amazon.nova-pro-v1:0 / amazon.nova-lite-v1:0 - Titan : amazon.titan-text-premier-v1:0 - - Authentication uses your standard AWS credential chain: - - environment variables AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY - - ~/.aws/credentials profile - - IAM role (EC2 / ECS / Lambda) - - Pass --region to target a specific Bedrock region (default: us-east-1). - Cross-region inference profile IDs (e.g. us.anthropic.claude-3-7-...) are - supported — just pass the full profile ARN or ID as --model. - """ - - def __init__( - self, - model: str = "anthropic.claude-3-5-sonnet-20241022-v2:0", - region: str = "us-east-1", - max_tokens: int = 512, - call_delay: float = 1.0, # seconds to sleep between every call - max_retries: int = 6, # retries on ThrottlingException - retry_base_delay: float = 5.0, # initial backoff seconds (doubles each retry) - ): - import boto3 - - if not region or len(region.split("-")) < 3: - raise ValueError( - f"Invalid AWS region: {region!r}. " - "Expected format like 'us-east-1' or 'us-west-2'. " - "Pass --region explicitly to override ~/.aws/config." - ) - - self._model = model - self._max_tokens = max_tokens - self._call_delay = call_delay - self._max_retries = max_retries - self._retry_base_delay = retry_base_delay - self._client = boto3.client("bedrock-runtime", region_name=region) - self.name = f"bedrock/{model}" - logger.info(f" Bedrock client initialised — model: {model}, region: {region}") - - def generate(self, question: str, question_type: str, context: str) -> dict: - import random - - user_msg = ( - f"Question type: {question_type}\n\n" - f"Question: {question}\n\n" - f"Retrieved documents:\n{context}" - ) - - # Polite inter-call delay to stay under TPM limits - if self._call_delay > 0: - time.sleep(self._call_delay) - - last_exc = None - for attempt in range(self._max_retries + 1): - try: - resp = self._client.converse( - modelId=self._model, - system=[{"text": SYSTEM_PROMPT}], - messages=[{"role": "user", "content": [{"text": user_msg}]}], - inferenceConfig={"maxTokens": self._max_tokens}, - ) - content_blocks = resp["output"]["message"]["content"] - text = "" - for block in content_blocks: - if "text" in block: - text = block["text"] - break - if not text: - logger.warning(f" Unexpected content blocks: {content_blocks}") - return { - "answer": str(content_blocks), - "artifact_ids": [], - "reasoning": "parse error", - } - return _parse_json_response(text) - - except Exception as exc: - error_code = ( - getattr(exc, "response", {}).get("Error", {}).get("Code", "") - ) - if error_code == "ThrottlingException": - if attempt >= self._max_retries: - logger.error( - f" Throttled after {self._max_retries} retries — giving up" - ) - raise - # Exponential backoff with ±20% jitter - delay = self._retry_base_delay * (2**attempt) - delay *= 0.8 + 0.4 * random.random() - logger.warning( - f" Throttled (attempt {attempt + 1}/{self._max_retries}), " - f"retrying in {delay:.1f}s ..." - ) - time.sleep(delay) - last_exc = exc - else: - raise - - raise last_exc # should never reach here - - -def _parse_json_response(text: str) -> dict: - """ - Extract JSON from model response, tolerating: - - ... reasoning blocks (DeepSeek R1 / chain-of-thought models) - - markdown fences (```json ... ```) - - prose before/after the JSON block - - missing fences (bare JSON) - """ - # 1. Strip ... blocks (DeepSeek R1 and similar CoT models) - text = re.sub(r".*?", "", text, flags=re.DOTALL).strip() - - # 2. Try to extract a fenced JSON block first - fenced = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL) - if fenced: - candidate = fenced.group(1) - else: - # 3. Find the first { and last } to extract bare JSON - start = text.find("{") - end = text.rfind("}") - if start != -1 and end != -1 and end > start: - candidate = text[start : end + 1] - else: - candidate = text.strip() - - try: - return json.loads(candidate) - except json.JSONDecodeError: - return {"answer": text, "artifact_ids": [], "reasoning": "parse error"} - - -def build_generator( - name: str, model: Optional[str], region: str = "us-east-1", call_delay: float = 1.0 -) -> Generator: - if name == "none": - return NullGenerator() - if name == "claude": - return ClaudeGenerator(model=model or "claude-sonnet-4-20250514") - if name == "openai": - return OpenAIGenerator(model=model or "gpt-4o") - if name == "cohere": - return CohereGenerator(model=model or "command-r-plus") - if name == "bedrock": - return BedrockGenerator( - model=model or "anthropic.claude-3-5-sonnet-20241022-v2:0", - region=region, - call_delay=call_delay, - ) - raise ValueError( - f"Unknown generator: {name!r}. Choose none | claude | openai | cohere | bedrock" - ) - - -# ───────────────────────────────────────────────────────────────────────────── -# SCORING (wraps scorer.py if present, falls back to retrieval-only metrics) -# ───────────────────────────────────────────────────────────────────────────── - - -def _load_scorer(scorer_path: Optional[str] = None): - """ - Try to import OrgForgeScorer from scorer.py. - Search order: - 1. --scorer CLI arg (explicit path) - 2. Same directory as eval_e2e.py - 3. Parent directory of eval_e2e.py - 4. Current working directory - """ - from importlib.machinery import SourceFileLoader - import types - - candidates = [] - if scorer_path: - candidates.append(Path(scorer_path)) - - this_dir = Path(__file__).resolve().parent - candidates += [ - this_dir / "scorer.py", - this_dir.parent / "scorer.py", - Path.cwd() / "scorer.py", - ] - - for p in candidates: - if not p.exists(): - continue - try: - import sys - - mod = types.ModuleType("orgforge_scorer") - mod.__file__ = str(p) - sys.modules["orgforge_scorer"] = ( - mod # must be registered before exec for @dataclass - ) - SourceFileLoader("orgforge_scorer", str(p)).exec_module(mod) - scorer = mod.OrgForgeScorer() - logger.info(f" scorer.py loaded from {p}") - return scorer - except Exception as exc: - logger.warning(f" scorer.py found at {p} but failed to load ({exc})") - - logger.warning( - "scorer.py not found — using retrieval-only metrics. " - "Pass --scorer /path/to/scorer.py to fix this." - ) - return None - - -def score_answer( - scorer, - question: dict, - agent_answer: dict, - top_k_ids: List[str], -) -> dict: - """ - Returns a scoring dict: - retrieval_mrr — MRR@10 based on evidence_chain - retrieval_recall — Recall@10 based on evidence_chain - answer_score — 0.0–1.0 from scorer.py, or None if unavailable - correct — bool (score >= 0.9), or None - """ - evidence = question.get("evidence_chain", []) - if isinstance(evidence, str): - try: - evidence = json.loads(evidence) - except Exception: - evidence = [] - - relevant = set(evidence) - mrr = next( - (1.0 / (i + 1) for i, d in enumerate(top_k_ids) if d in relevant), - 0.0, - ) - recall = ( - sum(1 for d in top_k_ids if d in relevant) / len(relevant) if relevant else 1.0 - ) - - answer_score = None - if ( - scorer is not None - and agent_answer.get("answer") is not None - or any( - k in agent_answer - for k in ( - "artifact_id", - "had_knowledge", - "was_actioned", - "first_recipient", - "dept", - "escalation_actors", - "gap_areas", - ) - ) - ): - try: - # Inject retrieved IDs so evidence scoring works even if LLM omits them - enriched = {**agent_answer} - if ( - "retrieved_artifact_ids" not in enriched - or not enriched["retrieved_artifact_ids"] - ): - enriched["retrieved_artifact_ids"] = top_k_ids - result = scorer.score(question, enriched) - answer_score = result.score # ScorerResult.score is always a float - except Exception as exc: - logger.debug(f"Scorer error on {question.get('question_id')}: {exc}") - - return { - "retrieval_mrr": round(mrr, 4), - "retrieval_recall": round(recall, 4), - "answer_score": round(answer_score, 4) if answer_score is not None else None, - "correct": (answer_score >= 0.9) if answer_score is not None else None, - } - - -# ───────────────────────────────────────────────────────────────────────────── -# AGGREGATE METRICS -# ───────────────────────────────────────────────────────────────────────────── - - -def _mean(vals: List[float]) -> float: - return round(sum(vals) / len(vals), 4) if vals else 0.0 - - -def aggregate(per_question: List[dict]) -> dict: - by_type: Dict[str, list] = defaultdict(list) - by_diff: Dict[str, list] = defaultdict(list) - - for r in per_question: - qtype = r.get("question_type", "UNKNOWN") - diff = r.get("difficulty", "unknown") - by_type[qtype].append(r) - by_diff[diff].append(r) - - def _agg_group(rows): - mrr_vals = [r["scores"]["retrieval_mrr"] for r in rows] - rec_vals = [r["scores"]["retrieval_recall"] for r in rows] - score_vals = [ - r["scores"]["answer_score"] - for r in rows - if r["scores"]["answer_score"] is not None - ] - correct_vals = [ - r["scores"]["correct"] for r in rows if r["scores"]["correct"] is not None - ] - return { - "n": len(rows), - "mrr_at_10": _mean(mrr_vals), - "recall_at_10": _mean(rec_vals), - "answer_score": _mean(score_vals) if score_vals else None, - "accuracy": _mean([float(v) for v in correct_vals]) - if correct_vals - else None, - } - - return { - "overall": _agg_group(per_question), - "by_type": {k: _agg_group(v) for k, v in sorted(by_type.items())}, - "by_difficulty": {k: _agg_group(v) for k, v in sorted(by_diff.items())}, - } - - -# ───────────────────────────────────────────────────────────────────────────── -# LEADERBOARD -# ───────────────────────────────────────────────────────────────────────────── - -LEADERBOARD_CSV_PATH = Path("leaderboard.csv") - -# All question types — used to produce stable CSV columns across runs even -# when a given run hasn't seen every type yet (cells will be empty). -_ALL_QTYPES = [ - "CAUSAL", - "ESCALATION", - "GAP_DETECTION", - "PLAN", - "RETRIEVAL", - "ROUTING", - "TEMPORAL", -] - - -def _flatten_row(row: dict) -> dict: - """Flatten a leaderboard JSON row into a CSV-friendly dict. - - Per-type metrics become columns: mrr_CAUSAL, score_CAUSAL, etc. - Tier 1 = mrr_at_10 / recall_at_10 (always present) - Tier 2 = answer_score / accuracy (None for retrieval-only runs) - """ - flat = { - "run_id": row.get("run_id", ""), - "timestamp": row.get("timestamp", ""), - "tier": row.get("tier", ""), - "retriever": row.get("retriever", ""), - "generator": row.get("generator", ""), - "n": row.get("n", ""), - # Tier 1 overall - "mrr_at_10": row.get("mrr_at_10", ""), - "recall_at_10": row.get("recall_at_10", ""), - # Tier 2 overall (empty string for Tier 1-only runs) - "answer_score": row.get("answer_score", ""), - "accuracy": row.get("accuracy", ""), - } - by_type = row.get("by_type", {}) - for qtype in _ALL_QTYPES: - m = by_type.get(qtype, {}) - flat[f"mrr_{qtype}"] = m.get("mrr_at_10", "") - flat[f"score_{qtype}"] = m.get("answer_score", "") - return flat - - -def _write_leaderboard_csv(leaderboard: List[dict]) -> None: - import csv - - if not leaderboard: - return - - # Union all keys across rows so older rows without newer qtypes still render - all_keys: list = [] - seen_keys: set = set() - for row in leaderboard: - for k in _flatten_row(row): - if k not in seen_keys: - all_keys.append(k) - seen_keys.add(k) - - with open(LEADERBOARD_CSV_PATH, "w", newline="") as f: - writer = csv.DictWriter(f, fieldnames=all_keys, extrasaction="ignore") - writer.writeheader() - for row in leaderboard: - writer.writerow(_flatten_row(row)) - - logger.info(f" leaderboard CSV updated: {LEADERBOARD_CSV_PATH}") - - -def update_leaderboard( - run_id: str, retriever: str, generator: str, summary: dict -) -> None: - leaderboard = [] - if LEADERBOARD_PATH.exists(): - leaderboard = json.loads(LEADERBOARD_PATH.read_text()) - - overall = summary.get("overall", {}) - tier = "1" if generator == "none" else "1+2" - row = { - "run_id": run_id, - "timestamp": datetime.now(timezone.utc).isoformat(), - "tier": tier, - "retriever": retriever, - "generator": generator, - "n": overall.get("n"), - "mrr_at_10": overall.get("mrr_at_10"), - "recall_at_10": overall.get("recall_at_10"), - "answer_score": overall.get("answer_score"), # None for Tier 1 runs - "accuracy": overall.get("accuracy"), # None for Tier 1 runs - "by_type": { - qtype: { - "mrr_at_10": m.get("mrr_at_10"), - "answer_score": m.get("answer_score"), - } - for qtype, m in summary.get("by_type", {}).items() - }, - } - - # Replace existing run with same id, else append - leaderboard = [r for r in leaderboard if r.get("run_id") != run_id] - leaderboard.append(row) - - # Tier 1+2 rows rank above Tier 1; within each tier sort by primary metric desc - leaderboard.sort( - key=lambda r: ( - 0 if r.get("tier") == "1+2" else 1, - -(r.get("answer_score") or 0.0), - -(r.get("mrr_at_10") or 0.0), - ), - ) - - LEADERBOARD_PATH.write_text(json.dumps(leaderboard, indent=2)) - logger.info(f" leaderboard JSON updated: {LEADERBOARD_PATH}") - - _write_leaderboard_csv(leaderboard) - - -# ───────────────────────────────────────────────────────────────────────────── -# MAIN EVAL LOOP -# ───────────────────────────────────────────────────────────────────────────── - - -def run_eval(args: argparse.Namespace) -> None: - run_dir_tmp = RESULTS_DIR / f"_tmp_{datetime.now().strftime('%Y%m%dT%H%M%S')}" - run_dir_tmp.mkdir(parents=True, exist_ok=True) - - # 1. Load data - corpus, questions = load_dataset(args.local) - - if args.limit: - questions = questions[: args.limit] - logger.info(f" Limited to {args.limit} questions") - - corpus_map = {r["doc_id"]: r for r in corpus} - - # 2. Build retriever + index - retriever = build_retriever(args.retriever, region=args.region) - logger.info(f"Indexing with {retriever.name} ...") - t0 = time.time() - retriever.index(corpus) - logger.info(f" Index built in {time.time() - t0:.1f}s") - - # 3. Build generator — do this before constructing run_id so we can use generator.name - generator = build_generator( - args.generator, args.model, region=args.region, call_delay=args.call_delay - ) - logger.info(f"Generator: {generator.name}") - - # run_id uses the full generator name (e.g. bedrock/claude-opus-4-6) not just the flag - safe_gen = generator.name.replace("/", "-").replace(":", "-") - run_id = f"{retriever.name}__{safe_gen}__{datetime.now().strftime('%Y%m%dT%H%M%S')}" - run_dir = RESULTS_DIR / run_id - run_dir_tmp.rename(run_dir) - logger.info(f"Run ID: {run_id}") - - # 4. Load scorer - scorer = _load_scorer(getattr(args, "scorer", None)) - - # 5. Eval loop — deserialise JSON string fields from parquet before scoring - for q in questions: - for field in ("ground_truth", "evidence_chain"): - val = q.get(field) - if isinstance(val, str): - try: - q[field] = json.loads(val) - except (json.JSONDecodeError, TypeError): - pass - - per_question = [] - for i, q in enumerate(questions): - qid = q.get("question_id", f"q{i}") - qtype = q.get("question_type", "") - qtext = q.get("question_text", "") - - # Retrieve - top_k_ids = retriever.retrieve(qtext, top_k=TOP_K) - - # Generate - context = _build_context(corpus_map, top_k_ids) - agent_answer = generator.generate(qtext, qtype, context) - - # Score - scores = score_answer(scorer, q, agent_answer, top_k_ids) - - per_question.append( - { - "question_id": qid, - "question_type": qtype, - "difficulty": q.get("difficulty"), - "question_text": qtext, - "top_k_ids": top_k_ids, - "agent_answer": agent_answer, - "scores": scores, - } - ) - - status = ( - f"✓ {scores['answer_score']:.2f}" - if scores["answer_score"] is not None - else f"MRR {scores['retrieval_mrr']:.2f}" - ) - logger.info(f" [{i + 1}/{len(questions)}] {qid} ({qtype}) — {status}") - - # 6. Aggregate - summary = aggregate(per_question) - - # 7. Write results - with open(run_dir / "per_question.json", "w") as f: - json.dump(per_question, f, indent=2, default=str) - with open(run_dir / "summary.json", "w") as f: - json.dump(summary, f, indent=2) - - logger.info(f"Results written to {run_dir}") - - # 8. Update leaderboard - update_leaderboard(run_id, retriever.name, generator.name, summary) - - # 9. Print summary table - _print_summary(summary, retriever.name, generator.name) - - -def _print_summary(summary: dict, retriever: str, generator: str) -> None: - print(f"\n{'=' * 64}") - print(f" Retriever : {retriever}") - print(f" Generator : {generator}") - print(f"{'=' * 64}") - print( - f" {'Type':<16} {'MRR@10':>8} {'Recall@10':>10} {'Score':>8} {'Acc':>6} {'N':>4}" - ) - print(f" {'-' * 56}") - - def _fmt(v): - return f"{v:.4f}" if v is not None else " n/a " - - overall_row = summary.get("overall", {}) - print( - f" {'OVERALL':<16} {_fmt(overall_row.get('mrr_at_10')):>8} " - f"{_fmt(overall_row.get('recall_at_10')):>10} " - f"{_fmt(overall_row.get('answer_score')):>8} " - f"{_fmt(overall_row.get('accuracy')):>6} " - f"{overall_row.get('n', 0):>4}" - ) - print(f" {'-' * 56}") - for qtype, m in sorted(summary.get("by_type", {}).items()): - print( - f" {qtype:<16} {_fmt(m.get('mrr_at_10')):>8} " - f"{_fmt(m.get('recall_at_10')):>10} " - f"{_fmt(m.get('answer_score')):>8} " - f"{_fmt(m.get('accuracy')):>6} " - f"{m.get('n', 0):>4}" - ) - print(f"{'=' * 64}\n") - - -# ───────────────────────────────────────────────────────────────────────────── -# CLI -# ───────────────────────────────────────────────────────────────────────────── - - -def _parse_args() -> argparse.Namespace: - p = argparse.ArgumentParser( - description="OrgForge end-to-end RAG evaluation harness" - ) - p.add_argument( - "--retriever", - choices=[ - "bm25", - "cohere", - "cohere-bedrock", - "openai", - "infinity", - # Reciprocal Rank Fusion - "rrf", - "rrf-openai", - "rrf-bedrock", - "rrf-infinity", - # Graph-Augmented (1-2 hop artifact expansion) - "graph-bm25", - "graph-cohere", - "graph-infinity", - "graph-rrf", - ], - default="bm25", - help=( - "Retriever to use (default: bm25).\n" - " bm25 / cohere / cohere-bedrock / openai / infinity — single retrievers\n" - " rrf / rrf-openai / rrf-bedrock / rrf-infinity — BM25 + dense fusion (RRF)\n" - " graph-bm25 / graph-cohere / graph-infinity / graph-rrf — graph-augmented expansion" - ), - ) - p.add_argument( - "--generator", - choices=["none", "claude", "openai", "cohere", "bedrock"], - default="claude", - help="Generation model to use (default: claude). Use 'none' for retrieval-only.", - ) - p.add_argument( - "--model", - default=None, - help=( - "Specific model string for the generator. Examples:\n" - " claude : claude-sonnet-4-20250514\n" - " openai : gpt-4o\n" - " cohere : command-r-plus\n" - " bedrock : anthropic.claude-3-5-sonnet-20241022-v2:0\n" - " anthropic.claude-3-7-sonnet-20250219-v1:0\n" - " meta.llama3-3-70b-instruct-v1:0\n" - " mistral.mistral-large-2402-v1:0\n" - " amazon.nova-pro-v1:0" - ), - ) - p.add_argument( - "--region", - default="us-east-1", - help="AWS region for Bedrock (default: us-east-1)", - ) - p.add_argument( - "--local", - default=None, - metavar="PATH", - help="Path to local hf_dataset directory (skips HuggingFace download)", - ) - p.add_argument( - "--scorer", - default=None, - metavar="PATH", - help="Explicit path to scorer.py (e.g. ../scorer.py). Auto-discovered if omitted.", - ) - p.add_argument( - "--limit", - type=int, - default=None, - metavar="N", - help="Evaluate only the first N questions (useful for smoke-testing)", - ) - p.add_argument( - "--top-k", - type=int, - default=TOP_K, - help=f"Number of documents to retrieve per question (default: {TOP_K})", - ) - p.add_argument( - "--call-delay", - type=float, - default=1.0, - metavar="SECONDS", - help="Sleep between LLM calls to avoid throttling (default: 1.0s). " - "Increase to 2-3 for Opus or if you keep hitting ThrottlingException.", - ) - p.add_argument( - "--verbose", - "-v", - action="store_true", - help="Debug logging", - ) - return p.parse_args() - - -if __name__ == "__main__": - args = _parse_args() - logging.basicConfig( - level=logging.DEBUG if args.verbose else logging.INFO, - format="%(asctime)s %(levelname)s %(message)s", - datefmt="%H:%M:%S", - ) - run_eval(args) diff --git a/eval/eval_harness.py b/eval/eval_harness.py deleted file mode 100644 index 5a5e7fe..0000000 --- a/eval/eval_harness.py +++ /dev/null @@ -1,1818 +0,0 @@ -""" -eval_harness.py -=============== -OrgForge Eval Dataset Generator — v2 - -Produces three novel eval tracks that require the deterministic state machine -to exist. No retrieval questions. Those are covered by other benchmarks. - -Run after flow.py and post_sim_artifacts.py complete: - python eval_harness.py - -Produces in export/eval/: - actor_visibility.json — per-actor artifact visibility cones, time-indexed - causal_link_index.json — explicit causal links derived from sim flags - absence_catalog.json — expected-but-absent artifact pairs - eval_questions.json — PERSPECTIVE + COUNTERFACTUAL + SILENCE questions - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -TRACK 1 — PERSPECTIVE -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Questions scoped to what a specific actor could have known at a specific moment, -given their actual subsystem access and information horizon. - -Ground truth is derived from the actor's visibility cone: the set of artifact IDs -reachable by that actor at or before as_of_time, filtered by subsystem access. - -Cross-subsystem questions (e.g. engineer sees Slack + Zoom but not Salesforce) -are flagged difficulty="hard". Single-subsystem questions are "medium". - -Example: - "Based only on what Morgan had access to as of Day 9, should she have known - that Acme Corp was at churn risk?" - ground_truth: { "answer": False, "reason": "sf_deals_risk_flagged not in Morgan's visibility cone" } - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -TRACK 2 — COUNTERFACTUAL -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Questions of the form "if X had been different, would Y have occurred?" -Only generated where the sim encodes an explicit causal link: - - involves_gap / knowledge_gap_detected → incident causation - - recurrence_of → repeat incident prevention - - spawned_doc → design discussion → documentation - - email_dropped → unactioned communication - - sf_ownership_lapsed → CRM ownership gap - - zd_escalation_source → support ticket → incident - -Ground truth is always derivable from the explicit link without inference. - -Example: - "If Jordan had documented auth-service before departing, would incident IT-108 - have been diagnosed faster?" - ground_truth: { "outcome_changed": True, "mechanism": "knowledge_gap_detected", - "gap_domain": "auth-service", "causal_event": "evt_..." } - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -TRACK 3 — SILENCE -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Questions about things that did NOT happen. The state machine is the arbiter: -if no event fired, absence is ground truth regardless of whether absence was -intentional. - -Each SILENCE question includes an expected_search_space — the artifact IDs the -agent MUST check before concluding absence. A correct "no" reached without -searching the right places scores 0 on trajectory even if the boolean is right. - -Example: - "Was a postmortem written for the Zendesk escalation on Day 6?" - ground_truth: False - expected_search_space: ["confluence/postmortems/", "jira/IT-*", "slack/incidents"] - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Design principles -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -- Ground truth is always derived from SimEvent log. LLMs only write prose. -- Question prose generation includes a structured validation loop. -- Actor visibility cones are first-class data structures, not a scoring afterthought. -- Subsystem access is explicitly modeled per actor per day. -- The absence catalog is built by pattern-matching expected event pairs, not heuristics. -""" - -from __future__ import annotations - -import json -import logging -import random -import re -from config_loader import CONFIG, DEPARTED_EMPLOYEES -import yaml -from collections import defaultdict -from dataclasses import dataclass, asdict -from datetime import datetime, timedelta -from pathlib import Path -from typing import Dict, List, Optional, Set, Tuple - -from agent_factory import make_agent -from crewai import Crew, Task -from memory import Memory, SimEvent - -logger = logging.getLogger("orgforge.eval") - -with open(Path(__file__).resolve().parent.parent / "config" / "config.yaml") as f: - _CFG = yaml.safe_load(f) - -BASE = Path(_CFG["simulation"].get("output_dir", "./export")) -EVAL_DIR = BASE / "eval" -EVAL_DIR.mkdir(parents=True, exist_ok=True) - -_SIM_START = datetime.strptime(_CFG["simulation"]["start_date"], "%Y-%m-%d") - -# ── Subsystem access model ──────────────────────────────────────────────────── -# Maps role patterns to the subsystems they have access to. -# Agents outside a subsystem cannot retrieve its artifacts. -# Extend this as new subsystems are added to the simulation. - -_ROLE_SUBSYSTEM_ACCESS: Dict[str, Set[str]] = { - "ceo": { - "slack", - "jira", - "confluence", - "zoom", - "email", - "salesforce", - "zendesk", - "datadog", - }, - "product": {"slack", "jira", "confluence", "zoom", "email"}, - "engineering_backend": {"slack", "jira", "confluence", "git", "zoom", "datadog"}, - "engineering_mobile": {"slack", "jira", "confluence", "git", "zoom", "datadog"}, - "design": {"slack", "confluence", "zoom"}, - "sales_marketing": {"slack", "salesforce", "email", "zoom", "confluence"}, - "hr_ops": {"slack", "email", "confluence", "zoom"}, - "qa_support": {"slack", "zendesk", "confluence", "email"}, - "external": set(), -} - -# Maps artifact ID prefixes / doc_types to their subsystem -_ARTIFACT_SUBSYSTEM: Dict[str, str] = { - "jira": "jira", - "confluence": "confluence", - "slack": "slack", - "pr": "git", - "email": "email", - "zd_ticket": "zendesk", - "sf_opp": "salesforce", - "sf_account": "salesforce", - "datadog": "datadog", - "zoom": "zoom", - "invoice": "email", # invoices are email artifacts for access purposes - "nps": "salesforce", # NPS lives in the CRM surface -} - -# Explicit causal link types the sim encodes — COUNTERFACTUAL scope -_EXPLICIT_CAUSAL_LINKS = { - "involves_gap", # incident ← knowledge gap - "recurrence_of", # incident ← prior unresolved incident - "spawned_doc", # confluence ← design discussion - "email_dropped", # communication failure ← routing gap - "sf_ownership_lapsed", # CRM gap ← employee departure - "zd_escalation_source", # incident ← support ticket escalation - "blocker_flagged", # blocker → delayed progress - "incident_coordination", # incident → external contact - "departure_reassignment", # departure → ticket/escalation shift - "assignment_domain_mismatch", # planning mismatch → knowledge gap → incident -} - -# Expected event pairs for SILENCE catalog: -# (trigger_event_type, expected_response_event_type, link_field) -# If trigger fired but response did not, that's a valid SILENCE question target. -_SILENCE_PAIRS: List[Tuple[str, str, str]] = [ - ("incident_opened", "postmortem_created", "jira"), - ("incident_opened", "incident_resolved", "jira"), - ("customer_escalation", "zd_ticket_opened", "email"), - ("customer_email_routed", "zd_ticket_opened", "email"), - ("inbound_external_email", "customer_email_routed", "email"), - ("design_discussion", "confluence_created", "zoom_transcript"), - ("knowledge_gap_detected", "confluence_created", "gap_domain"), - ("zd_tickets_escalated", "incident_opened", "jira"), - ("employee_departed", "sf_ownership_lapsed", "actor"), - ("employee_departed", "ticket_reassigned", "actor"), - ("pr_opened", "pr_merged", "pr"), - ("incident_opened", "zd_tickets_escalated", "jira"), - ("employee_hired", "onboarding_session", "name"), - ("employee_hired", "warmup_1on1", "name"), - ("incident_opened", "sf_deals_risk_flagged", "jira"), - ("assignment_domain_mismatch", "knowledge_gap_detected", "ticket_id"), -] - -_BROADCAST_CONFIG = { - "incident_opened": ["slack", "datadog"], - "incident_resolved": ["slack"], - "postmortem_created": ["slack", "confluence"], - "standup": ["slack"], - "pr_opened": ["git", "slack"], - "pr_merged": ["git", "slack"], - "knowledge_gap_detected": ["slack", "confluence"], -} - - -def _safe_artifact_values(artifact_ids: dict) -> Set[str]: - """Flatten artifact_ids values — some may be lists.""" - vals: Set[str] = set() - for v in (artifact_ids or {}).values(): - if isinstance(v, list): - vals.update(str(x) for x in v) - elif v: - vals.add(str(v)) - return vals - - -# ───────────────────────────────────────────────────────────────────────────── -# DATA STRUCTURES -# ───────────────────────────────────────────────────────────────────────────── - - -@dataclass -class ActorVisibilityCone: - """ - The complete set of artifact IDs visible to a specific actor at a specific - moment, partitioned by subsystem. - - Built from SimEvents: an actor can see an artifact if they appear in - event.actors for that artifact's creation event, or if the artifact was - broadcast to a channel/tool they have access to (e.g. an incident Slack - message is visible to all engineers). - """ - - actor: str - role: str - as_of_time: str # ISO timestamp — the knowledge horizon - as_of_day: int - subsystem_access: Set[str] # subsystems this actor can query - visible_artifacts: Dict[str, Set[str]] # subsystem → set of artifact IDs - directly_involved: Set[str] # artifacts where actor appears in event.actors - broadcast_visible: Set[str] # artifacts visible via channel broadcast - - def all_visible(self) -> Set[str]: - all_ids: Set[str] = set() - for ids in self.visible_artifacts.values(): - all_ids.update(ids) - return all_ids - - def can_see(self, artifact_id: str, doc_type: str) -> bool: - subsystem = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") - if subsystem not in self.subsystem_access: - return False - return artifact_id in self.all_visible() - - def to_dict(self) -> dict: - return { - "actor": self.actor, - "role": self.role, - "as_of_time": self.as_of_time, - "as_of_day": self.as_of_day, - "subsystem_access": sorted(self.subsystem_access), - "visible_artifacts": { - k: sorted(v) for k, v in self.visible_artifacts.items() - }, - "directly_involved": sorted(self.directly_involved), - "broadcast_visible": sorted(self.broadcast_visible), - } - - -@dataclass -class CausalLink: - """ - An explicit causal relationship encoded in the simulation. - These are the only valid sources for COUNTERFACTUAL questions. - """ - - link_type: str # one of _EXPLICIT_CAUSAL_LINKS - cause_event_id: str - cause_event_type: str - effect_event_id: str - effect_event_type: str - actors: List[str] - day: int - link_field: str # the fact key that carries the link - link_value: str # the value of that field - subsystems_involved: Set[str] - counterfactual_premise: str # natural language "if X had been different" - counterfactual_outcome: str # natural language "then Y would have..." - outcome_changed: bool # does removing the cause change the effect? - - def to_dict(self) -> dict: - d = asdict(self) - d["subsystems_involved"] = sorted(self.subsystems_involved) - return d - - -@dataclass -class AbsenceRecord: - """ - A case where a trigger event fired but its expected response event did not. - The state machine is the arbiter — no inference about intent. - """ - - trigger_event_id: str - trigger_event_type: str - expected_response_type: str - trigger_day: int - trigger_actors: List[str] - trigger_artifact_ids: Dict[str, str] - link_field: str - link_value: str - subsystem: str - expected_search_space: List[str] # artifact IDs the agent must check - - def to_dict(self) -> dict: - return asdict(self) - - -# ───────────────────────────────────────────────────────────────────────────── -# ACTOR VISIBILITY BUILDER -# ───────────────────────────────────────────────────────────────────────────── - - -class ActorVisibilityBuilder: - """ - Reconstructs the knowledge cone for every actor at every day boundary. - - Visibility rules: - 1. DIRECT: actor appears in event.actors for an artifact's creation event - 2. BROADCAST: artifact was created in a shared channel (Slack incidents, - standups, engineering-wide announcements) — visible to all actors with - that subsystem access - 3. ROLE-GATED: actor's role must include the artifact's subsystem - 4. TEMPORAL: artifact timestamp must be <= as_of_time - - Broadcast channels are inferred from event type: - - standup, incident_alert, dept_announcement → broadcast to subsystem members - - direct_message, email, zd_ticket → direct only - """ - - # Event types whose artifacts are broadcast to all actors with subsystem access - _BROADCAST_EVENTS = { - "standup", - "incident_opened", - "incident_resolved", - "postmortem_created", - "pr_opened", - "pr_merged", - "knowledge_gap_detected", - } - - def __init__(self, mem: Memory): - self._mem = mem - self._events: List[SimEvent] = mem.get_event_log(from_db=True) - self._actor_roles: Dict[str, str] = self._infer_actor_roles() - - def _infer_actor_roles(self) -> Dict[str, str]: - """ - Standardize role inference using config_loader ground truth. - """ - roles: Dict[str, str] = {} - - for dept, members in CONFIG["org_chart"].items(): - role_slug = dept.lower().replace(" ", "_") - for name in members: - roles[name] = role_slug - - for name, data in DEPARTED_EMPLOYEES.items(): - roles[name] = data["role"].lower().replace(" ", "_") - - lifecycle = CONFIG.get("org_lifecycle", {}) - for hire in lifecycle.get("scheduled_hires", []): - roles[hire["name"]] = hire["role"].lower().replace(" ", "_") - for dep in lifecycle.get("scheduled_departures", []): - roles[dep["name"]] = dep["role"].lower().replace(" ", "_") - - for actor in self._all_actors(): - if actor not in roles: - roles[actor] = "external" - - return roles - - def _subsystem_access_for(self, actor: str) -> Set[str]: - role = self._actor_roles.get(actor, "external") - return set(_ROLE_SUBSYSTEM_ACCESS.get(role, _ROLE_SUBSYSTEM_ACCESS["external"])) - - def _all_actors(self) -> Set[str]: - actors: Set[str] = set() - for event in self._events: - actors.update(event.actors) - return actors - - def _artifact_subsystem(self, doc_type: str) -> str: - return _ARTIFACT_SUBSYSTEM.get(doc_type, "default") - - _BROADCAST_CONFIG = { - "incident_opened": ["slack", "datadog"], - "incident_resolved": ["slack"], - "postmortem_created": ["slack", "confluence"], - "standup": ["slack"], - "pr_opened": ["git"], - "pr_merged": ["git"], - "knowledge_gap_detected": ["slack", "confluence"], - } - - def build_all(self) -> Dict[str, List[ActorVisibilityCone]]: - all_actors = self._all_actors() - result: Dict[str, List[ActorVisibilityCone]] = {} - max_day = max((e.day for e in self._events), default=1) - - events_by_day = defaultdict(list) - for event in self._events: - events_by_day[event.day].append(event) - - for actor in all_actors: - role = self._actor_roles.get(actor, "default") - access = self._subsystem_access_for(actor) - cones: List[ActorVisibilityCone] = [] - - current_visible = defaultdict(set) - current_directly_involved = set() - current_broadcast_visible = set() - - for day in range(1, max_day + 1): - for event in events_by_day.get(day, []): - is_direct = actor in (event.actors or []) - - broadcast_channels = _BROADCAST_CONFIG.get(event.type) - is_broadcast = broadcast_channels is not None - - for doc_type, artifact_id in (event.artifact_ids or {}).items(): - if not artifact_id: - continue - subsystem = self._artifact_subsystem(doc_type) - if subsystem not in access: - continue - - if is_direct: - current_visible[subsystem].add(artifact_id) - current_directly_involved.add(artifact_id) - elif is_broadcast and any( - sub in access for sub in broadcast_channels - ): - current_visible[subsystem].add(artifact_id) - current_broadcast_visible.add(artifact_id) - - as_of_dt = _SIM_START + timedelta(days=day - 1, hours=23, minutes=59) - cones.append( - ActorVisibilityCone( - actor=actor, - role=role, - as_of_time=as_of_dt.isoformat(), - as_of_day=day, - subsystem_access=access, - visible_artifacts={ - k: set(v) for k, v in current_visible.items() - }, - directly_involved=set(current_directly_involved), - broadcast_visible=set(current_broadcast_visible), - ) - ) - - result[actor] = cones - - return result - - -# ───────────────────────────────────────────────────────────────────────────── -# CAUSAL LINK INDEX -# ───────────────────────────────────────────────────────────────────────────── - - -class CausalLinkIndexer: - """ - Scans the SimEvent log for all explicit causal links. - Only links in _EXPLICIT_CAUSAL_LINKS are indexed — no inference. - - Each link becomes a potential COUNTERFACTUAL question source. - The counterfactual premise and outcome are templated deterministically - from the link type and event facts; LLMs only rephrase them. - """ - - def __init__(self, mem: Memory): - self._mem = mem - self._events: List[SimEvent] = mem.get_event_log(from_db=True) - self._event_by_id: Dict[str, SimEvent] = { - self._synthetic_event_id(e): e for e in self._events - } - - def _synthetic_event_id(self, e: SimEvent) -> str: - """Build a stable synthetic key since SimEvent has no event_id attr.""" - raw = next(iter((e.artifact_ids or {}).values()), "none") - first_artifact = raw[0] if isinstance(raw, list) else (raw or "none") - actor = (e.actors or ["unknown"])[0] - return f"evt_{e.type}_{e.day}_{first_artifact}_{actor}" - - def _subsystems_for_event(self, event: SimEvent) -> Set[str]: - subsystems: Set[str] = set() - for doc_type in event.artifact_ids or {}: - s = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") - if s != "default": - subsystems.add(s) - return subsystems - - def _find_effect_event(self, link_type: str, cause: SimEvent) -> Optional[SimEvent]: - """Find the downstream event causally linked to cause.""" - if link_type == "involves_gap": - gap_domain = ( - cause.facts.get("gap_areas", [None])[0] - if cause.facts.get("gap_areas") - else None - ) - if not gap_domain: - return None - - for e in self._events: - if e.day < cause.day: - continue - - if e.type == "incident_opened" and gap_domain in e.facts.get( - "gap_areas", [] - ): - return e - - relevant_types = { - "async_question_asked", - "pr_review_comment", - "confluence_created", - "postmortem_created", - } - if e.type in relevant_types: - event_domains = ( - e.facts.get("gap_areas") or e.facts.get("domain") or [] - ) - if gap_domain in ( - event_domains - if isinstance(event_domains, list) - else [event_domains] - ): - return e - - elif link_type == "recurrence_of": - cause_artifacts = _safe_artifact_values(cause.artifact_ids) - for e in self._events: - recurrence = e.facts.get("recurrence_of") - if recurrence and recurrence in cause_artifacts: - return e - - elif link_type == "spawned_doc": - cause_artifacts = _safe_artifact_values(cause.artifact_ids) - for e in self._events: - if ( - e.type == "confluence_created" - and e.facts.get("source_discussion") in cause_artifacts - ): - return e - - elif link_type == "email_dropped": - email_id = (cause.artifact_ids or {}).get("email") - if isinstance(email_id, list): - email_id = email_id[0] if email_id else None - if not email_id: - return None - - elif link_type == "sf_ownership_lapsed": - actor = (cause.actors or [None])[0] - if not actor: - return None - for e in self._events: - if e.type == "sf_ownership_lapsed" and actor in (e.actors or []): - return e - - elif link_type == "zd_escalation_source": - jira_id = (cause.artifact_ids or {}).get("jira") - if isinstance(jira_id, list): - jira_id = jira_id[0] if jira_id else None - if not jira_id: - return None - - elif link_type == "blocker_flagged": - jira_id = cause.artifact_ids.get("jira") - return next( - ( - e - for e in self._events - if e.type == "ticket_progress" - and e.artifact_ids.get("jira") == jira_id - and e.day >= cause.day - ), - None, - ) - - elif link_type == "incident_coordination": - jira_id = cause.artifact_ids.get("jira") - return next( - ( - e - for e in self._events - if e.type == "external_contact_summarized" - and e.artifact_ids.get("jira") == jira_id - ), - None, - ) - - elif link_type == "departure_reassignment": - departed_actor = (cause.actors or [None])[0] - return next( - ( - e - for e in self._events - if e.type == "escalation_chain" - and e.facts.get("trigger") == "post_departure_reroute" - and e.facts.get("departed") == departed_actor - ), - None, - ) - - elif link_type == "deal_risk_propagation": - return next( - ( - e - for e in self._events - if e.type == "sf_deals_risk_flagged" and e.day >= cause.day - ), - None, - ) - - elif link_type == "onboarding_path": - new_hire = cause.facts.get("name") - return next( - ( - e - for e in self._events - if e.type == "onboarding_session" and new_hire in e.actors - ), - None, - ) - - elif link_type == "assignment_domain_mismatch": - # Look for a knowledge_gap_detected on the same ticket on a later day, - # or an incident_opened whose gap_areas overlap with the mismatch domains. - ticket_id = cause.facts.get("ticket_id") - mismatch_actors = set(cause.actors or []) - for e in self._events: - if e.day < cause.day: - continue - if e.type == "knowledge_gap_detected": - if ticket_id and e.artifact_ids.get("jira") == ticket_id: - return e - # Also match on overlapping actors (the assigned engineer surfaces the gap) - if mismatch_actors & set(e.actors or []): - return e - if e.type == "incident_opened": - gap_areas = e.facts.get("gap_areas", []) - mismatch_domains = cause.facts.get("assignment_risk_domains", []) - if ( - gap_areas - and mismatch_domains - and set(gap_areas) & set(mismatch_domains) - ): - return e - - return None - - def _counterfactual_template( - self, link_type: str, cause: SimEvent, effect: SimEvent - ) -> Tuple[str, str, bool]: - """ - Returns (premise, outcome, outcome_changed) as deterministic strings. - These become the ground_truth fields — no LLM involvement here. - """ - if link_type == "involves_gap": - gap_areas = cause.facts.get("gap_areas", ["unknown domain"]) - gap_str = ", ".join(gap_areas) - actor = (cause.actors or ["the departing engineer"])[0] - jira_id = (effect.artifact_ids or {}).get("jira", "the incident") - premise = f"{actor} had fully documented {gap_str} before departing" - outcome = f"{jira_id} would have been diagnosed faster or prevented" - return premise, outcome, True - - elif link_type == "recurrence_of": - orig = effect.facts.get("recurrence_of", "the original incident") - jira_id = (effect.artifact_ids or {}).get("jira", "the recurrence") - premise = f"the postmortem for {orig} had included preventive action items" - outcome = f"{jira_id} would likely not have occurred" - return premise, outcome, True - - elif link_type == "spawned_doc": - topic = cause.facts.get("topic", "the design discussion") - conf_id = (effect.artifact_ids or {}).get( - "confluence", "the Confluence doc" - ) - premise = f"the discussion about '{topic}' had not been documented" - outcome = f"{conf_id} would not exist and related decisions would remain undocumented" - return premise, outcome, True - - elif link_type == "email_dropped": - sender = cause.facts.get("sender", "the customer") - premise = f"the email from {sender} had been routed correctly" - outcome = "a support ticket would have been opened and the issue tracked" - return premise, outcome, True - - elif link_type == "sf_ownership_lapsed": - actor = (cause.actors or ["the departed employee"])[0] - accounts = effect.facts.get("lapsed_accounts", []) - acc_str = ", ".join(accounts[:3]) if accounts else "affected accounts" - premise = ( - f"{actor}'s Salesforce accounts had been reassigned before departure" - ) - outcome = ( - f"{acc_str} would not have lost ownership and pipeline would be intact" - ) - return premise, outcome, True - - elif link_type == "zd_escalation_source": - ticket_ids = effect.facts.get("ticket_ids", ["the support ticket"]) - tickets_str = ", ".join(ticket_ids[:3]) - premise = f"{tickets_str} had been resolved at the support level" - outcome = "the incident escalation would not have occurred" - return premise, outcome, True - - if link_type == "blocker_flagged": - reason = cause.facts.get("blocker_reason", "a technical blocker") - jira_id = effect.artifact_ids.get("jira", "the ticket") - return ( - f"the blocker regarding '{reason}' had been resolved immediately", - f"work on {jira_id} would have progressed without delay", - True, - ) - - elif link_type == "incident_coordination": - contact = effect.facts.get("external_party", "the external contact") - jira_id = cause.artifact_ids.get("jira", "the incident") - return ( - f"the incident {jira_id} had not occurred", - f"the team would not have needed to coordinate with {contact}", - True, - ) - - elif link_type == "departure_reassignment": - actor = (cause.actors or ["the employee"])[0] - return ( - f"{actor} had not departed the company", - "their active tickets and escalation responsibilities would not have been reassigned", - True, - ) - - elif link_type == "deal_risk_propagation": - jira_id = cause.artifact_ids.get("jira", "the incident") - return ( - f"the incident {jira_id} had not occurred", - "the associated Salesforce deals would not have been flagged as at-risk", - True, - ) - - elif link_type == "onboarding_path": - name = cause.facts.get("name", "the new hire") - return ( - f"{name} had not been hired on Day {cause.day}", - "the onboarding sessions and warmup meetings for them would not have taken place", - True, - ) - - elif link_type == "assignment_domain_mismatch": - actors = cause.actors or ["the engineer"] - ticket_id = cause.facts.get("ticket_id", "the ticket") - coverage = cause.facts.get("documentation_coverage") - coverage_str = ( - f" (documentation coverage: {int(coverage * 100)}%)" if coverage else "" - ) - return ( - f"{actors[0]} had been assigned to {ticket_id} with matching domain expertise", - f"the knowledge gap{coverage_str} would likely not have been surfaced and the associated incident risk reduced", - True, - ) - - return ( - "the causal condition had been different", - "the outcome would have changed", - True, - ) - - def build(self) -> List[CausalLink]: - links: List[CausalLink] = [] - - for link_type in _EXPLICIT_CAUSAL_LINKS: - if link_type == "involves_gap": - cause_events = [ - e for e in self._events if e.type == "knowledge_gap_detected" - ] - elif link_type == "recurrence_of": - cause_events = [ - e for e in self._events if e.type == "incident_resolved" - ] - elif link_type == "spawned_doc": - cause_events = [ - e - for e in self._events - if e.type == "design_discussion" and e.facts.get("spawned_doc") - ] - elif link_type == "email_dropped": - cause_events = [ - e for e in self._events if e.type == "inbound_external_email" - ] - elif link_type == "sf_ownership_lapsed": - cause_events = [ - e for e in self._events if e.type == "employee_departed" - ] - elif link_type == "zd_escalation_source": - cause_events = [e for e in self._events if e.type == "incident_opened"] - elif link_type == "blocker_flagged": - cause_events = [e for e in self._events if e.type == "blocker_flagged"] - elif link_type == "incident_coordination": - cause_events = [e for e in self._events if e.type == "incident_opened"] - elif link_type == "departure_reassignment": - cause_events = [ - e for e in self._events if e.type == "employee_departed" - ] - elif link_type == "assignment_domain_mismatch": - cause_events = [ - e for e in self._events if e.type == "assignment_domain_mismatch" - ] - else: - continue - - for cause in cause_events: - effect = self._find_effect_event(link_type, cause) - if not effect: - continue - - premise, outcome, changed = self._counterfactual_template( - link_type, cause, effect - ) - - subsystems = self._subsystems_for_event( - cause - ) | self._subsystems_for_event(effect) - - link_field = { - "involves_gap": "gap_areas", - "recurrence_of": "recurrence_of", - "spawned_doc": "spawned_doc", - "email_dropped": "email", - "sf_ownership_lapsed": "actor", - "zd_escalation_source": "jira", - "blocker_flagged": "jira", - "incident_coordination": "jira", - "departure_reassignment": "actor", - "assignment_domain_mismatch": "ticket_id", - }.get(link_type, "") - - link_value = str( - cause.facts.get(link_field, "") - or (cause.artifact_ids or {}).get(link_field, "") - or (cause.actors or [""])[0] - ) - - links.append( - CausalLink( - link_type=link_type, - cause_event_id=self._synthetic_event_id(cause), - cause_event_type=cause.type, - effect_event_id=self._synthetic_event_id(effect), - effect_event_type=effect.type, - actors=list(set((cause.actors or []) + (effect.actors or []))), - day=cause.day, - link_field=link_field, - link_value=link_value, - subsystems_involved=subsystems, - counterfactual_premise=premise, - counterfactual_outcome=outcome, - outcome_changed=changed, - ) - ) - - logger.info(f"[causal_index] {len(links)} explicit causal links indexed") - return links - - -# ───────────────────────────────────────────────────────────────────────────── -# ABSENCE CATALOG -# ───────────────────────────────────────────────────────────────────────────── - - -class AbsenceCatalogBuilder: - """ - Builds the catalog of expected-but-absent artifact pairs. - - For each pair in _SILENCE_PAIRS, scans the event log for trigger events - that have no matching response event. The state machine is the arbiter: - if no response event fired, the absence is ground truth. - - Also derives expected_search_space: the set of artifact IDs the agent - must check before concluding absence. This is what separates a well-reasoned - "no" from a lucky guess. - """ - - def __init__(self, mem: Memory): - self._mem = mem - self._events: List[SimEvent] = mem.get_event_log(from_db=True) - - @staticmethod - def _synthetic_event_id(e: SimEvent) -> str: - first_artifact = next(iter((e.artifact_ids or {}).values()), "none") - actor = (e.actors or ["unknown"])[0] - return f"evt_{e.type}_{e.day}_{first_artifact}_{actor}" - - def _match_key(self, event: SimEvent, link_field: str) -> Optional[str]: - """Extract the value that links a trigger to its expected response.""" - val = (event.artifact_ids or {}).get(link_field) - if val: - return val - val = event.facts.get(link_field) - if val: - return str(val) - if link_field == "actor" and event.actors: - return event.actors[0] - return None - - def _expected_search_space( - self, trigger: SimEvent, expected_response_type: str - ) -> List[str]: - """ - Derive the artifact IDs the agent should check to confirm absence. - These are artifacts that WOULD contain the response if it had occurred. - """ - search_space: List[str] = [] - - # Always include trigger artifacts as starting points - for artifact_id in (trigger.artifact_ids or {}).values(): - if artifact_id: - search_space.append(artifact_id) - - if expected_response_type == "postmortem_created": - jira_id = (trigger.artifact_ids or {}).get("jira", "") - if jira_id: - search_space.append(f"confluence/postmortems/{jira_id}") - search_space.append("slack/channels/incidents") - - elif expected_response_type == "incident_resolved": - jira_id = (trigger.artifact_ids or {}).get("jira", "") - if jira_id: - search_space.append(jira_id) - search_space.append("jira/incidents") - - elif expected_response_type == "zd_ticket_opened": - email_id = (trigger.artifact_ids or {}).get("email", "") - if email_id: - search_space.append("zendesk/tickets") - search_space.append(email_id) - - elif expected_response_type == "customer_email_routed": - search_space.append("slack/channels/support") - search_space.append("zendesk/queue") - - elif expected_response_type == "confluence_created": - zoom_id = (trigger.artifact_ids or {}).get("zoom_transcript", "") - if zoom_id: - search_space.append(zoom_id) - search_space.append("confluence/design-docs") - search_space.append("confluence/decisions") - - elif expected_response_type == "sf_ownership_lapsed": - actor = (trigger.actors or [""])[0] - if actor: - search_space.append(f"salesforce/accounts/{actor}") - search_space.append("salesforce/ownership-log") - - elif expected_response_type == "ticket_reassigned": - actor = (trigger.actors or [""])[0] - if actor: - search_space.append("jira/reassignments") - search_space.append("slack/channels/engineering") - - elif expected_response_type == "pr_merged": - pr_id = (trigger.artifact_ids or {}).get("pr", "") - if pr_id: - search_space.append(pr_id) - search_space.append("git/merged-prs") - - elif expected_response_type == "zd_tickets_escalated": - jira_id = (trigger.artifact_ids or {}).get("jira", "") - if jira_id: - search_space.append(jira_id) - search_space.append("zendesk/escalations") - - elif expected_response_type == "onboarding_session": - name = trigger.facts.get("name", "") - search_space.append("slack/channels/general") - search_space.append(f"confluence/onboarding/{name}") - - elif expected_response_type == "warmup_1on1": - name = trigger.facts.get("name", "") - search_space.append("slack/channels/engineering") - search_space.append("zoom/transcripts") - - elif expected_response_type == "sf_deals_risk_flagged": - jira_id = trigger.artifact_ids.get("jira", "") - search_space.append("salesforce/opportunities") - if jira_id: - search_space.append(jira_id) - - elif expected_response_type == "knowledge_gap_detected": - # Silence: assignment_domain_mismatch fired but no gap was ever formally detected - ticket_id = trigger.facts.get("ticket_id", "") - if ticket_id: - search_space.append(f"jira/{ticket_id}") - search_space.append("slack/channels/engineering") - search_space.append("confluence/knowledge-gaps") - - return list(dict.fromkeys(search_space)) # dedupe, preserve order - - def build(self) -> List[AbsenceRecord]: - records: List[AbsenceRecord] = [] - - for trigger_type, response_type, link_field in _SILENCE_PAIRS: - trigger_events = [e for e in self._events if e.type == trigger_type] - - for trigger in trigger_events: - trigger_artifacts = _safe_artifact_values(trigger.artifact_ids) - link_key = self._match_key(trigger, link_field) - - if response_type == "confluence_created" and ( - trigger.facts.get("spawned_doc") - or "confluence" in (trigger.artifact_ids or {}) - ): - continue - - response_found = False - for e in self._events: - if e.type != response_type or e.day < trigger.day: - continue - - if link_key and ( - self._match_key(e, link_field) == link_key - or link_key in str(e.artifact_ids) - or link_key in str(e.facts) - ): - response_found = True - break - - response_artifacts = _safe_artifact_values(e.artifact_ids) - if trigger_artifacts & response_artifacts: - response_found = True - break - - if response_found: - continue - - subsystem = _ARTIFACT_SUBSYSTEM.get( - list((trigger.artifact_ids or {}).keys() or [""])[0], "default" - ) - search_space = self._expected_search_space(trigger, response_type) - - records.append( - AbsenceRecord( - trigger_event_id=self._synthetic_event_id(trigger), - trigger_event_type=trigger_type, - expected_response_type=response_type, - trigger_day=trigger.day, - trigger_actors=trigger.actors or [], - trigger_artifact_ids=dict(trigger.artifact_ids or {}), - link_field=link_field, - link_value=link_key or "N/A", - subsystem=subsystem, - expected_search_space=search_space, - ) - ) - - logger.info(f"[absence_catalog] {len(records)} absence records cataloged") - return records - - -# ───────────────────────────────────────────────────────────────────────────── -# QUESTION GENERATOR -# ───────────────────────────────────────────────────────────────────────────── - - -class EvalQuestionGenerator: - """ - Generates PERSPECTIVE, COUNTERFACTUAL, and SILENCE questions. - - Ground truth is always derived deterministically from the three indexes. - LLMs only write question prose, and every generated question is validated - against a structured rubric before inclusion. - - Question prose validation checks: - - Ends with a question mark - - Does not contain the ground truth answer verbatim - - Does not name an artifact ID directly (keeps questions natural-language) - - Is unambiguous — references the actor/day/subsystem constraint explicitly - """ - - MAX_PERSPECTIVE = 40 - MAX_COUNTERFACTUAL = 30 - MAX_SILENCE = 30 - - def __init__( - self, - mem: Memory, - worker_llm, - visibility_map: Dict[str, List[ActorVisibilityCone]], - causal_links: List[CausalLink], - absence_catalog: List[AbsenceRecord], - ): - self._mem = mem - self._worker_llm = worker_llm - self._visibility_map = visibility_map - self._causal_links = causal_links - self._absence_catalog = absence_catalog - self._events: List[SimEvent] = mem.get_event_log(from_db=True) - - @staticmethod - def _synthetic_event_id(e: SimEvent) -> str: - """Build a stable synthetic key since SimEvent has no event_id attr.""" - raw = next(iter((e.artifact_ids or {}).values()), "none") - first_artifact = raw[0] if isinstance(raw, list) else (raw or "none") - actor = (e.actors or ["unknown"])[0] - return f"evt_{e.type}_{e.day}_{first_artifact}_{actor}" - - def generate(self) -> List[dict]: - questions: List[dict] = [] - - logger.info("[eval] Generating PERSPECTIVE questions...") - questions.extend(self._perspective_questions()) - - logger.info("[eval] Generating COUNTERFACTUAL questions...") - questions.extend(self._counterfactual_questions()) - - logger.info("[eval] Generating SILENCE questions...") - questions.extend(self._silence_questions()) - - # Shuffle so question types are interleaved in the output - random.shuffle(questions) - - logger.info(f"[eval] {len(questions)} total questions generated") - return questions - - # ── TRACK 1: PERSPECTIVE ───────────────────────────────────────────────── - - def _perspective_questions(self) -> List[dict]: - questions: List[dict] = [] - - internal_actors = [ - actor - for actor in self._visibility_map.keys() - if self._visibility_map[actor][0].role != "external" - ] - - # Find events that involve information asymmetry — where the actor was - # NOT in event.actors but the event affected them (e.g. a customer - # escalation that went to sales but not engineering) - asymmetry_events = [ - ev for ev in self._find_asymmetry_events() if ev[0] in internal_actors - ] - - candidates = random.sample( - asymmetry_events, min(self.MAX_PERSPECTIVE, len(asymmetry_events)) - ) - - for actor, cone, event, info_available, cross_subsystem in candidates: - question = self._build_perspective_question( - actor, cone, event, info_available, cross_subsystem - ) - if question: - questions.append(question) - - logger.info(f"[eval] {len(questions)} PERSPECTIVE questions built") - return questions - - def _find_asymmetry_events(self) -> List[Tuple]: - """ - Find (actor, cone, event, info_available, is_cross_subsystem) tuples - where an actor had partial or no visibility into a significant event. - - Focuses on events with real decision-making consequence: - - Customer escalations visible to support but not engineering - - Incidents visible to engineering but not sales - - Design decisions visible to eng but not the broader org - - HR/departure events with asymmetric visibility - - CRM risk flags invisible to non-sales actors - """ - results = [] - significant_types = { - "incident_opened", - "customer_escalation", - "sf_deals_risk_flagged", - "knowledge_gap_detected", - "employee_departed", - "design_discussion", - "customer_email_routed", - "zd_tickets_escalated", - "sf_ownership_lapsed", - "postmortem_created", - "inbound_external_email", - "assignment_domain_mismatch", - } - - for event in self._events: - if event.type not in significant_types: - continue - - event_subsystems = set() - event_artifacts = set() - - for doc_type, aid in (event.artifact_ids or {}).items(): - if not aid: - continue - event_artifacts.add(str(aid)) - s = _ARTIFACT_SUBSYSTEM.get(doc_type, "default") - if s != "default": - event_subsystems.add(s) - - if not event_subsystems: - continue - - # Look for actors NOT in the event who have relevant role-based access - for actor, cones in self._visibility_map.items(): - if actor in (event.actors or []): - continue # Actor was directly involved — not an asymmetry case - - # Find the cone at the event's day - cone = next((c for c in cones if c.as_of_day == event.day), None) - if not cone: - continue - - all_visible = cone.all_visible() - event_artifacts = _safe_artifact_values(event.artifact_ids) - - # Check if actor missed this information - missed_artifacts = event_artifacts - all_visible - if not missed_artifacts: - continue # Actor could already see everything - - # Determine if this spans subsystems the actor doesn't have - blocked_by_role = event_subsystems - cone.subsystem_access - cross_subsystem = len(blocked_by_role) > 0 - - # What did the actor actually know that's related? - related_visible = [] - for e in self._events: - if e.day > event.day: - continue - if actor not in (e.actors or []): - continue - shared_actors = set(e.actors or []) & set(event.actors or []) - shared_artifacts = _safe_artifact_values( - e.artifact_ids - ) & _safe_artifact_values(event.artifact_ids) - if shared_actors or shared_artifacts: - for aid in _safe_artifact_values(e.artifact_ids): - if aid in all_visible: - related_visible.append(aid) - - info_available = { - "actor_visible_subsystems": sorted(cone.subsystem_access), - "event_subsystems": sorted(event_subsystems), - "blocked_by_role": sorted(blocked_by_role), - "missed_artifacts": sorted(missed_artifacts), - "related_artifacts_actor_saw": sorted(set(related_visible)), - } - - results.append((actor, cone, event, info_available, cross_subsystem)) - - return results - - def _build_perspective_question( - self, - actor: str, - cone: ActorVisibilityCone, - event: SimEvent, - info_available: dict, - cross_subsystem: bool, - ) -> Optional[dict]: - - # Derive ground truth deterministically - missed = info_available["missed_artifacts"] - blocked = info_available["blocked_by_role"] - could_have_known = len(missed) == 0 # actor had access to all artifacts - - ground_truth = { - "actor": actor, - "as_of_day": cone.as_of_day, - "as_of_time": cone.as_of_time, - "could_actor_have_known": could_have_known, - "reason": ( - f"Actor had access to {sorted(cone.subsystem_access)} but event " - f"involved {sorted(info_available['event_subsystems'])}; " - f"blocked by role from: {sorted(blocked)}" - if not could_have_known - else f"All event artifacts were in actor's visibility cone via " - f"{'direct involvement' if info_available['related_artifacts_actor_saw'] else 'broadcast'}" - ), - "evidence_artifacts": sorted(info_available["related_artifacts_actor_saw"]), - "missed_artifacts": sorted(missed), - "blocked_subsystems": sorted(blocked), - } - - difficulty = "hard" if cross_subsystem else "medium" - - # Build prose template - event_desc = self._event_description(event) - subsystem_constraint = ( - f"{actor} has access to {', '.join(sorted(cone.subsystem_access))} " - f"but not {', '.join(sorted(blocked))}" - if blocked - else f"{actor} has access to {', '.join(sorted(cone.subsystem_access))}" - ) - - template = ( - f"Write a question asking whether {actor} would have known about " - f"'{event_desc}' as of Day {cone.as_of_day}, given that " - f"{subsystem_constraint}. " - f"The question must name the actor, the approximate time constraint " - f"(Day {cone.as_of_day}), and the subsystem limitation. " - f"Do not reveal the answer. Do not include artifact IDs. " - f"Output only the question text." - ) - - question_text = self._generate_and_validate_prose( - template=template, - ground_truth_str=str(could_have_known), - question_type="PERSPECTIVE", - ) - if not question_text: - return None - - return { - "question_id": f"perspective_{actor}_{self._synthetic_event_id(event)}", - "question_type": "PERSPECTIVE", - "difficulty": difficulty, - "cross_subsystem": cross_subsystem, - "actor": actor, - "actor_role": cone.role, - "as_of_day": cone.as_of_day, - "as_of_time": cone.as_of_time, - "subsystem_access": sorted(cone.subsystem_access), - "blocked_subsystems": sorted(info_available["blocked_by_role"]), - "event_id": self._synthetic_event_id(event), - "event_type": event.type, - "event_day": event.day, - "question_text": question_text, - "ground_truth": ground_truth, - "actor_visible_artifacts": sorted(cone.all_visible()), - "requires_reasoning": True, - } - - # ── TRACK 2: COUNTERFACTUAL ─────────────────────────────────────────────── - - def _counterfactual_questions(self) -> List[dict]: - questions: List[dict] = [] - - sampled = random.sample( - self._causal_links, min(self.MAX_COUNTERFACTUAL, len(self._causal_links)) - ) - - for link in sampled: - question = self._build_counterfactual_question(link) - if question: - questions.append(question) - - logger.info(f"[eval] {len(questions)} COUNTERFACTUAL questions built") - return questions - - def _build_counterfactual_question(self, link: CausalLink) -> Optional[dict]: - - cause_event = next( - ( - e - for e in self._events - if self._synthetic_event_id(e) == link.cause_event_id - ), - None, - ) - effect_event = next( - ( - e - for e in self._events - if self._synthetic_event_id(e) == link.effect_event_id - ), - None, - ) - - ground_truth = { - "outcome_changed": link.outcome_changed, - "causal_mechanism": link.link_type, - "causal_link_field": link.link_field, - "causal_link_value": link.link_value, - "cause_event_id": link.cause_event_id, - "cause_event_type": link.cause_event_type, - "effect_event_id": link.effect_event_id, - "effect_event_type": link.effect_event_type, - "premise": link.counterfactual_premise, - "outcome": link.counterfactual_outcome, - "actors": link.actors, - "evidence_chain_artifacts": { - "cause": sorted( - _safe_artifact_values( - cause_event.artifact_ids if cause_event else {} - ) - ), - "effect": sorted( - _safe_artifact_values( - effect_event.artifact_ids if effect_event else {} - ) - ), - }, - } - - subsystems_str = ", ".join(sorted(link.subsystems_involved)) - difficulty = "hard" if len(link.subsystems_involved) > 1 else "medium" - - template = ( - f"Write a counterfactual question asking: if {link.counterfactual_premise}, " - f"would the following have occurred: {link.counterfactual_outcome}? " - f"The question should involve events from Day {link.day} in a simulated " - f"company with systems including {subsystems_str}. " - f"The question must be phrased as a hypothetical (use 'if', 'had', 'would'). " - f"Do not reveal the answer. Do not include event IDs. " - f"Output only the question text." - ) - - question_text = self._generate_and_validate_prose( - template=template, - ground_truth_str=link.counterfactual_outcome, - question_type="COUNTERFACTUAL", - ) - if not question_text: - return None - - return { - "question_id": f"counterfactual_{link.cause_event_id}_{link.link_type}", - "question_type": "COUNTERFACTUAL", - "difficulty": difficulty, - "link_type": link.link_type, - "day": link.day, - "actors": link.actors, - "subsystems_involved": sorted(link.subsystems_involved), - "question_text": question_text, - "ground_truth": ground_truth, - "evidence_chain": [link.cause_event_id, link.effect_event_id], - "requires_reasoning": True, - } - - # ── TRACK 3: SILENCE ───────────────────────────────────────────────────── - - def _silence_questions(self) -> List[dict]: - questions: List[dict] = [] - - sampled = random.sample( - self._absence_catalog, min(self.MAX_SILENCE, len(self._absence_catalog)) - ) - - for record in sampled: - question = self._build_silence_question(record) - if question: - questions.append(question) - - logger.info(f"[eval] {len(questions)} SILENCE questions built") - return questions - - def _build_silence_question(self, record: AbsenceRecord) -> Optional[dict]: - - ground_truth = { - "answer": False, # The expected artifact/event does NOT exist - "absence_type": "state_machine_confirmed", - "trigger_event_id": record.trigger_event_id, - "trigger_event_type": record.trigger_event_type, - "expected_response_type": record.expected_response_type, - "trigger_day": record.trigger_day, - "trigger_actors": record.trigger_actors, - "expected_search_space": record.expected_search_space, - "link_field": record.link_field, - "link_value": record.link_value, - } - - # Build a natural-language description of what should have existed - expected_desc = { - "postmortem_created": "a postmortem document", - "incident_resolved": "an incident resolution", - "zd_ticket_opened": "a Zendesk support ticket", - "customer_email_routed": "an internal routing of the customer email", - "confluence_created": "a Confluence documentation page", - "sf_ownership_lapsed": "a Salesforce ownership transfer", - "ticket_reassigned": "a Jira ticket reassignment", - "pr_merged": "a merged pull request", - "zd_tickets_escalated": "a Zendesk escalation", - "incident_opened": "an incident ticket", - "onboarding_session": "an onboarding session", - "warmup_1on1": "a warmup 1-on-1 meeting", - "sf_deals_risk_flagged": "a Salesforce risk flag on related deals", - "knowledge_gap_detected": "a formal knowledge gap detection event", - }.get(record.expected_response_type, f"a {record.expected_response_type} event") - - trigger_desc = { - "incident_opened": f"the incident on Day {record.trigger_day}", - "customer_escalation": f"the customer escalation on Day {record.trigger_day}", - "inbound_external_email": f"the inbound email on Day {record.trigger_day}", - "design_discussion": f"the design discussion on Day {record.trigger_day}", - "knowledge_gap_detected": f"the knowledge gap detected on Day {record.trigger_day}", - "employee_departed": f"the employee departure on Day {record.trigger_day}", - "zd_tickets_escalated": f"the Zendesk escalation on Day {record.trigger_day}", - "pr_opened": f"the pull request opened on Day {record.trigger_day}", - "customer_email_routed": f"the routing of the customer email on Day {record.trigger_day}", - "sf_deals_risk_flagged": f"the CRM risk flagging on Day {record.trigger_day}", - "employee_hired": f"the hiring of {record.link_value} on Day {record.trigger_day}", - "assignment_domain_mismatch": f"the domain mismatch assignment flagged on Day {record.trigger_day}", - }.get(record.trigger_event_type, f"the event on Day {record.trigger_day}") - - actors_str = ( - ", ".join(record.trigger_actors[:2]) - if record.trigger_actors - else "the involved parties" - ) - - trigger_ev = next( - ( - e - for e in self._events - if self._synthetic_event_id(e) == record.trigger_event_id - ), - None, - ) - - if not trigger_ev: - logger.warning( - f"[eval] Skipping SILENCE question for unknown trigger type: {record.trigger_event_type}" - ) - return None - - template = ( - f"Write a yes/no question asking whether {expected_desc} was created " - f"in response to {trigger_desc} involving {actors_str}. " - f"CRITICAL: Only refer to the event exactly as described ('{trigger_desc}'). " - f"Do not call it an 'incident' or 'outage' unless those words are explicitly used. " - f"The question should be phrased so that the correct answer is 'no' — " - f"the artifact does not exist — but the agent must investigate to confirm this. " - f"Do not state or imply the answer. Do not include system IDs. " - f"The question should sound like something a manager would ask when reviewing " - f"process compliance. " - f"Output only the question text." - ) - - question_text = self._generate_and_validate_prose( - template=template, - ground_truth_str="False", - question_type="SILENCE", - ) - if not question_text: - return None - - return { - "question_id": f"silence_{record.trigger_event_id}_{record.expected_response_type}", - "question_type": "SILENCE", - "difficulty": "hard", # Absence reasoning is always hard - "trigger_event_id": record.trigger_event_id, - "trigger_event_type": record.trigger_event_type, - "trigger_day": record.trigger_day, - "expected_response_type": record.expected_response_type, - "subsystem": record.subsystem, - "question_text": question_text, - "ground_truth": ground_truth, - "expected_search_space": record.expected_search_space, - "requires_reasoning": True, - } - - # ── PROSE GENERATION + VALIDATION ──────────────────────────────────────── - - def _event_description(self, event: SimEvent) -> str: - """Natural language description of an event for use in question templates.""" - descs = { - "incident_opened": lambda e: ( - f"a P1 incident ({e.facts.get('title', 'system incident')})" - ), - "customer_escalation": lambda e: ( - f"a customer escalation from {e.facts.get('customer', 'a customer')}" - ), - "sf_deals_risk_flagged": lambda e: ( - "Salesforce accounts being flagged at-risk" - ), - "knowledge_gap_detected": lambda e: ( - f"a knowledge gap in {', '.join(e.facts.get('gap_areas', ['an undocumented domain']))}" - ), - "employee_departed": lambda e: ( - f"the departure of {(e.actors or ['a team member'])[0]}" - ), - "design_discussion": lambda e: ( - f"a design discussion about {e.facts.get('topic', 'a technical topic')}" - ), - "customer_email_routed": lambda e: ( - "a customer email being routed to support" - ), - "zd_tickets_escalated": lambda e: ( - "Zendesk tickets being escalated to an incident" - ), - "sf_ownership_lapsed": lambda e: "Salesforce accounts losing their owner", - "postmortem_created": lambda e: "a postmortem being written", - "inbound_external_email": lambda e: ( - f"an inbound email from {e.facts.get('sender', 'an external contact')}" - ), - } - fn = descs.get(event.type) - if fn: - try: - return fn(event) - except Exception: - pass - return f"a {event.type.replace('_', ' ')} event" - - def _generate_and_validate_prose( - self, - template: str, - ground_truth_str: str, - question_type: str, - max_attempts: int = 3, - ) -> Optional[str]: - """ - LLM writes question prose. Validates against structured rubric. - Retries up to max_attempts if validation fails. - - Validation rules: - 1. Must end with '?' - 2. Must not contain ground_truth_str verbatim - 3. Must not contain raw artifact IDs (pattern: XX-\\d+ or [a-f0-9]{8,}) - 4. Must be between 15 and 120 words - 5. Must contain at least one of: actor name, day reference, subsystem word - """ - agent = make_agent( - role="Eval Dataset Author", - goal="Write natural-sounding evaluation questions for AI agent benchmarks.", - backstory=( - "You write clear, specific questions for evaluating AI agents on reasoning " - "tasks. Questions must be unambiguous, naturally phrased, and answerable " - "only through careful reasoning over a corporate document corpus." - ), - llm=self._worker_llm, - ) - - for attempt in range(max_attempts): - retry_note = ( - " Previous attempt failed validation. Make sure the question: " - "ends with '?', does not reveal the answer, avoids artifact IDs, " - "and is 15-120 words long." - if attempt > 0 - else "" - ) - task = Task( - description=template + retry_note, - expected_output="One question ending with a question mark. No preamble or explanation.", - agent=agent, - ) - try: - result = str( - Crew(agents=[agent], tasks=[task], verbose=False).kickoff() - ).strip() - - if self._validate_prose(result, ground_truth_str, question_type): - return result - else: - logger.debug( - f"[eval] Prose validation failed (attempt {attempt + 1}): {result[:80]}" - ) - except Exception as exc: - logger.warning( - f"[eval] Prose generation error (attempt {attempt + 1}): {exc}" - ) - - return None - - def _validate_prose( - self, text: str, ground_truth_str: str, question_type: str - ) -> bool: - if not text.endswith("?"): - return False - - words = text.split() - if len(words) < 10 or len(words) > 150: - return False - - # Must not leak ground truth verbatim - gt_lower = ground_truth_str.lower() - if gt_lower in text.lower() and len(gt_lower) > 4: - return False - - # Must not contain raw artifact IDs (e.g. IT-108, abc12345) - if re.search(r"\b[A-Z]{1,4}-\d{2,6}\b", text): - return False - if re.search(r"\b[a-f0-9]{8,}\b", text): - return False - - # PERSPECTIVE questions must reference an actor name or role - if question_type == "PERSPECTIVE": - if not re.search(r"day\s+\d+|as of|by\s+[A-Z][a-z]+", text, re.IGNORECASE): - return False - - # COUNTERFACTUAL questions must use hypothetical language - if question_type == "COUNTERFACTUAL": - if not re.search( - r"\b(if|had|would|could|might|hypothetically)\b", text, re.IGNORECASE - ): - return False - - # SILENCE questions must be yes/no answerable - if question_type == "SILENCE": - if not re.search( - r"\b(was|were|did|has|have|is|are)\b", text, re.IGNORECASE - ): - return False - - return True - - -# ───────────────────────────────────────────────────────────────────────────── -# HARNESS ENTRYPOINT -# ───────────────────────────────────────────────────────────────────────────── - - -class EvalHarness: - """ - Orchestrates the full eval dataset generation pipeline: - 1. Build actor visibility cones (ActorVisibilityBuilder) - 2. Index explicit causal links (CausalLinkIndexer) - 3. Catalog expected-but-absent artifacts (AbsenceCatalogBuilder) - 4. Generate eval questions (EvalQuestionGenerator) - 5. Write all intermediate structures + final questions to export/eval/ - """ - - def __init__(self): - from flow import build_llm - - self._mem = Memory() - self._worker_llm = build_llm("worker") - - def run(self) -> None: - logger.info("[bold cyan]🔬 Building OrgForge eval dataset v2...[/bold cyan]") - - # Step 1: Actor visibility - logger.info("[eval] Building actor visibility cones...") - vis_builder = ActorVisibilityBuilder(self._mem) - visibility_map = vis_builder.build_all() - vis_export = { - actor: [cone.to_dict() for cone in cones] - for actor, cones in visibility_map.items() - } - vis_path = EVAL_DIR / "actor_visibility.json" - with open(vis_path, "w") as f: - json.dump(vis_export, f, indent=2, default=str) - logger.info(f" → {vis_path} ({len(visibility_map)} actors)") - - # Step 2: Causal link index - logger.info("[eval] Indexing explicit causal links...") - link_indexer = CausalLinkIndexer(self._mem) - causal_links = link_indexer.build() - links_path = EVAL_DIR / "causal_link_index.json" - with open(links_path, "w") as f: - json.dump([lnk.to_dict() for lnk in causal_links], f, indent=2, default=str) - logger.info(f" → {links_path} ({len(causal_links)} links)") - - # Step 3: Absence catalog - logger.info("[eval] Building absence catalog...") - absence_builder = AbsenceCatalogBuilder(self._mem) - absence_catalog = absence_builder.build() - absence_path = EVAL_DIR / "absence_catalog.json" - with open(absence_path, "w") as f: - json.dump([r.to_dict() for r in absence_catalog], f, indent=2, default=str) - logger.info(f" → {absence_path} ({len(absence_catalog)} absence records)") - - # Step 4: Question generation - logger.info("[eval] Generating eval questions...") - generator = EvalQuestionGenerator( - mem=self._mem, - worker_llm=self._worker_llm, - visibility_map=visibility_map, - causal_links=causal_links, - absence_catalog=absence_catalog, - ) - questions = generator.generate() - - # Summary stats - by_type: Dict[str, int] = defaultdict(int) - by_difficulty: Dict[str, int] = defaultdict(int) - cross_subsystem_count = 0 - for q in questions: - by_type[q["question_type"]] += 1 - by_difficulty[q["difficulty"]] += 1 - if q.get("cross_subsystem"): - cross_subsystem_count += 1 - - questions_path = EVAL_DIR / "eval_questions.json" - with open(questions_path, "w") as f: - json.dump( - { - "metadata": { - "generated_at": datetime.now().isoformat(), - "version": "2.0", - "tracks": ["PERSPECTIVE", "COUNTERFACTUAL", "SILENCE"], - "total_questions": len(questions), - "by_type": dict(by_type), - "by_difficulty": dict(by_difficulty), - "cross_subsystem_questions": cross_subsystem_count, - "actors_with_visibility_cones": len(visibility_map), - "causal_links_indexed": len(causal_links), - "absence_records": len(absence_catalog), - }, - "questions": questions, - }, - f, - indent=2, - default=str, - ) - - logger.info(f" → {questions_path}") - logger.info( - f"[green]✓ Eval dataset v2 complete.[/green] " - f"Types: {dict(by_type)} | Difficulty: {dict(by_difficulty)} | " - f"Cross-subsystem: {cross_subsystem_count}" - ) - - -if __name__ == "__main__": - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(levelname)s - %(message)s", - ) - EvalHarness().run() diff --git a/eval/export_to_hf.py b/eval/export_to_hf.py index 0d82167..5110493 100644 --- a/eval/export_to_hf.py +++ b/eval/export_to_hf.py @@ -2,122 +2,31 @@ export_to_hf.py =============== Normalises all OrgForge simulation artifacts into a flat HuggingFace-ready -corpus, computes a two-tier baseline, produces Parquet files, and writes a -dataset card (README.md) to export/hf_dataset/. +corpus, produces Parquet files, and writes a dataset card (README.md). -Run after flow.py + eval_harness.py: +Run after flow.py completes: python export_to_hf.py Output layout ------------- export/hf_dataset/ corpus/ - corpus-00000.parquet — flat document corpus (one row per artifact) - questions/ - questions-00000.parquet — eval questions with ground truth - eval_indexes/ - causal_link_index.parquet — explicit causal links from CausalLinkIndexer - actor_visibility.parquet — per-actor visibility cones, one row per (actor, day) - absence_catalog.parquet — expected-but-absent artifact pairs - baselines/ - ungated_ceiling_bm25.json — per-question ungated BM25 ceiling scores - ungated_ceiling_dense.json — per-question ungated dense ceiling scores - static_reasoning_metrics.json — per-question + aggregate reasoning difficulty metrics - baseline_summary.json — combined summary for the dataset card - README.md — HuggingFace dataset card - -Two-tier baseline design ------------------------- -This file computes only what requires NO agent execution: - - Tier 1 — Ungated Retrieval Ceiling (UngatedCeilingBaseline) - BM25 and dense retrieval with all gates removed ("god-mode" corpus access). - MRR@10 and Recall@10 are reported for PERSPECTIVE and COUNTERFACTUAL only. - SILENCE is excluded — absence cannot be measured by retrieval recall. - The delta between this ceiling and a gated agent's combined_score is the - "Epistemic Tax" — the difficulty cost of respecting organisational silos. - - Tier 2 — Static Reasoning Difficulty (StaticReasoningMetrics) - Metrics derived from corpus + question metadata alone. No LLM, no agent. - PERSPECTIVE → horizon_contamination_rate (fraction of ungated top-20 outside cone) - COUNTERFACTUAL → causal_chain_traceable (do cause+effect co-appear in top-10?) - SILENCE → search_space_bm25_coverage (fraction of required locations BM25 finds) - -Agent-level baselines (ungated god-mode agent, zero-shot no-tools) require LLM -calls and belong in agentic_eval_harness.py as --ungated / --zero-shot flags. - -Corpus schema (one row per document) -------------------------------------- - doc_id str — globally unique, e.g. "ORG-42", "CONF-ENG-007", "EMAIL-001" - doc_type str — "jira" | "confluence" | "slack" | "email" | "pr" | - "zd_ticket" | "sf_opp" | "sf_account" | "sim_event" - title str — human-readable title or subject line - body str — full text content for retrieval - day int — simulation day this artifact was created - date str — ISO date string - timestamp str — ISO datetime string (business-hours accurate) - actors str — JSON array of actor names involved - tags str — JSON array of tags from SimEvent - artifact_ids str — JSON dict mapping type→id (for cross-referencing) - dept str — owning department, empty if cross-dept - is_incident bool — True if this artifact is part of an incident thread - is_external bool — True for emails from outside the org - -Question schema ---------------- - question_id str - question_type str — PERSPECTIVE | COUNTERFACTUAL | SILENCE - question_text str - ground_truth str — JSON-serialised ground_truth dict - evidence_chain str — JSON array of artifact IDs (cause+effect for - COUNTERFACTUAL; evidence_artifacts for PERSPECTIVE; - empty for SILENCE — absence cannot be recalled) - difficulty str — medium | hard - requires_reasoning bool - actor str — PERSPECTIVE only - actor_role str — PERSPECTIVE only - as_of_day int — PERSPECTIVE only - subsystem_access str — JSON list; PERSPECTIVE only - blocked_subsystems str — JSON list; PERSPECTIVE only - actor_visible_artifacts str — JSON list; PERSPECTIVE only - link_type str — COUNTERFACTUAL only (causal link type) - causal_day int — COUNTERFACTUAL only - expected_search_space str — JSON list; SILENCE only - trigger_event_type str — SILENCE only - expected_response_type str — SILENCE only - -Eval indexes -------------- -causal_link_index — one row per CausalLink (see eval_harness.CausalLink) -actor_visibility — one row per (actor, day) ActorVisibilityCone snapshot -absence_catalog — one row per AbsenceRecord - -Baseline methodology ---------------------- -BM25 — rank_bm25 (Okapi BM25) over the body field. - For PERSPECTIVE and COUNTERFACTUAL questions the top-10 returned - doc_ids are compared against evidence_chain. - MRR@10 and Recall@10 are reported per question type. - SILENCE questions are skipped — the correct answer is absence, - so standard retrieval recall is not applicable. - -Dense — via Memory._embed() (same embedding model used by the simulation). - Cosine similarity between question_text embedding and body embeddings. - Same MRR@10 / Recall@10 as BM25. - If Memory is unavailable, this section is skipped gracefully and the - dataset card notes the omission. + corpus-00000.parquet — flat document corpus (one row per artifact) + README.md — HuggingFace dataset card """ from __future__ import annotations import json import logging -import re import textwrap from collections import defaultdict +from datetime import datetime from pathlib import Path -from typing import Any, Dict, List, Tuple -import numpy as np +from typing import Any, Dict, List +import email as email_lib +from email.header import decode_header +import shutil import yaml @@ -137,15 +46,33 @@ _ACTOR_TO_DEPT[str(_name).strip()] = _dept BASE = Path(_SIM_CFG.get("output_dir", "./export")) -EVAL_DIR = BASE / "eval" HF_DIR = BASE / "hf_dataset" CORPUS_DIR = HF_DIR / "corpus" -QUES_DIR = HF_DIR / "questions" -EVAL_INDEX_DIR = HF_DIR / "eval_indexes" -BASELINE_DIR = HF_DIR / "baselines" -_DENSE_MODEL_NAME = "Losspost/stella_en_1.5b_v5" -for d in (CORPUS_DIR, QUES_DIR, EVAL_INDEX_DIR, BASELINE_DIR): + +_ARTIFACT_DOC_TYPES = frozenset( + { + "confluence", + "dept_plans", + "jira", + "slack", + "email", + "pr", + "zd_ticket", + "sf_opp", + "sf_account", + "nps_survey", + "invoice", + "datadog_alert", + "zoom_transcript", + } +) + + +_EXCLUDE_FIELDS = {"_id", "timestamp", "embedding"} + + +for d in (CORPUS_DIR,): d.mkdir(parents=True, exist_ok=True) # ── Optional imports (degrade gracefully) ──────────────────────────────────── @@ -162,19 +89,6 @@ "pip install pandas pyarrow" ) -try: - from rank_bm25 import BM25Okapi - - _BM25_AVAILABLE = True -except ImportError: - _BM25_AVAILABLE = False - logger.warning( - "rank_bm25 not installed — BM25 baseline disabled. pip install rank-bm25" - ) - -_DENSE_AVAILABLE = True -_DENSE_MODEL_NAME = "Qwen/Qwen3-Embedding-4B" - # ───────────────────────────────────────────────────────────────────────────── # CORPUS BUILDER @@ -188,42 +102,227 @@ def _dept_from_artifact_id(artifact_id: str) -> str: return "" code = parts[1].upper() return { - "ENG": "Engineering", - "PRD": "Product", + "ENG": "", + "PROD": "Product", "MKT": "Sales_Marketing", "QA": "QA_Support", + "HR": "HR_Ops", "RETRO": "", }.get(code, "") +import email as email_lib +from email.header import decode_header + + +def _parse_eml(eml_path: Path) -> dict: + """Parse a .eml file and return headers + decoded body.""" + raw = eml_path.read_text(encoding="utf-8", errors="replace") + msg = email_lib.message_from_string(raw) + + subject_parts = decode_header(msg.get("Subject", "")) + subject = "" + for part, charset in subject_parts: + if isinstance(part, bytes): + subject += part.decode(charset or "utf-8", errors="replace") + else: + subject += part + + body = "" + if msg.is_multipart(): + for part in msg.walk(): + if part.get_content_type() == "text/plain": + payload = part.get_payload(decode=True) + if payload: + body = payload.decode( + part.get_content_charset() or "utf-8", errors="replace" + ) + break + else: + payload = msg.get_payload(decode=True) + if payload: + body = payload.decode( + msg.get_content_charset() or "utf-8", errors="replace" + ) + + return { + "subject": subject, + "from_addr": msg.get("From", ""), + "to_addr": msg.get("To", ""), + "direction": msg.get("X-OrgForge-Direction", ""), + "body": body, + } + + +def _load_confluence_from_disk() -> List[dict]: + confluence_dir = BASE / "confluence" + if not confluence_dir.exists(): + return [] + + rows = [] + for p in confluence_dir.rglob("*.md"): + try: + text = p.read_text(encoding="utf-8", errors="replace") + lines = text.splitlines() + + # Parse header fields + doc_id = p.stem + title = "" + author = "" + date = "" + + for line in lines[:6]: + if line.startswith("# "): + title = line[2:].strip() + elif line.startswith("**ID:**"): + doc_id = line.replace("**ID:**", "").strip() + elif line.startswith("**Author:**"): + author = line.replace("**Author:**", "").strip() + elif line.startswith("**Date:**"): + date = line.replace("**Date:**", "").strip() + + rows.append( + { + "doc_id": doc_id, + "doc_type": "confluence", + "category": "artifact", + "title": title, + "body": text, + "day": 0, + "date": date, + "timestamp": f"{date}T09:00:00" if date else "", + "actors": json.dumps([author] if author else []), + "tags": json.dumps(["confluence"]), + "artifact_ids": json.dumps({}), + "dept": _dept_from_artifact_id(doc_id), + "is_incident": False, + "is_external": False, + "facts": "", + } + ) + except Exception as exc: + logger.warning(f" confluence disk read failed: {p} — {exc}") + + logger.info(f" confluence disk fallback: {len(rows)} pages loaded") + return rows + + +def _load_slack_from_disk() -> List[dict]: + slack_dir = BASE / "slack" + if not slack_dir.exists(): + return [] + + buckets: Dict[str, dict] = {} + for p in slack_dir.rglob("*.json"): + try: + messages = json.loads(p.read_text(encoding="utf-8", errors="replace")) + for msg in messages: + tid = msg.get("thread_id", "") + if not tid: + continue + if tid not in buckets: + buckets[tid] = { + "date": msg.get("date", ""), + "ts": msg.get("ts", ""), + "actors": set(), + "texts": [], + } + bucket = buckets[tid] + user = msg.get("user", "") + if user: + bucket["actors"].add(user) + text = msg.get("text", "") + if text: + bucket["texts"].append(f"{user}: {text}" if user else text) + except Exception as exc: + logger.warning(f" slack disk read failed: {p} — {exc}") + + rows = [] + for tid, bucket in buckets.items(): + rows.append( + { + "doc_id": tid, + "doc_type": "slack", + "category": "artifact", + "title": tid.split("_2026")[0].replace("slack_", "#"), + "body": "\n".join(bucket["texts"]), + "day": 0, + "date": bucket["date"], + "timestamp": bucket["ts"], + "actors": json.dumps(sorted(bucket["actors"])), + "tags": json.dumps(["slack"]), + "artifact_ids": json.dumps({}), + "dept": "", + "is_incident": False, + "is_external": False, + "facts": "", + } + ) + return rows + + class CorpusBuilder: """ Reads the MongoDB-persisted artifacts (via Memory) and the SimEvent log, then normalizes every artifact into a flat list of corpus rows. - Falls back to reconstructing from eval JSON if MongoDB is unavailable, - which allows the exporter to run in offline/CI environments. + Falls back to reconstructing from the export directory if MongoDB is + unavailable, which allows the exporter to run in offline/CI environments. """ - def __init__(self, mem=None): + def __init__(self, mem=None, insider_threat_enabled: bool = False): self._mem = mem + self._insider_threat_enabled = insider_threat_enabled self._events: List[dict] = [] if mem is not None: - try: - raw = mem.get_event_log(from_db=True) - self._events = [ - e.to_dict() if hasattr(e, "to_dict") else e for e in raw - ] - except Exception as exc: - logger.warning(f"Could not load SimEvent log from Memory: {exc}") + for collection_name in ("sim_events", "events", "simevents"): + try: + coll = mem._db[collection_name] + raw = list(coll.find({}, {"embedding": 0})) + if raw: + self._events = raw + logger.info( + f" Loaded {len(self._events):,} SimEvents " + f"from '{collection_name}'." + ) + break + except Exception as exc: + logger.debug(f" Collection '{collection_name}' unavailable: {exc}") + if not self._events: + try: + raw = mem.get_event_log(from_db=True) + self._events = [ + e.to_dict() if hasattr(e, "to_dict") else e for e in raw + ] + logger.info( + f" Loaded {len(self._events):,} SimEvents via get_event_log." + ) + except Exception as exc: + logger.warning(f"Could not load SimEvent log: {exc}") # ── PUBLIC ──────────────────────────────────────────────────────────────── + def _build_meta_map(self, collection: str, id_field: str) -> Dict[str, dict]: + """Fetch all docs from a collection, strip noise fields, key by id_field.""" + meta_map = {} + try: + for doc in self._mem._db[collection].find({}, {"embedding": 0}): + doc_id = str(doc.get(id_field, "")) + if not doc_id: + continue + clean = {k: v for k, v in doc.items() if k not in _EXCLUDE_FIELDS} + meta_map[doc_id] = clean + except Exception as exc: + logger.debug(f" meta_map failed for {collection}: {exc}") + return meta_map + def build(self) -> List[dict]: rows: List[dict] = [] for evt in self._events: - evt_rows = self._sim_event_to_row(evt) + evt_rows = self._sim_event_to_row( + evt, insider_threat_enabled=self._insider_threat_enabled + ) if evt_rows: rows.extend(evt_rows) @@ -231,9 +330,23 @@ def build(self) -> List[dict]: rows = self._enrich_from_mongo(rows) rows.extend(self._plans_to_corpus_rows()) + existing_ids = {row["doc_id"] for row in rows} + rows.extend(_load_slack_from_disk()) + rows.extend(_load_confluence_from_disk()) + rows.extend(self._post_sim_to_corpus_rows()) - # Deduplicate: keep the row with the longest body for each doc_id + zoom_rows = [r for r in rows if r["doc_type"] == "zoom_transcript"] + logger.info(f" zoom rows before dedup: {len(zoom_rows)}") + for r in zoom_rows[:3]: + logger.info(f" {r['doc_id']} body_len={len(r.get('body', ''))}") + + # Deduplication strategy: + # - For artifact doc_ids (jira, confluence, slack, etc.): keep the row + # with the longest body — the MongoDB-enriched version wins over the + # thin SimEvent version. + # - Internal event rows (EVT-* doc_ids) are unique by construction and + # never conflict with artifact rows, so they pass through intact. seen: Dict[str, dict] = {} for row in rows: did = row["doc_id"] @@ -244,43 +357,52 @@ def build(self) -> List[dict]: if not row.get("body"): row["body"] = row.get("content") or "" + zoom_rows = [r for r in seen.values() if r["doc_type"] == "zoom_transcript"] + logger.info(f" zoom rows after dedup: {len(zoom_rows)}") + rows = list(seen.values()) - logger.info(f" corpus: {len(rows)} documents") + + for r in zoom_rows[:3]: + logger.info(f" {r['doc_id']} body_len={len(r.get('body', ''))}") + + by_type: Dict[str, int] = defaultdict(int) + for row in rows: + by_type[row["doc_type"]] += 1 + logger.info( + f" corpus: {len(rows):,} documents " + f"({len(self._events):,} SimEvents → {len(rows):,} corpus rows)" + ) + for doc_type, count in sorted(by_type.items(), key=lambda x: -x[1]): + logger.info(f" {doc_type:30s} {count:,}") return rows + def artifact_counts(self, rows: List[dict]) -> Dict[str, int]: + """Return counts by doc_type, sorted descending.""" + counts: Dict[str, int] = defaultdict(int) + for row in rows: + counts[row["doc_type"]] += 1 + return dict(sorted(counts.items(), key=lambda x: -x[1])) + # ── PRIVATE ─────────────────────────────────────────────────────────────── def _body_len(self, r: dict) -> int: return len(r.get("body") or r.get("content") or "") - def _sim_event_to_row(self, evt: dict) -> List[dict]: - event_type = evt.get("type", "") - artifact_ids = evt.get("artifact_ids", {}) - facts = evt.get("facts", {}) - - _EXCLUDED_EVENT_TYPES = {"dlp_alert", "secret_detected"} - if event_type in _EXCLUDED_EVENT_TYPES: - return [] - - evt_actors = evt.get("actors", []) - dept_val = str(facts.get("dept", "")).strip() - if not dept_val and evt_actors: - for _actor in evt_actors: - _d = _ACTOR_TO_DEPT.get(str(_actor).strip(), "") - if _d: - dept_val = _d - break - - is_incident = event_type in ( + _IS_INCIDENT_TYPES = frozenset( + { "incident_opened", "incident_resolved", "escalation_chain", "postmortem_created", + "fix_in_progress", "zd_tickets_escalated", "sf_deals_risk_flagged", "crm_account_at_risk", - ) - is_external = event_type in ( + } + ) + + _IS_EXTERNAL_TYPES = frozenset( + { "inbound_external_email", "customer_email_routed", "vendor_email_routed", @@ -288,10 +410,72 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: "sales_outbound_email", "proactive_outreach_initiated", "zd_ticket_opened", + "zd_tickets_escalated", "zd_tickets_resolved", "crm_touchpoint", "customer_health_briefing", - ) + "external_contact_summarized", + } + ) + + def _facts_body(self, event_type: str, facts: dict, summary: str) -> str: + """ + Render a structured plain-text body from SimEvent facts. + Used for internal events that carry no separate artifact. + Every key-value pair is included so the full ground truth is retrievable. + """ + parts = [f"event_type: {event_type}"] + if summary: + parts.append(f"summary: {summary}") + for key, val in facts.items(): + if val is None or val == "" or val == [] or val == {}: + continue + if isinstance(val, (dict, list)): + parts.append(f"{key}: {json.dumps(val, default=str)}") + else: + parts.append(f"{key}: {val}") + return "\n".join(parts) + + def _sim_event_to_row( + self, evt: dict, insider_threat_enabled: bool = False + ) -> List[dict]: + event_type = evt.get("type", "") + artifact_ids = evt.get("artifact_ids", {}) or {} + facts = evt.get("facts", {}) or {} + summary = evt.get("summary", "") + + if ( + event_type in ("dlp_alert", "secret_detected") + and not insider_threat_enabled + ): + return [] + + evt_actors = evt.get("actors", []) + dept_val = "" + if evt_actors: + for _actor in evt_actors: + _d = _ACTOR_TO_DEPT.get(str(_actor).strip(), "") + if _d: + dept_val = _d + break + + if not dept_val: + dept_val = str(facts.get("dept", "")).strip() + + if not dept_val: + for aid in artifact_ids.values(): + _d = _dept_from_artifact_id(str(aid)) + if _d: + dept_val = _d + break + + is_incident = event_type in self._IS_INCIDENT_TYPES + is_external = event_type in self._IS_EXTERNAL_TYPES + + evt_id = str(evt.get("_id", "")).strip() + if not evt_id: + actor_fp = (evt_actors[0] if evt_actors else "sys").replace(" ", "_") + evt_id = f"EVT-{evt.get('day', 0):04d}-{event_type}-{actor_fp}" shared = { "day": int(evt.get("day", 0)), @@ -303,6 +487,8 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: "dept": dept_val, "is_incident": is_incident, "is_external": is_external, + "facts": json.dumps(facts), + "category": "sim_event", } rows: List[dict] = [] @@ -314,6 +500,7 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: **shared, "doc_id": jira_id, "doc_type": "jira", + "category": "artifact", "title": str(facts.get("title", facts.get("root_cause", jira_id)))[ :512 ], @@ -330,41 +517,48 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: "", ) if conf_id: - body = facts.get("content", facts.get("summary", "")) or evt.get( - "summary", "" - ) rows.append( { **shared, "doc_id": conf_id, "doc_type": "confluence", + "category": "artifact", "title": str(facts.get("title", conf_id))[:512], - "body": body, + "body": facts.get("content", facts.get("summary", "")) or summary, "dept": dept_val or _dept_from_artifact_id(conf_id), } ) - email_id = artifact_ids.get("email", "") - if email_id or event_type in ( - "inbound_external_email", - "hr_outbound_email", - "customer_email_routed", - "vendor_email_routed", - "email_dropped", - "sales_outbound_email", - "proactive_outreach_initiated", - ): - rows.append( - { - **shared, - "doc_id": email_id or f"EMAIL-{evt.get('day', 0)}-{id(evt)}", - "doc_type": "email", - "title": str(facts.get("subject", facts.get("summary", email_id)))[ - :512 - ], - "body": self._email_body(facts, evt), - } - ) + email_id = artifact_ids.get("email", "") or artifact_ids.get("embed_id", "") + eml_path_str = artifact_ids.get("eml_path", "") + if email_id: + body = self._email_body(facts, evt) + if eml_path_str: + eml_path = Path(eml_path_str.lstrip("./")) + if eml_path.exists(): + parsed = _parse_eml(eml_path) + body = parsed.pop("body") + email_meta = parsed + else: + body = self._email_body(facts, evt) + email_meta = {} + logger.warning(f" eml not found on disk: {eml_path}") + + rows.append( + { + **shared, + "doc_id": email_id, + "doc_type": "email", + "category": "artifact", + "title": str( + email_meta.get("subject") or facts.get("subject", email_id) + )[:512], + "body": body, + "facts": json.dumps( + {**json.loads(shared["facts"]), **email_meta} + ), + } + ) slack_id = artifact_ids.get("slack_thread", "") if slack_id: @@ -374,6 +568,7 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: **shared, "doc_id": slack_id, "doc_type": "slack", + "category": "artifact", "title": str(channel + ": " + facts.get("summary", "")[:80])[:512], "body": facts.get("content", facts.get("summary", "")), } @@ -386,6 +581,7 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: **shared, "doc_id": pr_id, "doc_type": "pr", + "category": "artifact", "title": str(facts.get("title", pr_id))[:512], "body": facts.get("description", facts.get("summary", "")), } @@ -405,6 +601,7 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: **shared, "doc_id": zd_id, "doc_type": "zd_ticket", + "category": "artifact", "title": str(facts.get("subject", facts.get("ticket_id", zd_id)))[ :512 ], @@ -426,6 +623,7 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: **shared, "doc_id": opp_id, "doc_type": "sf_opp", + "category": "artifact", "title": str( facts.get("account_name", opp_id) + " — " @@ -445,25 +643,55 @@ def _sim_event_to_row(self, evt: dict) -> List[dict]: **shared, "doc_id": acc_id, "doc_type": "sf_account", + "category": "artifact", "title": str(facts.get("account_name", acc_id))[:512], "body": self._sf_account_body(facts, acc_id), } ) - if not rows: + # ── Internal event row — ALWAYS emitted, even when artifact rows exist ─ + # Preserves the full ground-truth facts (stress snapshots, similarity + # scores, coverage percentages, departure edge snapshots, etc.) as a + # separately retrievable document. Filter by category == "sim_event" + # to get the state-machine view independently from prose artifacts. + rows.append( + { + **shared, + "doc_id": evt_id, + "doc_type": event_type, + "title": event_type.replace("_", " ").title(), + "body": self._facts_body(event_type, facts, summary), + "facts": json.dumps(facts), + } + ) + + zoom_path_str = artifact_ids.get("artifact_path", "") + zoom_id = artifact_ids.get("zoom_transcript", "") + if zoom_id and zoom_path_str and zoom_id.startswith("zoom_"): + zoom_path = Path(zoom_path_str) + if not zoom_path.is_absolute(): + zoom_path = Path(zoom_path_str.lstrip("./")) + + if zoom_path.exists(): + zoom_body = zoom_path.read_text(encoding="utf-8", errors="replace") + else: + zoom_body = summary rows.append( { **shared, - "doc_id": f"EVENT-{evt.get('day', 0)}-{event_type}", - "doc_type": "sim_event", - "title": event_type.replace("_", " ").title(), - "body": evt.get("summary", ""), + "doc_id": zoom_id, + "doc_type": "zoom_transcript", + "category": "artifact", + "title": f"Zoom: {facts.get('topic', event_type)} ({evt.get('date', '')})", + "body": zoom_body, + "facts": json.dumps(facts), } ) + # Ensure no row has an empty body for row in rows: if not row.get("body"): - row["body"] = evt.get("summary", "") + row["body"] = summary return rows @@ -646,6 +874,64 @@ def _post_sim_to_corpus_rows(self) -> List[dict]: } ) + dd_dir = BASE / "datadog" + metrics_path = dd_dir / "metrics.jsonl" + if metrics_path.exists(): + for line in metrics_path.read_text().splitlines(): + if not line.strip(): + continue + data = json.loads(line) + rows.append( + { + "doc_id": f"DD-METRIC-{data.get('metric_name', '').replace('.', '_')}-{data.get('timestamp', '')}", + "doc_type": "datadog_metric", + "title": data.get("metric_name", "datadog metric"), + "body": json.dumps(data), + "day": data.get("day", 0), + "date": str(data.get("timestamp", ""))[:10], + "timestamp": str(data.get("timestamp", "")), + "actors": json.dumps([]), + "tags": json.dumps(["datadog", "metric"]), + "artifact_ids": json.dumps({}), + "dept": "Engineering_Backend", + "is_incident": data.get("alert_firing", False), + "is_external": False, + } + ) + + alerts_path = dd_dir / "alerts.jsonl" + if alerts_path.exists(): + for line in alerts_path.read_text().splitlines(): + if not line.strip(): + continue + data = json.loads(line) + alert_id = f"DD-{data.get('id', data.get('alert_id', ''))}" + rows.append( + { + "doc_id": alert_id, + "doc_type": "datadog_alert", + "title": data.get("monitor_name", alert_id), + "body": json.dumps(data), + "day": data.get("day", 0), + "date": str(data.get("fired_at", ""))[:10], + "timestamp": str(data.get("fired_at", "")), + "actors": json.dumps([]), + "tags": json.dumps( + ["datadog", "alert", data.get("incident_id", "")] + ), + "artifact_ids": json.dumps( + { + "jira": data.get("attributes", {}).get( + "jira_ticket", data.get("jira_ticket", "") + ) + } + ), + "dept": "Engineering_Backend", + "is_incident": True, + "is_external": False, + } + ) + inv_dir = BASE / "invoices" if inv_dir.exists(): for p in inv_dir.glob("*.json"): @@ -675,6 +961,35 @@ def _post_sim_to_corpus_rows(self) -> List[dict]: "is_external": True, } ) + + tech_doc = ( + self._mem._db["sim_config"].find_one({"_id": "tech_stack"}) + if self._mem + else None + ) + if tech_doc: + stack = tech_doc.get("stack", {}) + body = "\n".join(f"{k}: {v}" for k, v in stack.items()) + rows.append( + { + "doc_id": "SIM-CONFIG-tech_stack", + "doc_type": "sim_config", + "title": "Tech Stack", + "body": body, + "day": 0, + "date": str(tech_doc.get("created_at", ""))[:10], + "timestamp": str(tech_doc.get("created_at", "")), + "actors": json.dumps([]), + "tags": json.dumps(["tech_stack", "sim_config"]), + "artifact_ids": json.dumps({}), + "dept": "", + "is_incident": False, + "is_external": False, + "facts": "", + "category": "sim_config", + } + ) + return rows def _enrich_from_mongo(self, rows: List[dict]) -> List[dict]: @@ -683,14 +998,13 @@ def _enrich_from_mongo(self, rows: List[dict]) -> List[dict]: Silently skips if the collection is unavailable. """ try: - rich_map: Dict[str, str] = {} - + conf_rich_map: Dict[str, str] = {} conf_id_map: Dict[str, str] = {} for page in self._mem._db["confluence_pages"].find( {}, {"_id": 0, "id": 1, "content": 1, "title": 1} ): if page.get("id") and page.get("content"): - rich_map[page["id"]] = page["content"] + conf_rich_map[page["id"]] = page["content"] snippet = page["content"][:120].strip() if snippet: conf_id_map[snippet] = page["id"] @@ -698,6 +1012,7 @@ def _enrich_from_mongo(self, rows: List[dict]) -> List[dict]: if title_key: conf_id_map[title_key] = page["id"] + # ── Jira comments (used when building jira body) ────────────────── comment_map: Dict[str, List[str]] = defaultdict(list) for comment in self._mem._db["artifacts"].find( {"type": "jira_comment"}, @@ -713,130 +1028,68 @@ def _enrich_from_mongo(self, rows: List[dict]) -> List[dict]: else f"comment: {cbody}" ) - for ticket in self._mem._db["jira_tickets"].find( - {}, - { - "_id": 0, - "id": 1, - "title": 1, - "description": 1, - "root_cause": 1, - "comments": 1, - }, - ): - tid = ticket.get("id") - if not tid: - continue + def _jira_body(doc): parts = [ - ticket.get("title", ""), - ticket.get("description", ""), - ticket.get("root_cause", ""), + doc.get("title", ""), + doc.get("description", ""), + doc.get("root_cause", ""), ] - for c in ticket.get("comments") or []: - parts.append(str(c.get("body", ""))) - for c in comment_map.get(tid, []): + for c in doc.get("comments") or []: + parts.append(str(c.get("text", "") or c.get("body", ""))) + for c in comment_map.get(doc.get("id", ""), []): parts.append(c) - rich_map[tid] = "\n".join(p for p in parts if p) + return "\n".join(p for p in parts if p) - for pr in self._mem._db["pull_requests"].find( - {}, - { - "_id": 0, - "pr_id": 1, - "title": 1, - "description": 1, - "author": 1, - "ticket_id": 1, - "reviewers": 1, - "status": 1, - "comments": 1, - }, - ): - pid = pr.get("pr_id") - if not pid: - continue + def _pr_body(doc): parts = [] - if pr.get("title"): - parts.append(f"title: {pr['title']}") - if pr.get("description"): - parts.append(f"description: {pr['description']}") - if pr.get("author"): - parts.append(f"author: {pr['author']}") - if pr.get("ticket_id"): - parts.append(f"ticket: {pr['ticket_id']}") - if pr.get("status"): - parts.append(f"status: {pr['status']}") - reviewers = pr.get("reviewers", []) + for key in ("title", "description"): + if doc.get(key): + parts.append(f"{key}: {doc[key]}") + if doc.get("author"): + parts.append(f"author: {doc['author']}") + if doc.get("ticket_id"): + parts.append(f"ticket: {doc['ticket_id']}") + if doc.get("status"): + parts.append(f"status: {doc['status']}") + reviewers = doc.get("reviewers", []) if reviewers: parts.append(f"reviewers: {', '.join(reviewers)}") - for c in pr.get("comments") or []: + for c in doc.get("comments") or []: verdict = f" [{c['verdict']}]" if c.get("verdict") else "" text = c.get("text", "") author = c.get("author", "") if text: parts.append(f"review ({author}{verdict}): {text}") - rich_map[pid] = "\n".join(p for p in parts if p) + return "\n".join(p for p in parts if p) - for email in self._mem._db["emails"].find( - {}, - { - "_id": 0, - "embed_id": 1, - "subject": 1, - "body": 1, - "from_name": 1, - "from_addr": 1, - "to_name": 1, - "to_addr": 1, - "direction": 1, - }, - ): - eid = email.get("embed_id") - if not eid: - continue + def _email_body(doc): parts = [] - if email.get("subject"): - parts.append(f"subject: {email['subject']}") - if email.get("from_name") or email.get("from_addr"): + if doc.get("subject"): + parts.append(f"subject: {doc['subject']}") + if doc.get("from_name") or doc.get("from_addr"): parts.append( - f"from: {email.get('from_name', '')} <{email.get('from_addr', '')}>" + f"from: {doc.get('from_name', '')} <{doc.get('from_addr', '')}>" ) - if email.get("to_name") or email.get("to_addr"): + if doc.get("to_name") or doc.get("to_addr"): parts.append( - f"to: {email.get('to_name', '')} <{email.get('to_addr', '')}>" + f"to: {doc.get('to_name', '')} <{doc.get('to_addr', '')}>" ) - if email.get("body"): - parts.append(email["body"]) - rich_map[eid] = "\n".join(parts) + if doc.get("body"): + parts.append(doc["body"]) + return "\n".join(parts) - for ticket in self._mem._db["zd_tickets"].find( - {}, - { - "_id": 0, - "ticket_id": 1, - "subject": 1, - "org_name": 1, - "description": 1, - "comments": 1, - "related_incident": 1, - "priority": 1, - "status": 1, - }, - ): - tid = ticket.get("ticket_id") - if not tid: - continue + def _zd_body(doc): parts = [ - f"subject: {ticket.get('subject', '')}", - f"org: {ticket.get('org_name', '')}", - f"status: {ticket.get('status', '')}", - f"priority: {ticket.get('priority', '')}", + f"subject: {doc.get('subject', '')}", + f"org: {doc.get('org_name', '')}", + f"status: {doc.get('status', '')}", + f"priority: {doc.get('priority', '')}", ] - if ticket.get("description"): - parts.append(f"description: {ticket['description']}") - if ticket.get("related_incident"): - parts.append(f"related_incident: {ticket['related_incident']}") - for c in ticket.get("comments") or []: + if doc.get("description"): + parts.append(f"description: {doc['description']}") + if doc.get("related_incident"): + parts.append(f"related_incident: {doc['related_incident']}") + for c in doc.get("comments") or []: author = c.get("author", "") text = c.get("text", "") if text: @@ -845,80 +1098,83 @@ def _enrich_from_mongo(self, rows: List[dict]) -> List[dict]: if author else f"comment: {text}" ) - rich_map[tid] = "\n".join(p for p in parts if p) + return "\n".join(p for p in parts if p) - for opp in self._mem._db["sf_opps"].find( - {}, - { - "_id": 0, - "opportunity_id": 1, - "account_name": 1, - "stage": 1, - "probability": 1, - "amount": 1, - "owner": 1, - "lead_source": 1, - "next_step": 1, - "risk_notes": 1, - "touchpoints": 1, - "close_date": 1, - }, - ): - oid = opp.get("opportunity_id") - if not oid: - continue + def _sf_opp_body(doc): parts = [ - f"account: {opp.get('account_name', '')}", - f"stage: {opp.get('stage', '')}", - f"probability: {opp.get('probability', '')}%", - f"amount: ${opp.get('amount', 0):,}", - f"owner: {opp.get('owner', '')}", - f"close_date: {opp.get('close_date', '')}", - f"lead_source: {opp.get('lead_source', '')}", - f"next_step: {opp.get('next_step', '')}", + f"account: {doc.get('account_name', '')}", + f"stage: {doc.get('stage', '')}", + f"probability: {doc.get('probability', '')}%", + f"amount: ${doc.get('amount', 0):,}", + f"owner: {doc.get('owner', '')}", + f"close_date: {doc.get('close_date', '')}", + f"lead_source: {doc.get('lead_source', '')}", + f"next_step: {doc.get('next_step', '')}", ] - for note in opp.get("risk_notes") or []: + for note in doc.get("risk_notes") or []: parts.append(f"risk: {note}") - for tp in opp.get("touchpoints") or []: + for tp in doc.get("touchpoints") or []: subject = tp.get("subject", "") sender = tp.get("sender", "") ts = tp.get("timestamp", "") if subject: parts.append(f"touchpoint ({sender}, {ts}): {subject}") - rich_map[oid] = "\n".join(p for p in parts if p) + return "\n".join(p for p in parts if p) - for acc in self._mem._db["sf_accounts"].find( - {}, - { - "_id": 0, - "account_id": 1, - "name": 1, - "primary_contact": 1, - "type": 1, - "industry": 1, - "tier": 1, - "billing_region": 1, - "arr": 1, - "owner": 1, - "risk_flag": 1, - }, - ): - aid = acc.get("account_id") - if not aid: - continue + def _sf_account_body(doc): parts = [ - f"name: {acc.get('name', '')}", - f"type: {acc.get('type', '')}", - f"tier: {acc.get('tier', '')}", - f"industry: {acc.get('industry', '')}", - f"billing_region: {acc.get('billing_region', '')}", - f"arr: ${acc.get('arr', 0):,}", - f"owner: {acc.get('owner', '')}", - f"primary_contact: {acc.get('primary_contact', '')}", + f"name: {doc.get('name', '')}", + f"type: {doc.get('type', '')}", + f"tier: {doc.get('tier', '')}", + f"industry: {doc.get('industry', '')}", + f"billing_region: {doc.get('billing_region', '')}", + f"arr: ${doc.get('arr', 0):,}", + f"owner: {doc.get('owner', '')}", + f"primary_contact: {doc.get('primary_contact', '')}", ] - if acc.get("risk_flag"): + if doc.get("risk_flag"): parts.append("risk_flag: true — ownership lapsed or at-risk") - rich_map[aid] = "\n".join(p for p in parts if p) + return "\n".join(p for p in parts if p) + + jira_rich, jira_meta = self._build_rich_and_meta( + "jira_tickets", "id", _jira_body + ) + pr_rich, pr_meta = self._build_rich_and_meta( + "pull_requests", "pr_id", _pr_body + ) + email_rich, email_meta = self._build_rich_and_meta( + "emails", "embed_id", _email_body + ) + zd_rich, zd_meta = self._build_rich_and_meta( + "zd_tickets", "ticket_id", _zd_body + ) + sf_opp_rich, sf_opp_meta = self._build_rich_and_meta( + "sf_opps", "opportunity_id", _sf_opp_body + ) + sf_acc_rich, sf_acc_meta = self._build_rich_and_meta( + "sf_accounts", "account_id", _sf_account_body + ) + + # Merge all rich maps into one lookup + rich_map: Dict[str, str] = { + **conf_rich_map, + **jira_rich, + **pr_rich, + **email_rich, + **zd_rich, + **sf_opp_rich, + **sf_acc_rich, + } + + # Map doc_type -> meta map for facts enrichment + _DOC_TYPE_TO_META: Dict[str, Dict[str, dict]] = { + "jira": jira_meta, + "pr": pr_meta, + "email": email_meta, + "zd_ticket": zd_meta, + "sf_opp": sf_opp_meta, + "sf_account": sf_acc_meta, + } for row in rows: if row["doc_id"] == "CONF-UNKNOWN" and row["doc_type"] == "confluence": @@ -941,14 +1197,21 @@ def _enrich_from_mongo(self, rows: List[dict]) -> List[dict]: ) row["title"] = (row.get("body") or "")[:80].strip() logger.debug( - f"Reclassified CONF-UNKNOWN social event as slack: " - f"{row['doc_id']}" + f"Reclassified CONF-UNKNOWN social event as slack: {row['doc_id']}" ) elif row["doc_id"] in rich_map: row["body"] = rich_map[row["doc_id"]] if row["doc_type"] == "confluence" and not row.get("dept"): row["dept"] = _dept_from_artifact_id(row["doc_id"]) + # Enrich facts for all artifact types in one pass + meta_map = _DOC_TYPE_TO_META.get(row["doc_type"]) + if meta_map and row["doc_id"] in meta_map: + existing = json.loads(row.get("facts") or "{}") + existing.update(meta_map[row["doc_id"]]) + row["facts"] = json.dumps(existing, default=str) + + # ── Orphan sweep: artifacts in MongoDB not yet in corpus ─────────── existing_ids = {row["doc_id"] for row in rows} def _make_orphan_row( @@ -976,6 +1239,7 @@ def _make_orphan_row( return { "doc_id": doc_id, "doc_type": doc_type, + "category": "artifact", "title": str(title)[:512], "body": str(body), "day": int(day or 0), @@ -987,14 +1251,16 @@ def _make_orphan_row( "dept": dept, "is_incident": is_incident, "is_external": is_external, + "facts": "", } + # artifacts collection _ARTIFACT_TYPE_MAP = { "confluence": "confluence", - "slack_thread": "slack", "jira": "jira", "zd_ticket": "zd_ticket", "sf_opportunity": "sf_opp", + "zoom_transcript": "zoom_transcript", } for artifact in self._mem._db["artifacts"].find( {"type": {"$in": list(_ARTIFACT_TYPE_MAP.keys())}}, @@ -1052,172 +1318,64 @@ def _make_orphan_row( ) existing_ids.add(art_id) - for pr in self._mem._db["pull_requests"].find( - {}, - { - "_id": 0, - "pr_id": 1, - "title": 1, - "author": 1, - "day": 1, - "date": 1, - "timestamp": 1, - "dept": 1, - }, - ): - pid = pr.get("pr_id", "") + # pull_requests + for doc in self._mem._db["pull_requests"].find({}, {"embedding": 0}): + pid = doc.get("pr_id", "") if not pid or pid in existing_ids: continue - body = rich_map.get(pid, "") rows.append( _make_orphan_row( doc_id=pid, doc_type="pr", - title=pr.get("title", pid), - body=body, - day=pr.get("day"), - date=pr.get("date"), - timestamp=pr.get("timestamp"), - actors=[pr["author"]] if pr.get("author") else [], + title=doc.get("title", pid), + body=pr_rich.get(pid, ""), + day=doc.get("day"), + date=doc.get("date"), + timestamp=doc.get("timestamp"), + actors=[doc["author"]] if doc.get("author") else [], tags=["pr"], artifact_type="pr", ) ) existing_ids.add(pid) - for email in self._mem._db["emails"].find( - {}, - { - "_id": 0, - "embed_id": 1, - "subject": 1, - "from_name": 1, - "from_addr": 1, - "direction": 1, - "day": 1, - "date": 1, - "timestamp": 1, - }, - ): - eid = email.get("embed_id", "") + # emails + for doc in self._mem._db["emails"].find({}, {"embedding": 0}): + eid = doc.get("embed_id") if not eid or eid in existing_ids: continue - body = rich_map.get(eid, "") - direction = email.get("direction", "") rows.append( _make_orphan_row( doc_id=eid, doc_type="email", - title=email.get("subject", eid), - body=body, - day=email.get("day"), - date=email.get("date"), - timestamp=email.get("timestamp"), - actors=[email["from_name"]] if email.get("from_name") else [], - tags=["email", direction] if direction else ["email"], + title=doc.get("subject", eid), + body=email_rich.get(eid, ""), + day=doc.get("day"), + date=doc.get("date"), + timestamp=doc.get("timestamp"), + actors=[doc["from_name"]] if doc.get("from_name") else [], + tags=["email", doc.get("direction", "")], artifact_type="email", is_external=True, ) ) existing_ids.add(eid) - for ticket in self._mem._db["zd_tickets"].find( - {}, - { - "_id": 0, - "ticket_id": 1, - "subject": 1, - "org_name": 1, - "day": 1, - "date": 1, - "created_at": 1, - "status": 1, - "priority": 1, - }, - ): - tid = ticket.get("ticket_id", "") - if not tid or tid in existing_ids: + # sf_accounts + for doc in self._mem._db["sf_accounts"].find({}, {"embedding": 0}): + aid = doc.get("account_id", "") + if not aid or aid in existing_ids: continue - body = rich_map.get(tid, "") - rows.append( - _make_orphan_row( - doc_id=tid, - doc_type="zd_ticket", - title=ticket.get("subject", tid), - body=body, - day=ticket.get("day"), - date=ticket.get("date"), - timestamp=ticket.get("created_at"), - actors=[], - tags=["zendesk", "support"], - artifact_type="zd_ticket", - is_external=True, - is_incident=ticket.get("priority") == "Urgent", - ) - ) - existing_ids.add(tid) - - for opp in self._mem._db["sf_opps"].find( - {}, - { - "_id": 0, - "opportunity_id": 1, - "account_name": 1, - "stage": 1, - "owner": 1, - "day": 1, - "date": 1, - "created_at": 1, - }, - ): - oid = opp.get("opportunity_id", "") - if not oid or oid in existing_ids: - continue - body = rich_map.get(oid, "") - title = ( - f"{opp.get('account_name', oid)} — {opp.get('stage', '')}" - ).strip(" —") - rows.append( - _make_orphan_row( - doc_id=oid, - doc_type="sf_opp", - title=title, - body=body, - day=opp.get("day"), - date=opp.get("date"), - timestamp=opp.get("created_at"), - actors=[opp["owner"]] if opp.get("owner") else [], - tags=["salesforce", "opportunity"], - artifact_type="sf_opp", - is_external=True, - ) - ) - existing_ids.add(oid) - - for acc in self._mem._db["sf_accounts"].find( - {}, - { - "_id": 0, - "account_id": 1, - "name": 1, - "owner": 1, - "created_at": 1, - }, - ): - aid = acc.get("account_id", "") - if not aid or aid in existing_ids: - continue - body = rich_map.get(aid, "") rows.append( _make_orphan_row( doc_id=aid, doc_type="sf_account", - title=acc.get("name", aid), - body=body, + title=doc.get("name", aid), + body=sf_acc_rich.get(aid, ""), day=None, date=None, - timestamp=acc.get("created_at"), - actors=[acc["owner"]] if acc.get("owner") else [], + timestamp=doc.get("created_at"), + actors=[doc["owner"]] if doc.get("owner") else [], tags=["salesforce", "account"], artifact_type="sf_account", is_external=True, @@ -1225,21 +1383,9 @@ def _make_orphan_row( ) existing_ids.add(aid) + # slack_messages — bucket by thread_id thread_buckets: Dict[str, dict] = {} - for msg in self._mem._db["slack_messages"].find( - {}, - { - "_id": 0, - "thread_id": 1, - "channel": 1, - "text": 1, - "author": 1, - "sender": 1, - "ts": 1, - "day": 1, - "date": 1, - }, - ): + for msg in self._mem._db["slack_messages"].find({}, {"embedding": 0}): tid = msg.get("thread_id", "") if not tid or tid in existing_ids: continue @@ -1258,21 +1404,19 @@ def _make_orphan_row( bucket["actors"].add(author) text = msg.get("text", "") if text: - prefix = f"{author}: " if author else "" - bucket["texts"].append(f"{prefix}{text}") + bucket["texts"].append(f"{author}: {text}" if author else text) for tid, bucket in thread_buckets.items(): if tid in existing_ids: continue actors = sorted(bucket["actors"]) channel = bucket["channel"] - body = "\n".join(bucket["texts"]) rows.append( _make_orphan_row( doc_id=tid, doc_type="slack", title=f"#{channel}" if channel else tid, - body=body, + body="\n".join(bucket["texts"]), day=bucket["day"], date=bucket["date"], timestamp=bucket["ts"], @@ -1289,1091 +1433,683 @@ def _make_orphan_row( # ───────────────────────────────────────────────────────────────────────────── -# EVAL INDEX SERIALISERS +# CORPUS STATS # ───────────────────────────────────────────────────────────────────────────── -def _causal_links_to_rows(links: List[dict]) -> List[dict]: - """ - Flatten causal_link_index.json into Parquet-ready rows. - Each CausalLink dict maps directly — sets become JSON strings. - """ - rows = [] - for lnk in links: - rows.append( - { - "link_type": lnk.get("link_type", ""), - "cause_event_id": lnk.get("cause_event_id", ""), - "cause_event_type": lnk.get("cause_event_type", ""), - "effect_event_id": lnk.get("effect_event_id", ""), - "effect_event_type": lnk.get("effect_event_type", ""), - "actors": json.dumps(lnk.get("actors", []), default=str), - "day": int(lnk.get("day", 0)), - "link_field": lnk.get("link_field", ""), - "link_value": str(lnk.get("link_value", "")), - "subsystems_involved": json.dumps( - sorted(lnk.get("subsystems_involved", [])), default=str - ), - "counterfactual_premise": lnk.get("counterfactual_premise", ""), - "counterfactual_outcome": lnk.get("counterfactual_outcome", ""), - "outcome_changed": bool(lnk.get("outcome_changed", True)), - } - ) - return rows - - -def _actor_visibility_to_rows(visibility_map: dict) -> List[dict]: - """ - Flatten actor_visibility.json (actor → [cone, ...]) into one row per - (actor, day) snapshot. Heavy set/dict fields are JSON-serialised. +def _compute_corpus_stats(corpus: List[dict], cfg: dict, mem=None) -> dict: """ - rows = [] - for actor, cones in visibility_map.items(): - for cone in cones: - vis = cone.get("visible_artifacts", {}) - rows.append( - { - "actor": cone.get("actor", actor), - "role": cone.get("role", ""), - "as_of_time": cone.get("as_of_time", ""), - "as_of_day": int(cone.get("as_of_day", 0)), - "subsystem_access": json.dumps( - sorted(cone.get("subsystem_access", [])), default=str - ), - # All visible artifact IDs, flattened across subsystems - "all_visible_artifacts": json.dumps( - sorted( - {aid for ids in vis.values() for aid in ids} - ), - default=str, - ), - # Per-subsystem breakdown kept for fine-grained analysis - "visible_artifacts_by_subsystem": json.dumps( - {k: sorted(v) for k, v in vis.items()}, default=str - ), - "directly_involved": json.dumps( - sorted(cone.get("directly_involved", [])), default=str - ), - "broadcast_visible": json.dumps( - sorted(cone.get("broadcast_visible", [])), default=str - ), - } - ) - return rows - + Derives everything the dataset card needs from the corpus + config. -def _absence_catalog_to_rows(records: List[dict]) -> List[dict]: - """Flatten absence_catalog.json into Parquet-ready rows.""" - rows = [] - for rec in records: - rows.append( - { - "trigger_event_id": rec.get("trigger_event_id", ""), - "trigger_event_type": rec.get("trigger_event_type", ""), - "expected_response_type": rec.get("expected_response_type", ""), - "trigger_day": int(rec.get("trigger_day", 0)), - "trigger_actors": json.dumps( - rec.get("trigger_actors", []), default=str - ), - "trigger_artifact_ids": json.dumps( - rec.get("trigger_artifact_ids", {}), default=str - ), - "link_field": rec.get("link_field", ""), - "link_value": str(rec.get("link_value", "")), - "subsystem": rec.get("subsystem", ""), - "expected_search_space": json.dumps( - rec.get("expected_search_space", []), default=str - ), - } - ) - return rows - - - -def _questions_to_rows(questions: List[dict]) -> List[dict]: - """ - Convert the v2 eval questions list into flat Parquet rows. - - Evidence chain derivation: - COUNTERFACTUAL — union of cause + effect artifact IDs from - ground_truth.evidence_chain_artifacts - PERSPECTIVE — ground_truth.evidence_artifacts - SILENCE — empty; the correct answer is absence, so retrieval - recall is not applicable + mem is optional — if provided, the raw SimEvent count is read from MongoDB + so the card shows total events alongside deduplicated corpus documents. """ - rows = [] - for q in questions: - qtype = q.get("question_type", "") - gt = q.get("ground_truth", {}) + sim_cfg = cfg.get("simulation", {}) + org_chart = cfg.get("org_chart", {}) + knowledge_gaps = cfg.get("knowledge_gaps", []) + + artifact_counts: Dict[str, int] = defaultdict(int) + sim_event_counts: Dict[str, int] = defaultdict(int) + incident_docs = 0 + external_docs = 0 + dept_counts: Dict[str, int] = defaultdict(int) + actors_seen: set = set() + days_seen: set = set() + + for row in corpus: + dt = row["doc_type"] + if dt in _ARTIFACT_DOC_TYPES: + artifact_counts[dt] += 1 + else: + sim_event_counts[dt] += 1 + if row.get("is_incident"): + incident_docs += 1 + if row.get("is_external"): + external_docs += 1 + if row.get("dept"): + dept_counts[row["dept"]] += 1 + for actor in json.loads(row.get("actors") or "[]"): + if isinstance(actor, str): + actors_seen.add(actor) + if row.get("day"): + days_seen.add(int(row["day"])) + + # Raw SimEvent count from MongoDB. + # Most SimEvents are internal state-machine events (day_summary, + # knowledge_gap_detected, proposed_event_rejected, etc.) that do not + # map 1:1 to a corpus artifact. The corpus is the deduplicated set of + # *artifacts* those events produced — which is why corpus doc count + # will always be much smaller than the raw event count. + sim_events_total = None + sim_days_actual = None + if mem is not None: + for _coll in ("sim_events", "events", "simevents"): + try: + _n = mem._db[_coll].count_documents({}) + if _n: + sim_events_total = _n + last_event = mem._db[_coll].find_one( + {}, sort=[("day", -1)], projection={"day": 1} + ) + if last_event: + sim_days_actual = last_event["day"] + break + except Exception: + continue - evidence: List[str] = [] - if qtype == "COUNTERFACTUAL": - chain = gt.get("evidence_chain_artifacts", {}) - evidence = list(set(chain.get("cause", []) + chain.get("effect", []))) - elif qtype == "PERSPECTIVE": - evidence = gt.get("evidence_artifacts", []) + genesis_gaps = [] + sim_start_str = sim_cfg.get("start_date", "") + for gap in knowledge_gaps: + departed_name = gap.get("name", "Unknown") + left_str = gap.get("left", "") + doc_pct = gap.get("documented_pct", 0.5) + domains = gap.get("knew_about", []) + days_before = 0 + if sim_start_str and left_str: + try: + sim_start = datetime.strptime(sim_start_str, "%Y-%m-%d") + left_dt = datetime.strptime(left_str, "%Y-%m") + days_before = (sim_start - left_dt).days + except ValueError: + pass - rows.append( + genesis_gaps.append( { - # ── Core fields (all types) ─────────────────────────────────── - "question_id": q.get("question_id", ""), - "question_type": qtype, - "question_text": q.get("question_text", ""), - "ground_truth": json.dumps(gt, default=str), - "evidence_chain": json.dumps(evidence, default=str), - "difficulty": q.get("difficulty", ""), - "requires_reasoning": bool(q.get("requires_reasoning", False)), - # ── PERSPECTIVE-specific fields ─────────────────────────────── - "actor": q.get("actor", ""), - "actor_role": q.get("actor_role", ""), - "as_of_day": int(q.get("as_of_day", 0)), - "subsystem_access": json.dumps( - q.get("subsystem_access", []), default=str - ), - "blocked_subsystems": json.dumps( - q.get("blocked_subsystems", []), default=str - ), - "actor_visible_artifacts": json.dumps( - q.get("actor_visible_artifacts", []), default=str - ), - # ── COUNTERFACTUAL-specific fields ──────────────────────────── - "link_type": q.get("link_type", ""), - "causal_day": int(q.get("day", 0)), - # ── SILENCE-specific fields ─────────────────────────────────── - "expected_search_space": json.dumps( - q.get("expected_search_space", []), default=str - ), - "trigger_event_type": q.get("trigger_event_type", ""), - "expected_response_type": q.get("expected_response_type", ""), + "name": departed_name, + "left": left_str, + "days_before_sim": days_before, + "documented_pct": doc_pct, + "domains": domains, + "role": gap.get("role", ""), + "dept": gap.get("dept", ""), } ) - return rows - + customers = [] + vendors = [] -def _tokenize(text: str) -> List[str]: - """Simple whitespace + punctuation tokeniser.""" - return re.sub(r"[^\w\s]", " ", text.lower()).split() - - -def _mrr_at_k(ranked_ids: List[str], relevant_ids: List[str], k: int = 10) -> float: - for i, did in enumerate(ranked_ids[:k], 1): - if did in set(relevant_ids): - return 1.0 / i - return 0.0 + domain_registry_count = 0 + company_description = sim_cfg.get("company_description", "") + domain = sim_cfg.get("domain", "") + legacy_system = cfg.get("legacy_system", {}).get("name", "") + insider_threat = cfg.get("insider_threat", {}).get("enabled", False) + if mem is not None: + try: + sources_doc = mem._db["sim_config"].find_one( + {"_id": "inbound_email_sources"} + ) + sources = sources_doc.get("sources", []) if sources_doc else [] + customers = [s for s in sources if s.get("category") == "customer"] + vendors = [s for s in sources if s.get("category") == "vendor"] + except Exception: + pass + try: + domain_registry_count = mem._db["domain_registry"].count_documents({}) + except Exception: + pass -def _recall_at_k(ranked_ids: List[str], relevant_ids: List[str], k: int = 10) -> float: - if not relevant_ids: - return 1.0 - hits = sum(1 for did in ranked_ids[:k] if did in set(relevant_ids)) - return hits / len(relevant_ids) + return { + "artifact_counts": dict(sorted(artifact_counts.items(), key=lambda x: -x[1])), + "sim_event_counts": dict(sorted(sim_event_counts.items(), key=lambda x: -x[1])), + "by_type": ..., + "total": len(corpus), + "incident_docs": incident_docs, + "external_docs": external_docs, + "dept_counts": dict(sorted(dept_counts.items(), key=lambda x: -x[1])), + "unique_actors": len(actors_seen), + "sim_days_covered": len(days_seen), + "sim_events_total": sim_events_total, + "org_size": sum(len(v) for v in org_chart.values() if isinstance(v, list)), + "company": sim_cfg.get("company_name", "OrgForge Simulated Corp"), + "domain": domain, + "insider_threat": insider_threat, + "legacy_system": legacy_system, + "company_description": company_description, + "industry": sim_cfg.get("industry", "Software"), + "genesis_gaps": genesis_gaps, + "customers": customers, + "vendors": vendors, + "domain_registry_count": domain_registry_count, + "num_days": sim_days_actual or sim_cfg.get("num_days", "?"), + } -class UngatedCeilingBaseline: +class DatasetCardWriter: """ - Tier 1 baseline: BM25 and dense retrieval with ALL gates removed. - - No visibility cones, no temporal horizons, no subsystem constraints — - the retriever has "god-mode" access to the full corpus. MRR@10 and - Recall@10 represent the information ceiling, not agent performance. + Produces the HuggingFace README.md dataset card. - SILENCE questions are excluded because the correct answer is absence; - retrieval recall is not applicable. - - The delta between these ceiling scores and a gated agent's combined_score - is the "Epistemic Tax" — the difficulty cost of respecting organisational - silos and actor knowledge horizons. + Tells the story of the corpus first — what it is, why the ground truth + is trustworthy, and what makes this dataset structurally different from + other synthetic benchmarks. Artifact counts and schema follow. """ - def __init__(self, corpus: List[dict], questions: List[dict], mem=None): - self._corpus = corpus - self._questions = questions - self._mem = mem - self._doc_ids = [row["doc_id"] for row in corpus] - self._bodies = [row.get("body") or row.get("content") or "" for row in corpus] - - if _BM25_AVAILABLE: - tokenised = [_tokenize(b) for b in self._bodies] - self._bm25 = BM25Okapi(tokenised) - else: - self._bm25 = None - - if _DENSE_AVAILABLE and mem is not None: - logger.info(" Embedding corpus for dense ceiling baseline...") - embeddings = [] - for i, body in enumerate(self._bodies): - text_to_embed = ( - body.strip() if body and body.strip() else "empty document" - ) - vec = self._mem._embed(text_to_embed, input_type="search_document") - embeddings.append(vec) - if (i + 1) % 500 == 0: - logger.info(f" embedded {i + 1}/{len(self._bodies)} docs...") - mat = np.array(embeddings, dtype=np.float32) - norms = np.linalg.norm(mat, axis=1, keepdims=True) - self._dense_matrix = mat / np.where(norms == 0, 1, norms) - else: - self._dense_matrix = None - - # ── PUBLIC ──────────────────────────────────────────────────────────────── - - def run_bm25(self) -> Tuple[List[dict], Dict[str, Any]]: - if self._bm25 is None: - return [], {"error": "rank_bm25 not installed"} - return self._run_retrieval(use_dense=False) - - def run_dense(self) -> Tuple[List[dict], Dict[str, Any]]: - if self._mem is None or self._dense_matrix is None: - return [], {"error": "Memory unavailable — dense ceiling requires MongoDB"} - return self._run_retrieval(use_dense=True) + def write(self, out_path: Path, corpus: List[dict], cfg: dict, mem=None) -> None: + stats = _compute_corpus_stats(corpus, cfg, mem=mem) + card = self._render(stats, cfg) + out_path.write_text(card, encoding="utf-8") + logger.info(f" → {out_path}") # ── PRIVATE ─────────────────────────────────────────────────────────────── - def _evidence_for_question(self, q: dict) -> List[str]: - """ - Flat list of corpus doc_ids that constitute the correct answer. - SILENCE returns empty — absence has no retrievable target. - """ - qtype = q.get("question_type", "") - gt = q.get("ground_truth", {}) - - if qtype == "COUNTERFACTUAL": - chain = gt.get("evidence_chain_artifacts", {}) - return list(set(chain.get("cause", []) + chain.get("effect", []))) - - if qtype == "PERSPECTIVE": - return gt.get("evidence_artifacts", []) - - return [] - - def _rank(self, query: str, use_dense: bool, top_k: int = 10) -> List[str]: - if use_dense and self._dense_matrix is not None: - q_vec = np.array( - self._mem._embed(query, input_type="search_query"), dtype=np.float32 + def _render(self, stats: dict, cfg: dict) -> str: + sim_cfg = cfg.get("simulation", {}) + org_lifecycle = cfg.get("org_lifecycle", {}) + + company = stats["company"] + industry = stats["industry"] + domain = stats["domain"] + legacy_system = stats["legacy_system"] + insider_threat = stats["insider_threat"] + company_description = stats["company_description"] + num_days = str(stats["num_days"]) + org_size = str(stats["org_size"]) + total_docs = f"{stats['total']:,}" + incident_docs = f"{stats['incident_docs']:,}" + external_docs = f"{stats['external_docs']:,}" + unique_actors = str(stats["unique_actors"]) + sim_events_total = ( + f"{stats['sim_events_total']:,}" if stats.get("sim_events_total") else "n/a" + ) + genesis_gaps = stats["genesis_gaps"] + artifact_counts = stats.get("artifact_counts", stats["by_type"]) + sim_event_counts = stats.get("sim_event_counts", {}) + n_customers = len(stats.get("customers", [])) + n_vendors = len(stats.get("vendors", [])) + tech_stack = stats.get("tech_stack", []) + domain_reg_count = stats.get("domain_registry_count", 0) + + gap_table = self._genesis_gap_table(genesis_gaps) + artifact_table = self._artifact_count_table(artifact_counts, "Artifact") + sim_event_table = self._artifact_count_table(sim_event_counts, "SimEvent") + dept_table = self._dept_count_table(stats["dept_counts"]) + schema_table = self._corpus_schema_table(sim_event_counts) + + scheduled_departures = org_lifecycle.get("scheduled_departures", []) + scheduled_hires = org_lifecycle.get("scheduled_hires", []) + enable_attrition = org_lifecycle.get("enable_random_attrition", False) + attrition_prob = org_lifecycle.get("random_attrition_daily_prob", 0.0) + + lifecycle_lines = [] + if scheduled_departures: + lifecycle_lines.append( + f"- **{len(scheduled_departures)} scheduled departure(s)** during the sim" ) - q_vec /= max(np.linalg.norm(q_vec), 1e-9) - scores = self._dense_matrix @ q_vec - indices = scores.argsort()[::-1][:top_k] - return [self._doc_ids[int(i)] for i in indices] - - elif not use_dense and self._bm25 is not None: - scores = self._bm25.get_scores(_tokenize(query)) - indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True) - return [self._doc_ids[i] for i in indices[:top_k]] - - return [] - - def _run_retrieval(self, use_dense: bool) -> Tuple[List[dict], Dict[str, Any]]: - per_question: List[dict] = [] - by_type: Dict[str, List[Tuple[float, float]]] = defaultdict(list) - - for q in self._questions: - qtype = q.get("question_type", "") - evidence = self._evidence_for_question(q) - if not evidence: - continue - - ranked_ids = self._rank(q.get("question_text", ""), use_dense=use_dense) - mrr = _mrr_at_k(ranked_ids, evidence, k=10) - recall = _recall_at_k(ranked_ids, evidence, k=10) - - per_question.append( - { - "question_id": q.get("question_id"), - "question_type": qtype, - "difficulty": q.get("difficulty"), - "mrr_at_10": round(mrr, 4), - "recall_at_10": round(recall, 4), - "top10": ranked_ids[:10], - } + if scheduled_hires: + lifecycle_lines.append( + f"- **{len(scheduled_hires)} scheduled hire(s)** during the sim " + "(backfill hires are generated with deliberate expertise gaps, " + "creating second-order knowledge problems that play out over subsequent days)" ) - by_type[qtype].append((mrr, recall)) - - def _mean(vals): - return round(sum(vals) / len(vals), 4) if vals else 0.0 - - aggregate = { - "method": "dense" if use_dense else "bm25", - "model": _DENSE_MODEL_NAME if use_dense else "BM25Okapi (rank-bm25)", - "overall": { - "mrr_at_10": _mean([r["mrr_at_10"] for r in per_question]), - "recall_at_10": _mean([r["recall_at_10"] for r in per_question]), - "n": len(per_question), - }, - "by_type": { - qtype: { - "mrr_at_10": _mean([v[0] for v in vals]), - "recall_at_10": _mean([v[1] for v in vals]), - "n": len(vals), - } - for qtype, vals in by_type.items() - }, - } - return per_question, aggregate - - -class StaticReasoningMetrics: - """ - Tier 2 baseline: reasoning difficulty metrics derived from corpus + - question metadata alone. No LLM calls, no agent execution required. - - These metrics answer "why is each track hard?" before any agent runs, - exposing the epistemic structure that makes naive retrieval insufficient. - - PERSPECTIVE → horizon_contamination_rate - Fraction of ungated BM25 top-20 that falls outside the actor's - visibility cone. High value = epistemic discipline is load-bearing; - retrieval alone surfaces mostly forbidden documents. - - COUNTERFACTUAL → causal_chain_traceable - Whether cause AND effect artifacts both appear in ungated top-10. - False = a single retrieval pass cannot close the causal chain; - multi-hop reasoning is required. - - SILENCE → search_space_bm25_coverage - Fraction of expected_search_space locations surfaced by BM25 on - the question text. Low value = the agent must enumerate absence-check - locations deliberately; naive search will miss them. - """ - - def __init__( - self, - questions: List[dict], - bm25, # BM25Okapi instance reused from UngatedCeilingBaseline - doc_ids: List[str], - ): - self._questions = questions - self._bm25 = bm25 - self._doc_ids = doc_ids - - def compute(self) -> dict: - per_question: List[dict] = [] - by_type: Dict[str, List[dict]] = defaultdict(list) - - dispatch = { - "PERSPECTIVE": self._perspective_metrics, - "COUNTERFACTUAL": self._counterfactual_metrics, - "SILENCE": self._silence_metrics, - } - - for q in self._questions: - qtype = q.get("question_type", "") - fn = dispatch.get(qtype) - if fn is None: - continue - row = { - "question_id": q.get("question_id"), - "question_type": qtype, - **fn(q), - } - per_question.append(row) - by_type[qtype].append(row) - - return { - "per_question": per_question, - "aggregate": self._aggregate(by_type), - } - - # ── Track-specific metric computations ─────────────────────────────────── - - def _perspective_metrics(self, q: dict) -> dict: - """ - Horizon contamination: what fraction of ungated top-20 results would - an actor NOT be permitted to see? High contamination means a naive - retriever is actively counter-productive for PERSPECTIVE questions. - """ - visible = set(q.get("actor_visible_artifacts", [])) - ranked = self._rank_bm25(q["question_text"], k=20) - if not ranked or not visible: - return { - "horizon_contamination_rate": None, - "first_in_cone_rank": None, - "in_cone_count_top20": None, - } + if enable_attrition: + lifecycle_lines.append( + f"- **Random attrition** enabled at {attrition_prob:.1%} daily probability" + ) + lifecycle_summary = ( + "\n".join(lifecycle_lines) + if lifecycle_lines + else "- No mid-sim departures or hires configured" + ) - out_of_cone = [r for r in ranked if r not in visible] - first_in_cone = next( - (i + 1 for i, r in enumerate(ranked) if r in visible), None + _fm = ( + "---\n" + "language:\n" + "- en\n" + "license: mit\n" + "configs:\n" + "- config_name: default\n" + " data_files:\n" + " - split: train\n" + ' path: "corpus/*.parquet"\n' + "task_categories:\n" + "- question-answering\n" + "- text-retrieval\n" + "- text-generation\n" + "- summarization\n" + "- text-classification\n" + "task_ids:\n" + "- open-domain-qa\n" + "- closed-domain-qa\n" + "- abstractive-qa\n" + "- open-domain-abstractive-qa\n" + "- document-retrieval\n" + "- fact-checking-retrieval\n" + "- dialogue-modeling\n" + "- explanation-generation\n" + "- multi-label-classification\n" + "- fact-checking\n" + "tags:\n" + "- rag\n" + "- enterprise\n" + "- synthetic\n" + "- orgforge\n" + "- causal-reasoning\n" + "- temporal-reasoning\n" + "- knowledge-graphs\n" + "- agentic-eval\n" + f'pretty_name: "OrgForge — {company} Enterprise Corpus"\n' + "size_categories:\n" + "- 1K dict: - """ - Causal chain traceability: do both cause and effect artifacts appear - in ungated top-10? If not, the agent must do multi-hop retrieval. - """ - chain = q.get("ground_truth", {}).get("evidence_chain_artifacts", {}) - cause_ids = set(chain.get("cause", [])) - effect_ids = set(chain.get("effect", [])) - if not cause_ids and not effect_ids: - return {"causal_chain_traceable": None} - - ranked = self._rank_bm25(q["question_text"], k=10) - ranked_set = set(ranked) - cause_found = bool(cause_ids & ranked_set) - effect_found = bool(effect_ids & ranked_set) - - cause_rank = next( - (i + 1 for i, r in enumerate(ranked) if r in cause_ids), None + sections = [] + + # ── Title + pitch ────────────────────────────────────────────────────── + sections.append(f"# OrgForge — {company} Enterprise Corpus") + sections.append("") + sections.append("![OrgForge corpus overview](orgforge_dataset_hero.png)") + sections.append("") + sections.append( + "OrgForge generates synthetic but causally grounded enterprise corpora from a\n" + "deterministic simulation engine. Every artifact in this dataset — Jira tickets,\n" + "Slack threads, Confluence pages, customer emails, Zendesk tickets, invoices, Zoom\n" + "transcripts, Datadog alerts — traces back to a single event log. No LLM invented\n" + "any facts. The state machine controls what happened; LLMs only wrote the prose." ) - effect_rank = next( - (i + 1 for i, r in enumerate(ranked) if r in effect_ids), None + sections.append("") + + # ── Why it exists ────────────────────────────────────────────────────── + sections.append("## Why it exists") + sections.append("") + sections.append( + "Evaluating agents that reason over institutional knowledge requires a corpus where\n" + "the ground truth is not just *present* but *verifiable*. You need to know not just\n" + "what the correct answer is, but why it is correct, when it became correct, and what\n" + "changed it. Existing synthetic datasets generate plausible-looking documents with no\n" + "guarantee of consistency across artifacts or time. OrgForge produces something\n" + "structurally different: a corpus where every fact has a cause, every cause has a\n" + "timestamp, and every timestamp connects to a retrievable artifact." ) - - return { - "cause_found_top10": cause_found, - "effect_found_top10": effect_found, - "causal_chain_traceable": cause_found and effect_found, - "cause_rank": cause_rank, - "effect_rank": effect_rank, - } - - def _silence_metrics(self, q: dict) -> dict: - """ - Search space BM25 coverage: what fraction of the required absence-check - locations does a naive BM25 search surface? Low coverage means the agent - must enumerate expected_search_space explicitly rather than relying on - retrieval to guide it to the right places to look. - """ - expected = q.get("expected_search_space", []) - if not expected: - return {"search_space_bm25_coverage": 1.0, "uncovered_locations": []} - - ranked = self._rank_bm25(q["question_text"], k=20) - - def _norm(s: str) -> str: - # Normalise path-style entries to their terminal component: - # "confluence/postmortems/IT-108" → "it-108" - return s.strip("/").split("/")[-1].lower() - - norm_expected = {_norm(e): e for e in expected} - norm_ranked = {_norm(r) for r in ranked} - ranked_lower = [r.lower() for r in ranked] - - covered = { - original - for norm_term, original in norm_expected.items() - if norm_term in norm_ranked - or any(norm_term in r for r in ranked_lower) - } - - return { - "search_space_bm25_coverage": round(len(covered) / len(expected), 4), - "uncovered_locations": sorted(set(expected) - covered), - } - - # ── Shared utilities ────────────────────────────────────────────────────── - - def _rank_bm25(self, query: str, k: int) -> List[str]: - if self._bm25 is None: - return [] - scores = self._bm25.get_scores(_tokenize(query)) - indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True) - return [self._doc_ids[i] for i in indices[:k]] - - def _aggregate(self, by_type: Dict[str, List[dict]]) -> dict: - def _mean(vals: list) -> float: - filtered = [v for v in vals if v is not None] - return round(sum(filtered) / len(filtered), 4) if filtered else 0.0 - - agg: dict = {} - - if rows := by_type.get("PERSPECTIVE", []): - agg["PERSPECTIVE"] = { - "n": len(rows), - "avg_horizon_contamination_rate": _mean( - [r.get("horizon_contamination_rate") for r in rows] - ), - "avg_first_in_cone_rank": _mean( - [r.get("first_in_cone_rank") for r in rows] - ), - "interpretation": ( - "High contamination = retrieval surfaces many out-of-cone docs; " - "epistemic discipline is load-bearing, not optional." - ), - } - - if rows := by_type.get("COUNTERFACTUAL", []): - pct_traceable = _mean( - [1.0 if r.get("causal_chain_traceable") else 0.0 for r in rows] + sections.append("") + sections.append( + f"This dataset is the output of a **{num_days}-day simulation** of **{company}**, a\n" + f"{industry} company which {company_description} with ~{org_size} employees. It is not a random walk through\n" + "enterprise activity — it was seeded with specific organizational crises and simulated\n" + "through to their resolution." + ) + sections.append("") + + # ── What makes it different ──────────────────────────────────────────── + sections.append("## What makes this corpus structurally different") + sections.append("") + sections.append( + "**Causal grounding.** Every artifact is downstream of a SimEvent. A Jira ticket,\n" + "the Slack thread that opened alongside it, the Confluence postmortem written the\n" + "next day, and the Zendesk tickets that escalated from the same incident all share\n" + "a causal ancestor. Cross-referencing between artifact types is not coincidental —\n" + "it reflects the actual information flow the simulation produced." + ) + sections.append("") + sections.append( + "**Temporal coherence.** Facts change over the simulation. An engineer present on\n" + "Day 1 is gone on Day 12. Ticket ownership, domain coverage scores, relationship\n" + "graph edge weights, and customer sentiment all evolve. The correct answer to a\n" + "question about org state depends on what day it is asked relative to the timeline.\n" + "Every corpus row carries a day, date, and timestamp accurate to the millisecond\n" + "the underlying event fired." + ) + sections.append("") + sections.append( + "**Verifiable ground truth.** The simulation snapshot and domain registry ship\n" + "alongside the corpus as structured reference files (see Supplemental Files). For\n" + "any question the corpus can raise — who owned this domain when this incident fired,\n" + "which customer was affected, what was the system health on the day this postmortem\n" + "was written — the answer exists as a queryable record independent of the text. You\n" + "do not need to parse the corpus to build your eval set." + ) + sections.append("") + sections.append( + "**Pre-simulation history.** The genesis knowledge gaps in this corpus pre-date the\n" + "simulation by months or years. An agent asked why a Day 15 postmortem surfaces a\n" + "specific knowledge gap must trace: current incident → semantic similarity match →\n" + "departed employee persona → genesis event dated before sim start. That causal chain\n" + "crosses a temporal boundary that does not exist in any other synthetic enterprise\n" + "dataset we are aware of." + ) + sections.append("") + sections.append( + "**State-driven external communication.** Customer emails, vendor alerts, and\n" + "Zendesk tickets are generated from actual simulation conditions, not randomly\n" + "sampled. Each external contact has a `depends_on_components` list mapped to the\n" + "tech stack — an outage to a component a customer depends on is what triggers their\n" + "email. Approximately 15% of customer emails are deliberately dropped with no action,\n" + "leaving ground-truth absences in the event log that an agent must detect through\n" + "negative evidence rather than positive retrieval." + ) + sections.append("") + sections.append( + "**Persona-consistent prose.** Every artifact is written by a character with a\n" + "specific tenure, stress level, writing style, and live CRM context. A Slack message\n" + "from an engineer during a contract negotiation reads differently from one written by\n" + "the same person on a quiet day. Stylometric and behavioral signals in the text\n" + "reflect the org's state at the moment of writing, not random LLM variation." + ) + sections.append("") + + # ── Use cases ────────────────────────────────────────────────────────── + sections.append("## Use cases") + sections.append("") + sections.append( + "- **Agentic reasoning** — tasks that require traversing causal chains across\n" + " artifact types, time, and org boundaries rather than finding a single relevant\n" + " document\n" + "- **Multi-hop question answering** — questions whose correct answer requires\n" + " joining facts from Jira, Confluence, Slack, CRM, and the simulation ground truth\n" + "- **Temporal reasoning** — questions where the correct answer depends on what day\n" + " they are asked relative to the simulation timeline\n" + "- **RAG pipeline evaluation** — a corpus with known causal structure allows\n" + " precise measurement of what a retrieval system found versus what it needed to\n" + " find to answer correctly\n" + "- **Org dynamics and knowledge loss research** — the simulation snapshot exposes\n" + " how knowledge concentration, engineer departure, and incident causation interact\n" + " over time in a controlled, reproducible setting" + ) + sections.append("") + + # ── Scope ────────────────────────────────────────────────────────────── + sections.append("## Scope and limitations") + sections.append("") + sections.append( + "This is not a dataset of real corporate communications. The company, employees,\n" + "customers, and vendors are entirely fictional. The simulation models organizational\n" + "behavior at the structural level — stress, knowledge concentration, incident\n" + "causation, relationship graph dynamics — but does not model everything. Affect,\n" + "politics, ambiguity, and the texture of real human communication are present only\n" + "to the extent that the persona and mood system introduces them through LLM-generated\n" + "prose. Researchers should treat this as a controlled benchmark environment, not a\n" + "proxy for real enterprise data." + ) + sections.append("") + sections.append("---") + sections.append("") + + # ── Genesis knowledge gaps ───────────────────────────────────────────── + sections.append("## Genesis Knowledge Gaps") + sections.append("") + sections.append( + "These gaps pre-date the simulation. They are the structural cause of the\n" + "organizational narrative in this corpus. Each departed employee's domains entered\n" + "Day 1 as orphaned — undocumented, unowned, and detectable only through semantic\n" + "similarity when new incidents touch the same systems." + ) + sections.append("") + if legacy_system: + sections.append( + "\n\n" + f"The primary technical fault line in this corpus is **{legacy_system}**, a " + f"{cfg.get('legacy_system', {}).get('description', 'legacy system')} whose " + f"instability is the proximate cause of most incidents during the simulation." ) - agg["COUNTERFACTUAL"] = { - "n": len(rows), - "pct_causal_chain_traceable_top10": pct_traceable, - "pct_requires_multi_hop": round(1.0 - pct_traceable, 4), - "interpretation": ( - "Low traceability = causal link cannot be closed by a single " - "retrieval pass; agent must traverse cause → effect explicitly." - ), - } - - if rows := by_type.get("SILENCE", []): - agg["SILENCE"] = { - "n": len(rows), - "avg_search_space_bm25_coverage": _mean( - [r.get("search_space_bm25_coverage") for r in rows] - ), - "pct_fully_covered": _mean( - [ - 1.0 - if r.get("search_space_bm25_coverage", 0) >= 1.0 - else 0.0 - for r in rows - ] - ), - "interpretation": ( - "Low coverage = BM25 misses required absence-check locations; " - "agent must enumerate expected_search_space explicitly." - ), - } - - return agg - - -# ───────────────────────────────────────────────────────────────────────────── -# DATASET CARD WRITER -# ───────────────────────────────────────────────────────────────────────────── - - -class DatasetCardWriter: - """Produces the HuggingFace README.md dataset card for the v2 eval.""" - - def write( - self, - out_path: Path, - corpus: List[dict], - questions: List[dict], - causal_links: List[dict], - actor_visibility: dict, - absence_catalog: List[dict], - baseline_summary: dict, - cfg: dict, - ) -> None: - card = self._render( - corpus, - questions, - causal_links, - actor_visibility, - absence_catalog, - baseline_summary, - cfg, + sections.append("") + sections.append(gap_table) + sections.append("") + sections.append("---") + sections.append("") + + # ── Org lifecycle ────────────────────────────────────────────────────── + sections.append("## Org Lifecycle") + sections.append("") + sections.append(lifecycle_summary) + sections.append("") + sections.append("---") + sections.append("") + + # ── Corpus summary ───────────────────────────────────────────────────── + sections.append("## Corpus Summary") + sections.append("") + sections.append("| Property | Value |") + sections.append("|---|---|") + sections.append(f"| Company | {company} |") + sections.append(f"| Description | {company} {company_description} |") + sections.append(f"| Domain | {domain} |") + sections.append(f"| Industry | {industry} |") + sections.append(f"| Simulation days | {num_days} |") + sections.append(f"| Org size | ~{org_size} employees |") + sections.append(f"| Customers | {n_customers} |") + sections.append(f"| Vendors | {n_vendors} |") + sections.append(f"| Total corpus documents | {total_docs} |") + sections.append(f"| Total SimEvents | {sim_events_total} |") + sections.append(f"| Incident-related documents | {incident_docs} |") + sections.append(f"| External-origin documents | {external_docs} |") + sections.append(f"| Unique actors | {unique_actors} |") + sections.append(f"| Domain registry entries | {domain_reg_count} |") + if tech_stack: + sections.append(f"| Tech stack | {', '.join(str(t) for t in tech_stack)} |") + sections.append("") + sections.append("### Artifacts") + sections.append("") + sections.append(artifact_table) + sections.append("") + sections.append("### SimEvents (internal state-machine records)") + sections.append("") + sections.append( + "SimEvents are the ground-truth event log entries that produced the artifacts above.\n" + "They are included in the corpus as separately retrievable records for researchers\n" + "who want the state-machine view alongside the prose artifacts." ) - out_path.write_text(card, encoding="utf-8") - logger.info(f" → {out_path}") - - # ── PRIVATE ─────────────────────────────────────────────────────────────── - - def _render( - self, - corpus: List[dict], - questions: List[dict], - causal_links: List[dict], - actor_visibility: dict, - absence_catalog: List[dict], - baseline_summary: dict, - cfg: dict, - ) -> str: - sim_cfg = cfg.get("simulation", {}) - num_days = sim_cfg.get("num_days", "?") - org_chart = cfg.get("org_chart", {}) - org_size = sum(len(v) for v in org_chart.values() if isinstance(v, list)) - company = sim_cfg.get("company_name", "OrgForge Simulated Corp") - industry = sim_cfg.get("industry", "Software") - num_sprints = sim_cfg.get("num_sprints", "?") - - # Corpus breakdown - by_type: Dict[str, int] = defaultdict(int) - for row in corpus: - by_type[row["doc_type"]] += 1 - - # Question breakdown - by_qtype: Dict[str, int] = defaultdict(int) - by_diff: Dict[str, int] = defaultdict(int) - for q in questions: - by_qtype[q.get("question_type", "?")] += 1 - by_diff[q.get("difficulty", "?")] += 1 - - # Causal link breakdown - by_link: Dict[str, int] = defaultdict(int) - for lnk in causal_links: - by_link[lnk.get("link_type", "?")] += 1 - - # Eval index counts - n_actors = len(actor_visibility) - n_cone_snapshots = sum(len(v) for v in actor_visibility.values()) - - # Baseline tables — two-tier system - ceiling = baseline_summary.get("ungated_ceiling", {}) - bm25_section = self._ungated_ceiling_table(ceiling.get("bm25", {})) - dense_section = self._ungated_ceiling_table(ceiling.get("dense", {})) - reasoning_section = self._reasoning_metrics_table( - baseline_summary.get("static_reasoning_metrics", {}) + sections.append("") + sections.append(sim_event_table) + sections.append("") + sections.append("### By department") + sections.append("") + sections.append(dept_table) + sections.append("") + sections.append("---") + sections.append("") + + # ── Supplemental files ───────────────────────────────────────────────── + sections.append("## Supplemental Files") + sections.append("") + sections.append( + "The corpus parquet contains the retrievable text artifacts. The following files\n" + "ship alongside it for eval construction, ground-truth lookups, and time-series\n" + "analysis. They are in `supplemental/`." ) - - return textwrap.dedent(f"""\ - --- - language: - - en - license: mit - configs: - - config_name: default - data_files: - - split: train - path: "**/*.parquet" - task_categories: - - question-answering - - text-retrieval - task_ids: - - extractive-qa - - document-retrieval - tags: - - rag - - enterprise - - synthetic - - orgforge - - causal-reasoning - - temporal-reasoning - - epistemic-reasoning - - agentic-eval - pretty_name: "OrgForge Enterprise Agentic RAG Benchmark" - size_categories: - - 1K A synthetic but causally-grounded benchmark for evaluating agentic RAG - > systems against realistic enterprise knowledge bases — with explicit - > trajectory scoring for epistemic discipline, causal reasoning, and - > absence verification. - - ## Dataset Summary - - This dataset was produced by **OrgForge**, an event-driven organisation - simulator that generates weeks of realistic enterprise activity — JIRA - tickets, Confluence pages, Slack threads, zoom transcripts, emails, PRs, Zendesk tickets, - and Salesforce records — in a controlled, reproducible way. - - All ground-truth answers are derived **deterministically** from the - simulation's event log via three purpose-built indexes: - - **Actor visibility cones** — what each actor could have known at each moment - - **Causal link index** — explicit cause→effect relationships encoded by the sim - - **Absence catalog** — expected-but-absent artifacts confirmed by the state machine - - No LLM invented any answer. LLMs only wrote question prose. - - | Property | Value | - |---|---| - | Company | {company} | - | Industry | {industry} | - | Simulation days | {num_days} | - | Sprints simulated | {num_sprints} | - | Org size (engineers + staff) | ~{org_size} | - | Total corpus documents | {len(corpus):,} | - | Total eval questions | {len(questions):,} | - | Causal links indexed | {len(causal_links):,} | - | Actors with visibility cones | {n_actors} | - | Visibility cone snapshots | {n_cone_snapshots:,} | - | Absence records | {len(absence_catalog):,} | - - ## Corpus - - Each document represents a real artifact produced by the simulation. - Stored in `corpus/corpus-00000.parquet`. - - | Artifact type | Count | - |---|---| - {self._table_rows(by_type)} - - ### Corpus Schema - - | Column | Type | Description | - |---|---|---| - | `doc_id` | str | Unique artifact ID (e.g. `IT-042`, `CONF-ENG-007`) | - | `doc_type` | str | `jira`, `confluence`, `slack`, `email`, `pr`, `zd_ticket`, `sf_opp`, `sf_account`, `sim_event` | - | `title` | str | Human-readable title or subject | - | `body` | str | Full retrievable text | - | `day` | int | Simulation day (1-indexed) | - | `date` | str | ISO date | - | `timestamp` | str | ISO datetime (business-hours-accurate) | - | `actors` | str | JSON list of actor names | - | `tags` | str | JSON list of semantic tags | - | `artifact_ids` | str | JSON dict of cross-references | - | `dept` | str | Owning department | - | `is_incident` | bool | True if part of an incident thread | - | `is_external` | bool | True for inbound external content | - - ## Eval Questions - - Questions are in `questions/questions-00000.parquet`. - - | Question type | Count | - |---|---| - {self._table_rows(by_qtype)} - - | Difficulty | Count | - |---|---| - {self._table_rows(by_diff)} - - ### Question Tracks - - | Track | Description | Score weights (answer / trajectory) | - |---|---|---| - | `PERSPECTIVE` | Could actor X have known about event Y as of Day N, given their subsystem access? | 0.40 / 0.60 | - | `COUNTERFACTUAL` | If condition X had been different, would outcome Y have occurred? | 0.50 / 0.50 | - | `SILENCE` | Was artifact X actually created in response to trigger Y? (correct answer: no) | 0.30 / 0.70 | - - #### PERSPECTIVE - Scored primarily on **epistemic discipline**: did the agent stay within the - actor's visibility cone and access only permitted subsystems? Trajectory - weight (0.60) exceeds answer weight (0.40) because using out-of-cone - artifacts to reach the correct answer is still a failure mode. - - #### COUNTERFACTUAL - Requires identifying the **explicit causal link** encoded by the simulation - (`involves_gap`, `recurrence_of`, `spawned_doc`, `email_dropped`, - `sf_ownership_lapsed`, `zd_escalation_source`, `blocker_flagged`, - `incident_coordination`, `departure_reassignment`). No inference — the - link must be traceable to real artifacts. - - #### SILENCE - Tests **absence-of-evidence reasoning**. Trajectory weight is highest - (0.70) because a correct "no" answer reached without searching - `expected_search_space` scores 0 on trajectory even if the boolean - is right. The agent must demonstrate it checked the right places. - - > **Retrieval baselines** are reported for PERSPECTIVE and COUNTERFACTUAL - > only. SILENCE questions test absence — standard retrieval recall is not - > applicable because the correct answer is that the artifact does not exist. - - ### Question Schema - - | Column | Type | Description | - |---|---|---| - | `question_id` | str | Unique question identifier | - | `question_type` | str | `PERSPECTIVE`, `COUNTERFACTUAL`, or `SILENCE` | - | `question_text` | str | Natural-language question | - | `ground_truth` | str | JSON-serialised answer dict | - | `evidence_chain` | str | JSON list of artifact IDs (empty for SILENCE) | - | `difficulty` | str | `medium` or `hard` | - | `requires_reasoning` | bool | Always True — all tracks require multi-step reasoning | - | `actor` | str | PERSPECTIVE: actor whose knowledge horizon is tested | - | `actor_role` | str | PERSPECTIVE: actor's role slug | - | `as_of_day` | int | PERSPECTIVE: knowledge horizon day | - | `subsystem_access` | str | PERSPECTIVE: JSON list of accessible subsystems | - | `blocked_subsystems` | str | PERSPECTIVE: JSON list of blocked subsystems | - | `actor_visible_artifacts` | str | PERSPECTIVE: JSON list of all visible artifact IDs | - | `link_type` | str | COUNTERFACTUAL: causal link type | - | `causal_day` | int | COUNTERFACTUAL: day the causal link was established | - | `expected_search_space` | str | SILENCE: JSON list of artifact paths agent must check | - | `trigger_event_type` | str | SILENCE: event type that should have triggered the response | - | `expected_response_type` | str | SILENCE: artifact/event type that was never created | - - ## Eval Indexes - - Stored in `eval_indexes/`. These are the ground-truth indexes that back - question generation — useful for building custom eval harnesses. - - ### causal_link_index.parquet - - One row per explicit causal link found in the simulation. - - | Column | Type | Description | - |---|---|---| - | `link_type` | str | One of the causal link types above | - | `cause_event_id` | str | Synthetic event ID of the cause | - | `cause_event_type` | str | Event type of the cause | - | `effect_event_id` | str | Synthetic event ID of the effect | - | `effect_event_type` | str | Event type of the effect | - | `actors` | str | JSON list of involved actor names | - | `day` | int | Simulation day the link was established | - | `counterfactual_premise` | str | Natural-language "if X had been different" | - | `counterfactual_outcome` | str | Natural-language "then Y would have..." | - | `outcome_changed` | bool | Always True — removing cause changes outcome | - - | Link type | Count | - |---|---| - {self._table_rows(by_link)} - - ### actor_visibility.parquet - - One row per (actor, day) snapshot. - - | Column | Type | Description | - |---|---|---| - | `actor` | str | Actor name | - | `role` | str | Role slug | - | `as_of_day` | int | Snapshot day | - | `subsystem_access` | str | JSON list of accessible subsystems | - | `all_visible_artifacts` | str | JSON list of all artifact IDs visible to this actor on this day | - | `visible_artifacts_by_subsystem` | str | JSON dict (subsystem → artifact IDs) | - | `directly_involved` | str | JSON list of artifacts where actor was in event.actors | - | `broadcast_visible` | str | JSON list of artifacts visible via broadcast channel | - - ### absence_catalog.parquet - - One row per expected-but-absent artifact pair. - - | Column | Type | Description | - |---|---|---| - | `trigger_event_id` | str | Event that should have triggered a response | - | `trigger_event_type` | str | Type of trigger event | - | `expected_response_type` | str | Type of artifact that was never created | - | `trigger_day` | int | Day the trigger event fired | - | `expected_search_space` | str | JSON list of artifact paths agent must check | - - ## Baselines and Reasoning Difficulty - - OrgForge uses a **two-tier baseline** system, computed entirely from - corpus metadata — no agent execution required. - - - **Tier 1 (Ungated Retrieval Ceiling):** What is the information ceiling - if all gates are removed? This is "god-mode" retrieval, and the gap - between it and a gated agent's score is the **Epistemic Tax**. - - **Tier 2 (Static Reasoning Difficulty):** Why is each track hard, - independent of any agent? These metrics characterise the epistemic - structure before any model runs. - - Agent-level baselines (ungated god-mode agent, zero-shot no-tools) - require LLM calls and are available as flags in `agentic_eval_harness.py`: - `--ungated` and `--zero-shot`. - - --- - - ### Tier 1 — Ungated Retrieval Ceiling - - BM25 and dense retrieval with **no gates**: no visibility cones, no - temporal horizons, no subsystem constraints ("god-mode" corpus access). - - The **Epistemic Tax** for a track is: - - ``` - epistemic_tax = ceiling_mrr@10 − gated_agent_combined_score - ``` - - A high tax on `PERSPECTIVE` means the question set heavily penalises - using information the actor was never supposed to have. - - #### BM25 (Okapi BM25 via rank-bm25) - - {bm25_section} - - #### Dense Retrieval (`{_DENSE_MODEL_NAME}`) - - {dense_section} - - --- - - ### Tier 2 — Static Reasoning Difficulty Metrics - - Computed from corpus metadata and question ground-truth alone — no agents, - no LLM calls required. These metrics characterise the epistemic structure - of each question before any agent touches it. - - {reasoning_section} - - --- - - ### How to beat these baselines - - | Track | To beat the ceiling... | - |---|---| - | `PERSPECTIVE` | Achieve a `violation_adjusted_combined_score` above the ceiling MRR@10 **while** keeping `avg_actor_gate_violations` near 0. High score + high violations = the agent is cheating. | - | `COUNTERFACTUAL` | Correctly identify the `causal_mechanism` for questions where `pct_requires_multi_hop = 1.0` — these cannot be answered by retrieval alone. | - | `SILENCE` | Cover `expected_search_space` exhaustively before concluding. `avg_search_space_bm25_coverage` shows how little a naive search covers — the agent must enumerate the rest deliberately. | - - --- - - ## Agentic Evaluation - - Use `agentic_eval_harness.py` to run a gated agent against the full - question set. The harness enforces temporal and actor visibility gates - per question type, logs the complete tool-call trajectory, and scores - both answer quality and trajectory quality. - - ```bash - # Standard gated evaluation - python agentic_eval_harness.py \\ - --questions export/eval/eval_questions.json \\ - --out export/eval/agentic_results.json \\ - --model claude-sonnet-4-6 \\ - --max-steps 15 - - # Ungated god-mode agent — establishes the Epistemic Tax denominator - python agentic_eval_harness.py \\ - --ungated \\ - --out export/eval/ungated_results.json - - # Zero-shot — no tools, no corpus — establishes the hallucination floor - python agentic_eval_harness.py \\ - --zero-shot \\ - --out export/eval/zero_shot_results.json - ``` - - ## Leaderboard - - Submissions are ranked by `violation_adjusted_combined_score` on the - **PERSPECTIVE** track. This is the primary axis because PERSPECTIVE is - the only track with a hard behavioral constraint (actor visibility cone) - that a capable-but-undisciplined agent can violate while still scoring - high on raw accuracy. - - ### Ranking Formula - - ``` - violation_rate = total_actor_gate_violations / total_tool_calls - compliance_factor = max(0, 1 − violation_rate) ** 2 - violation_adjusted_score = combined_score × compliance_factor - ``` - - The quadratic exponent means violations compound non-linearly: - - | Violation rate | Compliance factor | Effective score discount | - |---|---|---| - | 0% (fully compliant) | 1.00 | None | - | 10% | 0.81 | 19% | - | 25% | 0.56 | 44% | - | 50% | 0.25 | 75% | - | 75% | 0.06 | 94% | - - ### Compliance Tiers - - | Tier | Violation rate | Meaning | - |---|---|---| - | `compliant` | < 5% | Agent demonstrates genuine epistemic discipline | - | `borderline` | 5–20% | Agent occasionally accesses out-of-cone information | - | `non_compliant` | > 20% | Agent is effectively operating in god-mode on PERSPECTIVE | - - > `combined_score` is still reported for reference but **must not** be - > used as the primary ranking key. A model scoring 0.90 combined with a - > 50% violation rate has a `violation_adjusted_combined_score` of 0.225 - > and belongs in `non_compliant` — below a model scoring 0.70 combined - > with 0% violations (`violation_adjusted_combined_score` = 0.70, tier: - > `compliant`). - - ## Citation - - ```bibtex - @misc{{orgforge2026, - title = {{OrgForge: A Multi-Agent Simulation Framework for Verifiable Synthetic Corporate Corpora}}, - author = {{Jeffrey Flynt}}, - year = {{2026}}, - note = {{Synthetic benchmark generated by the OrgForge simulator v2}} - }} - ``` - - ## License - - MIT. The simulation engine that produced this dataset is independently - licensed; see the OrgForge repository for details. - """) - - def _table_rows(self, d: Dict[str, int]) -> str: - return "\n ".join( - f"| `{k}` | {v:,} |" for k, v in sorted(d.items(), key=lambda x: -x[1]) + sections.append("") + sections.append( + "**`simulation_snapshot.json`** — Full org state at simulation end: incidents with\n" + "open/resolve timestamps, morale curve, daily system health scores, relationship\n" + "graph edge weights, departed employees, new hires, and knowledge gap events. This\n" + "is the oracle for eval construction. Use it to build questions with verifiable\n" + "answers without parsing the corpus." + ) + sections.append("") + sections.append( + "**`assignment_scores.parquet`** — Per-sprint ticket assignment decisions with full\n" + "scoring breakdown: skill match (embedding cosine similarity), inverse stress, \n" + "betweenness centrality penalty, recency bonus, and composite score. One row per\n" + "(engineer, ticket, day) triple. Useful for eval questions about whether assignments\n" + "were optimal given org state at the time." + ) + sections.append("") + sections.append( + "**`domain_registry.json`** — Snapshot of all knowledge domains: owner history,\n" + "documentation coverage scores at each sim day, orphan status, and which incidents\n" + "triggered semantic similarity matches against each domain. Joinable to corpus rows\n" + "via the Confluence `doc_id` values that cover each domain." + ) + sections.append("") + sections.append( + "**`sim_config.json`** — Reference record for the org configuration: full customer\n" + "and vendor profiles (including `depends_on_components`, `sentiment_baseline`,\n" + "`trigger_on` conditions, and `persona_archetype`), tech stack, and org structure.\n" + "Useful for understanding why specific external communications were generated." + ) + sections.append("") + sections.append( + "**`datadog_metrics.parquet`** — Time-series telemetry at 15-minute intervals\n" + "across the simulation. Schema: `timestamp`, `metric_name`, `value`, `day`,\n" + "`alert_firing` (bool). Kept separate from the corpus because individual metric\n" + "ticks are not retrievable text documents. Datadog *alerts* are in the main corpus\n" + "as `doc_type: datadog_alert` and link back to incidents via `artifact_ids`." + ) + sections.append("") + sections.append("---") + sections.append("") + + # ── Schema ───────────────────────────────────────────────────────────── + sections.append("## Corpus Schema") + sections.append("") + sections.append( + "Stored in `corpus/corpus-00000.parquet`. One row per document." + ) + sections.append("") + sections.append(schema_table) + sections.append("") + sections.append("---") + sections.append("") + + # ── Usage ────────────────────────────────────────────────────────────── + sections.append("## Usage") + sections.append("") + sections.append("```python") + sections.append("from datasets import load_dataset") + sections.append("import json") + sections.append("") + sections.append('ds = load_dataset("aeriesec/orgforge")') + sections.append('corpus = ds["train"]') + sections.append("") + sections.append("# All incident-related documents") + sections.append('incidents = corpus.filter(lambda x: x["is_incident"])') + sections.append("") + sections.append("# All artifacts of a specific type") + sections.append('jira = corpus.filter(lambda x: x["doc_type"] == "jira")') + sections.append( + 'zoom = corpus.filter(lambda x: x["doc_type"] == "zoom_transcript")' + ) + sections.append( + 'alerts = corpus.filter(lambda x: x["doc_type"] == "datadog_alert")' + ) + sections.append("") + sections.append("# All documents involving a specific actor") + sections.append("actor_docs = corpus.filter(") + sections.append(' lambda x: "Jordan" in json.loads(x["actors"])') + sections.append(")") + sections.append("") + sections.append("# All documents from a specific sim day") + sections.append('day_5 = corpus.filter(lambda x: x["day"] == 5)') + sections.append("") + sections.append( + "# Cross-reference: find the Confluence postmortem linked to a Jira ticket" + ) + sections.append("def get_linked(corpus, doc_id, link_type):") + sections.append(' source = [r for r in corpus if r["doc_id"] == doc_id][0]') + sections.append( + ' linked_id = json.loads(source["artifact_ids"]).get(link_type, "")' + ) + sections.append(' return [r for r in corpus if r["doc_id"] == linked_id]') + sections.append("```") + sections.append("") + sections.append( + "The `artifact_ids` column is a JSON dict linking each document to related\n" + "artifacts produced from the same SimEvent. An incident ticket will carry\n" + "references to the Slack thread, PR, Confluence postmortem, and Datadog alert\n" + "that share its causal ancestor, allowing full chain reconstruction without\n" + "text matching." + ) + sections.append("") + sections.append("---") + sections.append("") + + # ── Citation + license ───────────────────────────────────────────────── + sections.append("## Citation") + sections.append("") + sections.append("If you use the OrgForge methodology or simulator, cite the paper:") + sections.append("") + sections.append("```bibtex") + sections.append("@misc{flynt2026orgforge,") + sections.append(" title = {OrgForge: A Multi-Agent Simulation Framework for Verifiable Synthetic Corporate Corpora},") + sections.append(" author = {Jeffrey Flynt},") + sections.append(" year = {2026},") + sections.append(" url = {https://arxiv.org/abs/2603.14997},") + sections.append(" note = {arXiv:2603.14997}") + sections.append("}") + sections.append("```") + sections.append("") + sections.append("If you use this dataset directly, cite the dataset:") + sections.append("") + sections.append("```bibtex") + sections.append("@misc{flynt2026orgforgedata,") + sections.append(f" title = {{OrgForge — {company} Enterprise Corpus}},") + sections.append(" author = {Jeffrey Flynt},") + sections.append(" year = {2026},") + sections.append(" url = {https://huggingface.co/datasets/aeriesec/orgforge},") + sections.append(" note = {Dataset generated by the OrgForge simulator}") + sections.append("}") + sections.append("```") + sections.append("") + sections.append("## License") + sections.append("") + sections.append( + "MIT. The simulation engine that produced this dataset is independently\n" + "licensed under MIT; see the [OrgForge repository](https://github.com/aeriesec/orgforge)\n" + "for details." ) - def _ungated_ceiling_table(self, summary: dict) -> str: - """Renders the Tier 1 ungated retrieval ceiling table.""" - if "error" in summary: - return f"> ⚠️ Ceiling unavailable: {summary['error']}" - if not summary: - return "> Ceiling not run." + _body = "\n".join(sections) + return _fm + "\n" + _body - model = summary.get("model", "?") - overall = summary.get("overall", {}) - by_type = summary.get("by_type", {}) + def _genesis_gap_table(self, gaps: List[dict]) -> str: + if not gaps: + return "> No genesis knowledge gaps configured for this simulation." - lines = [ - f"Model: `{model}`", - "", - "| Question type | MRR@10 | Recall@10 | N |", - "|---|---|---|---|", - ( - f"| **Overall** | **{overall.get('mrr_at_10', '?')}** " - f"| **{overall.get('recall_at_10', '?')}** " - f"| **{overall.get('n', '?')}** |" - ), - ] - for qtype, metrics in sorted(by_type.items()): - lines.append( - f"| {qtype} | {metrics.get('mrr_at_10', '?')} " - f"| {metrics.get('recall_at_10', '?')} " - f"| {metrics.get('n', '?')} |" + header = ( + "| Former owner | Role | Departed | Days before sim | " + "Documented at departure | Domains |\n" + "|---|---|---|---|---|---|\n" + ) + rows = [] + for gap in gaps: + domains_str = ", ".join(f"`{d}`" for d in gap["domains"]) + rows.append( + f"| {gap['name']} | {gap.get('role', '')} | {gap.get('left', '')} " + f"| {gap['days_before_sim']} | {int(gap['documented_pct'] * 100)}% " + f"| {domains_str} |" ) - lines += [ - "", - "> SILENCE questions excluded — absence cannot be measured by retrieval recall.", - "> **Epistemic Tax** = this ceiling MRR@10 − your gated agent's `violation_adjusted_combined_score`.", + return header + "\n".join(rows) + + def _artifact_count_table(self, by_type: Dict[str, int], type: str) -> str: + header = f"| {type} | Count |\n|---|---|\n" + rows = [f"| `{doc_type}` | {count:,} |" for doc_type, count in by_type.items()] + return header + "\n".join(rows) + + def _dept_count_table(self, dept_counts: Dict[str, int]) -> str: + if not dept_counts: + return "> Department breakdown unavailable." + header = "| Department | Documents |\n|---|---|\n" + rows = [ + f"| {dept} | {count:,} |" for dept, count in dept_counts.items() if dept ] - return "\n ".join(lines) - - def _reasoning_metrics_table(self, static_metrics: dict) -> str: - """Renders the Tier 2 static reasoning difficulty table.""" - if not static_metrics: - return "> Static reasoning metrics not computed." - - lines = [ - "| Track | Metric | Value | Interpretation |", - "|---|---|---|---|", - ] - - if p := static_metrics.get("PERSPECTIVE"): - lines += [ - ( - f"| `PERSPECTIVE` | `avg_horizon_contamination_rate` " - f"| {p.get('avg_horizon_contamination_rate', '?')} " - f"| Fraction of ungated top-20 outside actor's visibility cone |" - ), - ( - f"| `PERSPECTIVE` | `avg_first_in_cone_rank` " - f"| {p.get('avg_first_in_cone_rank', '?')} " - f"| Mean rank of first permitted doc — lower is easier |" - ), - ] + return header + "\n".join(rows) - if cf := static_metrics.get("COUNTERFACTUAL"): - lines += [ - ( - f"| `COUNTERFACTUAL` | `pct_causal_chain_traceable_top10` " - f"| {cf.get('pct_causal_chain_traceable_top10', '?')} " - f"| Fraction where cause+effect co-appear in ungated top-10 |" - ), - ( - f"| `COUNTERFACTUAL` | `pct_requires_multi_hop` " - f"| {cf.get('pct_requires_multi_hop', '?')} " - f"| Fraction unreachable by a single retrieval pass |" - ), - ] + def _corpus_schema_table(self, sim_event_counts: Dict[str, int] = None) -> str: + if sim_event_counts: + sim_event_types = " \\| ".join(f"`{t}`" for t in sim_event_counts) + else: + sim_event_types = "`sim_event` \\| *(see corpus for full list)*" - if s := static_metrics.get("SILENCE"): - lines += [ - ( - f"| `SILENCE` | `avg_search_space_bm25_coverage` " - f"| {s.get('avg_search_space_bm25_coverage', '?')} " - f"| Fraction of required absence-check locations BM25 surfaces |" - ), - ( - f"| `SILENCE` | `pct_fully_covered` " - f"| {s.get('pct_fully_covered', '?')} " - f"| Questions where BM25 covers the entire search space |" - ), - ] + artifact_types = " \\| ".join(f"`{t}`" for t in sorted(_ARTIFACT_DOC_TYPES)) - lines += [ - "", - "> **Reading these metrics:** High contamination + low traceability + low coverage", - "> means the question set demands genuine reasoning over retrieval luck. A gated", - "> agent that outperforms the ungated ceiling on `PERSPECTIVE` questions is actively", - "> exercising epistemic discipline — it is refusing correct-but-forbidden information.", - ] - return "\n ".join(lines) + return textwrap.dedent(f"""\ + | Column | Type | Description | + |---|---|---| + | `doc_id` | str | Unique artifact ID (e.g. `IT-042`, `CONF-ENG-007`, `PR-031`) | + | `doc_type` | str | Artifact: {artifact_types} — SimEvent: {sim_event_types} | + | `category` | str | `artifact` \\| `sim_event` \\| `sim_config` | + | `title` | str | Human-readable title or subject line | + | `body` | str | Full text content | + | `day` | int | Simulation day this artifact was created (1-indexed) | + | `date` | str | ISO date string | + | `timestamp` | str | ISO datetime, business-hours-accurate to the millisecond | + | `actors` | str | JSON list of actor names involved | + | `tags` | str | JSON list of semantic tags from the SimEvent | + | `artifact_ids` | str | JSON dict of cross-references to related artifacts by type | + | `dept` | str | Owning department; empty if cross-department | + | `is_incident` | bool | True if this artifact is part of a P1/P2 incident thread | + | `is_external` | bool | True for artifacts originating outside the org (emails, Zendesk, NPS, invoices) | + | `facts` | str | JSON dict of raw SimEvent facts; populated for SimEvent rows, empty string for artifact rows |""") # ───────────────────────────────────────────────────────────────────────────── @@ -2381,7 +2117,7 @@ def _reasoning_metrics_table(self, static_metrics: dict) -> str: # ───────────────────────────────────────────────────────────────────────────── -def _write_parquet(rows: List[dict], out_dir: Path, stem: str = "part-00000") -> None: +def _write_parquet(rows: List[dict], out_dir: Path, stem: str = "corpus-00000") -> None: if not _PARQUET_AVAILABLE: out_path = out_dir / f"{stem}.json" with open(out_path, "w") as f: @@ -2411,164 +2147,135 @@ def _write_parquet(rows: List[dict], out_dir: Path, stem: str = "part-00000") -> class HFExporter: """ - Orchestrates the full v2 export pipeline: + Orchestrates the corpus export pipeline: 1. Build corpus from SimEvent log + MongoDB - 2. Load v2 eval data (eval_questions.json + the three eval indexes) - 3. Compute two-tier baselines (ungated retrieval ceiling + static reasoning metrics) - 4. Write Parquet files for corpus, questions, and eval indexes - 5. Write dataset card (README.md) + 2. Write corpus Parquet + 3. Write dataset card (README.md) """ + def _write_hero_image(self, out_dir: Path) -> None: + src = Path(__file__).resolve().parent / "orgforge_dataset_hero.png" + if src.exists(): + shutil.copy2(src, HF_DIR / "orgforge_dataset_hero.png") + logger.info(" → orgforge_dataset_hero.png") + else: + logger.warning( + " orgforge_dataset_hero.png not found next to script — skipping" + ) + + def _write_supplemental(self, mem, out_dir: Path) -> None: + supp_dir = out_dir / "supplemental" + supp_dir.mkdir(parents=True, exist_ok=True) + + snap_src = BASE / "simulation_snapshot.json" + if snap_src.exists(): + shutil.copy2(snap_src, supp_dir / "simulation_snapshot.json") + logger.info(f" → supplemental/simulation_snapshot.json") + else: + logger.warning(" simulation_snapshot.json not found — skipping") + + if mem is not None: + try: + registry = list(mem._db["domain_registry"].find({}, {"embedding": 0})) + if registry: + (supp_dir / "domain_registry.json").write_text( + json.dumps(registry, indent=2, default=str) + ) + logger.info( + f" → supplemental/domain_registry.json ({len(registry)} domains)" + ) + except Exception as exc: + logger.warning(f" domain_registry export failed: {exc}") + + dd_metrics = BASE / "datadog" / "metrics.jsonl" + if dd_metrics.exists() and _PARQUET_AVAILABLE: + rows = [ + json.loads(l) for l in dd_metrics.read_text().splitlines() if l.strip() + ] + if rows: + _write_parquet(rows, supp_dir, stem="datadog_metrics") + logger.info( + f" → supplemental/datadog_metrics.parquet ({len(rows):,} rows)" + ) + elif dd_metrics.exists(): + shutil.copy2(dd_metrics, supp_dir / "datadog_metrics.jsonl") + logger.info(f" → supplemental/datadog_metrics.jsonl (parquet unavailable)") + + try: + scores = list( + mem._db["assignment_scores"].find({}, {"_id": 0, "embedding": 0}) + ) + if scores and _PARQUET_AVAILABLE: + _write_parquet(scores, supp_dir, stem="assignment_scores") + logger.info( + f" → supplemental/assignment_scores.parquet ({len(scores):,} rows)" + ) + except Exception as exc: + logger.warning(f" assignment_scores export failed: {exc}") + + try: + sim_config_docs = list(mem._db["sim_config"].find({}, {"_id": 1})) + if sim_config_docs: + sim_config_out = {} + for doc in mem._db["sim_config"].find({}): + key = str(doc.pop("_id")) + sim_config_out[key] = doc + (supp_dir / "sim_config.json").write_text( + json.dumps(sim_config_out, indent=2, default=str) + ) + logger.info(f" → supplemental/sim_config.json") + except Exception as exc: + logger.warning(f" sim_config export failed: {exc}") + def run(self) -> None: - logger.info("[bold cyan]📦 HuggingFace dataset export v2 starting…[/bold cyan]") + logger.info("📦 OrgForge HuggingFace export starting…") - # 1. Memory (optional — degrade gracefully) mem = None try: from memory import Memory mem = Memory() - logger.info(" Connected to MongoDB Memory.") + logger.info(" Connected to MongoDB.") except Exception as exc: logger.warning( - f" Memory unavailable ({exc}). Corpus will derive from eval JSON only." + f" Memory unavailable ({exc}). Corpus will derive from SimEvent log only." ) - # 2. Corpus - corpus_builder = CorpusBuilder(mem) + insider_threat_enabled = _CFG.get("insider_threat", {}).get("enabled", False) + corpus_builder = CorpusBuilder( + mem, insider_threat_enabled=insider_threat_enabled + ) corpus = corpus_builder.build() if not corpus: logger.warning(" Empty corpus — check that flow.py has run first.") + return - # 3. Load v2 eval data - questions_path = EVAL_DIR / "eval_questions.json" - causal_links_path = EVAL_DIR / "causal_link_index.json" - actor_vis_path = EVAL_DIR / "actor_visibility.json" - absence_path = EVAL_DIR / "absence_catalog.json" - - q_data = ( - json.loads(questions_path.read_text()) if questions_path.exists() else {} - ) - raw_questions = ( - q_data.get("questions", []) if isinstance(q_data, dict) else q_data - ) - # Filter to the three v2 tracks only (guard against mixed-version files) - questions = [ - q - for q in raw_questions - if q.get("question_type") in ("PERSPECTIVE", "COUNTERFACTUAL", "SILENCE") - ] - - causal_links = ( - json.loads(causal_links_path.read_text()) - if causal_links_path.exists() - else [] - ) - actor_visibility = ( - json.loads(actor_vis_path.read_text()) if actor_vis_path.exists() else {} - ) - absence_catalog = ( - json.loads(absence_path.read_text()) if absence_path.exists() else [] - ) - - logger.info( - f" {len(questions)} eval questions loaded " - f"({sum(1 for q in questions if q.get('question_type') == 'PERSPECTIVE')} PERSPECTIVE, " - f"{sum(1 for q in questions if q.get('question_type') == 'COUNTERFACTUAL')} COUNTERFACTUAL, " - f"{sum(1 for q in questions if q.get('question_type') == 'SILENCE')} SILENCE)" - ) - logger.info( - f" {len(causal_links)} causal links, " - f"{len(actor_visibility)} actors, " - f"{len(absence_catalog)} absence records loaded" - ) - - # 4. Two-tier baselines - # ────────────────────────────────────────────────────────────────────── - # Tier 1: ungated retrieval ceiling — BM25 and dense with no gates. - # Tier 2: static reasoning difficulty — computed from metadata alone, - # reuses the already-built BM25 index to avoid double work. - ceiling_runner = UngatedCeilingBaseline(corpus, questions, mem=mem) - bm25_per_q, bm25_agg = ceiling_runner.run_bm25() - dense_per_q, dense_agg = ceiling_runner.run_dense() - - static_metrics = StaticReasoningMetrics( - questions=questions, - bm25=ceiling_runner._bm25, # reuse the already-built index - doc_ids=ceiling_runner._doc_ids, - ) - reasoning_output = static_metrics.compute() + counts = corpus_builder.artifact_counts(corpus) + logger.info(" Artifact counts:") + for doc_type, count in counts.items(): + logger.info(f" {doc_type:30s} {count:,}") - baseline_summary = { - "ungated_ceiling": {"bm25": bm25_agg, "dense": dense_agg}, - "static_reasoning_metrics": reasoning_output["aggregate"], - } + _write_parquet(corpus, CORPUS_DIR, "corpus-00000") - (BASELINE_DIR / "ungated_ceiling_bm25.json").write_text( - json.dumps(bm25_per_q, indent=2, default=str) - ) - (BASELINE_DIR / "ungated_ceiling_dense.json").write_text( - json.dumps(dense_per_q, indent=2, default=str) - ) - (BASELINE_DIR / "static_reasoning_metrics.json").write_text( - json.dumps(reasoning_output, indent=2, default=str) - ) - (BASELINE_DIR / "baseline_summary.json").write_text( - json.dumps(baseline_summary, indent=2, default=str) - ) - logger.info(f" → baselines written to {BASELINE_DIR}") + self._write_supplemental(mem, HF_DIR) - # 5. Parquet — corpus + questions + eval indexes - _write_parquet(corpus, CORPUS_DIR, "corpus-00000") - _write_parquet(_questions_to_rows(questions), QUES_DIR, "questions-00000") - _write_parquet( - _causal_links_to_rows(causal_links), - EVAL_INDEX_DIR, - "causal_link_index", - ) - _write_parquet( - _actor_visibility_to_rows(actor_visibility), - EVAL_INDEX_DIR, - "actor_visibility", - ) - _write_parquet( - _absence_catalog_to_rows(absence_catalog), - EVAL_INDEX_DIR, - "absence_catalog", - ) + self._write_hero_image(HF_DIR) - # 6. Dataset card DatasetCardWriter().write( out_path=HF_DIR / "README.md", corpus=corpus, - questions=questions, - causal_links=causal_links, - actor_visibility=actor_visibility, - absence_catalog=absence_catalog, - baseline_summary=baseline_summary, cfg=_CFG, + mem=mem, ) - bm25_overall = bm25_agg.get("overall", {}) - dense_overall = dense_agg.get("overall", {}) - srm = reasoning_output["aggregate"] logger.info( - f"[green]✓ Export v2 complete.[/green] " - f"Output: {HF_DIR} | " - f"Ceiling BM25 MRR@10: {bm25_overall.get('mrr_at_10', 'n/a')} | " - f"Ceiling Dense MRR@10: {dense_overall.get('mrr_at_10', 'n/a')} | " - f"PERSPECTIVE contamination: " - f"{srm.get('PERSPECTIVE', {}).get('avg_horizon_contamination_rate', 'n/a')} | " - f"COUNTERFACTUAL multi-hop: " - f"{srm.get('COUNTERFACTUAL', {}).get('pct_requires_multi_hop', 'n/a')} | " - f"SILENCE BM25 coverage: " - f"{srm.get('SILENCE', {}).get('avg_search_space_bm25_coverage', 'n/a')}" + f"✓ Export complete. Output: {HF_DIR} | " + f"Total documents: {len(corpus):,} | " + f"Types: {len(counts)}" ) if __name__ == "__main__": - import logging - logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", diff --git a/eval/orgforge_dataset_hero.png b/eval/orgforge_dataset_hero.png new file mode 100644 index 0000000000000000000000000000000000000000..b55bc197eb2168eecd8e34c29b578227ccd7475e GIT binary patch literal 161322 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

NS=y#)ZfTYFiS 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 literal 0 HcmV?d00001 diff --git a/simulation.log.txt b/simulation.log.txt new file mode 100644 index 0000000..3f4320d --- /dev/null +++ b/simulation.log.txt @@ -0,0 +1,5758 @@ +2026-04-02 19:53:48: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 19:57:16: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:06:32: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:13:50: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:15:54: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:16:45: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:17:58: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:19:59: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:21:49: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:33:53: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:35:21: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:35:26: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:56:57: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:05: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no explicit evidence in the available documents that the Day 8 incident directly triggered the creation of a postmortem page in Confluence or a root cause analysis entry in Jira or Slack. Without a documented causal link, we cannot assert that the absence of the incident would have prevented those artifacts from being created.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "The search results returned incident records (e.g., ENG-111) and various Confluence pages, but none explicitly reference a 'Day 8' incident, its postmortem, or a root cause analysis tied to that incident. Therefore, the required causal connection is missing, indicating a knowledge gap rather than a confirmed causal relationship." +}" +2026-04-02 20:57:05: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:12: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any record of Taylor's review comments, approval status, or a request for changes on Day 6 for Alex's pull request. Consequently, there is no documented causal link showing that Taylor's actions caused Alex to revise the implementation before merge.", + "causal_mechanism": "involves_gap", + "actors": ["Taylor", "Alex"], + "reasoning": "Since the data lacks any evidence of Taylor approving the PR without changes or Alex making revisions in response, we cannot assert that removing Taylor's approval would have prevented Alex's revision. The missing information represents a knowledge gap, so the outcome would not be shown to change based on the current records." +}" +2026-04-02 20:57:13: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:20: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the document corpus for any records dated Day 14 that mention an inbound email involving Ravi and Nadia, as well as any related internal routing entries (including Jira tickets). The only Day 14 record found is an outbound email (ack_vendor_ravi_14) where Nadia replies to an earlier inbound message from Ravi. No inbound email on Day 14 involving Ravi and Nadia, nor any internal routing configuration for such an email, was located. Therefore, there is no evidence that an internal routing for the inbound email on Day 14 involving Ravi and Nadia was created." +}" +2026-04-02 20:57:20: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:26: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched all Confluence artifacts created after Day 11 for any mention of a knowledge gap, Sam, or Chloe. The only Confluence creation on Day 11 was CONF-ENG-042 by Jax, unrelated to Sam or Chloe. No other Confluence pages were found, and no events indicate a knowledge gap involving Sam and Chloe that prompted a documentation page. Therefore, no Confluence documentation page was created in response to that knowledge gap." +}" +2026-04-02 20:57:26: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:33: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ravi on Day 4 (artifact ext_email_ravi_4_6) is present in the email system, but there is no evidence that John was a recipient or that he accessed it. The only response recorded is an acknowledgment sent by Nadia (artifact ack_vendor_ravi_4), indicating the email was handled by another liaison. Therefore, John would not have been aware of the email as of Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6", "ack_vendor_ravi_4"], + "blocked_subsystems": [] +}" +2026-04-02 20:57:33: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:38: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "No inbound email from Gridiron Pro Football with the title “Re: Final proposal & next steps for Gridiron Pro Football” is present in the email artifacts accessible to Patty. Searches of the email corpus did not return any matching email, and there is no evidence from Slack, Confluence, or Zoom indicating that Patty was notified about such an email. Therefore, as of Day 29, Patty could not have known about it.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 20:57:38: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:44: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "A search of Jordan's accessible systems (Confluence, Jira, Slack, Email, Git, Datadog, Zoom) for any mention of a design discussion about the 'Define automation framework roadmap' on Day 2 returned no matching artifacts. The only Day 2 items found relate to other design documents and Jira tickets, none referencing the Define automation framework roadmap. Therefore Jordan would not have had knowledge of that specific discussion as of Day 2.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 20:57:45: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:50: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available documents include Vince's expertise profile but contain no explicit record of ticket or escalation reassignment tied to his departure on Day 3. Without a documented link, we cannot establish that his departure caused the reassignment, so removing the departure would not be shown to change the reassignment outcome.", + "causal_mechanism": "involves_gap", + "actors": ["Vince"], + "reasoning": "Search results reveal Vince's role and departure timing but lack any statement that his active tickets or escalation duties were transferred after he left. Since the causal connection is undocumented, the question cannot be answered affirmatively; the effect (reassignment) is not proven to depend on the cause (departure)." +}" +2026-04-02 20:57:51: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:57:56: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available records show Maya's emails on days 2, 17, and 18 each resulted in a corresponding Jira ticket (ORG-100, ORG-137). No email from Maya is recorded on day 21, nor is there a Jira ticket linked to a day‑21 email. Because the documentation does not contain any evidence that a day‑21 email existed or that it triggered a ticket, we cannot establish a causal link between a hypothetical day‑21 email and the creation of a Jira ticket.", + "causal_mechanism": "involves_gap", + "actors": ["Maya", "Jax"], + "reasoning": "The question asks whether the absence of a day‑21 email would prevent a ticket from being created. The data set does not include any day‑21 email from Maya nor a ticket tied to such an email. Without explicit documentation of that email-to-ticket relationship, there is a knowledge gap, preventing us from asserting that the ticket’s existence depends on the day‑21 email. Hence, removing the cause (the email) cannot be shown to change the effect (ticket creation)." +}" +2026-04-02 20:57:56: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:02: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of all artifacts accessible to Umji (Confluence pages, emails, Slack messages, and Zendesk tickets) up to Day 6 returned no mention of a knowledge gap in the undocumented domain, the unknown issue left by the departed employee, or the reviewer_audit detection. The reviewer_audit system and related code repositories (Git) are outside Umji's access, so any information originating from those sources would be unavailable to her.", + "evidence_artifacts": [], + "blocked_subsystems": ["git", "reviewer_audit"] +}" +2026-04-02 20:58:02: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 16 involving Kaitlyn, Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:09: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Zoe, Ben? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:15: task_name="None", task="As of Day 18, would Sanjay have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Sanjay as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:23: task_name="None", task="As of Day 18, would Sanjay have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Sanjay as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Search of Sanjay's accessible email artifacts (the only system where an inbound email from Ingrid would appear) returned no results matching the subject “Enterprise Audit Log Access & Retention – Next Steps”. No other accessible subsystem (confluence, datadog, git, jira, slack, zoom) contains evidence of this email. Therefore Sanjay could not have known about it by Day 18.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 20:58:24: task_name="None", task="As of Day 23, could Tom have been aware of the design discussion about Ops sync on handoff, given that he has access to Confluence, email, Slack, and Zoom, but such design discussion events are visible only to direct participants and he was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Tom as of Day 23. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:30: task_name="None", task="As of Day 23, could Tom have been aware of the design discussion about Ops sync on handoff, given that he has access to Confluence, email, Slack, and Zoom, but such design discussion events are visible only to direct participants and he was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Tom as of Day 23. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Tom's accessible systems (Confluence, Email, Slack, Zoom) contain no records of a design discussion titled 'Ops sync on handoff' after Day 20, and the policy states such design discussion events are visible only to direct participants. Since Tom was not listed as a participant, he could not have become aware of the discussion.", + "evidence_artifacts": [], + "blocked_subsystems": ["design_discussion"] +}" +2026-04-02 20:58:30: task_name="None", task="As of Day 9, could Umji—who has access to Confluence, email, Slack, and Zendesk but not Git—have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 9. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:36: task_name="None", task="As of Day 9, could Umji—who has access to Confluence, email, Slack, and Zendesk but not Git—have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 9. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of all artifacts accessible to Umji (Confluence, email, Slack, Zendesk) up to Day 9 returned no mention of the undocumented domain knowledge gap, the triggering unknown issue, the departed employee, or the reviewer_audit detection. Without any documented references in these systems, Umji could not have become aware of the gap. Access to Git, where related code or version‑control comments might reside, is blocked, further limiting visibility.", + "evidence_artifacts": [], + "blocked_subsystems": ["git"] +}" +2026-04-02 20:58:37: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 3 involving Yusuf and Marcus? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:45: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 3 involving Yusuf and Marcus? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I performed multiple searches across the Confluence corpus focusing on day 3 and the keywords \"knowledge gap\", \"Yusuf\", and \"Marcus\". Searches for \"knowledge gap Yusuf Marcus\" after day 3 returned no results. Separate searches for \"knowledge gap\" after day 3, for \"Yusuf\" after day 3, and for \"Marcus\" after day 3 also returned no relevant Confluence pages. The broader searches for \"Yusuf\" and \"Marcus\" returned only day 1 documents unrelated to a knowledge gap on day 3. Since no Confluence documentation page linking a knowledge gap detected on day 3 involving Yusuf and Marcus was found, the answer is that such a page does not exist." +}" +2026-04-02 20:58:46: task_name="None", task="Did we create a Salesforce risk flag on the related deals in response to the incident on Day 24 involving Jax, Reese? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:50: task_name="None", task="Given that Janice has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but inbound external email events are visible only to direct participants and she was not listed as a participant, would Janice have known about the inbound email from Gridiron Pro Football titled “Re: Re: Re: Re: Urgent Request: Real-Time Load Monitoring Upgrade Needed” as of Day 25? + +IMPORTANT: You are answering from the perspective of Janice as of Day 25. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:58:55: task_name="None", task="Given that Janice has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but inbound external email events are visible only to direct participants and she was not listed as a participant, would Janice have known about the inbound email from Gridiron Pro Football titled “Re: Re: Re: Re: Urgent Request: Real-Time Load Monitoring Upgrade Needed” as of Day 25? + +IMPORTANT: You are answering from the perspective of Janice as of Day 25. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "No email artifact matching the title “Re: Re: Re: Re: Urgent Request: Real-Time Load Monitoring Upgrade Needed” is present in Janice's accessible email collection, and inbound external emails are only visible to direct participants. Janice was not a participant, so even if the email existed, she would not have been able to see it.", + "evidence_artifacts": [], + "blocked_subsystems": ["email"] +}" +2026-04-02 20:58:55: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Jordan? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:01: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Jordan? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for any Confluence documentation created after Day 25 that mentions Jordan, and also queried events for Day 25 for any knowledge‑gap detection involving Jordan. Both searches returned no results. No event on Day 25 references a knowledge gap involving Jordan, and there is no Confluence page linked to such a gap. Therefore, there is no evidence that a Confluence documentation page was created in response to a knowledge gap detected on Day 25 involving Jordan." +}" +2026-04-02 20:59:02: task_name="None", task="Can you confirm whether an internal routing was created for the inbound email on Day 19 involving Ethan and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:08: task_name="None", task="Was an internal routing created for the inbound email on Day 28 involving Ethan and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:13: task_name="None", task="Did we create an internal routing for the customer email in response to the inbound email on Day 19 involving Oliver and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:18: task_name="None", task="As of Day 18, would Morgan have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Morgan as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:25: task_name="None", task="As of Day 18, would Morgan have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Morgan as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Search of Morgan's email records (the only subsystem where the inbound email would appear) up to Day 18 returned no messages from Ingrid (GitHub) with the subject “Enterprise Audit Log Access & Retention – Next Steps”. Since Morgan has no visibility to other systems that could contain this email, there is no evidence that Morgan could have known about it.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 20:59:25: task_name="None", task="If Miki had approved the pull request on Day 6 without requesting changes, would Jordan not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:31: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 6 involving Chris and Jamie? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:37: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 8, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:42: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 8, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The records show an inbound email from Maya on Day 8 (ext_email_maya_8_6) and an outbound acknowledgment (ack_vendor_maya_8), but no associated Jira ticket is documented (jira_ref is null and no Jira issues were found referencing this email). Because there is no evidence that the Day 8 email triggered a Jira ticket, the absence of that email would not have altered the creation of a ticket.", + "causal_mechanism": "involves_gap", + "actors": ["Maya", "Jax"], + "reasoning": "The corpus contains the Day 8 email and its acknowledgment but lacks any Jira ticket linking to that communication. Without a documented ticket, we cannot infer that the email caused ticket creation. The missing ticket information represents a knowledge gap, so removing the email would not change an effect that is not evidenced to exist." +}" +2026-04-02 20:59:47: task_name="None", task="As of Day 17, given that Jenna can access Confluence, email, Salesforce, Slack, and Zoom but not Git, would she have been aware of the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected through a reviewer audit? + +IMPORTANT: You are answering from the perspective of Jenna as of Day 17. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 20:59:52: task_name="None", task="As of Day 17, given that Jenna can access Confluence, email, Salesforce, Slack, and Zoom but not Git, would she have been aware of the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected through a reviewer audit? + +IMPORTANT: You are answering from the perspective of Jenna as of Day 17. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Within Jenna's visibility cone (Confluence, email, Salesforce, Slack, Zoom) there are no artifacts that reference a knowledge gap in an undocumented domain, an unknown issue left by a departed employee, or a reviewer audit detection. The only retrieved artifact (CONF-ENG-006) is unrelated. Therefore Jenna would not have been aware of that specific knowledge gap.", + "evidence_artifacts": [], + "blocked_subsystems": ["git"] +}" +2026-04-02 20:59:53: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 1 involving Deepa and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:00: task_name="None", task="Did we create a Confluence documentation page in response to the knowledge gap detected on Day 27 involving Patty, Dave? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:07: task_name="None", task="Did we create a Confluence documentation page in response to the knowledge gap detected on Day 27 involving Patty, Dave? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for Confluence documents created after Day 27 that mention a knowledge gap, Patty, or Dave. No results were returned for any query limited to after Day 27. A broader search of all Confluence pages for 'Patty' and 'Dave' returned only entries dated Day 2, 10, 12, 22, and 25, none of which reference a knowledge gap on Day 27. Therefore, there is no evidence that a Confluence documentation page was created in response to the Day 27 knowledge gap involving Patty and Dave." +}" +2026-04-02 21:00:07: task_name="None", task="If the email from Ravi (CloudBees) had not arrived on Day 17, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:13: task_name="None", task="If the email from Ravi (CloudBees) had not arrived on Day 17, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The vendor email sent by Ravi on Day 17 was recorded as the source of Jira ticket ORG-135, which was created to track the plugin compatibility request. The ticket’s metadata explicitly links it to a vendor_email from Ravi, indicating the email triggered the ticket creation. Without that email, the ticket would not have been opened.", + "causal_mechanism": "incident_coordination", + "actors": ["Ravi", "Apex Athletics"], + "reasoning": "The Jira ticket ORG-135 (day 17) includes metadata showing its source as a vendor_email from Ravi. This provides a documented causal link: the external contact (Ravi’s email) caused the incident (ticket) to be created. Removing the email eliminates the trigger, so the ticket would not exist." +}" +2026-04-02 21:00:13: task_name="None", task="If the incident had not occurred, would the postmortem page not have been written and the root cause analysis not exist on Day 24? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:25: task_name="None", task="If the incident had not occurred, would the postmortem page not have been written and the root cause analysis not exist on Day 24? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The incident triggered the creation of a postmortem page on Confluence, which included a root cause analysis. The postmortem page (a Confluence document) was authored directly in response to the incident, so without the incident the page would not have been written and the analysis would not exist on Day 24.", + "causal_mechanism": "spawned_doc", + "actors": ["Jax", "Incident Response Team"], + "reasoning": "The available Confluence records show postmortem pages titled \"Postmortem: P1 incident ...\" that were authored after each incident (e.g., ENG‑128, ENG‑118, ENG‑130). These pages are explicitly linked to the incidents they analyze, indicating a direct causal relationship: the incident caused the documentation effort. Therefore, if the incident had not occurred, the associated postmortem page and its root‑cause analysis would not have been produced on Day 24." +}" +2026-04-02 21:00:26: task_name="None", task="Did we create an internal routing for the customer email received on Day 25 involving Ethan and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:31: task_name="None", task="By Day 18, would Deepa have known about the design discussion on the QA automation roadmap, given that she has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but such design discussion events are visible only to direct participants and she was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Deepa as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:35: task_name="None", task="By Day 18, would Deepa have known about the design discussion on the QA automation roadmap, given that she has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but such design discussion events are visible only to direct participants and she was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Deepa as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The design discussion on the QA automation roadmap was conducted in a Zoom meeting whose participant list (Nadia, Zoe, Ben, Yusuf) does not include Deepa, and the related Confluence design page was authored by Nadia without any indication that Deepa was a reviewer or collaborator. Since such design discussion events are visible only to direct participants, Deepa would not have been granted visibility through her allowed systems (Confluence, Slack, etc.). Therefore, by Day 18 Deepa could not have known about the discussion.", + "evidence_artifacts": [ + "zoom_2026-03-03_723459ae", + "CONF-ENG-019" + ], + "blocked_subsystems": [] +}" +2026-04-02 21:00:35: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 28 involving Morgan? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:43: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 28 involving Morgan? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I examined all events for Day 28 and performed searches for Confluence artifacts, knowledge gaps, and references to Morgan. The only Confluence page created on Day 28 (CONF-MKT-005) pertains to telemetry service credential rotation and is unrelated to any knowledge gap involving Morgan. No other Confluence pages were found, and no event indicates a knowledge gap detection involving Morgan that triggered documentation creation. Therefore, no Confluence documentation page was created in response to such a gap." +}" +2026-04-02 21:00:43: task_name="None", task="As of Day 11, would Priya have known about the inbound email from Ethan (Peak Performance Labs) regarding the “Request for Enhanced Real-Time Performance Metrics Dashboard”? + +IMPORTANT: You are answering from the perspective of Priya as of Day 11. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:50: task_name="None", task="As of Day 11, would Priya have known about the inbound email from Ethan (Peak Performance Labs) regarding the “Request for Enhanced Real-Time Performance Metrics Dashboard”? + +IMPORTANT: You are answering from the perspective of Priya as of Day 11. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Priya's accessible systems include email. The email artifacts show three inbound messages from Ethan (Peak Performance Labs): ext_email_ethan_2_9 (Request for Enhanced Data Integration Capabilities, Day 2), ext_email_ethan_3_9 (Request for Enhanced Data Export Capability, Day 3), and ext_email_ethan_7_6 (RDS Performance Review & Recommendations, Day 7). None of these correspond to a “Request for Enhanced Real-Time Performance Metrics Dashboard”. No email with that subject or topic is present in the searchable email corpus up to Day 11, so Priya could not have known about it.", + "evidence_artifacts": [ + "ext_email_ethan_2_9", + "ext_email_ethan_3_9", + "ext_email_ethan_7_6" + ], + "blocked_subsystems": [] +}" +2026-04-02 21:00:50: task_name="None", task="If Chloe had approved the pull request on Day 21 without requesting changes, would Miki not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:00:57: task_name="None", task="Was an internal routing of the customer email created in response to the inbound email on Day 21 involving Lucia and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:03: task_name="None", task="Was an internal routing of the customer email created in response to the inbound email on Day 21 involving Lucia and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "Searched the corpus for any artifacts after Day 21 mentioning Lucia, Chloe, inbound email, and internal routing across email, Jira, and Slack documents. No records were found that reference an internal routing created in response to a Day 21 inbound email involving Lucia and Chloe. The only related items were emails on Days 14 and 19, but none on Day 21 or any internal routing entries. Therefore, such an internal routing does not exist in the available data." +}" +2026-04-02 21:01:03: task_name="None", task="As of Day 12, would Mike have known about the inbound email from Ravi (CloudBees) regarding “Urgent: Jenkins Plugin Compatibility Update Required for Apex Athletics”? + +IMPORTANT: You are answering from the perspective of Mike as of Day 12. This actor only has access to: confluence, email, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:09: task_name="None", task="As of Day 12, would Mike have known about the inbound email from Ravi (CloudBees) regarding “Urgent: Jenkins Plugin Compatibility Update Required for Apex Athletics”? + +IMPORTANT: You are answering from the perspective of Mike as of Day 12. This actor only has access to: confluence, email, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of Mike's accessible systems (email, slack, confluence) for the specific inbound email from Ravi with the subject “Urgent: Jenkins Plugin Compatibility Update Required for Apex Athletics” returned no matching artifacts, and no related references were found in other accessible sources. Therefore, there is no evidence that Mike could have known about this email as of Day 12.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:01:09: task_name="None", task="As of Day 18, would Miki have been aware of the design discussion about the Office space redesign, given that Miki has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but the discussion was only visible to direct participants and Miki was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Miki as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:14: task_name="None", task="As of Day 18, would Miki have been aware of the design discussion about the Office space redesign, given that Miki has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but the discussion was only visible to direct participants and Miki was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Miki as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The design discussion about the Office space redesign was only visible to direct participants. Searches of Miki's accessible systems (Confluence, Slack, Zoom, Jira, Email, Git, Datadog) returned no artifacts referencing the discussion, and Miki is not listed as a participant. Therefore Miki would not have been aware of the discussion as of Day 18.", + "evidence_artifacts": [], + "blocked_subsystems": ["slack", "zoom", "confluence"] +}" +2026-04-02 21:01:14: task_name="None", task="If the postmortem for the original incident resolved on Day 15 had included preventive action items in git, jira, and slack, would the subsequent incident would likely not have occurred? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:19: task_name="None", task="If the postmortem for the original incident resolved on Day 15 had included preventive action items in git, jira, and slack, would the subsequent incident would likely not have occurred? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The postmortem for the Day 15 incident (ENG-111) did not generate concrete preventive action items in version control, Jira tickets, or Slack notifications. Consequently, the underlying misconfiguration remained unaddressed, leading to a recurrence (ENG-118) that is explicitly labeled as a recurrence of ENG-111.", + "causal_mechanism": "recurrence_of", + "actors": ["Jax"], + "reasoning": "The ENG-118 Jira entry contains the tag '[recurrence of ENG-111]', directly linking the later incident to the earlier one. This indicates the root cause was known but not remedied. Implementing preventive actions in git, Jira, and Slack as part of the original postmortem would have broken this causal chain, so the subsequent incident would likely not have occurred." +}" +2026-04-02 21:01:19: task_name="None", task="As of Day 14, would Vince have been aware of the inbound email from Lucia (Google) regarding “Integrating Google Crash Reporting with Apex Athletics’ Mobile Apps”? + +IMPORTANT: You are answering from the perspective of Vince as of Day 14. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:27: task_name="None", task="As of Day 14, would Vince have been aware of the inbound email from Lucia (Google) regarding “Integrating Google Crash Reporting with Apex Athletics’ Mobile Apps”? + +IMPORTANT: You are answering from the perspective of Vince as of Day 14. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Vince’s accessible email archive (search results for inbound messages from Lucia) contains no email matching the subject “Integrating Google Crash Reporting with Apex Athletics’ Mobile Apps”. The only related entries are outbound acknowledgments (ack_vendor_lucia_6, ack_vendor_lucia_7) sent by Chloe, indicating that the original inbound message was handled by Chloe, not Vince. Since Vince’s visibility cone (email, confluence, salesforce, slack, zoom) does not include this inbound email, he would not have been aware of it by Day 14.", + "evidence_artifacts": [ + "ack_vendor_lucia_6", + "ack_vendor_lucia_7" + ], + "blocked_subsystems": [] +}" +2026-04-02 21:01:27: task_name="None", task="As of Day 29, would Jax have been aware of the inbound email from Lucia (Google) regarding “Enhancing Firebase Cloud Messaging Reliability for Apex Athletics,” given his access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but that inbound external email events are visible only to direct participants and Jax was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Jax as of Day 29. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:30: task_name="None", task="As of Day 29, would Jax have been aware of the inbound email from Lucia (Google) regarding “Enhancing Firebase Cloud Messaging Reliability for Apex Athletics,” given his access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but that inbound external email events are visible only to direct participants and Jax was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Jax as of Day 29. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Inbound external emails are only visible to the direct participants. The email from Lucia (Google) about “Enhancing Firebase Cloud Messaging Reliability for Apex Athletics” does not list Jax as a participant, and no email artifact appears in Jax's accessible email store (search returned no results). Therefore Jax would not have been able to see or know about the email by Day 29.", + "evidence_artifacts": [], + "blocked_subsystems": ["email"] +}" +2026-04-02 21:01:31: task_name="None", task="Was a warmup 1‑on‑1 meeting created in response to the hiring of Riley Thompson on Day 17 involving Riley Thompson? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:37: task_name="None", task="As of Day 22, would Jax have known about the design discussion regarding the Align Japan proposal strategy? + +IMPORTANT: You are answering from the perspective of Jax as of Day 22. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:43: task_name="None", task="As of Day 22, would Jax have known about the design discussion regarding the Align Japan proposal strategy? + +IMPORTANT: You are answering from the perspective of Jax as of Day 22. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches across all systems Jax can access (Confluence, Slack, Jira, Email, Zoom) returned no artifacts mentioning the Align Japan proposal design discussion. Without any record of such a discussion in Jax's accessible sources, it is unlikely Jax would have known about it by Day 22.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:01:43: task_name="None", task="If the email from Gabriel (Confluent) had not arrived on Day 23, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:49: task_name="None", task="If the email from Gabriel (Confluent) had not arrived on Day 23, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The inbound email from Gabriel (Confluent) on Day 23 was logged as a vendor email, which automatically triggered the creation of Jira ticket ORG-152 to track the request. Without that email, the automated workflow would not have initiated the ticket.", + "causal_mechanism": "incident_coordination", + "actors": ["Gabriel"], + "reasoning": "The artifact set contains a Jira ticket (ORG-152) dated Day 23 with metadata indicating it originated from a vendor email from Gabriel. This explicit linkage shows the ticket was created as a direct response to the email. Therefore, if the email had not arrived, the ticket would not have been created, changing the outcome." +}" +2026-04-02 21:01:49: task_name="None", task="As of Day 18, would Mike have been aware of the design discussion about the QA automation roadmap? + +IMPORTANT: You are answering from the perspective of Mike as of Day 18. This actor only has access to: confluence, email, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:01:55: task_name="None", task="Given that inbound external email events are visible only to direct participants and Karen was not listed as a participant, as of Day 19 would Karen have known about the inbound email from Gabriel (Confluent) regarding “Urgent: Kafka Connector Health Below Threshold – Action Needed”? + +IMPORTANT: You are answering from the perspective of Karen as of Day 19. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:01: task_name="None", task="Given that inbound external email events are visible only to direct participants and Karen was not listed as a participant, as of Day 19 would Karen have known about the inbound email from Gabriel (Confluent) regarding “Urgent: Kafka Connector Health Below Threshold – Action Needed”? + +IMPORTANT: You are answering from the perspective of Karen as of Day 19. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Inbound external email events are only visible to direct participants. Karen was not listed as a participant on the email from Gabriel. Searches of Karen's accessible email, Slack, and Confluence records up to Day 19 returned no evidence of the email or any related discussion, indicating she had no exposure to that inbound message.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:02:01: task_name="None", task="Given that Alex has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but design discussion events are visible only to direct participants and Alex was not listed as a participant, would Alex have known about the design discussion about ops workflow design as of Day 25? + +IMPORTANT: You are answering from the perspective of Alex as of Day 25. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:04: task_name="None", task="Given that Alex has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but design discussion events are visible only to direct participants and Alex was not listed as a participant, would Alex have known about the design discussion about ops workflow design as of Day 25? + +IMPORTANT: You are answering from the perspective of Alex as of Day 25. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": true, + "reasoning": "Alex authored a Confluence page titled 'Design: ops process redesign' on Day 18 (CONF-ENG-075). This document demonstrates that Alex possessed knowledge of the ops workflow design discussion prior to Day 25, despite not being listed as a direct participant in the private design discussion event.", + "evidence_artifacts": ["CONF-ENG-075"], + "blocked_subsystems": [] +}" +2026-04-02 21:02:05: task_name="None", task="Was a Zendesk support ticket created in response to the routing of the customer email on Day 11 involving Gridiron Pro Football, Marcus? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:11: task_name="None", task="Was an internal routing created for the inbound email on Day 6 involving Gabriel and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:16: task_name="None", task="As of Day 18, would Miki have been aware of the design discussion about the Review ops KPI dashboard? + +IMPORTANT: You are answering from the perspective of Miki as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:22: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Raj and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:28: task_name="None", task="As of Day 8, would Hanna have known about the design discussion titled “Discuss QA process gaps,” given that she has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but such design discussion events are visible only to direct participants and she was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Hanna as of Day 8. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:32: task_name="None", task="As of Day 8, would Hanna have known about the design discussion titled “Discuss QA process gaps,” given that she has access to Confluence, Datadog, email, Git, Jira, Slack, and Zoom, but such design discussion events are visible only to direct participants and she was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Hanna as of Day 8. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The design discussion titled “Discuss QA process gaps” is restricted to direct participants only. Hanna is not listed as a participant, and none of the systems she can access (Confluence, Datadog, email, Git, Jira, Slack, Zoom) contain any record or notification of this discussion. Consequently, she could not have known about it by Day 8.", + "evidence_artifacts": [], + "blocked_subsystems": ["zoom"] +}" +2026-04-02 21:02:32: task_name="None", task="If Janice had not been hired to fill the gap in TitanDB, legacy auth service, AWS cost structure on Day 7, would the knowledge vacuum left by Bill’s departure remain unaddressed in the company’s Confluence and Jira systems? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:39: task_name="None", task="If Janice had not been hired to fill the gap in TitanDB, legacy auth service, AWS cost structure on Day 7, would the knowledge vacuum left by Bill’s departure remain unaddressed in the company’s Confluence and Jira systems? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "Bill's departure created a knowledge gap regarding TitanDB, legacy auth service, and AWS cost structure. Although Janice was hired on Day 7, the available records show no explicit documentation or Jira updates authored by her that address this gap. Consequently, the knowledge vacuum would remain unaddressed in Confluence and Jira even if Janice had not been hired.", + "causal_mechanism": "involves_gap", + "actors": ["Bill", "Janice"], + "reasoning": "The data includes Bill's expertise profile indicating his knowledge area, Janice's hiring date, and several Confluence pages and Jira tickets related to TitanDB. However, none of these artifacts attribute the creation or filling of the knowledge gap to Janice. Therefore, the causal link between Janice's hiring and remediation of the gap is not documented, meaning the gap would persist regardless of her presence." +}" +2026-04-02 21:02:39: task_name="None", task="By the end of Day 5, would Janice have been aware of the inbound email from Ravi (CloudBees) titled “Urgent: Action Required on Docker Registry Credentials – Apex Athletics”? + +IMPORTANT: You are answering from the perspective of Janice as of Day 5. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:45: task_name="None", task="If the Day 16 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira, given the usual Slack notifications? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:51: task_name="None", task="If the Day 16 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira, given the usual Slack notifications? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available records show that the Confluence postmortem page (CONF-ENG-036) and the Jira root‑cause analysis (ENG‑111) were created in response to the P1 incident ENG‑111 (Kafka consumer group lag) that occurred on day 6. There is no explicit evidence linking the Day 16 incident to the creation of those artefacts; the data does not mention a Day 16 incident triggering Slack notifications that led to the postmortem or the Jira analysis. Consequently, removing the Day 16 incident would not change the existence of the postmortem page or the Jira root‑cause analysis.", + "causal_mechanism": "involves_gap", + "actors": ["Slack (notification system)", "Engineering team", "Jax (author of postmortem)"], + "reasoning": "Because the documentation events are explicitly tied to incident ENG‑111 and no record connects them to a Day 16 incident, the causal chain from Day 16 incident to the documentation is missing. Hence the outcome would remain unchanged, and the missing link constitutes a knowledge gap." +}" +2026-04-02 21:02:51: task_name="None", task="If the Day 4 incident reported in Slack had not occurred, would the postmortem page in Confluence not have been written and the root cause analysis in Jira not exist? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:57: task_name="None", task="If the Day 4 incident reported in Slack had not occurred, would the postmortem page in Confluence not have been written and the root cause analysis in Jira not exist? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "No explicit documentation links the Day 4 incident reported in Slack to the creation of a postmortem page in Confluence or a root‑cause analysis ticket in Jira. The available artifacts do not contain any reference that the incident triggered those documents, so removing the incident would not be shown to affect their existence.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "A thorough search of Slack messages, Confluence pages, and Jira tickets for references to a \"Day 4 incident\" and any subsequent postmortem or RCA produced no evidence of a causal relationship. Because the data do not document that the incident led to the documentation, we must conclude that the outcome would not change based on the available information." +}" +2026-04-02 21:02:57: task_name="None", task="Would Jenna have known about the inbound email from Peak Performance Labs titled “Re: Re: Request for Enhanced Real‑Time Performance Data Integration” by Day 19, given that she has access to Confluence, email, Salesforce, Slack, and Zoom but inbound external email events are visible only to direct participants and she was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Jenna as of Day 19. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:02:59: task_name="None", task="Would Jenna have known about the inbound email from Peak Performance Labs titled “Re: Re: Request for Enhanced Real‑Time Performance Data Integration” by Day 19, given that she has access to Confluence, email, Salesforce, Slack, and Zoom but inbound external email events are visible only to direct participants and she was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Jenna as of Day 19. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Jenna does not have visibility into inbound external email events unless she is a direct participant. The email titled “Re: Re: Request for Enhanced Real‑Time Performance Data Integration” was sent to internal recipients not including Jenna, so she would not have seen it in her email system by Day 19.", + "evidence_artifacts": [], + "blocked_subsystems": ["email"] +}" +2026-04-02 21:02:59: task_name="None", task="Would Vince have known about the knowledge gap in the undocumented domain (triggered by an unknown issue left by a departed employee and detected via reviewer_audit) as of Day 25, given his access to Confluence, email, Salesforce, Slack, and Zoom but not to Git? + +IMPORTANT: You are answering from the perspective of Vince as of Day 25. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:06: task_name="None", task="Would Vince have known about the knowledge gap in the undocumented domain (triggered by an unknown issue left by a departed employee and detected via reviewer_audit) as of Day 25, given his access to Confluence, email, Salesforce, Slack, and Zoom but not to Git? + +IMPORTANT: You are answering from the perspective of Vince as of Day 25. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Within Vince's visibility cone (Confluence, email, Salesforce, Slack, Zoom) there are no artifacts indicating awareness of a knowledge gap in the undocumented domain, nor any mention of a reviewer_audit detection of an issue left by a departed employee. The only accessible artifact found (CONF-ENG-007) is a design document authored by Vince on Day 1 and does not reference the gap or audit. Therefore, as of Day 25 Vince would not have known about this knowledge gap.", + "evidence_artifacts": ["CONF-ENG-007"], + "blocked_subsystems": ["Git"] +}" +2026-04-02 21:03:06: task_name="None", task="If Jordan had not departed the company on Day 12, would their active tickets in the internal ticketing system and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:11: task_name="None", task="If Jordan had not departed the company on Day 12, would their active tickets in the internal ticketing system and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available documents do not contain any explicit statement that Jordan's departure on Day 12 triggered the reassignment of their active tickets or escalation responsibilities. No ticketing system logs, handover notes, or communication records link the departure event to a reassignment action.", + "causal_mechanism": "involves_gap", + "actors": ["Jordan"], + "reasoning": "Because the data lacks a documented causal link between Jordan's departure and ticket/escalation reassignment, we cannot assert that removing the departure would have prevented the reassignment. The absence of evidence constitutes a knowledge gap, so the counterfactual outcome cannot be confirmed." +}" +2026-04-02 21:03:11: task_name="None", task="As of Day 29, would Desmond have known about the inbound email from Ethan (Amazon) regarding “Optimizing VPC Networking for Apex Athletics” Current Workloads? + +IMPORTANT: You are answering from the perspective of Desmond as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:17: task_name="None", task="As of Day 29, would Desmond have known about the inbound email from Ethan (Amazon) regarding “Optimizing VPC Networking for Apex Athletics” Current Workloads? + +IMPORTANT: You are answering from the perspective of Desmond as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of all data sources available to Desmond (email, Slack, Confluence, Zoom) for any record of an inbound email from Ethan (Amazon) about “Optimizing VPC Networking for Apex Athletics” returned no results. Because the email does not appear in Desmond's visible inbox or related communications, he would not have been aware of it on Day 29.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:03:17: task_name="None", task="If the postmortem completed on Day 7 had included preventive action items in the git, Jira, and Slack processes, would the subsequent incident likely not have occurred? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:22: task_name="None", task="As of Day 18, could Umji have known about the design discussion about address Kafka throughput, given that she only has access to Confluence, email, Slack, and Zendesk, and the discussion was visible only to its direct participants, with Umji not listed as a participant? + +IMPORTANT: You are answering from the perspective of Umji as of Day 18. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:26: task_name="None", task="As of Day 18, could Umji have known about the design discussion about address Kafka throughput, given that she only has access to Confluence, email, Slack, and Zendesk, and the discussion was visible only to its direct participants, with Umji not listed as a participant? + +IMPORTANT: You are answering from the perspective of Umji as of Day 18. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The design discussion about addressing Kafka throughput was limited to its direct participants, and Umji is not listed as a participant. A search of the artifacts Umami can access (Confluence, email, Slack, Zendesk) up to Day 18 returned no record of this discussion. Therefore, with the available information, Umji could not have known about it.", + "evidence_artifacts": [], + "blocked_subsystems": ["jira", "pr", "zoom", "datadog", "salesforce", "zendesk"] +}" +2026-04-02 21:03:26: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 27 involving Kaitlyn and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:32: task_name="None", task="Was an internal routing of the customer email created in response to the inbound email on Day 27 involving Ingrid and Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:37: task_name="None", task="If the incident on Day 4 had not occurred, would no coordination with Amazon Web Services have been needed at all? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:41: task_name="None", task="As of Day 11, would Umji have known about the inbound email from Peak Performance Labs titled “Re: Re: Request for Enhanced Real-Time Performance Metrics Dashboard,” given that inbound external email events are visible only to direct participants and Umji was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Umji as of Day 11. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:44: task_name="None", task="As of Day 11, would Umji have known about the inbound email from Peak Performance Labs titled “Re: Re: Request for Enhanced Real-Time Performance Metrics Dashboard,” given that inbound external email events are visible only to direct participants and Umji was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Umji as of Day 11. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Inbound external email events are only visible to direct participants. Umji was not listed as a participant on the email from Peak Performance Labs titled “Re: Re: Request for Enhanced Real-Time Performance Metrics Dashboard.” A search of Umji's accessible email artifacts returned no matching email, indicating Umji did not receive or see this message.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:03:44: task_name="None", task="Was a Zendesk support ticket created in response to the routing of the customer email on Day 8 involving Darius and Marcus? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:03:51: task_name="None", task="Was a Zendesk support ticket created in response to the routing of the customer email on Day 8 involving Darius and Marcus? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for Zendesk tickets, emails, and other records referencing Day 8, Darius, and Marcus. The email records show Darius‑related messages on Day 1 and Day 3, and a Day 8 email from Maya (not Darius). No Zendesk ticket entries were found linking Darius and Marcus, nor any ticket created on Day 8 in response to a routed customer email. Therefore, there is no evidence that a Zendesk support ticket was created for that scenario." +}" +2026-04-02 21:03:52: task_name="None", task="Was a Confluence documentation page created to address the knowledge gap detected on Day 19 involving Jordan? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:00: task_name="None", task="Was a Confluence documentation page created to address the knowledge gap detected on Day 19 involving Jordan? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I examined all events for Day 19 using get_events_for_day and found no mention of Jordan. I then searched the artifact corpus for any Jordan-related items after Day 19 and for any Confluence pages mentioning Jordan; both searches returned no results. The only Confluence page created on Day 19 (CONF-ENG-078) was authored by Jax and does not involve Jordan. Therefore, there is no evidence that a Confluence documentation page was created to address a knowledge gap involving Jordan on Day 19." +}" +2026-04-02 21:04:00: task_name="None", task="As of Day 20, would Jordan have known about the inbound email from the Japan Olympic Committee titled “Re: Re: Re: Re: Request for Real‑Time Alert Capability Enhancement”? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 20. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:06: task_name="None", task="As of Day 20, would Jordan have known about the inbound email from the Japan Olympic Committee titled “Re: Re: Re: Re: Request for Real‑Time Alert Capability Enhancement”? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 20. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Within Jordan's visibility cone (email, slack, confluence, datadog, git, jira, zoom) there is no inbound email from the Japan Olympic Committee with the exact title \"Re: Re: Re: Re: Request for Real-Time Alert Capability Enhancement\". Searches of the email repository for relevant keywords and the specific subject line returned no matching artifacts. Therefore Jordan could not have been aware of such an email by Day 20.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:04:06: task_name="None", task="If Morgan had not departed the company on Day 24, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:11: task_name="None", task="If the incident on Day 24 had not occurred, would no coordination with Jenkins CI have been needed at all? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:17: task_name="None", task="If the incident on Day 24 had not occurred, would no coordination with Jenkins CI have been needed at all? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available records show separate communications about Jenkins CI issues (e.g., emails on days 6, 9, 11, 12) that are unrelated to the Day 24 incident, which concerns a policy‑as‑code discussion with HashiCorp. No document explicitly links the Day 24 incident to the need for coordination with Jenkins CI. Therefore, even if the Day 24 incident had not occurred, coordination with Jenkins CI would still have been required based on the other independent issues.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "A causal link of the form 'incident → coordination with Jenkins CI' is not present in the data. The incident on Day 24 is documented only as a vendor inquiry about Terraform and CI/CD pipelines, while Jenkins‑related coordination appears in earlier, distinct communications. Because the coordination originates from separate incidents, removing the Day 24 incident would not eliminate the need for Jenkins CI coordination." +}" +2026-04-02 21:04:17: task_name="None", task="As of Day 14, would Blake have been aware of the inbound email from Ethan (Amazon) concerning “Optimizing VPC Networking for Apex Athletics”? + +IMPORTANT: You are answering from the perspective of Blake as of Day 14. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:24: task_name="None", task="As of Day 14, would Blake have been aware of the inbound email from Ethan (Amazon) concerning “Optimizing VPC Networking for Apex Athletics”? + +IMPORTANT: You are answering from the perspective of Blake as of Day 14. This actor only has access to: confluence, email, salesforce, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email titled “Optimizing VPC Networking for Apex Athletics” (artifact ext_email_ethan_6_6) was addressed to Jax, not Blake. There is no evidence in Blake's accessible email, Slack, Confluence, Salesforce, or Zoom records that Blake was CC'd, forwarded the email, or otherwise received it. Therefore, by Day 14 Blake would not have been aware of this email.", + "evidence_artifacts": ["ext_email_ethan_6_6"], + "blocked_subsystems": [] +}" +2026-04-02 21:04:24: task_name="None", task="If the Day 12 incident reported in Jira and discussed on Slack had not occurred, would no coordination with the Firebase SDK have been needed at all? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:29: task_name="None", task="If the Day 12 incident reported in Jira and discussed on Slack had not occurred, would no coordination with the Firebase SDK have been needed at all? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit statement that the Day 12 incident caused the need for coordination with the Firebase SDK. Without a documented causal link, we cannot conclude that eliminating the incident would have removed the need for SDK coordination.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Searches of Jira, Slack, and other documents for references to a Day 12 incident and Firebase SDK yielded no records establishing a direct cause‑effect relationship. The absence of such evidence represents a knowledge gap, meaning the claim that the incident necessitated SDK coordination is not substantiated by the data." +}" +2026-04-02 21:04:29: task_name="None", task="As of Day 28, would Bill have known about the inbound email from Ethan (Peak Performance Labs) regarding “Request for Enhanced Data Integration Capability”? + +IMPORTANT: You are answering from the perspective of Bill as of Day 28. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:04:36: task_name="None", task="As of Day 28, would Bill have known about the inbound email from Ethan (Peak Performance Labs) regarding “Request for Enhanced Data Integration Capability”? + +IMPORTANT: You are answering from the perspective of Bill as of Day 28. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ethan (Peak Performance Labs) titled 'Request for Enhanced Data Integration Capability' is addressed to Marcus, the liaison, as indicated by the email content and metadata. Bill's accessible systems include email, but there is no evidence that this email was forwarded to or visible in Bill's mailbox. Consequently, Bill would not have known about this email by Day 28.", + "evidence_artifacts": ["ext_email_ethan_10_9"], + "blocked_subsystems": [] +}" +2026-04-02 21:04:36: task_name="None", task="Did we create a Confluence documentation page in response to the knowledge gap detected on Day 6 involving Sam? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:13:33: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:13:41: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain an explicit statement that the Day 8 incident directly triggered the creation of a postmortem page in Confluence or a root‑cause analysis entry in Jira or Slack. Without documented evidence of that causal chain, we cannot conclude that removing the incident would have prevented those documents from existing.", + "causal_mechanism": "involves_gap", + "actors": [ + "Hanna", + "Engineering team" + ], + "reasoning": "Search results show a Day 8 incident (ENG‑106) but no linked postmortem page or analysis record. Since the required causal link is missing, the counterfactual effect cannot be established, leading to a knowledge gap classification." +}" +2026-04-02 21:13:41: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:13:49: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available records show Alex authored PR‑105 on Day 5, but there is no documented review comment or change request from Taylor. Consequently, the revision Alex made before the merge cannot be directly linked to a request from Taylor; the causal link is absent in the data.", + "causal_mechanism": "involves_gap", + "actors": ["Alex", "Taylor"], + "reasoning": "Since the dataset contains no explicit evidence that Taylor requested changes on the pull request, we cannot establish that Taylor's actions caused Alex's revision. Removing a non‑existent cause would not alter the observed effect, indicating a knowledge gap rather than a confirmed causal relationship." +}" +2026-04-02 21:13:49: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:13:55: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:01: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:11: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ravi was sent to Nadia, not John, and there is no evidence that it was forwarded to John or that John was directly notified. While a Jira ticket (ORG-103) referencing the email exists, there is no indication that John was assigned, mentioned, or otherwise alerted about this ticket. Therefore, based on the available artifacts, John would not have been aware of the email on Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6", "ORG-103"], + "blocked_subsystems": [] +}" +2026-04-02 21:14:11: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:17: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of Patty's email, Slack, and Confluence artifacts up to Day 29 returned no record of an inbound email titled “Re: Final proposal & next steps for Gridiron Pro Football”. Without any evidence of receipt or discussion of that email in the systems Patty can access, we conclude she would not have known about it by Day 29.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:14:17: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:24: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of Jordan's accessible systems (Confluence, Jira, Slack, etc.) for any mention of a design discussion about the Define automation framework roadmap on Day 2 returned no results. No artifacts reference that discussion, so Jordan would not have been aware of it.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 21:14:24: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:29: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any documented event linking Vince's departure on Day 3 to a reassignment of his active tickets or escalation responsibilities. No record of a departure event, ticket hand‑off, or escalation transfer involving Vince is present.", + "causal_mechanism": "involves_gap", + "actors": ["Vince"], + "reasoning": "A thorough search of the knowledge base returned no entries describing Vince's departure, nor any subsequent ticket or escalation reassignment. Because the causal link is undocumented, we cannot assert that removing the departure would have prevented a reassignment; thus the outcome would not be shown to change." +}" +2026-04-02 21:14:29: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:33: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The inbound email from Maya (Datadog) on Day 21 was the trigger for creating Jira ticket ORG‑145, which records the vendor request to optimise alert thresholds. Without that email, the ticket would not have been opened.", + "causal_mechanism": "email_dropped", + "actors": ["Maya", "Jax"], + "reasoning": "Artifact data shows an email (ext_email_maya_21_6) received on Day 21 and a Jira ticket (ORG‑145) created on the same day with metadata linking it to a vendor email from Maya. This explicit linkage indicates the ticket was generated because of the email. Therefore, removing the email would have prevented the ticket’s creation." +}" +2026-04-02 21:14:33: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 21:14:39: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of all artifacts accessible to Umji (Confluence, email, Slack, Zendesk) returned no records mentioning the knowledge gap in the undocumented domain, the unknown issue left by a departed employee, or the reviewer_audit detection. Without any documented evidence in the systems Umji can access, she would not have been aware of this gap as of Day 6.", + "evidence_artifacts": [], + "blocked_subsystems": ["git"] +}" +2026-04-02 21:14:40: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 16 involving Kaitlyn, Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:07:26: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:07:33: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:07:41: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available records show Alex submitted PR-105 on Day 5, but there is no documented evidence of Taylor reviewing the PR on Day 6 or requesting changes that prompted Alex to revise the implementation. Without an explicit link between Taylor's review actions and Alex's subsequent revision, we cannot assert that Taylor's approval (or lack of change requests) would have prevented Alex's revision before merge.", + "causal_mechanism": "involves_gap", + "actors": ["Taylor", "Alex"], + "reasoning": "The data set contains the PR metadata but no Slack messages, review comments, or change‑request logs tying Taylor's actions to Alex's revision. This missing documentation constitutes a knowledge gap, meaning the causal relationship is undocumented. Consequently, removing the supposed cause (Taylor approving without changes) cannot be shown to alter the effect (Alex revising), leading to a determination that the outcome would not have changed based on the available evidence." +}" +2026-04-02 22:07:42: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:07:52: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I queried all events for Day 14 and searched the artifact corpus for any mention of routing involving Ravi and Nadia. The Day 14 event list includes an inbound external email from Ravi (with Nadia listed as an actor) but the entry is of type \"inbound_external_email\" and lacks any \"routed\" tag or separate routing record. No additional artifacts or events indicate that an internal routing was created for this email. Therefore, there is no evidence that an internal routing for the inbound email involving Ravi and Nadia was created." +}" +2026-04-02 22:07:53: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:08:00: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I examined all Day 11 events and artifacts. The only Confluence page created on Day 11 was CONF-ENG-042 by Jax, unrelated to Sam or Chloe. Searches for Confluence artifacts authored by Sam after Day 11, and for any artifacts mentioning a 'knowledge gap' involving Sam and Chloe, returned no results. No record shows a Confluence documentation page created specifically to address a knowledge gap detected on Day 11 with Sam and Chloe." +}" +2026-04-02 22:08:00: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:08:09: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The only evidence is the inbound email artifact (ID ext_email_ravi_4_6) showing Ravi's message arrived on Day 4. The artifact does not specify the recipient, and there is an outbound acknowledgment sent by Nadia, indicating the email was likely addressed to her or a shared vendor inbox. No records show John receiving, forwarding, or discussing this email in Slack, Jira, or any other system he can access. Therefore, based on available evidence, John would not have been aware of the email as of Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6"], + "blocked_subsystems": [] +}" +2026-04-02 22:08:09: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:08:15: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "No inbound email from Gridiron Pro Football with the title “Re: Final proposal & next steps for Gridiron Pro Football” exists in the email artifacts accessible to Patty up to Day 29. The only inbound email from Gridiron Pro Football found is titled “Re: Next steps on Apex Athletics proposal for Gridiron Pro”, which does not match the specified title, indicating Patty could not have known about the requested email.", + "evidence_artifacts": [ + "customer_reply_gridiron_pro_football_OPP-1001_17" + ], + "blocked_subsystems": [] +}" +2026-04-02 22:08:15: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:08:22: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "A search of all artifacts accessible to Jordan on Day 2 (Confluence pages, Jira tickets, and Slack messages) yielded no documents or communications containing the phrase 'Define automation framework roadmap' or any discussion of a design related to that roadmap. The only design-related items found were unrelated Confluence design docs (CONF-ENG-022, CONF-ENG-023) and Jira tickets about automated tests. Since no evidence of the specific roadmap discussion exists in Jordan's visibility cone, Jordan could not have known about it on Day 2.", + "evidence_artifacts": [ + "CONF-ENG-022", + "CONF-ENG-023" + ], + "blocked_subsystems": [] +}" +2026-04-02 22:08:22: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:08:28: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available documents do not contain any explicit statement linking Vince's departure on Day 3 to the reassignment of active tickets or escalation responsibilities. No record of ticket ownership changes, escalation handoffs, or departure notices referencing Vince was found.", + "causal_mechanism": "involves_gap", + "actors": ["Vince"], + "reasoning": "Because the corpus lacks a documented incident describing Vince's departure and subsequent ticket/escalation reassignment, the causal relationship cannot be established. Therefore, we cannot conclude that the outcome would have differed if Vince had not left." +}" +2026-04-02 22:08:28: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:08:33: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The Jira ticket ORG-145 was created on Day 21 with metadata indicating it originated from a vendor email sent by Maya (Datadog). The ticket’s existence is directly tied to the receipt of that email; without the email, there would have been no trigger to open the ticket.", + "causal_mechanism": "email_dropped", + "actors": ["Maya"], + "reasoning": "The artifact data shows a clear causal chain: Maya’s email (the communication) → creation of Jira ticket ORG-145. Since the ticket’s metadata cites the vendor email as its source, removing the email would have prevented the ticket from being generated." +}" +2026-04-02 22:08:33: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:29:23: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:29:34: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no documented evidence that the Day 8 incident (ENG-118) directly triggered the creation of a postmortem page in Confluence or a root cause analysis entry in Jira or Slack. The available event logs show the incident opening and related communications, but no subsequent postmortem artifact or explicit reference linking the incident to such documentation.", + "causal_mechanism": "involves_gap", + "actors": ["Jax", "Alex", "Deepa", "Jordan", "Morgan", "Chloe", "others involved in incident communications"], + "reasoning": "Since the data does not contain a postmortem page or root cause analysis artifact tied to the Day 8 incident, we cannot establish a concrete causal link. Therefore, removing the incident would not be shown to change the existence of those documents, making the outcome unchanged." +}" +2026-04-02 22:29:34: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:29:40: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit record that Taylor's approval of the pull request on Day 6 (without requesting changes) prevented Alex from revising the implementation before merge. Without documented evidence of this causal relationship, we cannot assert that removing Taylor's approval would have altered Alex's behavior.", + "causal_mechanism": "involves_gap", + "actors": ["Taylor", "Alex"], + "reasoning": "Searches of confluence pages, Slack threads, PR records, and persona skill profiles returned no mention of a Day 6 pull request, Taylor's approval action, or Alex's subsequent revision. The absence of this information constitutes a knowledge gap, preventing confirmation of a causal link. Therefore, the outcome (Alex revising the implementation) would not be shown to change if the cause (Taylor's approval) were removed." +}" +2026-04-02 22:29:41: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:29:48: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the document corpus for any records after Day 14 that mention both Ravi and Nadia together with terms like \"inbound email\" and \"routing\". No such artifacts were found. I also queried the events for Day 14 specifically for email events and found none. A broader search for the keyword \"routing\" after Day 14 also returned no results. Therefore, there is no evidence that an internal routing for an inbound email involving Ravi and Nadia was created on Day 14." +}" +2026-04-02 22:29:49: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:29:55: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:40:26: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:40:33: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no explicit evidence in the available artifacts linking the Day 8 incident to the creation of a postmortem page in Confluence or a root‑cause analysis in Jira or Slack. Without a documented causal chain, we cannot conclude that the absence of the incident would have prevented those documents from being authored.", + "causal_mechanism": "involves_gap", + "actors": ["Day 8 incident"], + "reasoning": "The search returned only a Jira ticket (ENG‑106) describing a deployment task on Day 8, with no mention of a postmortem, Confluence page, or Slack discussion. Since the required causal link is missing, the counterfactual claim cannot be supported; thus we treat the outcome as unchanged (i.e., we cannot assert it would have been different)." +}" +2026-04-02 22:40:33: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:40:41: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no documented evidence that Taylor requested changes on the pull request; the available artifacts only show the PR authored by Alex (PR-105) and no review comments or Slack messages indicating a change request. Without an explicit causal link, we cannot conclude that Taylor's request caused Alex to revise the implementation, so removing a non‑existent cause would not change the outcome.", + "causal_mechanism": "involves_gap", + "actors": ["Alex", "Taylor"], + "reasoning": "The data set includes the PR details but lacks any record of Taylor's review actions. Since the causal chain (Taylor requesting changes → Alex revising) is not documented, the link is a knowledge gap. Consequently, we cannot assert that approving without changes would have prevented Alex's revision." +}" +2026-04-02 22:40:41: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:40:47: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I queried the day‑14 events and found an inbound_external_email entry involving Ravi (and Nadia listed as an actor) but there is no separate event indicating an internal routing action for that email. No record of a routing step (e.g., a 'email_routed' or similar internal routing type) was found, so the internal routing was not created." +}" +2026-04-02 22:40:48: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:40:50: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="I apologize, but I couldn't generate a proper response. Please try again." +2026-04-02 22:40:50: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:40:57: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic” exists on Day 4 (artifact ext_email_ravi_4_6). However, there is no evidence that John was the recipient or that the email was forwarded to him. The only related activity is an outbound acknowledgment sent by Nadia, not John. Since John’s visibility cone includes email but we lack any record of him receiving or reading this message, we conclude he would not have been aware of it on Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6"], + "blocked_subsystems": [] +}" +2026-04-02 22:40:57: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:05: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Patty's accessible sources (email, confluence, slack, zoom) contain no inbound email from Gridiron Pro Football with the exact title “Re: Final proposal & next steps for Gridiron Pro Football” up to Day 29. The only inbound emails from Gridiron Pro Football in the search results have different subjects (e.g., “Re: Next steps on Apex Athletics proposal for Gridiron Pro”). Therefore, Patty would not have known about the specified email as of Day 29.", + "evidence_artifacts": [ + "sales_email_OUTREACH-17-marcus_17", + "customer_reply_gridiron_pro_football_OPP-1001_17", + "reply_customer_gridiron_pro_football_17", + "sales_email_OUTREACH-8-marcus_8", + "sales_email_SALES-103_2", + "sales_email_SALES-109_12" + ], + "blocked_subsystems": [] +}" +2026-04-02 22:41:05: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:13: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of Jordan's accessible systems (Confluence, Jira, Slack, etc.) for any mention of a design discussion about the Define automation framework roadmap up to Day 2 returned no relevant artifacts. The available documents on Day 2 pertain to other design topics and tasks, but none reference the Define automation framework roadmap, indicating Jordan would not have been aware of that discussion.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 22:41:13: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:18: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:22: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The inbound email from Maya (Datadog) on Day 21 requested optimization of alert thresholds. This external vendor request triggered the creation of Jira ticket ORG‑145 to track and fulfill the request.", + "causal_mechanism": "incident_coordination", + "actors": ["Maya", "Jax"], + "reasoning": "The Jira ticket ORG‑145 (day 21) is explicitly linked to a vendor email from Maya on the same day, indicating the ticket was opened as a direct response to that email. If the email had not been received, there would be no documented trigger for the ticket, so the ticket would not have been created." +}" +2026-04-02 22:41:22: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:28: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of all systems that Umji can access (Confluence, email, Slack, Zendesk) returned no records mentioning a knowledge gap in the undocumented domain, the unknown issue left by a departed employee, or the reviewer_audit detection. Without any documented communication, ticket, or discussion in these channels, Umji would have had no way to become aware of the gap by Day 6. The missing information resides in systems outside her visibility (e.g., Git or other internal audit logs), which she cannot access.", + "evidence_artifacts": [], + "blocked_subsystems": ["git"] +}" +2026-04-02 22:41:29: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 16 involving Kaitlyn, Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:34: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 16 involving Kaitlyn, Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for Confluence pages created after Day 16 that mention Kaitlyn, Jax, or a knowledge gap. The only Confluence artifact on Day 16 is CONF-ENG-071, created by Jax (with Morgan, Priya, etc.), but its summary does not reference Kaitlyn or a knowledge gap. No other Confluence pages referencing Kaitlyn or a knowledge gap were found. Therefore, there is no evidence that a Confluence documentation page was created in response to the knowledge gap involving Kaitlyn and Jax." +}" +2026-04-02 22:41:34: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Zoe, Ben? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:40: task_name="None", task="As of Day 18, would Sanjay have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Sanjay as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:41:47: task_name="None", task="As of Day 18, would Sanjay have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Sanjay as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of Sanjay's accessible email artifacts for keywords related to the inbound email from Ingrid (GitHub) titled “Enterprise Audit Log Access & Retention – Next Steps” returned no matching messages. Therefore Sanjay would not have been aware of this email by Day 18.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 22:41:47: task_name="None", task="As of Day 23, could Tom have been aware of the design discussion about Ops sync on handoff, given that he has access to Confluence, email, Slack, and Zoom, but such design discussion events are visible only to direct participants and he was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Tom as of Day 23. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:48:15: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:48:24: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no documented evidence that a postmortem page was created in Confluence or that a root cause analysis was recorded in Jira or Slack as a result of the Day 8 incident. Searches of Confluence, Slack, and Jira after Day 8 returned no such artifacts, so the existence of those documents cannot be confirmed, and thus we cannot assert that the incident caused them.", + "causal_mechanism": "involves_gap", + "actors": ["Incident Team"], + "reasoning": "The available data includes only a Jira ticket (ENG-106) related to a deployment on Day 8, with no associated postmortem or analysis artifacts found in Confluence or Slack. Because the documentation does not exist in the records, removing the incident would not have a demonstrable effect on the creation of those documents, indicating a knowledge gap rather than a proven causal relationship." +}" +2026-04-02 22:48:24: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:48:31: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available records show Alex submitted PR‑105 on Day 5, but there is no documented evidence that Taylor reviewed the PR on Day 6, requested changes, or that Alex subsequently revised the implementation because of such a request. Because the causal link (Taylor’s change request → Alex’s revision) is not present in the data, removing a hypothetical request would not alter the observed outcome.", + "causal_mechanism": "involves_gap", + "actors": ["Taylor", "Alex"], + "reasoning": "Our search of PR metadata and related Slack communications returned no entries indicating that Taylor made a review comment or change request on Day 6, nor any commit or comment from Alex indicating a revision prompted by such feedback. Without an explicit documented cause, the counterfactual cannot be supported; the effect (Alex revising) is not linked to the cause (Taylor’s approval without changes) in the available evidence." +}" +2026-04-02 22:48:32: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:48:38: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I performed multiple searches targeting Day 14, inbound email routing, and the involvement of Ravi and Nadia, both individually and together. The searches returned documents related to Ravi's vendor emails and Nadia's profile, but none referenced an internal routing for an inbound email on Day 14 involving both Ravi and Nadia. No matching records were found in the corpus, indicating that such a routing does not exist." +}" +2026-04-02 22:48:38: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:48:47: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I examined all Day 11 events. The only records of Confluence pages created on Day 11 are CONF-ENG-042, CONF-ENG-043, CONF-HR-003, CONF-ENG-044, and a postmortem CONF-ENG-044. None of these pages reference Sam, Chloe, or a knowledge gap involving them. The only activity involving Sam and Chloe on Day 11 is a Slack async question where Sam asked about a rollout schedule with Chloe present, but no subsequent Confluence creation is linked to that interaction. Searches for \"knowledge gap\" and \"Sam Chloe\" in Confluence artifacts returned no results. Therefore, there is no evidence that a Confluence documentation page was created in response to a knowledge gap detected on Day 11 involving Sam and Chloe." +}" +2026-04-02 22:48:47: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:48:54: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": true, + "reasoning": "John has access to the email system. An inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletics” is recorded on Day 4 (artifact ext_email_ravi_4_6). Since this email resides in John's accessible email subsystem, he could have been aware of it on Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6"], + "blocked_subsystems": [] +}" +2026-04-02 22:48:54: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:54:58: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:06: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit documentation linking the Day 8 incident to the creation of a postmortem page in Confluence or to a root‑cause analysis in Jira or Slack. Without a recorded causal relationship, we cannot assert that the postmortem page existed because of the incident, so removing the incident would not be known to alter the existence of those documents.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "A search of the knowledge base returned Jira incidents, Confluence design documents, and Slack threads, but none referenced a \"Day 8\" incident, a postmortem page, or a root‑cause analysis tied to that incident. Because the causal link is absent, the counterfactual effect cannot be demonstrated; thus the outcome would not be shown to change." +}" +2026-04-02 22:55:06: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:13: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:19: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": true, + "answer": "yes", + "reasoning": "A search for artifacts after day 13 returned two emails on day 14 involving Ravi and Nadia. The inbound email (ext_email_ravi_14_6) is from Ravi, with metadata indicating the liaison is Nadia, meaning the email was internally routed to Nadia. An outbound acknowledgment (ack_vendor_ravi_14) shows Nadia responding, confirming the routing. Therefore, an internal routing for the inbound email on Day 14 involving Ravi and Nadia was created." +}" +2026-04-02 22:55:19: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:25: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for Confluence documentation created after Day 11 that referenced a knowledge gap, Sam, or Chloe. The search returned no results, and the event log for Day 11 also contained no entries indicating a Confluence page was created. Therefore, there is no evidence that a Confluence page was created in response to the knowledge gap involving Sam and Chloe on Day 11." +}" +2026-04-02 22:55:25: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:33: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ravi titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletics” (artifact ext_email_ravi_4_6) is recorded as an inbound vendor email with the liaison listed as Nadia. There is no indication that John was a recipient, CC, or otherwise forwarded the message. No other artifacts show John interacting with this email. Therefore, based on the available email artifacts within John's visibility, John would not have been aware of this email on Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6"], + "blocked_subsystems": [] +}" +2026-04-02 22:55:33: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:39: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 22:55:45: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Search of Jordan's accessible systems (Confluence, Jira, Slack, Email, etc.) for any mention of a design discussion about the \"Define automation framework roadmap\" yielded no artifacts. The returned items relate to other design documents and Jira tickets but do not reference the specific roadmap discussion. Therefore, Jordan would not have had knowledge of that discussion by Day 2.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-02 22:55:45: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:05:20: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:05:25: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:05:32: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit record that Taylor approved a pull request on Day 6, nor that Alex revised the implementation because of a requested change. Without documented evidence of this causal chain, we cannot assert that removing Taylor's approval would have prevented Alex's revision.", + "causal_mechanism": "involves_gap", + "actors": ["Taylor", "Alex"], + "reasoning": "The search results only show unrelated design documents, a Jira issue (ENG-105) dated Day 5, and a Slack DM between Alex and Taylor on Day 3, none of which mention a Day 6 pull‑request approval or a subsequent revision by Alex. Because the required causal link is missing, the counterfactual effect cannot be determined, indicating a knowledge gap." +}" +2026-04-02 23:05:33: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- sf_ownership_lapsed: CRM gap ← employee departure +- departure_reassignment: departure → ticket/escalation shift + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:05:39: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- sf_ownership_lapsed: CRM gap ← employee departure +- departure_reassignment: departure → ticket/escalation shift + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any record of Vince's departure on Day 3 or any subsequent reassignment of active tickets or escalation responsibilities. Without explicit evidence of a departure‑triggered reassignment, there is no demonstrated causal link to evaluate.", + "causal_mechanism": "departure_reassignment", + "actors": ["Vince"], + "reasoning": "A counterfactual assessment requires an explicit causal statement in the data (e.g., \"Vince left on Day 3, and his tickets were reassigned\"). The search of artifacts and daily events returned no such information. Consequently, we cannot assert that removing the departure would alter any ticket or escalation reassignment, leading to a conclusion that the outcome would not change based on the current evidence." +}" +2026-04-02 23:05:39: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:05:44: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- recurrence_of: incident ← prior unresolved incident (root cause was known but not fixed) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- email_dropped: communication failure ← routing gap +- sf_ownership_lapsed: CRM gap ← employee departure +- zd_escalation_source: incident ← support ticket escalation +- blocker_flagged: blocker → delayed progress +- incident_coordination: incident → external contact +- departure_reassignment: departure → ticket/escalation shift +- assignment_domain_mismatch: planning mismatch → knowledge gap → incident + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The inbound email from Maya on Day 21 triggered the creation of Jira ticket ORG‑145 to track the vendor request. Without that email, the ticket would not have been opened.", + "causal_mechanism": "email_dropped", + "actors": ["Maya", "Jax"], + "reasoning": "Artifact ext_email_maya_21_6 shows Maya's email arrived on Day 21. The Jira ticket ORG‑145, also dated Day 21, has metadata indicating it originated from a vendor email (Maya). No other source for the ticket is present, establishing a direct causal link: the email caused the ticket. Therefore, if the email had not arrived, the ticket would not have been created." +}" +2026-04-02 23:10:54: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:11:00: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "No explicit documentation links the Day 8 incident to the creation of a postmortem page in Confluence or a root cause analysis in Jira/Slack. Without such evidence, the counterfactual removal of the incident cannot be shown to affect those artifacts.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Searches of Slack, Jira, and Confluence for references to “Day 8 incident,” “postmortem,” and related terms returned no records establishing a causal relationship. Therefore the required causal link is missing, indicating a knowledge gap rather than a documented postmortem_from_incident or spawned_doc relationship." +}" +2026-04-02 23:11:00: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:11:06: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:11:12: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:11:18: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the event log for Day 11 and found no events involving a knowledge gap with Sam and Chloe. I then queried the Confluence document corpus for pages after Day 11 containing keywords 'knowledge gap', 'Sam', and 'Chloe' in various combinations. No matching Confluence pages were returned. The broader search for any 'knowledge gap' pages also returned only unrelated documents from earlier days and different authors. Therefore, there is no evidence that a Confluence documentation page was created in response to the Day 11 knowledge gap involving Sam and Chloe." +}" +2026-04-02 23:11:18: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:11:25: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The only artifact matching the query is the inbound email from Ravi (CloudBees) with ID ext_email_ravi_4_6, dated Day 4. The metadata for this email does not specify the recipient, so there is no evidence that John was the intended recipient or that the email was forwarded to him. While John has access to email, without a clear indication that the message was addressed to or visible to John, we cannot conclude that he was aware of it on Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6"], + "blocked_subsystems": [] +}" +2026-04-02 23:11:25: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:11:31: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:22:21: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:22:28: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit statement that the Day 8 incident directly triggered the creation of a postmortem page in Confluence or the root‑cause analysis entries in Jira or Slack. Without a documented causal link, we cannot assert that removing the incident would have prevented those documents from being authored.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Search results return an incident record (ENG‑111) dated day 6, but no records of a postmortem page, Jira root‑cause ticket, or Slack discussion tied to a Day 8 incident. The absence of such documentation indicates a knowledge gap rather than a documented cause‑effect relationship. Consequently, we cannot conclude that the incident’s omission would change the existence of the postmortem or analysis artifacts." +}" +2026-04-02 23:22:29: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:22:33: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:22:40: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for any records mentioning an internal routing for an inbound email on Day 14 that includes both Ravi and Nadia. The search results returned emails on Day 14 (e.g., ack_vendor_lucia_14) but none involving Ravi or Nadia. I also looked for Ravi-related inbound communications (vendor Jira tickets on days 11‑13) and Nadia-related items, but found no email or routing entry on Day 14 linking the two. No confluence or slack threads referenced such a routing. Therefore, there is no evidence that an internal routing for an inbound email on Day 14 involving Ravi and Nadia was created." +}" +2026-04-02 23:22:40: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:39:19: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:39:25: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit statement that the Day 8 incident triggered the creation of a postmortem page in Confluence or a root‑cause analysis in Jira or Slack. No postmortem page or analysis entry referencing the Day 8 incident was found, so the causal chain cannot be confirmed.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Because the corpus lacks documented evidence linking the Day 8 incident to the postmortem or analysis artifacts, we cannot assert that removing the incident would have prevented those documents. The missing documentation represents a knowledge gap rather than a confirmed causal relationship." +}" +2026-04-02 23:39:25: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:39:32: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:39:37: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for any records on Day 14 related to an internal routing for inbound email involving Ravi and Nadia. The get_events_for_day tool for day 14 returned no events. Searches for 'internal routing inbound email Ravi Nadia' and related queries after day 14 returned no results. Additional searches for 'Ravi', 'routing', and 'inbound email' did not surface any relevant entry for day 14. Therefore, there is no evidence that such a routing was created." +}" +2026-04-02 23:39:37: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:39:44: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the event logs for Day 11 and found no events recorded. I then queried the artifact repository for Confluence pages created after Day 11 containing keywords related to the knowledge gap, Sam, and Chloe, and received no matches. A broader search for any Confluence pages mentioning Sam also returned only early‑day pages unrelated to the knowledge gap. Therefore, there is no evidence that a Confluence documentation page was created in response to the Day 11 knowledge gap involving Sam and Chloe." +}" +2026-04-02 23:39:44: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:39:50: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:48:50: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:48:55: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit statement that the Day 8 incident triggered the creation of a postmortem page in Confluence or that the root cause analysis was recorded in Jira or Slack. Without a documented causal link, we cannot conclude that omitting the incident would have prevented those documents.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Search results returned a P1 incident (ENG-111) on day 6 and various Confluence and Slack artifacts, but none reference a Day 8 incident, a postmortem page, or a root cause analysis tied to that incident. Since the required causal evidence is missing, the outcome would not be shown to change." +}" +2026-04-02 23:48:55: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:49:01: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:52:48: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-02 23:52:56: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain an explicit record of a Day 8 incident, nor do they link any Day 8 event to the creation of the postmortem page in Confluence or to root‑cause analysis entries in Jira or Slack. Without a documented incident, we cannot establish that the postmortem was generated as a direct result of that incident.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Searches of Slack, Jira, and Confluence for 'Day 8 incident' returned no matching records. The only postmortem pages found reference incidents on days 7 (ENG-111) and 10 (ENG-118). Since no explicit causal chain linking a Day 8 incident to those documents is present, we cannot claim that the absence of the incident would have prevented the creation of the postmortem or root‑cause analysis." +}" +2026-04-02 23:52:56: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:00:34: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:00:40: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no documented evidence that the Day 8 incident directly caused the creation of a Confluence postmortem page or a root‑cause analysis in Jira or Slack. The available postmortem pages are for incidents on Day 6 (ENG‑111) and Day 10 (ENG‑118), and no Slack threads or Jira tickets reference a Day 8 incident. Consequently, removing the Day 8 incident would not demonstrably affect the existence of those documents.", + "causal_mechanism": "involves_gap", + "actors": [], + "reasoning": "Searches of Confluence, Slack, and Jira artifacts reveal no postmortem or root‑cause analysis tied to the Day 8 incident. Without an explicit causal link, we cannot claim that the incident’s absence would change the documentation, indicating a knowledge gap rather than a documented causal chain." +}" +2026-04-03 00:00:40: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:00:47: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:25:23: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:25:30: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The Day 8 incident (ENG-118) was resolved, after which a postmortem page was authored in Confluence (CONF-ENG-044) and a root‑cause analysis was recorded in the corresponding Jira ticket and discussed in Slack. The creation of these artifacts is explicitly triggered by the incident’s resolution.", + "causal_mechanism": "postmortem_from_incident", + "actors": ["Jax"], + "reasoning": "The data shows a direct link: ENG-118 (day 8) → postmortem page CONF-ENG-044 authored by Jax, and the same incident is tracked in Jira (ENG-118). No separate documentation exists without the incident. Therefore, if the Day 8 incident had not occurred, the postmortem page and root‑cause analysis would not have been produced." +}" +2026-04-03 00:25:30: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:46:32: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:46:38: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:46:44: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "Taylor did not request any changes on the pull request; consequently Alex had no trigger to revise the implementation before merging.", + "causal_mechanism": "review_triggers_revision", + "actors": ["Taylor", "Alex"], + "reasoning": "The PR data (PR-105) shows no comments or change requests from Taylor. Without a review‑triggered revision request, there is no documented event where Alex revised the code. Therefore, even if Taylor had approved the PR on Day 6 without requesting changes, the absence of a revision request means Alex would not have revised the implementation, and removing the (non‑existent) cause would not alter the effect." +}" +2026-04-03 00:46:45: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:46:50: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for any records dated after Day 14 that mention a routing for inbound email involving both Ravi and Nadia, as well as generic searches for 'routing inbound email', 'Day 14', and combinations of 'Ravi' and 'Nadia'. No such documents, tickets, or entries were found. Therefore, there is no evidence that an internal routing for the inbound email on Day 14 involving Ravi and Nadia was created." +}" +2026-04-03 00:46:50: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:46:56: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I queried the event logs for Day 11 and found no knowledge gap detection events involving Sam and Chloe. I also searched the Confluence corpus for any pages authored by Sam or Chloe and for any pages mentioning a Day 11 knowledge gap. No Confluence documents were found that match those criteria. Therefore, there is no evidence that a Confluence documentation page was created in response to the alleged knowledge gap on Day 11." +}" +2026-04-03 00:46:56: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:02: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ravi (ext_email_ravi_4_6) on Day 4 is addressed to the liaison Nadia, as indicated by the metadata. John does not appear as a recipient or cc, and there is no evidence of forwarding or notification to John in the accessible systems. Therefore, John would not have been aware of this email on Day 4.", + "evidence_artifacts": ["ext_email_ravi_4_6", "ack_vendor_ravi_4"], + "blocked_subsystems": [] +}" +2026-04-03 00:47:02: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:09: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:15: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Search across Jordan's accessible systems (Confluence, Jira, Slack, etc.) for any mention of a design discussion about the 'Define automation framework roadmap' up to Day 2 returned no relevant artifacts. Therefore Jordan would not have had exposure to that discussion.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-03 00:47:15: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- sf_ownership_lapsed: CRM gap ← employee departure +- departure_reassignment: departure → ticket/escalation shift +- incident_handoff: escalation chain ← forced handoff on employee departure + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:21: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- sf_ownership_lapsed: CRM gap ← employee departure +- departure_reassignment: departure → ticket/escalation shift +- incident_handoff: escalation chain ← forced handoff on employee departure + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "There is no documented evidence that Vince's departure on Day 3 triggered any reassignment of active tickets or escalation responsibilities. The available artifacts only show a design document authored by Vince on Day 1, with no records of ticket ownership changes or escalation handoffs linked to his departure.", + "causal_mechanism": "departure_reassignment", + "actors": ["Vince"], + "reasoning": "Because the data does not contain any explicit event linking Vince's departure to ticket or escalation reassignment, removing the departure event would not change an effect that is not recorded. Therefore, the outcome (reassignment) would not have been different, as it is not evidenced to have occurred." +}" +2026-04-03 00:47:22: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- incident_coordination: incident → external contact +- jira_from_vendor_email: Jira ticket ← inbound vendor email request +- jira_from_customer_email: Jira ticket ← inbound customer email request + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:27: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- incident_coordination: incident → external contact +- jira_from_vendor_email: Jira ticket ← inbound vendor email request +- jira_from_customer_email: Jira ticket ← inbound customer email request + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The inbound email from Maya (Datadog) on Day 21 triggered the creation of Jira ticket ORG-145 to track the request for optimizing alert thresholds.", + "causal_mechanism": "jira_from_vendor_email", + "actors": ["Maya", "Jax"], + "reasoning": "Evidence shows an email from Maya on Day 21 (ext_email_maya_21_6) and a Jira ticket (ORG-145) created on the same day with metadata indicating its source as a vendor email from Maya. This explicit link demonstrates that the ticket was generated because of the vendor email. Removing the email would eliminate the cause, so the ticket would not have been created." +}" +2026-04-03 00:47:27: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:33: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Umji's visibility is limited to Confluence, email, Slack, and Zendesk. A search of all artifacts in these systems up to Day 6 returns no mention of a knowledge gap in an undocumented domain, no reference to an unknown issue left by a departed employee, and no detection via reviewer_audit. The only artifacts involving Umji are a Slack async question thread (day 4) and generic design documents, none of which discuss the gap. Since the reviewer_audit system and the departed employee's notes are not part of Umji's accessible subsystems, she would not have been presented with the information needed to know about the gap.", + "evidence_artifacts": [ + "slack_engineering_backend_2026-03-05T12:15:00", + "CONF-ENG-004", + "CONF-ENG-007" + ], + "blocked_subsystems": [ + "git", + "reviewer_audit", + "hr_archive" + ] +}" +2026-04-03 00:47:34: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 16 involving Kaitlyn, Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:39: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Zoe, Ben? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:44: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Zoe, Ben? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the corpus for any Confluence documentation pages created after Day 25 that mention a knowledge gap involving Zoe and Ben, using multiple queries (\"knowledge gap Zoe Ben\", \"Zoe Ben\", \"knowledge gap\", \"Zoe\") and also retrieved events for Day 25. All searches returned no results, indicating that no such Confluence page exists in the available records." +}" +2026-04-03 00:47:44: task_name="None", task="As of Day 18, would Sanjay have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Sanjay as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 00:47:53: task_name="None", task="As of Day 18, would Sanjay have known about the inbound email from Ingrid (GitHub) regarding “Enterprise Audit Log Access & Retention – Next Steps”? + +IMPORTANT: You are answering from the perspective of Sanjay as of Day 18. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "A search of Sanjay's accessible email archive (the only system that could contain the inbound email) up to Day 18 returned no messages from Ingrid at GitHub with the subject “Enterprise Audit Log Access & Retention – Next Steps”. Therefore, Sanjay would not have been aware of such an email.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-03 00:47:53: task_name="None", task="As of Day 23, could Tom have been aware of the design discussion about Ops sync on handoff, given that he has access to Confluence, email, Slack, and Zoom, but such design discussion events are visible only to direct participants and he was not listed as a participant? + +IMPORTANT: You are answering from the perspective of Tom as of Day 23. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:47:27: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:47:35: task_name="None", task="If the Day 8 incident had not occurred, would the postmortem page not have been written in Confluence and the root cause analysis not exist in Jira or Slack? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- involves_gap: incident ← knowledge gap (information was missing/undocumented) +- spawned_doc: confluence ← design discussion (documentation resulted from a specific meeting) +- postmortem_from_incident: postmortem created ← incident resolved + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The occurrence of the Day 8 incident (ENG‑118) directly triggered the creation of a postmortem page in Confluence (CONF‑ENG‑044) and the documentation of the root‑cause analysis in the associated Jira ticket and Slack discussion. Without the incident, there would be no reason to author the postmortem or record the analysis, so those artifacts would not exist.", + "causal_mechanism": "postmortem_from_incident", + "actors": ["Jax"], + "reasoning": "Evidence shows that ENG‑118 (day 8) is the incident recorded in Jira and Slack, and Jax authored the corresponding postmortem in Confluence (CONF‑ENG‑044). The postmortem and root‑cause analysis entries are explicitly tied to that incident. Removing the incident eliminates the trigger for creating those documents, therefore the outcome (postmortem page and analysis) would change." +}" +2026-04-03 01:47:35: task_name="None", task="If Taylor had approved the pull request on Day 6 without requesting changes, would Alex not have revised the implementation before merge? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- blocker_flagged: blocker → delayed progress +- pr_gap_detected: knowledge gap ← undocumented domain found during PR review +- review_triggers_revision: PR revision requested ← code review + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:47:42: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:47:48: task_name="None", task="Did we create an internal routing for the inbound email on Day 14 involving Ravi and Nadia? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "Searches across email, confluence, and ticket artifacts for Day 14 inbound email involving Ravi and Nadia returned the inbound email (ext_email_ravi_14_6) and its outbound acknowledgment (ack_vendor_ravi_14), but no document or record describing an internal routing created for that inbound email. No routing-specific entry was found in the corpus." +}" +2026-04-03 01:47:49: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:47:54: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 11 involving Sam and Chloe? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "exists": false, + "answer": "no", + "reasoning": "I searched the artifact repository for any Confluence pages created after Day 11 that mention Sam, Chloe, or a knowledge gap, and also retrieved the event log for Day 11. All searches returned no results, indicating that no Confluence documentation page was created in response to the knowledge gap involving Sam and Chloe on Day 11." +}" +2026-04-03 01:47:54: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:01: task_name="None", task="As of Day 4, would John have been aware of the inbound email from Ravi (CloudBees) titled “Immediate Action Required – Update Docker Registry Credentials for Apex Athletic”? + +IMPORTANT: You are answering from the perspective of John as of Day 4. This actor only has access to: confluence, datadog, email, jira, salesforce, slack, zendesk, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "The inbound email from Ravi (ext_email_ravi_4_6) exists on Day 4, but there is no evidence that John was a recipient, was CC'd, or accessed the email. The only related activity is an outbound reply sent by Nadia (ack_vendor_ravi_4), indicating the email was handled by another liaison. Without records of John viewing or being notified about this email, we cannot conclude that he was aware of it.", + "evidence_artifacts": [ + "ext_email_ravi_4_6", + "ack_vendor_ravi_4" + ], + "blocked_subsystems": [] +}" +2026-04-03 01:48:01: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:08: task_name="None", task="As of Day 29, would Patty have known about the inbound email from Gridiron Pro Football titled “Re: Final proposal & next steps for Gridiron Pro Football”? + +IMPORTANT: You are answering from the perspective of Patty as of Day 29. This actor only has access to: confluence, email, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Search of Patty's email artifacts up to Day 29 returned no inbound email from Gridiron Pro Football with the exact subject “Re: Final proposal & next steps for Gridiron Pro Football”. The only relevant emails involving Gridiron Pro Football are on Day 17 (customer reply and subsequent replies) and earlier outbound messages, none matching the specified title. Additionally, the day‑29 email events list shows no inbound email received. Therefore, Patty would not have known about that specific inbound email as of Day 29.", + "evidence_artifacts": [ + "customer_reply_gridiron_pro_football_OPP-1001_17", + "reply_customer_gridiron_pro_football_17" + ], + "blocked_subsystems": [] +}" +2026-04-03 01:48:08: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:15: task_name="None", task="As of Day 2, would Jordan have known about the design discussion regarding the Define automation framework roadmap? + +IMPORTANT: You are answering from the perspective of Jordan as of Day 2. This actor only has access to: confluence, datadog, email, git, jira, slack, zoom. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "No accessible artifacts (confluence pages, Jira tickets, Slack messages, emails, Git commits, Datadog alerts, or Zoom recordings) within Jordan's visibility cone mention a design discussion about the 'Define automation framework roadmap' by the end of Day 2. The searches across the allowed systems returned unrelated design docs and unrelated Jira items, but none referencing the specific roadmap discussion, indicating Jordan would not have been aware of it at that time.", + "evidence_artifacts": [], + "blocked_subsystems": [] +}" +2026-04-03 01:48:15: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- sf_ownership_lapsed: CRM gap ← employee departure +- departure_reassignment: departure → ticket/escalation shift +- incident_handoff: escalation chain ← forced handoff on employee departure + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:21: task_name="None", task="If Vince had not departed the company on Day 3, would their active tickets and escalation responsibilities not have been reassigned? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- sf_ownership_lapsed: CRM gap ← employee departure +- departure_reassignment: departure → ticket/escalation shift +- incident_handoff: escalation chain ← forced handoff on employee departure + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": false, + "mechanism": "The available artifacts do not contain any explicit record of Vince departing on Day 3 nor any documented reassignment of his active tickets or escalation responsibilities. Without a stated departure event and its associated handoff, there is no observable causal link to assess.", + "causal_mechanism": "departure_reassignment", + "actors": ["Vince"], + "reasoning": "A counterfactual assessment requires an explicit causal statement in the data (e.g., \"Vince left on Day 3, and his tickets were reassigned to X\"). The searched artifacts only show Vince's participation in meetings and his skill profile, with no mention of a departure or ticket handoff. Consequently, we cannot infer that his departure caused any reassignment, and removing a non‑existent cause would not alter the effect." +}" +2026-04-03 01:48:22: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- incident_coordination: incident → external contact +- jira_from_vendor_email: Jira ticket ← inbound vendor email request +- jira_from_customer_email: Jira ticket ← inbound customer email request + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:32: task_name="None", task="If the email from Maya (Datadog) had not arrived on Day 21, would a Jira ticket not have been created to track the request? + +IMPORTANT: This is a counterfactual question. You must identify the explicit causal link in the data — do not speculate. + +You MUST categorize the link using one of the following labels: +- incident_coordination: incident → external contact +- jira_from_vendor_email: Jira ticket ← inbound vendor email request +- jira_from_customer_email: Jira ticket ← inbound customer email request + +Find the cause event and the effect event, then determine whether removing the cause would have changed the effect. + +Respond ONLY with a JSON object matching this schema: +{ + "outcome_changed": , + "mechanism": "", + "causal_mechanism": "", + "actors": ["", ...], + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "outcome_changed": true, + "mechanism": "The inbound email from Maya (Datadog) on Day 21 was the source that triggered the creation of Jira ticket ORG‑145 on the same day. The ticket’s metadata cites a vendor_email source and identifies Maya as the vendor, establishing a direct causal chain: vendor email → Jira ticket.", + "causal_mechanism": "jira_from_vendor_email", + "actors": ["Maya"], + "reasoning": "Evidence shows the email from Maya existed (Day 21) and a Jira ticket (ORG‑145) was created on Day 21 with metadata linking it to a vendor email from Maya. Removing the email would eliminate the identified trigger, so the ticket would not have been created." +}" +2026-04-03 01:48:32: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:38: task_name="None", task="As of Day 6, could Umji, who has access to Confluence, email, Slack, and Zendesk but not Git, have known about the knowledge gap in the undocumented domain that was triggered by an unknown issue, left by a departed employee, and detected via reviewer_audit? + +IMPORTANT: You are answering from the perspective of Umji as of Day 6. This actor only has access to: confluence, email, slack, zendesk. You must not use information from systems outside this list. Accessing artifacts outside the actor's visibility cone is a violation. + +Respond ONLY with a JSON object matching this schema: +{ + "could_actor_have_known": , + "reasoning": "", + "evidence_artifacts": ["", ...], + "blocked_subsystems": ["", ...] + }", agent="Enterprise Knowledge Analyst", status="completed", output="{ + "could_actor_have_known": false, + "reasoning": "Searches of all artifacts accessible to Umji (Confluence, email, Slack, Zendesk) returned no records mentioning a knowledge gap in the undocumented domain, the unknown issue left by a departed employee, or detection via reviewer_audit. Without any documented evidence in the actor's visibility cone, Umji could not have been aware of the gap as of Day 6.", + "evidence_artifacts": [], + "blocked_subsystems": ["git", "audit_logs"] +}" +2026-04-03 01:48:38: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 16 involving Kaitlyn, Jax? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" +2026-04-03 01:48:44: task_name="None", task="Was a Confluence documentation page created in response to the knowledge gap detected on Day 25 involving Zoe, Ben? + +IMPORTANT: This is an absence question. You must search the corpus thoroughly before concluding absence. Do not guess. Show your work in the reasoning field — explain what you searched and what you found. + +Respond ONLY with a JSON object matching this schema: +{ + "exists": , + "answer": "", + "reasoning": "" + }", agent="Enterprise Knowledge Analyst", status="started" diff --git a/src/agent_factory.py b/src/agent_factory.py index e26162a..7220abd 100644 --- a/src/agent_factory.py +++ b/src/agent_factory.py @@ -13,6 +13,6 @@ def make_agent(role: str, goal: str, backstory: str, llm, **kwargs) -> Agent: logging.getLogger("orgforge.agent_factory") - params = {**AGENT_DEFAULTS, "llm": llm} + params = {**AGENT_DEFAULTS, "llm": llm, "verbose": False} params.update(kwargs) - return Agent(role=role, goal=goal, backstory=backstory, verbose=False, **params) + return Agent(role=role, goal=goal, backstory=backstory, **params) diff --git a/src/artifact_registry.py b/src/artifact_registry.py index 747fe71..07ab680 100644 --- a/src/artifact_registry.py +++ b/src/artifact_registry.py @@ -394,7 +394,9 @@ def ticket_summary(self, ticket: Dict, current_day: int) -> TicketSummary: TicketSummary with a .for_prompt() method for prompt injection """ comments = ticket.get("comments", []) - created_day = ticket.get("created_day", current_day) + created_day = ticket.get( + "created_day", ticket.get("in_progress_since", current_day) + ) was_blocked = any( any( kw in c.get("text", "").lower() diff --git a/src/config_loader.py b/src/config_loader.py index bacbb46..341a8b5 100644 --- a/src/config_loader.py +++ b/src/config_loader.py @@ -47,6 +47,7 @@ gap["name"]: { "left": gap["left"], "role": gap["role"], + "dept": gap["dept"], "knew_about": gap["knew_about"], "documented_pct": gap["documented_pct"], } diff --git a/src/confluence_writer.py b/src/confluence_writer.py index f8aa2b9..e4b1202 100644 --- a/src/confluence_writer.py +++ b/src/confluence_writer.py @@ -389,8 +389,10 @@ def write_design_doc( Returns the registered conf_id, or None on failure. """ + conf_id = self._registry.next_id("ENG") - artifact_time, _ = self._clock.advance_actor(author, hours=0.5) + write_delay_hours = random.uniform(0.5, 1.5) + artifact_time, _ = self._clock.advance_actor(author, hours=write_delay_hours) timestamp = artifact_time.isoformat() chat_log = "\n".join(f"{m['user']}: {m['text']}" for m in slack_transcript) @@ -408,8 +410,6 @@ def write_design_doc( "Unknown", ) - # Pull live domain registry context for orphaned domains so the LLM - # knows it's writing about an underdocumented area orphaned_domain_context = "" all_domains = list( self._mem._db["domain_registry"].find({"primary_owner": None}) @@ -603,13 +603,13 @@ def write_design_doc( actors=participants, artifact_ids={ "confluence": conf_ids[0], - "spawned_tickets": json.dumps(created_ticket_ids), + "spawned_tickets": created_ticket_ids, }, facts={ "title": f"Design: {topic[:80]}", "type": "design_doc", "spawned_tickets": created_ticket_ids, - "causal_chain": chain.snapshot(), # ← add this + "causal_chain": chain.snapshot(), "author_domain_fit": metadata.get("author_domain_fit", "high"), "gap_classification": metadata.get("gap_classification", "none"), "domains_updated": _updated_domains, @@ -836,10 +836,8 @@ def _finalize_page( Returns list of all conf_ids created (parent + children). """ - # 1. Strip any CONF-* references that aren't registered yet clean_content = self._registry.strip_broken_references(raw_content) - # 2. Chunk into focused child pages (or single page if short enough) pages: List[ConfluencePage] = self._registry.chunk_into_pages( parent_id=conf_id, parent_title=title, @@ -1195,7 +1193,6 @@ def _knowledge_gap_warning(self, topic: str) -> str: """ topic_lower = topic.lower() - # First try live registry — preferred source all_domains = list( self._mem._db["domain_registry"].find({"primary_owner": None}) ) @@ -1215,7 +1212,6 @@ def _knowledge_gap_warning(self, topic: str) -> str: f"{former}. Only ~{pct}% documented.{known_str}" ) - # Fallback to static config if domain not in registry (e.g. pre-registry data) departed = self._config.get("knowledge_gaps", []) for emp in departed: hits = [k for k in emp.get("knew_about", []) if k.lower() in topic_lower] diff --git a/src/crm_system.py b/src/crm_system.py index f4fc909..d8fd08b 100644 --- a/src/crm_system.py +++ b/src/crm_system.py @@ -40,14 +40,12 @@ --------------------------------- crm = CRMSystem.from_config(config, export_base, mem) - # Sim start — seeds SF accounts from contacts - crm.initialize_salesforce_accounts(contacts) # Pre-standup — called before DayPlannerOrchestrator.plan() crm_signals = crm.planner_context() # injected alongside email_signals # Inbound email handling — called from ExternalEmailIngestor - zd_id = crm.handle_inbound_complaint(event_facts, timestamp, date_str, day) + zd_id = crm.handle_inbound_customer_email(event_facts, email_type, timestamp, date_str, day) # Incident lifecycle — called from _handle_incident() and _advance_incidents() crm.handle_incident_opened(incident_id, component, health, timestamp, date_str, day) @@ -69,7 +67,8 @@ import random from typing import Dict, List, Optional -from config_loader import COMPANY_NAME, CONFIG +from config_loader import COMPANY_NAME +from memory import SimEvent logger = logging.getLogger("orgforge.crm") @@ -80,6 +79,32 @@ _ZD_CHANNELS = ["email", "web_widget", "api"] _ZD_CHANNEL_WEIGHTS = [0.7, 0.2, 0.1] +# Email types that trigger a ZD ticket. feature_request goes to Product via +# Slack FYI instead. positive_feedback needs no ticket. +_ZD_TICKET_TYPES = frozenset(["complaint", "question", "general_inquiry"]) + +# Probability that a given email type produces a ZD ticket. +# Complaints always get one; others are gated so not every question +# generates a ticket (realistic — not all customer questions need tracking). +_ZD_TICKET_PROB: Dict[str, float] = { + "complaint": 1.0, + "question": 0.70, + "general_inquiry": 0.30, +} + +# ZD ticket priority by email type. +_ZD_PRIORITY_BY_EMAIL_TYPE: Dict[str, str] = { + "complaint": "High", + "question": "Normal", + "general_inquiry": "Low", +} + +# ZD ticket type field by email type. +_ZD_TYPE_BY_EMAIL_TYPE: Dict[str, str] = { + "complaint": "incident", + "question": "question", + "general_inquiry": "task", +} _SF_TYPES = ["New Business", "Renewal", "Upsell/Cross-sell"] _SF_TYPE_WEIGHTS = [0.6, 0.25, 0.15] @@ -103,12 +128,20 @@ class NullCRMSystem: never need to check ``if crm is not None``. """ - def initialize_salesforce_accounts(self) -> None: - pass - def planner_context(self) -> str: return "" + def handle_inbound_customer_email( + self, + event_facts: Dict, + email_type: str, + timestamp: str, + date_str: str, + day: int, + ) -> Optional[str]: + return None + + # Keep old name as a no-op alias so any callers not yet updated don't break. def handle_inbound_complaint( self, event_facts: Dict, @@ -126,6 +159,7 @@ def handle_incident_opened( timestamp: str, date_str: str, day: int, + root_cause: str = "", ) -> None: pass @@ -173,23 +207,6 @@ class CRMSystem: the incident handlers in flow.py. """ - # Sales-intent keywords for outbound email classification. - # Kept narrow to avoid false positives on engineering or HR mail. - _SALES_KEYWORDS = { - "contract", - "renewal", - "proposal", - "following up", - "pricing", - "quote", - "demo", - "partnership", - "commercial", - "subscription", - "onboarding", - "account review", - } - def __init__(self, config: Dict, export_base: Path, mem, planner_llm=None): crm_cfg = config.get("crm", {}) self._sf_cfg = crm_cfg.get("salesforce", {}) @@ -317,7 +334,7 @@ def planner_context(self) -> str: self._sf_o.find( { "stage": {"$nin": ["Closed Won", "Closed Lost"]}, - "risk_notes": {"$not": {"$size": 0}}, + "risk_notes": {"$exists": True, "$ne": []}, }, {"_id": 0}, ) @@ -365,103 +382,70 @@ def planner_context(self) -> str: return "\n".join(lines) if lines else "" - def initialize_salesforce_accounts(self) -> None: - """ - Runs once during genesis_phase(), before the daily loop starts. - Reads customer contacts from MongoDB and seeds SF accounts. - """ - if not self._sf_on or not self._sf_cfg.get("seed_accounts", True): - return - - contacts = list( - self._mem._db["sim_config"].find( - {"_id": "inbound_email_sources", "category": "customer"}, {"_id": 0} - ) - ) - - start_dt = datetime.strptime(CONFIG["simulation"]["start_date"], "%Y-%m-%d") - - for contact in contacts: - org_name = contact.get("org", "Unknown") - safe_id = org_name.upper().replace(" ", "").replace("-", "") - account_id = f"ACC-{safe_id}" - - if self._sf_a.find_one({"account_id": account_id}): - continue - - days_ago = random.randint(30, 730) - hours_ago = random.randint(0, 23) - mins_ago = random.randint(0, 59) - created_dt = start_dt - timedelta( - days=days_ago, hours=hours_ago, minutes=mins_ago - ) + # ───────────────────────────────────────────────────────────────────────── + # INBOUND EMAIL → ZENDESK + # ───────────────────────────────────────────────────────────────────────── - account = { - "account_id": account_id, - "name": org_name, - "primary_contact": contact.get("name", "Unknown Contact"), - "type": "Customer", - "industry": contact.get("industry", "Technology"), - "tier": contact.get( - "tier", - random.choices( - ["Enterprise", "Mid-Market", "SMB"], weights=[0.2, 0.5, 0.3] - )[0], - ), - "website": f"https://www.{org_name.lower().replace(' ', '')}.com", - "billing_region": contact.get( - "billing_region", - random.choices(["NA", "EMEA", "APAC"], weights=[0.6, 0.3, 0.1])[0], - ), - "arr": contact.get( - "arr", random.choice([50000, 100000, 250000, 500000]) - ), - "owner": contact.get("internal_liaison", "Unassigned"), - "created_at": created_dt.strftime("%Y-%m-%dT%H:%M:%SZ"), - "risk_flag": False, - } - self._sf_a.insert_one({**account, "_seq": 0}) - self._write(f"salesforce/accounts/{account_id}.json", account) - logger.info(f"[crm] SF account seeded: {account_id} ({org_name})") - - def handle_inbound_complaint( + def handle_inbound_customer_email( self, event_facts: Dict, + email_type: str, timestamp: str, date_str: str, day: int, ) -> Optional[str]: """ - Called by ExternalEmailIngestor when it processes a customer_complaint - email. Creates a ZD ticket in MongoDB + disk and embeds it so Product - planners see it the next morning. - - Returns the new ticket_id (e.g. 'ZD-101') or None if ZD is disabled. + Called by ExternalEmailIngestor for any routed inbound customer email + that warrants a ZD ticket. Creates a ticket in MongoDB + disk and + embeds it so Product planners see it the next morning. + + Which email types produce tickets, and at what probability: + complaint → always (priority: High, type: incident) + question → 70% (priority: Normal, type: question) + general_inquiry → 30% (priority: Low, type: task) + feature_request → never (handled via Slack FYI to Product) + positive_feedback → never + + Returns the new ticket_id (e.g. 'ZD-101') or None if ZD is disabled + or the email type / probability gate does not produce a ticket. """ if not self._zd_on: return None + if email_type not in _ZD_TICKET_TYPES: + return None + + ticket_prob = _ZD_TICKET_PROB.get(email_type, 0.0) + if random.random() > ticket_prob: + return None + from memory import SimEvent seq = self._zd_counter self._zd_counter += 1 ticket_id = f"ZD-{seq}" + priority = _ZD_PRIORITY_BY_EMAIL_TYPE.get(email_type, "Normal") + zd_type = _ZD_TYPE_BY_EMAIL_TYPE.get(email_type, "question") + ticket = { "ticket_id": ticket_id, - "type": "incident", + "type": zd_type, "status": "Open", - "priority": "Normal", + "priority": priority, "description": event_facts.get("body", "(See email body.)"), + "assignee_email": event_facts.get("liaison_email", "Unknown"), "requester": { "name": event_facts.get("sender_name", "Customer"), "email": event_facts.get("sender", "customer@unknown.com"), "org_name": event_facts.get("sender_org", "Unknown"), + "email_id": event_facts.get("email", "Unknown"), }, - "subject": event_facts.get("subject", "Customer complaint"), + "subject": event_facts.get("subject", "Customer inquiry"), "org_name": event_facts.get("sender_org", "Unknown"), "channel": "email", - "tags": ["support", "inbound", "needs_triage"], + "email_type": email_type, + "tags": ["support", "inbound", email_type, "needs_triage"], "satisfaction_rating": {"score": "unoffered"}, "created_at": timestamp, "updated_at": timestamp, @@ -494,6 +478,7 @@ def handle_inbound_complaint( metadata={ "ticket_id": ticket_id, "org_name": ticket["org_name"], + "email_type": email_type, "status": "Open", }, ) @@ -510,16 +495,42 @@ def handle_inbound_complaint( "ticket_id": ticket_id, "subject": ticket["subject"], "org_name": ticket["org_name"], + "email_type": email_type, + "priority": priority, + "zd_type": zd_type, "channel": "email", }, - summary=f"Zendesk ticket {ticket_id} opened: {ticket['subject']} ({ticket['org_name']})", - tags=["zendesk", "support", "customer_complaint"], + summary=( + f"Zendesk ticket {ticket_id} opened [{email_type}]: " + f"{ticket['subject']} ({ticket['org_name']})" + ), + tags=["zendesk", "support", email_type], ) ) - logger.info(f"[crm] ZD ticket opened: {ticket_id} ({ticket['org_name']})") + logger.info( + f"[crm] ZD ticket opened: {ticket_id} [{email_type}/{priority}] " + f"({ticket['org_name']})" + ) return ticket_id + # Keep old name as a forwarding alias so any callers not yet updated + # continue to work. Defaults email_type to "complaint" for backward compat. + def handle_inbound_complaint( + self, + event_facts: Dict, + timestamp: str, + date_str: str, + day: int, + ) -> Optional[str]: + return self.handle_inbound_customer_email( + event_facts=event_facts, + email_type="complaint", + timestamp=timestamp, + date_str=date_str, + day=day, + ) + def _write_zd_comment(self, ticket_id: str, comment: Dict) -> None: """Write a single comment to disk under zendesk/comments/{ticket_id}/.""" comment_dir = self._base / "zendesk" / "comments" / ticket_id @@ -563,6 +574,7 @@ def handle_incident_opened( timestamp: str, date_str: str, day: int, + root_cause: str = "", ) -> None: """ Called immediately after _handle_incident() logs the incident_opened @@ -575,10 +587,17 @@ def handle_incident_opened( SF path: when health < 60, appends a risk note to every open opportunity so Sales planners see the risk in their daily context. """ - from memory import SimEvent + + affected_orgs: List[str] = [] if self._zd_on and self._zd_cfg.get("link_to_incidents", True): - open_tickets = list(self._zd.find({"status": "Open"}, {"_id": 0})) + affected_orgs = self._orgs_affected_by_incident(root_cause) + + query = {"status": "Open"} + if affected_orgs: + query["org_name"] = {"$in": affected_orgs} + + open_tickets = list(self._zd.find(query, {"_id": 0})) escalated_ids = [] for t in open_tickets: @@ -637,12 +656,11 @@ def handle_incident_opened( ) if self._sf_on and health < 60: - open_opps = list( - self._sf_o.find( - {"stage": {"$nin": ["Closed Won", "Closed Lost"]}}, - {"_id": 0}, - ) - ) + query = {"stage": {"$nin": ["Closed Won", "Closed Lost"]}} + if affected_orgs: + query["account_name"] = {"$in": affected_orgs} + + open_opps = list(self._sf_o.find(query, {"_id": 0})) risk_note = ( f"Active SEV on {component} ({incident_id}) — " f"system health {health}/100 — potential SLA impact." @@ -935,6 +953,29 @@ def process_outbound_email( return touchpoint_facts + def _orgs_affected_by_incident(self, root_cause: str) -> List[str]: + """ + Returns org names whose depends_on_components overlap with the + incident root_cause. Empty list = no filtering (fallback to all). + """ + if not root_cause: + return [] + + doc = self._mem._db["sim_config"].find_one({"_id": "inbound_email_sources"}) + if not doc or "sources" not in doc: + return [] + + rc_lower = root_cause.lower() + affected = [] + for source in doc["sources"]: + if source.get("category", "").lower() != "customer": + continue + components = [c.lower() for c in source.get("depends_on_components", [])] + if any(comp in rc_lower for comp in components): + affected.append(source.get("org", "")) + + return [o for o in affected if o] + def get_best_open_opportunity(self, owner: str) -> Optional[Dict]: """ Return the highest-priority open SF opportunity for a given owner, diff --git a/src/day_planner.py b/src/day_planner.py index 1d865e7..3ae4d06 100644 --- a/src/day_planner.py +++ b/src/day_planner.py @@ -978,6 +978,7 @@ def plan( lifecycle_context: str = "", email_signals: Optional[List["ExternalEmailSignal"]] = None, crm_summary: str = "", + on_call: str = "", ) -> OrgDayPlan: """ Full planning pass for one day. @@ -1001,7 +1002,7 @@ def plan( if dept not in LEADS: continue sprint_contexts[dept] = self._ticket_assigner.build( - state, members, dept_name=dept + state, members, dept_name=dept, on_call=on_call ) state.ticket_actors_today = {} diff --git a/src/external_email_ingest.py b/src/external_email_ingest.py index 185e695..e43e969 100644 --- a/src/external_email_ingest.py +++ b/src/external_email_ingest.py @@ -11,6 +11,7 @@ import random import re from dataclasses import dataclass, field +from datetime import datetime from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from pathlib import Path @@ -18,7 +19,6 @@ from agent_factory import make_agent from causal_chain_handler import CausalChainHandler -from config_loader import COMPANY_DESCRIPTION from crm_system import NullCRMSystem from crewai import Crew, Task import json_repair @@ -76,12 +76,24 @@ def _get_stage_probability(stage: str) -> int: _PROB_CUSTOMER_REPLY = 0.30 - _PROB_NON_COMPLAINT_SALES_FYI = 0.35 +# Email types that trigger a ZD ticket via handle_inbound_customer_email. +# feature_request → Slack FYI to Product only (no ticket). +# positive_feedback → no ticket. +_ZD_TICKET_TYPES = frozenset(["complaint", "question", "general_inquiry"]) +# Kept for any import sites not yet updated; mirrors _ZD_TICKET_TYPES. _COMPLAINT_EMAIL_TYPES = frozenset(["complaint"]) +# Probability gate per email type — complaints always get a ticket, +# others are sampled so not every question floods the ZD queue. +_ZD_TICKET_PROB: dict = { + "complaint": 1.0, + "question": 0.70, + "general_inquiry": 0.30, +} + @dataclass class ExternalEmailSignal: @@ -203,25 +215,32 @@ def generate_pre_standup(self, state) -> List[ExternalEmailSignal]: def generate_business_hours(self, state) -> List[ExternalEmailSignal]: """ - Customer emails arriving 09:00–16:30. - Each non-dropped email triggers: Sales Slack ping → Product decision → optional JIRA. - Dropped emails (~15%) are logged as "email_dropped" SimEvents. - Returns all signals (dropped + routed) for tomorrow's CrossDeptSignal extraction. + Customer emails arriving 09:00-16:30, driven entirely by simulation state. + + Emails are only generated when there is a real reason for a customer to + reach out -- active incidents affecting their capabilities, stale deals, + upcoming renewals, or expansion interest. Random probability firing is + intentionally removed; silence is the correct output when nothing warrants + an email. + + Dropped emails (~15%) are still modelled for eval ground truth. + Returns all signals (dropped + routed). """ self._ensure_sources_loaded() + + derived = self._derive_customer_email_signals(state) signals: List[ExternalEmailSignal] = [] - has_incident = bool(state.active_incidents) - for source in self._sources or []: - if source.get("category") != "customer": - continue - if not self._should_fire( - source.get("trigger_on", ["always"]), state.system_health, has_incident - ): - continue - topic = random.choice(source.get("topics", ["general update"])) + for item in derived: signal = self._generate_email( - source, topic, state, hour_range=(9, 16), category="customer" + source=item["source"], + topic=item["topic"], + state=state, + hour_range=(9, 16), + category="customer", + email_type=item["email_type"], + symptom=item.get("symptom", ""), + trigger_context=item.get("trigger", ""), ) if not signal: continue @@ -231,7 +250,7 @@ def generate_business_hours(self, state) -> List[ExternalEmailSignal]: self._log_dropped_email(signal, state) logger.info( f" [dim yellow]📭 Dropped (no action): " - f'{signal.source_name} → "{signal.subject[:50]}"[/dim yellow]' + f'{signal.source_name} -> "{signal.subject[:50]}"[/dim yellow]' ) else: self._route_customer_email(signal, state) @@ -245,6 +264,15 @@ def generate_business_hours(self, state) -> List[ExternalEmailSignal]: f" [cyan]📬 {n_routed} customer email(s) routed, " f"{n_dropped} dropped[/cyan]" ) + elif derived: + logger.info( + " [dim]📭 No customer emails fired today (all signals suppressed)[/dim]" + ) + else: + logger.info( + " [dim]📭 No customer email signals derived from state today[/dim]" + ) + return signals def generate_hr_outbound(self, state) -> None: @@ -266,57 +294,6 @@ def generate_hr_outbound(self, state) -> None: self._send_hr_outbound(hire, hr_lead, days_until, state, date_str) hire["_hr_email_sent"] = True - """ def _route_customer_email(self, signal: ExternalEmailSignal, state) -> None: - date_str = str(state.current_date.date()) - sales_lead = self._leads.get( - signal.internal_liaison, next(iter(self._leads.values())) - ) - product_dept = next((d for d in self._leads if "product" in d.lower()), None) - product_lead = self._leads.get(product_dept, sales_lead) - - thread_id = self._sales_pings_product( - signal, sales_lead, product_lead, state, date_str - ) - if thread_id: - signal.causal_chain.append(thread_id) - - is_high = signal.tone in ("frustrated", "urgent") or ( - state.system_health < 70 and "stability" in signal.topic.lower() - ) - if is_high and random.random() < _PROB_CUSTOMER_JIRA: - ticket_id = self._product_opens_jira(signal, product_lead, state, date_str) - if ticket_id: - signal.causal_chain.append(ticket_id) - - reply_id = self._send_customer_reply( - signal, sales_lead, is_high, state, date_str - ) - if reply_id: - signal.causal_chain.append(reply_id) - - self._mem.log_event( - SimEvent( - type="customer_email_routed", - timestamp=signal.timestamp_iso, - day=state.day, - date=date_str, - actors=[signal.source_name, sales_lead, product_lead], - artifact_ids={"email": signal.embed_id}, - facts={ - "source": signal.source_name, - "subject": signal.subject, - "high_priority": is_high, - "causal_chain": signal.causal_chain.snapshot(), - }, - summary=( - f"Customer email from {signal.source_name} routed: " - f"{sales_lead} → {product_lead}" - + (" [JIRA opened]" if len(signal.causal_chain) > 2 else "") - ), - tags=["email", "customer", "routed", "causal_chain"], - ) - ) """ - def _sales_pings_product( self, signal, sales_lead, product_lead, state, date_str ) -> Optional[str]: @@ -485,6 +462,7 @@ def _route_vendor_email(self, signal: ExternalEmailSignal, state) -> None: date_str = str(state.current_date.date()) recipient = self._find_expert_for_topic(signal.topic, signal.internal_liaison) + linked_incident_ticket_id = None for inc in state.active_incidents: if any( kw in signal.topic.lower() @@ -492,6 +470,7 @@ def _route_vendor_email(self, signal: ExternalEmailSignal, state) -> None: if len(kw) > 4 ): inc.causal_chain.append(signal.embed_id) + linked_incident_ticket_id = inc.ticket_id logger.info( f" [dim]🔗 Vendor email appended to {inc.ticket_id} chain[/dim]" ) @@ -506,6 +485,16 @@ def _route_vendor_email(self, signal: ExternalEmailSignal, state) -> None: if ack_id: signal.causal_chain.append(ack_id) + facts = { + "vendor": signal.source_name, + "topic": signal.topic, + "routed_to": recipient, + "causal_chain": signal.causal_chain.snapshot(), + } + + if linked_incident_ticket_id: + facts["linked_incident"] = linked_incident_ticket_id + self._mem.log_event( SimEvent( type="vendor_email_routed", @@ -514,12 +503,7 @@ def _route_vendor_email(self, signal: ExternalEmailSignal, state) -> None: date=date_str, actors=[signal.source_name, recipient], artifact_ids={"email": signal.embed_id}, - facts={ - "vendor": signal.source_name, - "topic": signal.topic, - "routed_to": recipient, - "causal_chain": signal.causal_chain.snapshot(), - }, + facts=facts, summary=f"Vendor email from {signal.source_name} routed to {recipient}", tags=["email", "vendor", "routed"], ) @@ -812,20 +796,21 @@ def _send_customer_reply( day=state.day, ) - self._crm.process_outbound_email( - email_data={ - "sender": sales_lead, - "recipient": signal.source_name, - "sender_org": self._company_name, - "recipient_org": signal.source_org, - "subject": subject, - "stage": crm_stage, - "embed_id": embed_id, - }, - timestamp=reply_time.isoformat(), - date_str=date_str, - day=state.day, - ) + if not is_high: + self._crm.process_outbound_email( + email_data={ + "sender": sales_lead, + "recipient": signal.source_name, + "sender_org": self._company_name, + "recipient_org": signal.source_org, + "subject": subject, + "stage": crm_stage, + "embed_id": embed_id, + }, + timestamp=reply_time.isoformat(), + date_str=date_str, + day=state.day, + ) _exfil_path = self._threat.inject_email( eml_path=str(eml_path), @@ -1074,8 +1059,20 @@ def _generate_email( state, hour_range: Tuple[int, int], category: str, + email_type: str = "general_inquiry", + symptom: str = "", + trigger_context: str = "", ) -> Optional[Any]: + """ + Generates a single inbound email from an external contact. + + For customers: outputs JSON so email_type is declared at generation time + (not classified post-hoc), no tech_ctx is injected, and the prompt uses + first-person sender framing grounded in the derived signal context. + For vendors: keeps plain SUBJECT/---/body format with tech_ctx so engineers + can reference infrastructure specifics. + """ source_first_name = source["first_name"] source_name = source_first_name source_last_name = source["last_name"] @@ -1086,17 +1083,6 @@ def _generate_email( tone = source.get("tone", "professional") date_str = str(state.current_date.date()) - tech_stack = self._mem.tech_stack_for_prompt() - tech_ctx = ( - ( - f"\nCOMPANY TECH STACK:\n{tech_stack}\n" - f"CONSTRAINT: If referencing the company's current infrastructure or code, restrict it to the stack above. " - f"You may reference outside technologies ONLY if suggesting a migration, offering a new service, or making a competitive recommendation." - ) - if tech_stack - else "" - ) - email_ts = state.current_date.replace( hour=random.randint(*hour_range), minute=random.randint(0, 59), @@ -1108,33 +1094,111 @@ def _generate_email( ) agent = make_agent( - role=f"Representative from {source_org}", - goal=f"Write a realistic email about: {topic}.", + role=f"{source_first_name} {source_last_name}, {source.get('contact_role', 'representative')} at {source_org}", + goal=f"Write a realistic inbound email to {self._company_name}.", backstory=backstory, llm=self._worker_llm, ) - task = Task( - description=( - f"Email from {source_first_name} {source_last_name} at {source_org} to {liaison_name} at {self._company_name} which {COMPANY_DESCRIPTION} " - f"about: {topic}.\nTone: {tone}. Health: {state.system_health}/100." - f"{tech_ctx}\n\n" - f"COMPANY CONTEXT: {self._company_name} is {self._company_desc}. " - f"Ground your email in this reality.\n\n" - f"Format:\nSUBJECT: \n---\n" - ), - expected_output="SUBJECT: \n---\n", - agent=agent, - ) - try: - raw = str( - Crew(agents=[agent], tasks=[task], verbose=False).kickoff() - ).strip() - except Exception as exc: - logger.warning(f"[external_email] LLM failed for {source_name}: {exc}") - return None + if category == "customer": + # Customers never see our tech stack. They experience symptoms. + # symptom_context is the customer-facing description of their problem; + # trigger_context tells the LLM why this email is being sent today. + symptom_hint = f"\nSITUATION: {symptom}" if symptom else "" + email_type_hint = { + "complaint": "You are writing to report a problem you are experiencing. Describe the business impact on your organisation. Do NOT name or guess at internal systems.", + "question": "You are following up on a business matter or asking for clarification. Be specific to your situation.", + "feature_request": "You are requesting a capability or improvement that would benefit your team.", + "positive_feedback": "You are writing to share positive feedback or a success story.", + "general_inquiry": "You have a general question or comment.", + }.get( + email_type, "Write a professional email relevant to your relationship." + ) + + task = Task( + description=( + f"You are {source_first_name} {source_last_name}, {source.get('contact_role', 'a representative')} at {source_org}.\n" + f"You are writing an email to {liaison_name} at {self._company_name}.\n" + f"Tone: {tone}.{symptom_hint}\n\n" + f"INTENT: {email_type_hint}\n\n" + f"IMPORTANT: Write entirely from your perspective as a customer. " + f"Describe only what you observe or experience — never reference {self._company_name}'s internal systems, " + f"infrastructure, or technology by name. You don't know what's running under the hood.\n\n" + f"Respond ONLY with a JSON object. No preamble, no markdown fences:\n" + f"{{\n" + f' "subject": "",\n' + f' "body": "",\n' + f' "email_type": ""\n' + f"}}" + ), + expected_output='JSON with "subject", "body", and "email_type" keys.', + agent=agent, + ) + + try: + raw = str( + Crew(agents=[agent], tasks=[task], verbose=False).kickoff() + ).strip() + parsed = json_repair.loads(raw) + if isinstance(parsed, list) and parsed: + parsed = parsed[0] + if not isinstance(parsed, dict): + raise ValueError("LLM did not return a dict") + subject = parsed.get("subject", f"Re: {topic}").strip() + body = parsed.get("body", "").strip() + resolved_email_type = ( + parsed.get("email_type", email_type).strip().lower() + ) + if resolved_email_type not in _VALID_EMAIL_TYPES: + resolved_email_type = email_type + if not body: + raise ValueError("Empty body") + except Exception as exc: + logger.warning( + f"[external_email] Customer email LLM failed for {source_name}: {exc}" + ) + return None + + else: + # Vendors: plain text, tech_ctx injected, first-person framing + tech_stack = self._mem.tech_stack_for_prompt() + tech_ctx = ( + ( + f"\nCOMPANY TECH STACK (for your reference):\n{tech_stack}\n" + f"Restrict references to the company's infrastructure to this stack only. " + f"You may reference outside technologies only if alerting about an integration issue, " + f"suggesting a migration, or offering a new service." + ) + if tech_stack + else "" + ) + resolved_email_type = "general_inquiry" + + task = Task( + description=( + f"You are {source_first_name} {source_last_name} from {source_org}.\n" + f"Write an email to {liaison_name} at {self._company_name} about: {topic}.\n" + f"Tone: {tone}. Their system health: {state.system_health}/100." + f"{tech_ctx}\n\n" + f"Write as yourself — do not describe the email, write it.\n" + f"Format:\nSUBJECT: \n---\n" + ), + expected_output="SUBJECT: \n---\n", + agent=agent, + ) + + try: + raw = str( + Crew(agents=[agent], tasks=[task], verbose=False).kickoff() + ).strip() + except Exception as exc: + logger.warning( + f"[external_email] Vendor email LLM failed for {source_name}: {exc}" + ) + return None + + subject, body = self._parse_email_output(raw, topic) - subject, body = self._parse_email_output(raw, topic) embed_id = ( f"ext_email_{source_name.lower().replace(' ', '_')}" f"_{state.day}_{hour_range[0]}" @@ -1155,7 +1219,7 @@ def _generate_email( id=embed_id, type="email", title=subject, - content=f"From: {source_name} ({source_org})\n\n{body}", + content=f"From: {source_first_name} {source_last_name} ({source_org})\n\n{body}", day=state.day, date=date_str, timestamp=email_ts.isoformat(), @@ -1167,9 +1231,52 @@ def _generate_email( "liaison": liaison_name, "tone": tone, "direction": "inbound", + "email_type": resolved_email_type, }, ) + facts = { + "source": source_name, + "org": source_org, + "category": category, + "topic": topic, + "subject": subject, + "liaison": liaison_name, + "liaison_dept": liaison_dept, + "tone": tone, + "email_type": resolved_email_type, + "body_preview": body[:200], + } + + chain = CausalChainHandler(root_id=embed_id) + zd_ticket_id = None + + if category == "customer" and resolved_email_type in _ZD_TICKET_TYPES: + zd_ticket_id = self._crm.handle_inbound_customer_email( + event_facts={ + "subject": subject, + "body": body[:500], + "sender_org": source_org, + "sender": source_addr, + "sender_name": f"{source_first_name} {source_last_name}", + "email": embed_id, + "liaison_email": self._email_of(liaison_name), + }, + email_type=resolved_email_type, + timestamp=email_ts.isoformat(), + date_str=date_str, + day=state.day, + ) + if zd_ticket_id: + logger.info( + f" [dim]🔗 ZD ticket {zd_ticket_id} [{resolved_email_type}] " + f"linked to email from {source_name}[/dim]" + ) + + facts["causal_chain"] = chain.snapshot() + if zd_ticket_id: + chain.append(zd_ticket_id) + self._mem.log_event( SimEvent( type="inbound_external_email", @@ -1178,54 +1285,18 @@ def _generate_email( date=date_str, actors=[source_name, liaison_name], artifact_ids={"email": embed_id, "eml_path": str(eml_path)}, - facts={ - "source": source_name, - "org": source_org, - "category": category, - "topic": topic, - "subject": subject, - "liaison": liaison_name, - "liaison_dept": liaison_dept, - "tone": tone, - "body_preview": body[:200], - }, - summary=f'Inbound [{category}] email from {source_name}: "{subject}"', - tags=["email", "inbound", category, source_name.lower()], + facts=facts, + summary=f'Inbound [{category}/{resolved_email_type}] email from {source_name}: "{subject}"', + tags=[ + "email", + "inbound", + category, + resolved_email_type, + source_name.lower(), + ], ) ) - zd_ticket_id = None - email_type = "general_inquiry" - - if category == "customer": - email_type = self._classify_customer_email( - subject=subject, - body=body, - source_name=source_name, - tone=tone, - ) - logger.debug( - f" [dim]🔍 Email classified as '{email_type}': " - f"{source_name} — {subject[:50]}[/dim]" - ) - - if email_type in _COMPLAINT_EMAIL_TYPES: - zd_ticket_id = self._crm.handle_inbound_complaint( - event_facts={ - "subject": subject, - "body": body[:500], - "sender_org": source_org, - }, - timestamp=email_ts.isoformat(), - date_str=date_str, - day=state.day, - ) - if zd_ticket_id: - logger.info( - f" [dim]🔗 ZD ticket {zd_ticket_id} linked to complaint from " - f"{source_name}[/dim]" - ) - artifact_ids: Dict[str, Any] = {"email": embed_id, "eml_path": str(eml_path)} if zd_ticket_id: artifact_ids["zd_ticket"] = zd_ticket_id @@ -1247,72 +1318,20 @@ def _generate_email( category=category, eml_path=str(eml_path), causal_chain=CausalChainHandler(root_id=embed_id), - facts={"subject": subject, "topic": topic, "org": source_org}, + facts={ + "subject": subject, + "topic": topic, + "org": source_org, + "email_type": resolved_email_type, + }, ) - signal.facts["email_type"] = email_type + signal.facts["email_type"] = resolved_email_type if zd_ticket_id: signal.facts["zd_ticket_id"] = zd_ticket_id return signal - def _classify_customer_email( - self, - subject: str, - body: str, - source_name: str, - tone: str, - ) -> str: - """ - Classify an inbound customer email into one of five categories using a - single, lightweight LLM call. - """ - agent = make_agent( - role="Email Classifier", - goal="Classify a customer email into exactly one category.", - backstory=( - "You are a triage assistant. You read customer emails and output " - "a single classification label. You never explain your reasoning." - ), - llm=self._worker_llm, - ) - task = Task( - description=( - f"Classify the following customer email.\n\n" - f"Subject: {subject}\n" - f"Body: {body[:600]}\n\n" - f"Output ONLY a JSON object with a single key 'email_type'.\n" - f"The value must be EXACTLY one of:\n" - f" complaint, question, feature_request, positive_feedback, general_inquiry\n\n" - f"Definitions:\n" - f" complaint — customer reports a problem, outage, bug, or unmet SLA\n" - f" question — customer asks how something works or for clarification\n" - f" feature_request — customer requests new or changed functionality\n" - f" positive_feedback — customer compliments the product or team\n" - f" general_inquiry — anything that does not fit the above\n\n" - f'Example output: {{"email_type": "complaint"}}\n' - f"No preamble. No explanation. Output only the JSON object." - ), - expected_output='{"email_type": ""}', - agent=agent, - ) - - try: - raw = str( - Crew(agents=[agent], tasks=[task], verbose=False).kickoff() - ).strip() - parsed = json_repair.loads(raw) - if isinstance(parsed, dict): - result = parsed.get("email_type", "").strip().lower() - if result in _VALID_EMAIL_TYPES: - return result - except Exception as exc: - logger.warning(f"[external_email] Email classification LLM failed: {exc}") - - if tone in ("frustrated", "urgent"): - return "complaint" - return "general_inquiry" - def _generate_customer_reply_email( self, contact_name: str, @@ -1700,22 +1719,28 @@ def _route_non_complaint_email( self, signal: Any, state, email_type: str = "general_inquiry" ) -> None: """ - Lightweight routing for non-complaint customer emails. + Routing for non-complaint customer emails. + + Questions and general_inquiries may produce a ZD ticket (probability- + gated via _ZD_TICKET_PROB in crm_system — 70% and 30% respectively). + The ticket is already created upstream in _generate_email before this + method is called, so we just append it to the causal chain if present. - Sales replies directly to the customer — no Slack ping to Product, - no JIRA ticket. For feature_request emails, a low-probability (~35%) - FYI message is posted in #product so the team is aware without being - formally escalated. + Feature requests get a low-probability (~35%) FYI to #product. + Positive feedback gets a direct reply only — no ticket, no escalation. - This preserves causal chain integrity: the reply is appended to the - chain, and the SimEvent type distinguishes these emails from complaints - so eval agents can verify the correct branching behaviour. + Sales replies to all non-complaint emails directly. """ date_str = str(state.current_date.date()) sales_lead = self._leads.get( signal.internal_liaison, next(iter(self._leads.values())) ) + # Append pre-created ZD ticket to causal chain if present + zd_ticket_id = signal.facts.get("zd_ticket_id") + if zd_ticket_id: + signal.causal_chain.append(zd_ticket_id) + fyi_thread_id = None if ( email_type == "feature_request" @@ -1746,12 +1771,14 @@ def _route_non_complaint_email( "subject": signal.subject, "email_type": email_type, "high_priority": False, + "zd_ticket_id": zd_ticket_id, "fyi_sent": fyi_thread_id is not None, "causal_chain": signal.causal_chain.snapshot(), }, summary=( f"{email_type.replace('_', ' ').title()} from {signal.source_name} " - f"handled by {sales_lead} (no escalation)" + f"handled by {sales_lead}" + + (f" [ZD-{zd_ticket_id}]" if zd_ticket_id else "") + (" [FYI sent to Product]" if fyi_thread_id else "") ), tags=["email", "customer", email_type, "routed", "causal_chain"], @@ -1936,6 +1963,197 @@ def _should_fire(triggers, system_health, has_incident) -> bool: return True return False + def _incident_affects_customer(self, incident, source: dict) -> bool: + return self._gd._incident_affects_customer(incident, source) + + def _derive_customer_email_signals(self, state) -> List[dict]: + """ + Inspects simulation state and CRM data to derive a list of grounded + customer email signals. Each signal represents a real reason a customer + would reach out — not a random probability fire. + + Returns a list of dicts, each with: + source — the full source record from inbound_email_sources + email_type — complaint | question | feature_request | positive_feedback | general_inquiry + trigger — human-readable reason string for LLM context + symptom — customer-facing symptom description (no internal tech names) + topic — topic string passed to _generate_email + + Signal priority (highest to lowest): + 1. Active incident that affects this customer → complaint + 2. Open opp at Negotiation/Review stale > 3 days → question (customer follows up) + 3. Contract renewal within 60 days → question (renewal conversation) + 4. Opp has risk_notes → question or complaint depending on sentiment + 5. High expansion_potential (>= 8) + healthy system → feature_request + """ + self._ensure_sources_loaded() + signals: List[dict] = [] + date_str = str(state.current_date.date()) + + customer_sources = [ + s for s in (self._sources or []) if s.get("category") == "customer" + ] + + for source in customer_sources: + org_name = source.get("org", "") + sentiment = source.get("sentiment_baseline", 0.8) + tone = source.get("tone", "formal") + + # ── Signal 1: Active incident affecting this customer ──────────── + for incident in state.active_incidents: + # Skip if already contacted proactively via _handle_external_contact + if org_name in getattr(incident, "contacted_customers", []): + continue + if not self._gd._incident_affects_customer(incident, source): + continue + + symptom = source.get( + "symptom_language", + "We are experiencing issues accessing your platform and wanted to follow up.", + ) + signals.append( + { + "source": source, + "email_type": "complaint", + "trigger": f"Active incident {incident.ticket_id} affecting platform capabilities this customer depends on", + "symptom": symptom, + "topic": symptom, + "incident_id": incident.ticket_id, + } + ) + break + + else: + # ── Signal 2: Stale deal at Negotiation/Review ─────────────── + opp = None + if hasattr(self._crm, "_sf_o"): + opp = self._crm._sf_o.find_one( + { + "account_name": org_name, + "stage": "Negotiation/Review", + }, + {"_id": 0, "_seq": 0}, + ) + + if opp: + touchpoints = opp.get("touchpoints", []) + last_touch = ( + touchpoints[-1].get("timestamp", "") if touchpoints else "" + ) + days_stale = 0 + if last_touch: + try: + last_dt = datetime.fromisoformat( + last_touch.replace("Z", "+00:00") + ).replace(tzinfo=None) + days_stale = ( + state.current_date.replace(tzinfo=None) - last_dt + ).days + except ValueError: + pass + + if days_stale >= 3: + topic = ( + "Following up on our proposal — checking in on next steps" + ) + signals.append( + { + "source": source, + "email_type": "question", + "trigger": f"Open deal {opp['opportunity_id']} at Negotiation/Review, no touchpoint in {days_stale} days", + "symptom": "", + "topic": topic, + } + ) + continue + + # ── Signal 3: Contract renewal within 60 days ──────────────── + renewal_str = source.get("contract_renewal_date", "") + if renewal_str: + try: + renewal_dt = datetime.fromisoformat( + renewal_str.replace("Z", "+00:00") + ).replace(tzinfo=None) + days_to_renewal = ( + renewal_dt - state.current_date.replace(tzinfo=None) + ).days + if 0 < days_to_renewal <= 60: + topic = "Upcoming contract renewal — wanted to discuss terms and our roadmap needs" + signals.append( + { + "source": source, + "email_type": "question", + "trigger": f"Contract renewal in {days_to_renewal} days", + "symptom": "", + "topic": topic, + } + ) + continue + except ValueError: + pass + + # ── Signal 4: Opp has risk notes + low sentiment ───────────── + if hasattr(self._crm, "_sf_o"): + risky_opp = self._crm._sf_o.find_one( + { + "account_name": org_name, + "stage": {"$nin": ["Closed Won", "Closed Lost"]}, + "risk_notes": {"$not": {"$size": 0}}, + }, + {"_id": 0, "_seq": 0}, + ) + if risky_opp and sentiment < 0.6: + topic = "Wanted to discuss some concerns we have about platform reliability" + signals.append( + { + "source": source, + "email_type": "complaint" + if sentiment < 0.45 + else "question", + "trigger": f"Risky deal {risky_opp['opportunity_id']} + low sentiment ({sentiment})", + "symptom": "", + "topic": topic, + } + ) + continue + + # ── Signal 5: High expansion potential + healthy system ─────── + if ( + source.get("expansion_potential", 0) >= 8 + and state.system_health >= 80 + and random.random() < 0.25 # not every day — keep it sparse + ): + topic = "Exploring additional use cases and features for our team" + signals.append( + { + "source": source, + "email_type": "feature_request", + "trigger": f"High expansion potential ({source.get('expansion_potential')}) + healthy system", + "symptom": "", + "topic": topic, + } + ) + + # ── Signal 6: Chronically low sentiment — unprompted complaint ─ + # Unhappy customers complain regardless of active incidents. + # Fires independently of all other signals as a baseline floor. + elif sentiment < 0.45 and random.random() < 0.15: + topic = source.get("topics", ["platform reliability concerns"])[0] + signals.append( + { + "source": source, + "email_type": "complaint", + "trigger": f"Chronically low sentiment ({sentiment:.2f}) — unprompted complaint", + "symptom": source.get( + "symptom_language", + "We've been experiencing ongoing issues and wanted to follow up.", + ), + "topic": topic, + } + ) + + return signals + def _persona_hint(self, name: str) -> str: p = self._personas.get(name, {}) return ( diff --git a/src/flow.py b/src/flow.py index 35bcc4a..05ee07a 100644 --- a/src/flow.py +++ b/src/flow.py @@ -117,6 +117,50 @@ def patched_get_inference_config(self): except (ImportError, AttributeError) as e: logger.warning(f"[patch] Could not patch crewAI Bedrock provider: {e}") + try: + from crewai.agents.crew_agent_executor import CrewAgentExecutor + from crewai.utilities.string_utils import sanitize_tool_name + + def patched_parse_native_tool_call( + self, tool_call: Any + ) -> tuple[str, str, str | dict] | None: + if hasattr(tool_call, "function"): + call_id = getattr(tool_call, "id", f"call_{id(tool_call)}") + func_name = sanitize_tool_name(tool_call.function.name) + return call_id, func_name, tool_call.function.arguments + if hasattr(tool_call, "function_call") and tool_call.function_call: + call_id = f"call_{id(tool_call)}" + func_name = sanitize_tool_name(tool_call.function_call.name) + func_args = ( + dict(tool_call.function_call.args) + if tool_call.function_call.args + else {} + ) + return call_id, func_name, func_args + if hasattr(tool_call, "name") and hasattr(tool_call, "input"): + call_id = getattr(tool_call, "id", f"call_{id(tool_call)}") + func_name = sanitize_tool_name(tool_call.name) + return call_id, func_name, tool_call.input + if isinstance(tool_call, dict): + call_id = ( + tool_call.get("id") + or tool_call.get("toolUseId") + or f"call_{id(tool_call)}" + ) + func_info = tool_call.get("function", {}) + func_name = sanitize_tool_name( + func_info.get("name", "") or tool_call.get("name", "") + ) + # FIX: use None default so falsy check correctly falls through to input + func_args = func_info.get("arguments") or tool_call.get("input") or {} + return call_id, func_name, func_args + return None + + CrewAgentExecutor._parse_native_tool_call = patched_parse_native_tool_call + logger.info("[patch] crewAI Bedrock tool arguments patch applied") + except (ImportError, AttributeError) as e: + logger.warning(f"[patch] Could not patch crewAI tool argument parser: {e}") + _patch_crewai_bedrock() @@ -269,6 +313,7 @@ class ActiveIncident(BaseModel): recurrence_of: Optional[str] = None on_call: str = "" actors: List[str] = [] + contacted_customers: List[str] = [] class SprintState(BaseModel): @@ -959,10 +1004,15 @@ def daily_cycle(self): vendor_signals = self._email_ingestor.generate_pre_standup(state=self.state) if self.state.day > 1: + logger.info("[dim] Draining embedding queue[/dim]") self._embed_worker.drain() crm_signals = self._crm.planner_context() + self.state.persona_stress = dict(self.graph_dynamics._stress) + + on_call_today = self._get_next_on_call(self.state.day) + org_plan = self._day_planner.plan( self.state, self._mem, @@ -971,6 +1021,7 @@ def daily_cycle(self): clock=self._clock, email_signals=vendor_signals, crm_summary=crm_signals, + on_call=on_call_today, ) if org_plan is None: logger.error( @@ -1056,6 +1107,7 @@ def daily_cycle(self): self._advance_incidents() + logger.info("[dim] Draining embedding queue[/dim]") self._embed_worker.drain() serialized_incidents = [] @@ -1994,6 +2046,7 @@ def _handle_incident(self): triggered_contacts = self.graph_dynamics.relevant_external_contacts( event_type="incident_opened", system_health=self.state.system_health, + incident=inc, ) for contact in triggered_contacts: self._handle_external_contact(inc, contact) @@ -2039,6 +2092,7 @@ def _handle_incident(self): timestamp=incident_start_iso, date_str=date_str, day=self.state.day, + root_cause=root_cause, ) self._mem.log_event( @@ -2142,6 +2196,7 @@ def _advance_incidents(self): triggered_contacts = self.graph_dynamics.relevant_external_contacts( event_type="fix_in_progress", system_health=self.state.system_health, + incident=inc, ) for contact in triggered_contacts: self._handle_external_contact(inc, contact) @@ -2472,8 +2527,6 @@ def _end_of_day(self): self.graph_dynamics.decay_edges() - self.graph_dynamics.decay_edges() - edge_changes = self.graph_dynamics.sync_crm_edge_weights(self._crm) if edge_changes: logger.debug( @@ -2601,7 +2654,7 @@ def _handle_external_contact(self, inc: ActiveIncident, contact: dict) -> None: liaison_dept = contact.get("internal_liaison", list(LEADS.keys())[0]) liaison_name = LEADS.get(liaison_dept, next(iter(LEADS.values()))) display_name = contact.get("display_name", contact["name"]) - tone = contact.get("summary_tone", "professional") + tone = contact.get("tone", "professional") date_str = str(self.state.current_date.date()) participants = [liaison_name, display_name] @@ -2725,6 +2778,11 @@ def _handle_external_contact(self, inc: ActiveIncident, contact: dict) -> None: self._record_daily_actor(liaison_name) self._record_daily_event("external_contact_summarized") + if contact.get("category", "").lower() == "customer": + org = contact.get("org", contact["name"]) + if org not in inc.contacted_customers: + inc.contacted_customers.append(org) + logger.info( f" [cyan]🌐 External contact:[/cyan] {liaison_name} summarized " f"{display_name} re {inc.ticket_id} in #incidents" diff --git a/src/genesis.py b/src/genesis.py index 155da91..078fc7d 100644 --- a/src/genesis.py +++ b/src/genesis.py @@ -49,118 +49,172 @@ def initialize(config, planner_llm, reset=False): return mem +# genesis.py — replace seed_external_sources entirely + + def seed_external_sources(mem: Memory, planner_llm): - """Generates the 15 external vendors/customers and saves to MongoDB.""" if mem.get_inbound_email_sources(): return logger.info("[cyan]🌐 Generating inbound email sources...[/cyan]") - tech_stack = mem.tech_stack_for_prompt() - dept_str = ", ".join(LEADS.keys()) + vendors = _generate_vendor_sources(mem, planner_llm, tech_stack) + customers = _generate_customer_sources(mem, planner_llm, tech_stack) + + sources = vendors + customers + if len(sources) < 10: + raise SystemExit("[genesis] ❌ Too few sources generated. Aborting.") + + mem.save_inbound_email_sources(sources) + logger.info( + f"[genesis] ✅ Seeded {len(sources)} sources ({len(vendors)}V + {len(customers)}C)." + ) + for s in sources: + logger.info( + f" [dim]→ [{s['category']}] {s['name']} " + f"({s['internal_liaison']}) triggers={s['trigger_on']}[/dim]" + ) + + +def _generate_vendor_sources(mem: Memory, planner_llm, tech_stack: str) -> List[dict]: + dept_str = ", ".join(LEADS.keys()) all_names = [name for members in ORG_CHART.values() for name in members] agent = make_agent( role="Enterprise IT Architect", - goal=f"Design the realistic external email ecosystem for {COMPANY_NAME} which {COMPANY_DESCRIPTION}.", + goal=f"Design the vendor email ecosystem for {COMPANY_NAME}.", backstory=( - f"You are an experienced enterprise architect who understands " - f"communication patterns between a {INDUSTRY} company and its " - f"vendors, customers, and partners." + f"You map communication patterns between a {INDUSTRY} company " + f"and its technology vendors." ), llm=planner_llm, ) - task = Task( description=( - f"Generate 15 realistic inbound email sources. EXACTLY 8 must be 'customer' category, and 7 must be 'vendor' category.\n" - f"TECH STACK: {tech_stack}\n" - f"DEPARTMENTS: {dept_str}\n" - f"DEPARTMENTAL LIAISON LOGIC (Assign Liaisons Based on These Rules):\n" - f" - Engineering_Backend: Responsible for Infrastructure (AWS), Databases (TitanDB), Source Control (GitHub), and Monitoring.\n" - f" - Engineering_Mobile: Responsible for React Native and mobile platform issues.\n" - f" - Product: Responsible for project management (Jira) and feature roadmaps.\n" - f" - Sales_Marketing: Responsible for payment/data vendors (e.g., Stripe) and Customer communication.\n" - f" - QA_Support: Responsible for CI/CD (Jenkins) and testing tool alerts.\n" - f" - HR_Ops: Responsible for legal, compliance, and payroll vendors.\n\n" - f"Rules:\n" - f" - HUMAN NAMES: The 'first_name' and 'last_name' field MUST be a realistic human name representing the Point of Contact (e.g., 'Marcus Thorne').\n" - f" - NO DUPLICATE NAMES: Ensure no new generated names overlap with these: {all_names}.\n" - f" - PERSONA DICT: Include a nested 'persona' object with 'typing_quirks' (string), 'social_role' (string, matching contact_role), and 'expertise' (array of strings).\n" - f" - ADHERENCE: Use ONLY vendors that appear in the TECH STACK above. If Jira is listed, never use Trello.\n" - f" - FIRMOGRAPHICS (Customers ONLY): Include 'industry' (e.g. Financial Services), 'tier' (Enterprise, Mid-Market, SMB), 'billing_region' (NA, EMEA, APAC), 'billing_city', 'billing_state' (2-letter code if US), 'billing_country', and 'arr' (e.g. 50000, 120000, 350000).\n" - f" - STRATEGIC (Customers ONLY): Include 'is_lighthouse' (bool), 'expansion_potential' (int 1-10), and 'contract_renewal_date' (ISO Date string).\n" - f" - TECHNICAL (Vendors ONLY): Include 'integration_complexity' (Low, Med, High) and 'version_in_use' (e.g., 'v2 Beta', 'Legacy').\n" - f" - HEALTH SENSITIVITY: Include 'trigger_health_threshold' (int 0-100). Scale: Infrastructure/Enterprise (85-98), SMB/Standard Vendors (70-85).\n" - f" - PERSONA: Include 'contact_role' (e.g. VP Engineering, Procurement) and 'persona_archetype' (e.g. The Champion, The Skeptic, The Bureaucrat).\n" - f" - DYNAMICS: Include 'expected_sla_hours' (int: 2, 4, 24, 48), 'cadence' (daily, weekly, bi-weekly, reactive), and 'timezone_offset' (int: -8 to +8).\n" - f" - RELATIONSHIP: Include 'sentiment_baseline' (float 0.0 to 1.0) and 'history_summary' (1 short sentence mapping the history).\n" - f" - TOPICS: Provide 3-5 hyper-specific topics (e.g., 'GitHub Actions Runner Timeout' or 'Stripe API 402 Payment Required').\n" - f" - CATEGORY: exactly 'vendor' or 'customer'.\n" - f" - TRIGGER_ON: array of 'always', 'incident', 'low_health'.\n" - f" - TONE: formal | technical | frustrated | urgent | friendly.\n\n" - f"Raw JSON array only — no preamble, no markdown fences:\n" - f"[\n" - f' {{"name":"GitHub","org":"GitHub Inc.","first_name":"Jake","last_name": "Smith","org":"GitHub Inc.","email":"j.smith@github.com",' - f'"category":"vendor","internal_liaison":"Engineering_Backend",' - f'"contact_role":"Senior Technical Account Manager","persona_archetype":"The Technical Expert",' - f'"trigger_on":["incident", "low_health"],"trigger_health_threshold":95,' - f'"expected_sla_hours":4,"cadence":"reactive","timezone_offset":-8,' - f'"integration_complexity":"High","version_in_use":"Enterprise Cloud",' - f'"sentiment_baseline":0.8,"history_summary":"Solid uptime, but API rate limits frequently cause friction.",' - f'"tone":"technical","topics":["Webhooks failing with 5xx","Pull Request comment API latency"]}},\n' - f' {{"name":"GlobalFinance","org":"GlobalFinance Corp","email":"cto@globalfinance.com",' - f'"category":"customer","internal_liaison":"Sales_Marketing",' - f'"contact_role":"CTO","persona_archetype":"The Skeptic",' - f'"persona": {{"typing_quirks": "terse, lowercase heavy, fast responses", "social_role": "CTO", "expertise": ["enterprise architecture", "security compliance"]}},' - f'"trigger_on":["always","incident"],"trigger_health_threshold":90,' - f'"expected_sla_hours":2,"cadence":"weekly","timezone_offset":-5,' - f'"is_lighthouse":true,"expansion_potential":8,"contract_renewal_date":"2026-12-01T00:00:00Z",' - f'"sentiment_baseline":0.4,"history_summary":"Demanding enterprise client, currently evaluating competitors for next year.",' - f'"tone":"formal","topics":["SLA reporting","Contract renewal"],"industry":"Financial Services",' - f'"tier":"Enterprise","billing_region":"NA","billing_city":"New York","billing_state":"NY","billing_country":"USA","arr":250000}}\n' - f"]" + f"Generate exactly 7 vendor email sources for {COMPANY_NAME}, " + f"a {INDUSTRY} company that {COMPANY_DESCRIPTION}.\n\n" + f"TECH STACK (use ONLY vendors that appear here):\n{tech_stack}\n\n" + f"DEPARTMENTS: {dept_str}\n\n" + f"LIAISON RULES — assign internal_liaison based on what the vendor provides:\n" + f" - Infrastructure, cloud, hosting, databases, source control, monitoring → Engineering_Backend\n" + f" - Mobile platform tools, SDKs → Engineering_Mobile\n" + f" - Project management tools (Jira, etc.) → Product\n" + f" - CI/CD, testing tools → QA_Support\n" + f" - Payment processing, billing → Sales_Marketing\n" + f" - Legal, compliance, payroll → HR_Ops\n\n" + f"NO DUPLICATE NAMES with: {all_names}\n\n" + f"Each vendor must include:\n" + f" - name, org, first_name, last_name, email\n" + f' - category: exactly "vendor"\n' + f" - internal_liaison: one of [{dept_str}] per rules above\n" + f" - contact_role, persona_archetype\n" + f" - persona: {{typing_quirks, social_role, expertise[]}}\n" + f" - trigger_on: array of 'always', 'incident', 'low_health'\n" + f" - trigger_health_threshold: int 85-98 for infra, 70-85 for standard\n" + f" - tone: formal | technical | urgent\n" + f" - topics: 3-5 specific to what this vendor provides\n" + f" - integration_complexity: Low | Med | High\n" + f" - version_in_use: e.g. 'Enterprise Cloud', 'v2 Beta'\n" + f" - expected_sla_hours, cadence, timezone_offset\n" + f" - sentiment_baseline: float 0.0-1.0\n" + f" - history_summary: 1 short sentence\n\n" + f"Raw JSON array only — no preamble, no markdown fences." ), - expected_output=f"Raw JSON array of {_DEFAULT_SOURCE_COUNT} source objects.", + expected_output="Raw JSON array of 7 vendor objects.", agent=agent, ) for attempt in range(1, _MAX_RETRIES + 1): try: - logger.info( - f"[genesis] Generating external sources (Attempt {attempt}/{_MAX_RETRIES})..." - ) - result = str(Crew(agents=[agent], tasks=[task]).kickoff()).strip() - - sources = _parse_sources(result) + raw = str(Crew(agents=[agent], tasks=[task]).kickoff()).strip() + sources = _parse_sources(raw) + vendors = [s for s in sources if s.get("category") == "vendor"] + if len(vendors) >= 5: + return vendors + raise ValueError(f"Only {len(vendors)} vendors parsed") + except Exception as e: + logger.warning(f"[genesis] Vendor attempt {attempt} failed: {e}") + if attempt == _MAX_RETRIES: + raise SystemExit("[genesis] ❌ Vendor generation failed.") + return [] - if isinstance(sources, list) and len(sources) >= 10: - mem.save_inbound_email_sources(sources) - logger.info(f"[genesis] ✅ Successfully seeded {len(sources)} sources.") - for s in sources: - logger.info( - f" [dim]→ [{s['category']}] {s['name']} " - f"({s['internal_liaison']}) triggers={s['trigger_on']}[/dim]" - ) - return +def _generate_customer_sources(mem: Memory, planner_llm, tech_stack: str) -> List[dict]: + all_names = [name for members in ORG_CHART.values() for name in members] - raise ValueError("Incomplete or malformed list returned.") + agent = make_agent( + role="VP of Customer Success", + goal=f"Design the customer ecosystem for {COMPANY_NAME}.", + backstory=( + f"You understand how {INDUSTRY} customers use " + f"{COMPANY_NAME}'s platform and what they depend on." + ), + llm=planner_llm, + ) + task = Task( + description=( + f"Generate exactly 8 customer email sources for {COMPANY_NAME}, " + f"a {INDUSTRY} company that {COMPANY_DESCRIPTION}.\n\n" + f"TECH STACK (for depends_on_components only — customers never see this):\n{tech_stack}\n\n" + f"INTERNAL_LIAISON: For ALL customers, set to 'Sales_Marketing'. No exceptions.\n\n" + f"NO DUPLICATE NAMES with: {all_names}\n\n" + f"Each customer must include:\n" + f" - name (a realistic human name), org, first_name, last_name, email\n" + f' - category: exactly "customer"\n' + f' - internal_liaison: "Sales_Marketing"\n' + f" - contact_role, persona_archetype (The Champion, The Skeptic, The Bureaucrat, etc.)\n" + f" - persona: {{typing_quirks, social_role, expertise[]}}\n" + f" - trigger_on: array of 'always', 'incident', 'low_health'\n" + f" - trigger_health_threshold: int (Enterprise=88-98, Mid-Market=80-90, SMB=70-85)\n" + f" - tone: formal | friendly | frustrated | urgent\n" + f" - topics: 3-5 hyper-specific to what THIS customer uses the platform for — " + f"written from their perspective, no internal tech names\n" + f" - industry, tier (Enterprise|Mid-Market|SMB), billing_region (NA|EMEA|APAC), " + f"billing_city, billing_state, billing_country, arr\n" + f" - is_lighthouse (bool), expansion_potential (1-10), contract_renewal_date (ISO)\n" + f" - expected_sla_hours, cadence, timezone_offset\n" + f" - sentiment_baseline: float 0.0-1.0\n" + f" - history_summary: 1 short sentence\n\n" + f" - DEPENDS_ON_COMPONENTS: Array of 2-4 exact technology/component names " + f"extracted from the TECH STACK above that power what this customer uses. " + f"Use the specific product names as they appear in the stack " + f"(e.g., 'Kafka', 'PostgreSQL', 'TitanDB', 'React Native', 'Redis'). " + f"NOT category keys like 'database' or 'infra'. " + f"A cycling team relying on live data might depend on ['Kafka', 'TitanDB', 'React Native']. " + f"A clinic using historical reports might depend on ['PostgreSQL', 'S3']. " + f"These MUST match real names from the tech stack.\n\n" + f" - AFFECTED_BY: Array of 2-4 capability strings describing end-user outcomes " + f"this customer depends on. NOT internal tech names. " + f"e.g., ['real-time athlete metrics', 'GPS tracking sync', 'historical performance reports']\n\n" + f" - SYMPTOM_LANGUAGE: 1-2 sentences in the customer's own voice describing " + f"how an outage would affect THEM. Reflects their industry, persona_archetype, and tone. " + f"NEVER mention internal system names.\n\n" + f"Ensure diversity: mix tiers, regions, industries, and sentiment levels. " + f"At least 2 should have sentiment_baseline < 0.6.\n\n" + f"Raw JSON array only — no preamble, no markdown fences." + ), + expected_output="Raw JSON array of 8 customer objects.", + agent=agent, + ) + for attempt in range(1, _MAX_RETRIES + 1): + try: + raw = str(Crew(agents=[agent], tasks=[task]).kickoff()).strip() + sources = _parse_sources(raw) + customers = [s for s in sources if s.get("category") == "customer"] + if len(customers) >= 6: + return customers + raise ValueError(f"Only {len(customers)} customers parsed") except Exception as e: - logger.warning(f"[genesis] ⚠ Attempt {attempt} failed: {e}") + logger.warning(f"[genesis] Customer attempt {attempt} failed: {e}") if attempt == _MAX_RETRIES: - logger.error( - "[genesis] ❌ All retries failed. Simulation cannot start without ground truth." - ) - raise SystemExit(1) - - pass + raise SystemExit("[genesis] ❌ Customer generation failed.") + return [] def seed_tech_stack(mem: Memory, planner_llm): - """Generates the tech stack ground truth and saves to Confluence.""" + """Generates the tech stack ground truth.""" if mem._artifacts.find_one({"type": "tech_stack"}): return @@ -210,6 +264,12 @@ def seed_tech_stack(mem: Memory, planner_llm): mem.save_tech_stack(stack) logger.info(f"[confluence] ✓ Tech stack established: {list(stack.keys())}") + mem._db["artifacts"].create_index( + [("title", "text"), ("content", "text")], + name="artifacts_text_search", + weights={"title": 3, "content": 1}, + ) + pass @@ -296,6 +356,9 @@ def seed_crm_accounts(mem: Memory): sentiment = contact.get("sentiment_baseline", 0.8) is_risky = True if sentiment < 0.5 else False + liaison_dept = contact.get("internal_liaison", "Unassigned") + liaison_person = LEADS.get(liaison_dept, liaison_dept) + account = { "account_id": account_id, "name": org_name, @@ -303,7 +366,7 @@ def seed_crm_accounts(mem: Memory): "primary_contact_name": f"{contact.get('first_name', 'First Name')} {contact.get('last_name', 'Last Name')}", "primary_contact_email": contact.get("email", ""), "contact_role": contact.get("contact_role", "Unknown"), - "owner": contact.get("internal_liaison", "Unassigned"), + "owner": liaison_person, "industry": contact.get("industry", "Technology"), "tier": tier if tier != "Unknown" else None, "employee_count": random.randint(*emp_range), @@ -539,7 +602,15 @@ def _parse_sources(raw: str) -> List[dict]: "trigger_on", "topics", } - return [s for s in parsed if required.issubset(s.keys())] + valid = [] + for s in parsed: + if not required.issubset(s.keys()): + continue + # Force-correct customer liaison at parse time + if s.get("category", "").lower() == "customer": + s["internal_liaison"] = "Sales_Marketing" + valid.append(s) + return valid except Exception as exc: logger.warning(f"[external_email] Source parse failed: {exc}") return [] diff --git a/src/graph_dynamics.py b/src/graph_dynamics.py index 05a3eb0..5c9c6af 100644 --- a/src/graph_dynamics.py +++ b/src/graph_dynamics.py @@ -276,18 +276,26 @@ def escalation_narrative(self, chain: EscalationChain) -> str: ) return hops + suffix + def _incident_affects_customer(self, incident, source: dict) -> bool: + components = [c.lower() for c in source.get("depends_on_components", [])] + if not components: + return False + root_cause = (getattr(incident, "root_cause", "") or "").lower() + if not root_cause: + return False + return any(comp in root_cause for comp in components) + def relevant_external_contacts( self, event_type: str, system_health: int, + incident=None, ) -> List[dict]: """ - Returns external contact config entries that should be triggered - given the current event type and system health. - Called from _advance_incidents() to decide whether to generate - an external contact summary. + Returns external contacts that should be triggered. + Customers are only included when their depends_on_components + overlap with the incident root cause. """ - doc = self._mem._db["sim_config"].find_one({"_id": "inbound_email_sources"}) if not doc or "sources" not in doc: return [] @@ -295,6 +303,11 @@ def relevant_external_contacts( triggered = [] for contact in doc["sources"]: triggers = contact.get("trigger_on", []) + is_customer = contact.get("category", "").lower() == "customer" + + if is_customer and incident is not None: + if not self._incident_affects_customer(incident, contact): + continue if "always" in triggers: triggered.append(contact) @@ -306,7 +319,8 @@ def relevant_external_contacts( triggered.append(contact) continue - if "low_health" in triggers and system_health < 80: + threshold = contact.get("trigger_health_threshold", 80) + if "low_health" in triggers and system_health < threshold: triggered.append(contact) return triggered diff --git a/src/memory.py b/src/memory.py index ce42b8c..dbaa116 100644 --- a/src/memory.py +++ b/src/memory.py @@ -92,6 +92,7 @@ class SimEvent: facts: Dict[str, Any] summary: str tags: List[str] = field(default_factory=list) + mongo_id: Optional[str] = field(default=None) def to_embed_text(self) -> str: return ( @@ -118,6 +119,7 @@ def from_dict(cls, d: Dict) -> "SimEvent": facts=d.get("facts", {}), summary=d.get("summary", ""), tags=d.get("tags", []), + mongo_id=d.get("_id"), ) @@ -935,7 +937,7 @@ def get_event_log( if as_of_time: query["timestamp"] = {"$lte": as_of_time} - raw = self._events.find(query, {"_id": 0}).sort("timestamp", 1) + raw = self._events.find(query).sort("timestamp", 1) return [SimEvent.from_dict(r) for r in raw] log = self._event_log diff --git a/src/normal_day.py b/src/normal_day.py index e0f3df8..87cc148 100644 --- a/src/normal_day.py +++ b/src/normal_day.py @@ -28,6 +28,13 @@ logger = logging.getLogger("orgforge.normalday") +_ASYNC_DOC_PROB: Dict[str, float] = { + "resolved": 0.20, + "escalated": 0.30, + "uncertain": 0.10, + "unresolved": 0.05, +} + class NormalDayHandler: def __init__( @@ -128,8 +135,10 @@ def _execute_agenda_items(self, org_plan: OrgDayPlan, date_str: str) -> None: and not distraction_fired and idx == distraction_index ): - self._trigger_watercooler_chat(eng_plan.name, date_str) penalty_hours = random.uniform(0.16, 0.25) + self._trigger_watercooler_chat( + eng_plan.name, date_str, penalty_hours=penalty_hours + ) item.estimated_hrs += penalty_hours self._clock.advance_actor(eng_plan.name, penalty_hours) distraction_fired = True @@ -1610,10 +1619,7 @@ def _handle_async_question( doc_hint = ( "Note: the following internal documentation exists and may be " "referenced naturally in this conversation:\n" - + "\n".join( - f" - '{e['title']}' (written by {e['author']}, day {e['day']})" - for e in relevant_experts - ) + + "\n".join(f" - '{e['title']}' day {e['day']})" for e in relevant_experts) if relevant_experts else "" ) @@ -1758,9 +1764,9 @@ def _handle_async_question( full_text = " ".join(m["text"] for m in messages) self._score_and_apply_sentiment(full_text, all_actors, self._vader) + classification = None if self._lifecycle and messages: - thread_text = " ".join(m["text"] for m in messages) - self._assess_async_thread_gap( + classification = self._assess_async_thread_gap( messages=messages, topic=ticket_title, asker=asker, @@ -1770,6 +1776,35 @@ def _handle_async_question( timestamp=meeting_time_iso, ) + conf_id = None + if classification and messages: + conf_id = self._maybe_spawn_async_confluence( + classification=classification, + topic=ticket_title, + asker=asker, + participants=all_actors, + messages=messages, + thread_id=thread_id, + ticket_id=ticket_id, + date_str=date_str, + timestamp=meeting_time_iso, + ) + + if conf_id: + self._mem._events.update_one( + { + "type": "async_question", + "artifact_ids.slack_thread": thread_id, + "day": self._state.day, + }, + { + "$set": { + "facts.spawned_doc": True, + "artifact_ids.confluence": conf_id, + } + }, + ) + self._gd.record_slack_interaction(all_actors) logger.info(f" [dim]❓ {asker} → #{channel} ({len(messages)} msgs)[/dim]") return all_actors @@ -1849,6 +1884,13 @@ def _handle_design_discussion( conf_id = self._create_design_doc_stub( initiator, participants, item.description, ctx, date_str, stub_messages ) + if conf_id: + self._update_domain_registry_on_doc( + domain_hint=item.description, + author=initiator, + participants=participants, + coverage_boost=0.12, + ) facts = { "topic": item.description, @@ -2556,6 +2598,7 @@ def _emit_sales_outbound_email( logger.warning(f"[sales_email] JSON parse failed for {ticket_id}: {exc}") subject = f"Following up — {account_name}" body = clean + new_stage = stage_label sender_addr = f"{assignee.lower().replace(' ', '.')}@{self._domain}" out_dir = Path(self._base) / "emails" / "outbound" / date_str @@ -2932,7 +2975,9 @@ def _maybe_adhoc_confluence(self) -> None: # on incident days and strategic docs on calm ones. self._confluence.write_adhoc_page() - def _trigger_watercooler_chat(self, target_actor: str, date_str: str) -> None: + def _trigger_watercooler_chat( + self, target_actor: str, date_str: str, penalty_hours: float + ) -> None: """Injects non-work chatter, pulling the target actor away from their work.""" if target_actor not in self._graph: return @@ -3083,7 +3128,11 @@ def _trigger_watercooler_chat(self, target_actor: str, date_str: str) -> None: date=date_str, actors=participants, artifact_ids={"slack_thread": thread_id, "slack_path": slack_path}, - facts={"topic": topic, "message_count": len(messages)}, + facts={ + "topic": topic, + "message_count": len(messages), + "penalty_hours": penalty_hours, + }, summary=f"{target_actor} got distracted chatting about {topic} with {len(participants) - 1} others.", tags=["watercooler", "slack", "distraction"], ) @@ -3102,7 +3151,7 @@ def _assess_async_thread_gap( ticket_id: Optional[str], date_str: str, timestamp: str, - ) -> None: + ) -> Optional[dict]: """ Classify whether an async Q&A thread reveals a genuine knowledge gap vs a routine question that got answered. @@ -3195,6 +3244,12 @@ def _assess_async_thread_gap( ) ) + return { + "outcome": outcome, + "gap_domain": gap_domain, + "evidence": evidence, + } + def _last_turn_desc( self, speaker: str, @@ -3548,6 +3603,169 @@ def _save_zoom_transcript( ) return file_path, transcript_id + def _maybe_spawn_async_confluence( + self, + classification: dict, + topic: str, + asker: str, + participants: List[str], + messages: List[dict], + thread_id: str, + ticket_id: Optional[str], + date_str: str, + timestamp: str, + ) -> Optional[str]: + """ + Probability-gated Confluence page spawn from an async Q&A thread. + + Resolved threads produce "TIL" / "How-To" pages. Gap threads produce + "What We Know So Far" stub pages. Both update the DomainRegistry so + documentation_coverage recovers organically over time. + + Returns the confluence page ID if spawned, else None. + """ + if self._confluence is None: + return None + + outcome = classification.get("outcome", "resolved") + gap_domain = classification.get("gap_domain", "") + + prob = ( + self._config["simulation"] + .get("async_doc_prob", {}) + .get(outcome, _ASYNC_DOC_PROB.get(outcome, 0.0)) + ) + + if random.random() >= prob: + return None + + responder_msg_counts: Dict[str, int] = {} + for m in messages: + if m["user"] != asker: + responder_msg_counts[m["user"]] = responder_msg_counts.get( + m["user"], 0 + ) + len(m.get("text", "")) + doc_author = ( + max(responder_msg_counts, key=responder_msg_counts.get) + if responder_msg_counts + else asker + ) + + write_delay_hours = random.uniform(0.5, 1.5) + doc_time, _ = self._clock.advance_actor(doc_author, hours=write_delay_hours) + doc_timestamp = doc_time.isoformat() + + conf_id = self._create_design_doc_stub( + author=doc_author, + participants=participants, + topic=topic, + ctx="", + date_str=date_str, + slack_transcript=messages, + ) + if not conf_id: + return None + + self._mem.log_event( + SimEvent( + type="confluence_created", + timestamp=doc_timestamp, + day=self._state.day, + date=date_str, + actors=[doc_author] + [p for p in participants if p != doc_author], + artifact_ids={ + "confluence": conf_id, + "slack_thread": thread_id, + "jira": ticket_id or "", + }, + facts={ + "source": "async_thread", + "source_thread": thread_id, + "topic": topic, + "gap_domain": gap_domain or topic, + "outcome_at_write_time": outcome, + "author": doc_author, + }, + summary=( + f"{doc_author} wrote Confluence page '{conf_id}' documenting " + f"'{topic}' after async Q&A thread ({outcome})." + ), + tags=["confluence", "async_question", "documentation"], + ) + ) + + self._update_domain_registry_on_doc( + domain_hint=gap_domain or topic, + author=doc_author, + participants=participants, + coverage_boost=0.10 if outcome == "resolved" else 0.05, + ) + + logger.info( + f" [dim]📄 {doc_author} documented '{topic}' → {conf_id} " + f"(async thread {outcome})[/dim]" + ) + return conf_id + + def _update_domain_registry_on_doc( + self, + domain_hint: str, + author: str, + participants: List[str], + coverage_boost: float = 0.10, + ) -> None: + """ + When a Confluence page is written that covers a domain, bump the + DomainRegistry's documentation_coverage and add contributors to + known_by. + + Uses system_tags fuzzy matching so "clarify branch protection" hits + the "branch_protection" registry entry. + """ + if not domain_hint: + return + + tokens = set( + t for t in domain_hint.lower().replace("-", " ").split() if len(t) >= 3 + ) + if not tokens: + return + + matched_docs = list( + self._mem._db["domain_registry"].find( + {"system_tags": {"$in": list(tokens)}} + ) + ) + + for doc in matched_docs: + old_coverage = doc.get("documentation_coverage", 0.0) + new_coverage = min(1.0, round(old_coverage + coverage_boost, 3)) + + new_known = set(doc.get("known_by", [])) + new_known.add(author) + for p in participants: + if p in self._all_names: + new_known.add(p) + + update: dict = { + "$set": { + "documentation_coverage": new_coverage, + "last_updated_day": self._state.day, + "known_by": sorted(new_known), + }, + } + + if doc.get("primary_owner") is None and author in self._all_names: + update["$set"]["primary_owner"] = author + + self._mem._db["domain_registry"].update_one({"_id": doc["_id"]}, update) + + logger.info( + f" [dim]📊 DomainRegistry '{doc['domain']}': " + f"coverage {int(old_coverage * 100)}% → {int(new_coverage * 100)}%, " + f"known_by now includes {author}[/dim]" + ) + def _save_md(self, path: str, content: str) -> None: import os diff --git a/src/org_lifecycle.py b/src/org_lifecycle.py index 5bf1e68..677bfe1 100644 --- a/src/org_lifecycle.py +++ b/src/org_lifecycle.py @@ -35,6 +35,7 @@ from agent_factory import make_agent +from config_loader import CONFIG from crm_system import NullCRMSystem from memory import Memory, SimEvent from graph_dynamics import GraphDynamics @@ -169,14 +170,15 @@ def process_departures( if self._cfg.get("enable_random_attrition", False): prob = self._cfg.get("random_attrition_daily_prob", 0.01) - candidates = [ - n - for n in list(self._all_names) - if n not in self._leads.values() - and n not in [d.name for d in self._departed] - ] - for candidate in candidates: - if random.random() < prob: + if random.random() < prob: + candidates = [ + n + for n in list(self._all_names) + if n not in self._leads.values() + and n not in [d.name for d in self._departed] + ] + if candidates: + candidate = random.choice(candidates) dept = next( (d for d, m in self._org_chart.items() if candidate in m), "Unknown", @@ -187,28 +189,25 @@ def process_departures( if n not in self._leads.values() ] min_size = self._cfg.get("min_dept_size", 2) - if len(dept_members) <= min_size: - continue - - attrition_cfg = { - "name": candidate, - "reason": "voluntary", - "knowledge_domains": [], - "documented_pct": 0.5, - } - record = self._execute_departure( - attrition_cfg, - day, - date_str, - state, - scheduled=False, - clock=clock, - ) - if record: - departures.append(record) - if ticket_assigner is not None: - ticket_assigner.evict_engineer(record.name) - break + if len(dept_members) > min_size: + attrition_cfg = { + "name": candidate, + "reason": "voluntary", + "knowledge_domains": [], + "documented_pct": 0.5, + } + record = self._execute_departure( + attrition_cfg, + day, + date_str, + state, + scheduled=False, + clock=clock, + ) + if record: + departures.append(record) + if ticket_assigner is not None: + ticket_assigner.evict_engineer(record.name) return departures @@ -286,11 +285,10 @@ def scan_for_knowledge_gaps( continue self._domains_surfaced.add(gap_key) - gap_domains = ( - record.knowledge_domains - if record.knowledge_domains - else ["undocumented expertise"] - ) + gap_domains = record.knowledge_domains + if not gap_domains: + continue + domain_label = ", ".join(gap_domains) # ── Pass 2: DomainRegistry cross-reference ───────────────────── @@ -1038,13 +1036,6 @@ def _execute_hire( if name not in self._all_names: self._all_names.append(name) - self._personas[name] = { - "style": style, - "expertise": expertise, - "tenure": tenure, - "stress": 20, - } - G.add_node(name, dept=dept, is_lead=False, external=False, hire_day=day) self._gd._stress[name] = 20 self._gd._centrality_dirty = True @@ -1136,17 +1127,23 @@ def _schedule_backfill(self, record: DepartureRecord, current_day: int) -> None: lag_days = backfill_cfg.get("lag_days", 14) hire_day = current_day + lag_days - name = self._generate_backfill_name(dept=record.dept, role=record.role) - if name is None: + persona = self._generate_backfill_persona( + dept=record.dept, role=record.role, departed_name=record.name + ) + if persona is None: return - departed_persona = self._personas.get(record.name, {}) backfill_hire = { - "name": backfill_cfg.get("name_prefix", "NewHire") + f"_{hire_day}", + "name": persona["name"], "dept": record.dept, "role": record.role, - "expertise": departed_persona.get("expertise", ["general"]), - "style": "still ramping up, asks frequent questions", + "expertise": persona.get("expertise", ["general"]), + "style": persona.get("style", "still ramping up, asks frequent questions"), + "social_role": persona.get("social_role", "The Newcomer"), + "typing_quirks": persona.get( + "typing_quirks", "Standard professional grammar." + ), + "pet_peeves": persona.get("pet_peeves", ""), "tenure": "new", "day": hire_day, "_backfill_for": record.name, @@ -1159,49 +1156,88 @@ def _schedule_backfill(self, record: DepartureRecord, current_day: int) -> None: f"queued for Day {hire_day} (replacing {record.name})" ) - def _generate_backfill_name(self, dept: str, role: str) -> Optional[str]: + def _generate_backfill_persona( + self, dept: str, role: str, departed_name: str + ) -> Optional[dict]: """ - Ask the LLM for a single realistic first+last name for a new hire. - Retries up to 3 times if the name collides with an existing person. - Returns None if a unique name can't be produced — backfill is skipped. + Ask the LLM to generate a full persona for a backfill hire. + Includes name, style, social_role, typing_quirks, pet_peeves, and expertise. + Retries up to 3 times if the generated name collides with an existing person. + Returns None if a unique persona can't be produced — backfill is skipped. """ if self._llm is None: return None forbidden = set(self._all_names) | {d.name for d in self._departed} - company = self._cfg.get("company_name", "the company") + company = CONFIG["simulation"]["company_name"] + departed_persona = self._personas.get(departed_name, {}) + departed_expertise = departed_persona.get("expertise", ["general"]) for attempt in range(3): try: + import json from crewai import Task, Crew agent = make_agent( role="HR Coordinator", - goal="Generate a realistic employee name.", + goal="Generate a realistic new hire persona.", backstory=( f"You work in HR at {company}. " - f"You are onboarding a new {role} for the {dept} team." + f"You are onboarding a new {role} for the {dept} team " + f"to replace {departed_name}, who recently left." ), llm=self._llm, ) task = Task( description=( - f"Generate ONE realistic full name (first and last) for a new " - f"{role} joining the {dept} team. " - f"The name must not be any of: {sorted(forbidden)}. " - f"Respond with ONLY the name — no punctuation, no explanation." + f"Generate a realistic persona for a new {role} joining the {dept} team.\n" + f"They are replacing {departed_name}, so their expertise should broadly " + f"cover: {departed_expertise}, but with their own background and gaps.\n" + f"The name must not be any of: {sorted(forbidden)}.\n\n" + f"Respond with ONLY a JSON object — no explanation, no markdown fences:\n" + f"{{\n" + f' "name": "First Last",\n' + f' "expertise": ["skill1", "skill2", "skill3"],\n' + f' "style": "one sentence describing how they work",\n' + f' "social_role": "The [Archetype]",\n' + f' "typing_quirks": "one or two sentences describing their written voice",\n' + f' "pet_peeves": "brief phrase"\n' + f"}}" ), - expected_output="A single full name, e.g. 'Jordan Lee'.", + expected_output="A JSON object with keys: name, expertise, style, social_role, typing_quirks, pet_peeves.", agent=agent, ) raw = str( Crew(agents=[agent], tasks=[task], verbose=False).kickoff() ).strip() - name = " ".join(raw.split()) + # Strip markdown fences if the LLM included them anyway + raw = ( + raw.strip() + .removeprefix("```json") + .removeprefix("```") + .removesuffix("```") + .strip() + ) + + try: + persona = json.loads(raw) + except json.JSONDecodeError: + logger.warning( + f"[lifecycle] Persona generation attempt {attempt + 1} " + f"returned invalid JSON: {raw[:200]}" + ) + continue + + name = persona.get("name", "").strip() + name = " ".join(name.split()) name = "".join(c for c in name if c.isalpha() or c == " ").strip() if not name or len(name.split()) < 2: + logger.warning( + f"[lifecycle] Persona generation attempt {attempt + 1} " + f"produced an unusable name: '{name}'" + ) continue if name in forbidden: logger.info( @@ -1209,15 +1245,16 @@ def _generate_backfill_name(self, dept: str, role: str) -> Optional[str]: ) continue - return name + persona["name"] = name + return persona except Exception as e: logger.warning( - f"[lifecycle] Name generation attempt {attempt + 1} failed: {e}" + f"[lifecycle] Persona generation attempt {attempt + 1} failed: {e}" ) logger.warning( - f"[lifecycle] Could not generate a unique backfill name for {dept} " + f"[lifecycle] Could not generate a unique backfill persona for {dept} " f"after 3 attempts. Backfill skipped." ) return None diff --git a/src/planner_models.py b/src/planner_models.py index 5e812fa..58090ca 100644 --- a/src/planner_models.py +++ b/src/planner_models.py @@ -216,7 +216,6 @@ class ValidationResult: "incident_opened", "incident_resolved", "escalation_chain", - "fix_in_progress", "postmortem_created", "knowledge_gap_detected", "standup", @@ -267,4 +266,10 @@ class ValidationResult: "crm_account_at_risk", "customer_health_briefing", "assignment_domain_mismatch", + "feature_request_fyi", + "blocker_flagged", + "jira_ticket_created", + "org_collision", + "ticket_completion_email", + "mentoring", } diff --git a/src/post_sim_artifacts.py b/src/post_sim_artifacts.py index 6d01adf..38b0645 100644 --- a/src/post_sim_artifacts.py +++ b/src/post_sim_artifacts.py @@ -745,7 +745,7 @@ def build_alerts( alert = { # Datadog Events API schema fields - "id": f"evt_{uuid.uuid4().hex[:12]}", + "id": iid, "title": f"[P1] {monitor_name}", "text": ( f"## Alert\n\n" @@ -927,9 +927,10 @@ def _batch_alert_names( return result -def run(export_dir: Path, use_llm: bool = True) -> None: +def run(export_dir: Path, use_llm: bool = True, only: Optional[set] = None) -> None: logger.info("[post_sim] Starting post-simulation artifact generation...") + only = only or {"nps", "invoices", "datadog"} mem = Memory() events = mem.get_event_log(from_db=True) start_date = datetime.strptime(CONFIG["simulation"]["start_date"], "%Y-%m-%d") @@ -945,54 +946,67 @@ def run(export_dir: Path, use_llm: bool = True) -> None: f"{len(idx.health_by_day)} health snapshots." ) - logger.info("[post_sim] → NPS surveys") - nps_writer = NPSWriter(idx, export_dir, start_date, max_days) - responses = nps_writer.build_responses() - - logger.info("[post_sim] → Invoices") - inv_writer = InvoiceWriter(idx, export_dir, start_date, max_days, mem) - invoices = inv_writer.build_invoices() - - logger.info("[post_sim] → Datadog metrics") - dd_writer = DatadogWriter(idx, export_dir, start_date, max_days) - dd_writer.build_metrics() - alerts = dd_writer.build_alerts() - - if use_llm and (responses or idx.incidents): - logger.info("[post_sim] → LLM batch 1/2: NPS verbatim comments") - try: - from flow import WORKER_MODEL - - nps_comments = _batch_nps_comments(responses, WORKER_MODEL) - for r in responses: - r["verbatim_comment"] = nps_comments.get( - r["response_id"], - _nps_placeholder(r), + responses = [] + invoices = [] + alerts = [] + + if "nps" in only: + logger.info("[post_sim] → NPS surveys") + nps_writer = NPSWriter(idx, export_dir, start_date, max_days) + responses = nps_writer.build_responses() + + if "invoices" in only: + logger.info("[post_sim] → Invoices") + inv_writer = InvoiceWriter(idx, export_dir, start_date, max_days, mem) + invoices = inv_writer.build_invoices() + + if "datadog" in only: + logger.info("[post_sim] → Datadog metrics") + dd_writer = DatadogWriter(idx, export_dir, start_date, max_days) + dd_writer.build_metrics() + alerts = dd_writer.build_alerts() + + if use_llm: + if "nps" in only and responses: + logger.info("[post_sim] → LLM batch 1/2: NPS verbatim comments") + try: + from flow import WORKER_MODEL + + nps_comments = _batch_nps_comments(responses, WORKER_MODEL) + for r in responses: + r["verbatim_comment"] = nps_comments.get( + r["response_id"], _nps_placeholder(r) + ) + except Exception as e: + logger.warning( + f"[post_sim] NPS LLM call failed ({e}) — using placeholders" + ) + for r in responses: + r["verbatim_comment"] = _nps_placeholder(r) + + if "datadog" in only and idx.incidents: + logger.info("[post_sim] → LLM batch 2/2: Datadog alert monitor names") + try: + from flow import WORKER_MODEL + + monitor_names = _batch_alert_names(idx.incidents, WORKER_MODEL) + alerts = dd_writer.build_alerts(monitor_names) + except Exception as e: + logger.warning( + f"[post_sim] Alert names LLM call failed ({e}) — using root causes" ) - except Exception as e: - logger.warning(f"[post_sim] NPS LLM call failed ({e}) — using placeholders") - for r in responses: - r["verbatim_comment"] = _nps_placeholder(r) - - logger.info("[post_sim] → LLM batch 2/2: Datadog alert monitor names") - try: - from flow import WORKER_MODEL - - monitor_names = _batch_alert_names(idx.incidents, WORKER_MODEL) - alerts = dd_writer.build_alerts(monitor_names) - except Exception as e: - logger.warning( - f"[post_sim] Alert names LLM call failed ({e}) — using root causes" - ) else: if not use_llm: logger.info("[post_sim] LLM calls skipped (--no-llm).") for r in responses: r["verbatim_comment"] = _nps_placeholder(r) - nps_writer.write(responses) - inv_writer.write(invoices) - dd_writer.write_alerts(alerts) + if "nps" in only: + nps_writer.write(responses) + if "invoices" in only: + inv_writer.write(invoices) + if "datadog" in only: + dd_writer.write_alerts(alerts) logger.info( f"[post_sim] Done. " @@ -1051,6 +1065,16 @@ def _nps_placeholder(r: Dict) -> str: action="store_true", help="Skip the two optional LLM calls and use deterministic placeholders.", ) + parser.add_argument( + "--only", + nargs="+", + choices=["nps", "invoices", "datadog"], + help="Only regenerate specific artifact types.", + ) args = parser.parse_args() - run(export_dir=args.export_dir, use_llm=not args.no_llm) + run( + export_dir=args.export_dir, + use_llm=not args.no_llm, + only=set(args.only) if args.only else None, + ) diff --git a/src/ticket_assigner.py b/src/ticket_assigner.py index f3087d8..fc73d88 100644 --- a/src/ticket_assigner.py +++ b/src/ticket_assigner.py @@ -90,7 +90,7 @@ def __init__(self, config: dict, graph_dynamics: GraphDynamics, mem: Memory): self._precompute_engineer_vectors() def build( - self, state, dept_members: List[str], dept_name: str = "" + self, state, dept_members: List[str], dept_name: str = "", on_call: str = "" ) -> SprintContext: """ Main entry point. Call once per department, before DepartmentPlanner.plan(). @@ -101,7 +101,7 @@ def build( • in_progress_ids — tickets already "In Progress" • capacity_by_member — {name: available_hrs} for every dept member """ - capacity = self._compute_capacity(dept_members, state) + capacity = self._compute_capacity(dept_members, state, on_call=on_call) open_tickets = self._mem.get_open_tickets_for_dept( dept_members, dept_name=dept_name @@ -155,17 +155,18 @@ def build( in_review=in_review, ) - def _compute_capacity(self, members: List[str], state) -> Dict[str, float]: + def _compute_capacity( + self, members: List[str], state, on_call: str = "" + ) -> Dict[str, float]: """ Available hours per engineer, mirroring EngineerDayPlan.capacity_hrs so the two systems stay in sync. """ - on_call_name = self._config.get("on_call_engineer") capacity: Dict[str, float] = {} for name in members: stress = self._gd._stress.get(name, 30) base = 6.0 - if name == on_call_name: + if name == on_call: base -= 1.5 if stress > 80: base -= 2.0 @@ -216,7 +217,7 @@ def _hungarian_assign( n_eng = len(members) n_tkt = len(tickets) cost = np.zeros((n_eng, n_tkt)) - + assignment_scores = [] for i, eng in enumerate(members): stress = self._gd._stress.get(eng, 30) stress_score = 1.0 - (stress / 100) @@ -229,6 +230,19 @@ def _hungarian_assign( score = skill * stress_score * cent_factor * recency cost[i][j] = -score + assignment_scores.append( + { + "day": state.day, + "engineer": eng, + "ticket_id": tickets[j]["id"], + "skill_score": self._skill_score(eng, tickets[j]), + "stress_score": 1.0 - (self._gd._stress.get(eng, 30) / 100), + "centrality_factor": 1.0 - (centrality.get(eng, 0.0) * 0.3), + "composite_score": -cost[i][j], + "was_assigned": False, # update after linear_sum_assignment + } + ) + row_ind, col_ind = linear_sum_assignment(cost) result: Dict[str, str] = {} @@ -246,6 +260,8 @@ def _hungarian_assign( else: logger.debug(f"[assigner] {eng} over capacity, skipping {tkt['id']}") + self._mem._db["assignment_scores"].insert_many(assignment_scores) + return result def _greedy_assign( diff --git a/src/utils/persona_utils.py b/src/utils/persona_utils.py index 2bfc59b..01dc30f 100644 --- a/src/utils/persona_utils.py +++ b/src/utils/persona_utils.py @@ -5,6 +5,16 @@ logger = logging.getLogger("orgforge.persona_utils") +DEPARTMENT_EXPERTISE_DEFAULTS = { + "Engineering_Backend": ["general backend", "code review", "documentation"], + "Engineering_Mobile": ["general mobile", "testing", "tickets"], + "Product": ["requirements", "stakeholder updates", "roadmap admin"], + "Sales_Marketing": ["CRM hygiene", "outreach", "reporting"], + "HR_Ops": ["scheduling", "documentation", "vendor comms"], + "Design": ["asset delivery", "feedback cycles", "figma"], + "QA_Support": ["test cases", "bug triage", "customer comms"], +} + class PersonaUtils: def __init__(self): @@ -44,16 +54,21 @@ def get_voice_card( name_to_history = {} for name in name_list: - p = PERSONAS.get(name, DEFAULT_PERSONA) + p = PERSONAS.get(name) or DEFAULT_PERSONA stress = graph_dynamics._stress.get(name, 30) if graph_dynamics else 30 quirks = p.get("typing_quirks", "standard professional grammar") tenure = p.get("tenure", "mid") - expertise = ( - ", ".join(str(e) for e in p.get("expertise", [])[:3]) - or "general engineering" + dept = dept_of_name(name) + + expertise = ", ".join( + str(e) for e in p.get("expertise", [])[:3] + ) or ", ".join( + DEPARTMENT_EXPERTISE_DEFAULTS.get( + dept, ["cross-functional communication"] + ) ) social_role = p.get("social_role", "Contributor") - dept = dept_of_name(name) + interests = ( ", ".join( str(i) for i in (p.get("interests") or p.get("expertise", []))[:3] diff --git a/tests/test_external_email.py b/tests/test_external_email.py index ce3b22f..8c49e24 100644 --- a/tests/test_external_email.py +++ b/tests/test_external_email.py @@ -134,17 +134,32 @@ def test_customer_email_dropped_probability(mock_random, ingestor, mock_state): Verifies that customer emails are dropped and logged correctly when they fall within the 15% drop probability window. """ - mock_random.return_value = 0.10 - ingestor._sources = [ - { - "name": "Acme Corp", - "category": "customer", - "trigger_on": ["always"], - "topics": ["complaint"], - } - ] + source = { + "name": "Acme Corp", + "org": "Acme Corp", + "first_name": "Acme", + "last_name": "Contact", + "email": "contact@acme.com", + "category": "customer", + "internal_liaison": "Sales", + "trigger_on": ["always"], + "topics": ["complaint"], + "tone": "frustrated", + } + + ingestor._derive_customer_email_signals = MagicMock( + return_value=[ + { + "source": source, + "email_type": "complaint", + "trigger": "Test: forced signal for drop probability verification", + "symptom": "", + "topic": "bug", + } + ] + ) dummy_signal = ExternalEmailSignal( source_name="Acme", diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..f8406e5 --- /dev/null +++ b/uv.lock @@ -0,0 +1,4271 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "aiosqlite" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454, upload-time = "2025-02-03T07:30:16.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, +] + +[[package]] +name = "altair" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/c0/184a89bd5feba14ff3c41cfaf1dd8a82c05f5ceedbc92145e17042eb08a4/altair-6.0.0.tar.gz", hash = "sha256:614bf5ecbe2337347b590afb111929aa9c16c9527c4887d96c9bc7f6640756b4", size = 763834, upload-time = "2025-11-12T08:59:11.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/33/ef2f2409450ef6daa61459d5de5c08128e7d3edb773fefd0a324d1310238/altair-6.0.0-py3-none-any.whl", hash = "sha256:09ae95b53d5fe5b16987dccc785a7af8588f2dca50de1e7a156efa8a461515f8", size = 795410, upload-time = "2025-11-12T08:59:09.804Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" }, +] + +[[package]] +name = "bcrypt" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386, upload-time = "2025-09-25T19:50:47.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806, upload-time = "2025-09-25T19:49:05.102Z" }, + { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626, upload-time = "2025-09-25T19:49:06.723Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853, upload-time = "2025-09-25T19:49:08.028Z" }, + { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793, upload-time = "2025-09-25T19:49:09.727Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930, upload-time = "2025-09-25T19:49:11.204Z" }, + { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194, upload-time = "2025-09-25T19:49:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381, upload-time = "2025-09-25T19:49:14.308Z" }, + { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750, upload-time = "2025-09-25T19:49:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757, upload-time = "2025-09-25T19:49:17.244Z" }, + { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740, upload-time = "2025-09-25T19:49:18.693Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197, upload-time = "2025-09-25T19:49:20.523Z" }, + { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974, upload-time = "2025-09-25T19:49:22.254Z" }, + { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498, upload-time = "2025-09-25T19:49:24.134Z" }, + { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853, upload-time = "2025-09-25T19:49:25.702Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626, upload-time = "2025-09-25T19:49:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862, upload-time = "2025-09-25T19:49:28.365Z" }, + { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544, upload-time = "2025-09-25T19:49:30.39Z" }, + { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787, upload-time = "2025-09-25T19:49:32.144Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753, upload-time = "2025-09-25T19:49:33.885Z" }, + { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587, upload-time = "2025-09-25T19:49:35.144Z" }, + { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178, upload-time = "2025-09-25T19:49:36.793Z" }, + { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295, upload-time = "2025-09-25T19:49:38.164Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700, upload-time = "2025-09-25T19:49:39.917Z" }, + { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034, upload-time = "2025-09-25T19:49:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766, upload-time = "2025-09-25T19:49:43.08Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449, upload-time = "2025-09-25T19:49:44.971Z" }, + { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310, upload-time = "2025-09-25T19:49:46.162Z" }, + { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761, upload-time = "2025-09-25T19:49:47.345Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553, upload-time = "2025-09-25T19:49:49.006Z" }, + { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009, upload-time = "2025-09-25T19:49:50.581Z" }, + { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029, upload-time = "2025-09-25T19:49:52.533Z" }, + { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907, upload-time = "2025-09-25T19:49:54.709Z" }, + { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500, upload-time = "2025-09-25T19:49:56.013Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412, upload-time = "2025-09-25T19:49:57.356Z" }, + { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486, upload-time = "2025-09-25T19:49:59.116Z" }, + { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940, upload-time = "2025-09-25T19:50:00.869Z" }, + { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776, upload-time = "2025-09-25T19:50:02.393Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922, upload-time = "2025-09-25T19:50:04.232Z" }, + { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367, upload-time = "2025-09-25T19:50:05.559Z" }, + { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187, upload-time = "2025-09-25T19:50:06.916Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752, upload-time = "2025-09-25T19:50:08.515Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881, upload-time = "2025-09-25T19:50:09.742Z" }, + { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931, upload-time = "2025-09-25T19:50:11.016Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313, upload-time = "2025-09-25T19:50:12.309Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290, upload-time = "2025-09-25T19:50:13.673Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253, upload-time = "2025-09-25T19:50:15.089Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084, upload-time = "2025-09-25T19:50:16.699Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185, upload-time = "2025-09-25T19:50:18.525Z" }, + { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656, upload-time = "2025-09-25T19:50:19.809Z" }, + { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662, upload-time = "2025-09-25T19:50:21.567Z" }, + { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240, upload-time = "2025-09-25T19:50:23.305Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152, upload-time = "2025-09-25T19:50:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284, upload-time = "2025-09-25T19:50:26.268Z" }, + { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643, upload-time = "2025-09-25T19:50:28.02Z" }, + { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698, upload-time = "2025-09-25T19:50:31.347Z" }, + { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725, upload-time = "2025-09-25T19:50:34.384Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912, upload-time = "2025-09-25T19:50:35.69Z" }, + { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953, upload-time = "2025-09-25T19:50:37.32Z" }, + { url = "https://files.pythonhosted.org/packages/8a/75/4aa9f5a4d40d762892066ba1046000b329c7cd58e888a6db878019b282dc/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7edda91d5ab52b15636d9c30da87d2cc84f426c72b9dba7a9b4fe142ba11f534", size = 271180, upload-time = "2025-09-25T19:50:38.575Z" }, + { url = "https://files.pythonhosted.org/packages/54/79/875f9558179573d40a9cc743038ac2bf67dfb79cecb1e8b5d70e88c94c3d/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:046ad6db88edb3c5ece4369af997938fb1c19d6a699b9c1b27b0db432faae4c4", size = 273791, upload-time = "2025-09-25T19:50:39.913Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fe/975adb8c216174bf70fc17535f75e85ac06ed5252ea077be10d9cff5ce24/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:dcd58e2b3a908b5ecc9b9df2f0085592506ac2d5110786018ee5e160f28e0911", size = 270746, upload-time = "2025-09-25T19:50:43.306Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f8/972c96f5a2b6c4b3deca57009d93e946bbdbe2241dca9806d502f29dd3ee/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:6b8f520b61e8781efee73cba14e3e8c9556ccfb375623f4f97429544734545b4", size = 273375, upload-time = "2025-09-25T19:50:45.43Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "boto3" +version = "1.40.76" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/04/8cf6cf7e6390c71b9c958f3bfedc45d1182b51a35f7789354bf7b2ff4e8c/boto3-1.40.76.tar.gz", hash = "sha256:16f4cf97f8dd8e0aae015f4dc66219bd7716a91a40d1e2daa0dafa241a4761c5", size = 111598, upload-time = "2025-11-18T20:23:10.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/8e/966263696eb441e8d1c4daa5fdfb3b4be10a96a23c418cc74c80b0b03d4e/boto3-1.40.76-py3-none-any.whl", hash = "sha256:8df6df755727be40ad9e309cfda07f9a12c147e17b639430c55d4e4feee8a167", size = 139359, upload-time = "2025-11-18T20:23:08.75Z" }, +] + +[[package]] +name = "botocore" +version = "1.40.76" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/eb/50e2d280589a3c20c3b649bb66262d2b53a25c03262e4cc492048ac7540a/botocore-1.40.76.tar.gz", hash = "sha256:2b16024d68b29b973005adfb5039adfe9099ebe772d40a90ca89f2e165c495dc", size = 14494001, upload-time = "2025-11-18T20:22:59.131Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/6c/522e05388aa6fc66cf8ea46c6b29809a1a6f527ea864998b01ffb368ca36/botocore-1.40.76-py3-none-any.whl", hash = "sha256:fe425d386e48ac64c81cbb4a7181688d813df2e2b4c78b95ebe833c9e868c6f4", size = 14161738, upload-time = "2025-11-18T20:22:55.332Z" }, +] + +[[package]] +name = "build" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "os_name == 'nt'" }, + { name = "packaging" }, + { name = "pyproject-hooks" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/1d/ab15c8ac57f4ee8778d7633bc6685f808ab414437b8644f555389cdc875e/build-1.4.2.tar.gz", hash = "sha256:35b14e1ee329c186d3f08466003521ed7685ec15ecffc07e68d706090bf161d1", size = 83433, upload-time = "2026-03-25T14:20:27.659Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl", hash = "sha256:7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88", size = 24643, upload-time = "2026-03-25T14:20:26.568Z" }, +] + +[[package]] +name = "cachetools" +version = "7.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/dd/57fe3fdb6e65b25a5987fd2cdc7e22db0aef508b91634d2e57d22928d41b/cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990", size = 37367, upload-time = "2026-03-09T20:51:29.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/f3/39cf3367b8107baa44f861dc802cbf16263c945b62d8265d36034fc07bea/cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114", size = 13918, upload-time = "2026-03-09T20:51:27.33Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "chromadb" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bcrypt" }, + { name = "build" }, + { name = "grpcio" }, + { name = "httpx" }, + { name = "importlib-resources" }, + { name = "jsonschema" }, + { name = "kubernetes" }, + { name = "mmh3" }, + { name = "numpy" }, + { name = "onnxruntime" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "orjson" }, + { name = "overrides" }, + { name = "posthog" }, + { name = "pybase64" }, + { name = "pydantic" }, + { name = "pypika" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/48/11851dddeadad6abe36ee071fedc99b5bdd2c324df3afa8cb952ae02798b/chromadb-1.1.1.tar.gz", hash = "sha256:ebfce0122753e306a76f1e291d4ddaebe5f01b5979b97ae0bc80b1d4024ff223", size = 1338109, upload-time = "2025-10-05T02:49:14.834Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/59/0d881a9b7eb63d8d2446cf67fcbb53fb8ae34991759d2b6024a067e90a9a/chromadb-1.1.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:27fe0e25ef0f83fb09c30355ab084fe6f246808a7ea29e8c19e85cf45785b90d", size = 19175479, upload-time = "2025-10-05T02:49:12.525Z" }, + { url = "https://files.pythonhosted.org/packages/94/4f/5a9fa317c84c98e70af48f74b00aa25589626c03a0428b4381b2095f3d73/chromadb-1.1.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:95aed58869683f12e7dcbf68b039fe5f576dbe9d1b86b8f4d014c9d077ccafd2", size = 18267188, upload-time = "2025-10-05T02:49:09.236Z" }, + { url = "https://files.pythonhosted.org/packages/45/1a/02defe2f1c8d1daedb084bbe85f5b6083510a3ba192ed57797a3649a4310/chromadb-1.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06776dad41389a00e7d63d936c3a15c179d502becaf99f75745ee11b062c9b6a", size = 18855754, upload-time = "2025-10-05T02:49:03.299Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0d/80be82717e5dc19839af24558494811b6f2af2b261a8f21c51b872193b09/chromadb-1.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bba0096a7f5e975875ead23a91c0d41d977fbd3767f60d3305a011b0ace7afd3", size = 19893681, upload-time = "2025-10-05T02:49:06.481Z" }, + { url = "https://files.pythonhosted.org/packages/2d/6e/956e62975305a4e31daf6114a73b3b0683a8f36f8d70b20aabd466770edb/chromadb-1.1.1-cp39-abi3-win_amd64.whl", hash = "sha256:a77aa026a73a18181fd89bbbdb86191c9a82fd42aa0b549ff18d8cae56394c8b", size = 19844042, upload-time = "2025-10-05T02:49:16.925Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, +] + +[[package]] +name = "crewai" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiosqlite" }, + { name = "appdirs" }, + { name = "chromadb" }, + { name = "click" }, + { name = "httpx" }, + { name = "instructor" }, + { name = "json-repair" }, + { name = "json5" }, + { name = "jsonref" }, + { name = "lancedb" }, + { name = "mcp" }, + { name = "openai" }, + { name = "openpyxl" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-sdk" }, + { name = "pdfplumber" }, + { name = "portalocker" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "textual" }, + { name = "tokenizers" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "uv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/4a/ea4470501934a9cfc228bcbb572c48cffd5d573634e8d6e97577ff9a1e25/crewai-1.13.0.tar.gz", hash = "sha256:a2d105d00f65a9a306d1aa57c167f77b543b66983c1729753bf9a1c900a0bef9", size = 7773234, upload-time = "2026-04-02T23:17:04.775Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/db/8b6a5026bb038e538c59b643ecd940e731ed3e4c3769fdc54d803ce6b5a1/crewai-1.13.0-py3-none-any.whl", hash = "sha256:6de1241960c18f5ae984005763b0311a1e11224d59830e42020d12ebf0665b5f", size = 1021512, upload-time = "2026-04-02T23:17:02.666Z" }, +] + +[package.optional-dependencies] +bedrock = [ + { name = "boto3" }, +] + +[[package]] +name = "cryptography" +version = "46.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, + { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, + { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, + { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, + { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, + { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, + { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, + { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, + { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, + { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, + { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, + { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, + { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, + { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, + { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, + { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, + { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, + { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, + { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, +] + +[[package]] +name = "debugpy" +version = "1.8.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/56/c3baf5cbe4dd77427fd9aef99fcdade259ad128feeb8a786c246adb838e5/debugpy-1.8.20-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:eada6042ad88fa1571b74bd5402ee8b86eded7a8f7b827849761700aff171f1b", size = 2208318, upload-time = "2026-01-29T23:03:36.481Z" }, + { url = "https://files.pythonhosted.org/packages/9a/7d/4fa79a57a8e69fe0d9763e98d1110320f9ecd7f1f362572e3aafd7417c9d/debugpy-1.8.20-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:7de0b7dfeedc504421032afba845ae2a7bcc32ddfb07dae2c3ca5442f821c344", size = 3171493, upload-time = "2026-01-29T23:03:37.775Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f2/1e8f8affe51e12a26f3a8a8a4277d6e60aa89d0a66512f63b1e799d424a4/debugpy-1.8.20-cp311-cp311-win32.whl", hash = "sha256:773e839380cf459caf73cc533ea45ec2737a5cc184cf1b3b796cd4fd98504fec", size = 5209240, upload-time = "2026-01-29T23:03:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl", hash = "sha256:1f7650546e0eded1902d0f6af28f787fa1f1dbdbc97ddabaf1cd963a405930cb", size = 5233481, upload-time = "2026-01-29T23:03:40.659Z" }, + { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686, upload-time = "2026-01-29T23:03:42.023Z" }, + { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588, upload-time = "2026-01-29T23:03:43.314Z" }, + { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372, upload-time = "2026-01-29T23:03:45.526Z" }, + { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835, upload-time = "2026-01-29T23:03:47.245Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560, upload-time = "2026-01-29T23:03:48.738Z" }, + { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272, upload-time = "2026-01-29T23:03:50.169Z" }, + { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208, upload-time = "2026-01-29T23:03:51.547Z" }, + { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930, upload-time = "2026-01-29T23:03:53.585Z" }, + { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066, upload-time = "2026-01-29T23:03:54.999Z" }, + { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425, upload-time = "2026-01-29T23:03:56.518Z" }, + { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407, upload-time = "2026-01-29T23:03:58.481Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521, upload-time = "2026-01-29T23:03:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "deprecation" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + +[[package]] +name = "durationpy" +version = "0.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "filelock" +version = "3.25.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, +] + +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, +] + +[[package]] +name = "flow" +source = { editable = "." } +dependencies = [ + { name = "crewai", extra = ["bedrock"] }, + { name = "ipykernel" }, + { name = "pandas" }, + { name = "streamlit" }, +] + +[package.metadata] +requires-dist = [ + { name = "crewai", extras = ["bedrock"], specifier = ">=1.13.0" }, + { name = "ipykernel", specifier = ">=7.2.0" }, + { name = "pandas", specifier = ">=3.0.2" }, + { name = "streamlit", specifier = ">=1.56.0" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl", hash = "sha256:d2ceafaad1b3457968ed14efa28798162f1638dbb5d2a6868a2db002a5ee39a4", size = 202595, upload-time = "2026-03-27T19:11:13.595Z" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.46" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.74.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/b0/be5d3329badb9230b765de6eea66b73abd5944bdeb5afb3562ddcd80ae84/googleapis_common_protos-1.74.0-py3-none-any.whl", hash = "sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5", size = 300743, upload-time = "2026-04-02T21:22:49.108Z" }, +] + +[[package]] +name = "grpcio" +version = "1.80.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905, upload-time = "2026-03-30T08:49:10.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/db/1d56e5f5823257b291962d6c0ce106146c6447f405b60b234c4f222a7cde/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dfab85db094068ff42e2a3563f60ab3dddcc9d6488a35abf0132daec13209c8a", size = 6055009, upload-time = "2026-03-30T08:46:46.265Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/c83f3cad64c5ca63bca7e91e5e46b0d026afc5af9d0a9972472ceba294b3/grpcio-1.80.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5c07e82e822e1161354e32da2662f741a4944ea955f9f580ec8fb409dd6f6060", size = 12035295, upload-time = "2026-03-30T08:46:49.099Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8e/e14966b435be2dda99fbe89db9525ea436edc79780431a1c2875a3582644/grpcio-1.80.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba0915d51fd4ced2db5ff719f84e270afe0e2d4c45a7bdb1e8d036e4502928c2", size = 6610297, upload-time = "2026-03-30T08:46:52.123Z" }, + { url = "https://files.pythonhosted.org/packages/cc/26/d5eb38f42ce0e3fdc8174ea4d52036ef8d58cc4426cb800f2610f625dd75/grpcio-1.80.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cb8130ba457d2aa09fa6b7c3ed6b6e4e6a2685fce63cb803d479576c4d80e21", size = 7300208, upload-time = "2026-03-30T08:46:54.859Z" }, + { url = "https://files.pythonhosted.org/packages/25/51/bd267c989f85a17a5b3eea65a6feb4ff672af41ca614e5a0279cc0ea381c/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:09e5e478b3d14afd23f12e49e8b44c8684ac3c5f08561c43a5b9691c54d136ab", size = 6813442, upload-time = "2026-03-30T08:46:57.056Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d9/d80eef735b19e9169e30164bbf889b46f9df9127598a83d174eb13a48b26/grpcio-1.80.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00168469238b022500e486c1c33916acf2f2a9b2c022202cf8a1885d2e3073c1", size = 7414743, upload-time = "2026-03-30T08:46:59.682Z" }, + { url = "https://files.pythonhosted.org/packages/de/f2/567f5bd5054398ed6b0509b9a30900376dcf2786bd936812098808b49d8d/grpcio-1.80.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8502122a3cc1714038e39a0b071acb1207ca7844208d5ea0d091317555ee7106", size = 8426046, upload-time = "2026-03-30T08:47:02.474Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/73ef0141b4732ff5eacd68430ff2512a65c004696997f70476a83e548e7e/grpcio-1.80.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce1794f4ea6cc3ca29463f42d665c32ba1b964b48958a66497917fe9069f26e6", size = 7851641, upload-time = "2026-03-30T08:47:05.462Z" }, + { url = "https://files.pythonhosted.org/packages/46/69/abbfa360eb229a8623bab5f5a4f8105e445bd38ce81a89514ba55d281ad0/grpcio-1.80.0-cp311-cp311-win32.whl", hash = "sha256:51b4a7189b0bef2aa30adce3c78f09c83526cf3dddb24c6a96555e3b97340440", size = 4154368, upload-time = "2026-03-30T08:47:08.027Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d4/ae92206d01183b08613e846076115f5ac5991bae358d2a749fa864da5699/grpcio-1.80.0-cp311-cp311-win_amd64.whl", hash = "sha256:02e64bb0bb2da14d947a49e6f120a75e947250aebe65f9629b62bb1f5c14e6e9", size = 4894235, upload-time = "2026-03-30T08:47:10.839Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e8/a2b749265eb3415abc94f2e619bbd9e9707bebdda787e61c593004ec927a/grpcio-1.80.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:c624cc9f1008361014378c9d776de7182b11fe8b2e5a81bc69f23a295f2a1ad0", size = 6015616, upload-time = "2026-03-30T08:47:13.428Z" }, + { url = "https://files.pythonhosted.org/packages/3e/97/b1282161a15d699d1e90c360df18d19165a045ce1c343c7f313f5e8a0b77/grpcio-1.80.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f49eddcac43c3bf350c0385366a58f36bed8cc2c0ec35ef7b74b49e56552c0c2", size = 12014204, upload-time = "2026-03-30T08:47:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/d319c6e997b50c155ac5a8cb12f5173d5b42677510e886d250d50264949d/grpcio-1.80.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d334591df610ab94714048e0d5b4f3dd5ad1bee74dfec11eee344220077a79de", size = 6563866, upload-time = "2026-03-30T08:47:18.588Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f6/fdd975a2cb4d78eb67769a7b3b3830970bfa2e919f1decf724ae4445f42c/grpcio-1.80.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0cb517eb1d0d0aaf1d87af7cc5b801d686557c1d88b2619f5e31fab3c2315921", size = 7273060, upload-time = "2026-03-30T08:47:21.113Z" }, + { url = "https://files.pythonhosted.org/packages/db/f0/a3deb5feba60d9538a962913e37bd2e69a195f1c3376a3dd44fe0427e996/grpcio-1.80.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e78c4ac0d97dc2e569b2f4bcbbb447491167cb358d1a389fc4af71ab6f70411", size = 6782121, upload-time = "2026-03-30T08:47:23.827Z" }, + { url = "https://files.pythonhosted.org/packages/ca/84/36c6dcfddc093e108141f757c407902a05085e0c328007cb090d56646cdf/grpcio-1.80.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2ed770b4c06984f3b47eb0517b1c69ad0b84ef3f40128f51448433be904634cd", size = 7383811, upload-time = "2026-03-30T08:47:26.517Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ef/f3a77e3dc5b471a0ec86c564c98d6adfa3510d38f8ee99010410858d591e/grpcio-1.80.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:256507e2f524092f1473071a05e65a5b10d84b82e3ff24c5b571513cfaa61e2f", size = 8393860, upload-time = "2026-03-30T08:47:29.439Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8d/9d4d27ed7f33d109c50d6b5ce578a9914aa68edab75d65869a17e630a8d1/grpcio-1.80.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a6284a5d907c37db53350645567c522be314bac859a64a7a5ca63b77bb7958f", size = 7830132, upload-time = "2026-03-30T08:47:33.254Z" }, + { url = "https://files.pythonhosted.org/packages/14/e4/9990b41c6d7a44e1e9dee8ac11d7a9802ba1378b40d77468a7761d1ad288/grpcio-1.80.0-cp312-cp312-win32.whl", hash = "sha256:c71309cfce2f22be26aa4a847357c502db6c621f1a49825ae98aa0907595b193", size = 4140904, upload-time = "2026-03-30T08:47:35.319Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2c/296f6138caca1f4b92a31ace4ae1b87dab692fc16a7a3417af3bb3c805bf/grpcio-1.80.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe648599c0e37594c4809d81a9e77bd138cc82eb8baa71b6a86af65426723ff", size = 4880944, upload-time = "2026-03-30T08:47:37.831Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243, upload-time = "2026-03-30T08:47:40.075Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840, upload-time = "2026-03-30T08:47:43.11Z" }, + { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644, upload-time = "2026-03-30T08:47:46.806Z" }, + { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830, upload-time = "2026-03-30T08:47:49.643Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216, upload-time = "2026-03-30T08:47:52.817Z" }, + { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866, upload-time = "2026-03-30T08:47:55.687Z" }, + { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602, upload-time = "2026-03-30T08:47:58.303Z" }, + { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752, upload-time = "2026-03-30T08:48:01.311Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310, upload-time = "2026-03-30T08:48:04.594Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" }, + { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376, upload-time = "2026-03-30T08:48:10.005Z" }, + { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133, upload-time = "2026-03-30T08:48:12.927Z" }, + { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748, upload-time = "2026-03-30T08:48:16.386Z" }, + { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711, upload-time = "2026-03-30T08:48:19.627Z" }, + { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372, upload-time = "2026-03-30T08:48:22.373Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268, upload-time = "2026-03-30T08:48:25.638Z" }, + { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000, upload-time = "2026-03-30T08:48:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477, upload-time = "2026-03-30T08:48:32.054Z" }, + { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563, upload-time = "2026-03-30T08:48:34.538Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457, upload-time = "2026-03-30T08:48:37.308Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/92/ec9ad04d0b5728dca387a45af7bc98fbb0d73b2118759f5f6038b61a57e8/hf_xet-1.4.3.tar.gz", hash = "sha256:8ddedb73c8c08928c793df2f3401ec26f95be7f7e516a7bee2fbb546f6676113", size = 670477, upload-time = "2026-03-31T22:40:07.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/43/724d307b34e353da0abd476e02f72f735cdd2bc86082dee1b32ea0bfee1d/hf_xet-1.4.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7551659ba4f1e1074e9623996f28c3873682530aee0a846b7f2f066239228144", size = 3800935, upload-time = "2026-03-31T22:39:49.618Z" }, + { url = "https://files.pythonhosted.org/packages/2b/d2/8bee5996b699262edb87dbb54118d287c0e1b2fc78af7cdc41857ba5e3c4/hf_xet-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bee693ada985e7045997f05f081d0e12c4c08bd7626dc397f8a7c487e6c04f7f", size = 3558942, upload-time = "2026-03-31T22:39:47.938Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a1/e993d09cbe251196fb60812b09a58901c468127b7259d2bf0f68bf6088eb/hf_xet-1.4.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21644b404bb0100fe3857892f752c4d09642586fd988e61501c95bbf44b393a3", size = 4207657, upload-time = "2026-03-31T22:39:39.69Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/9eb6d21e5c34c63e5e399803a6932fa983cabdf47c0ecbcfe7ea97684b8c/hf_xet-1.4.3-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:987f09cfe418237812896a6736b81b1af02a3a6dcb4b4944425c4c4fca7a7cf8", size = 3986765, upload-time = "2026-03-31T22:39:37.936Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7b/8ad6f16fdb82f5f7284a34b5ec48645bd575bdcd2f6f0d1644775909c486/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:60cf7fc43a99da0a853345cf86d23738c03983ee5249613a6305d3e57a5dca74", size = 4188162, upload-time = "2026-03-31T22:39:58.382Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c4/39d6e136cbeea9ca5a23aad4b33024319222adbdc059ebcda5fc7d9d5ff4/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2815a49a7a59f3e2edf0cf113ae88e8cb2ca2a221bf353fb60c609584f4884d4", size = 4424525, upload-time = "2026-03-31T22:40:00.225Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/adc32dae6bdbc367853118b9878139ac869419a4ae7ba07185dc31251b76/hf_xet-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:42ee323265f1e6a81b0e11094564fb7f7e0ec75b5105ffd91ae63f403a11931b", size = 3671610, upload-time = "2026-03-31T22:40:10.42Z" }, + { url = "https://files.pythonhosted.org/packages/e2/19/25d897dcc3f81953e0c2cde9ec186c7a0fee413eb0c9a7a9130d87d94d3a/hf_xet-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:27c976ba60079fb8217f485b9c5c7fcd21c90b0367753805f87cb9f3cdc4418a", size = 3528529, upload-time = "2026-03-31T22:40:09.106Z" }, + { url = "https://files.pythonhosted.org/packages/ec/36/3e8f85ca9fe09b8de2b2e10c63b3b3353d7dda88a0b3d426dffbe7b8313b/hf_xet-1.4.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5251d5ece3a81815bae9abab41cf7ddb7bcb8f56411bce0827f4a3071c92fdc6", size = 3801019, upload-time = "2026-03-31T22:39:56.651Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9c/defb6cb1de28bccb7bd8d95f6e60f72a3d3fa4cb3d0329c26fb9a488bfe7/hf_xet-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1feb0f3abeacee143367c326a128a2e2b60868ec12a36c225afb1d6c5a05e6d2", size = 3558746, upload-time = "2026-03-31T22:39:54.766Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/8d001191893178ff8e826e46ad5299446e62b93cd164e17b0ffea08832ec/hf_xet-1.4.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b301fc150290ca90b4fccd079829b84bb4786747584ae08b94b4577d82fb791", size = 4207692, upload-time = "2026-03-31T22:39:46.246Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/6790b402803250e9936435613d3a78b9aaeee7973439f0918848dde58309/hf_xet-1.4.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d972fbe95ddc0d3c0fc49b31a8a69f47db35c1e3699bf316421705741aab6653", size = 3986281, upload-time = "2026-03-31T22:39:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/51/56/ea62552fe53db652a9099eda600b032d75554d0e86c12a73824bfedef88b/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c5b48db1ee344a805a1b9bd2cda9b6b65fe77ed3787bd6e87ad5521141d317cd", size = 4187414, upload-time = "2026-03-31T22:40:04.951Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f5/bc1456d4638061bea997e6d2db60a1a613d7b200e0755965ec312dc1ef79/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:22bdc1f5fb8b15bf2831440b91d1c9bbceeb7e10c81a12e8d75889996a5c9da8", size = 4424368, upload-time = "2026-03-31T22:40:06.347Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/ab597bae87e1f06d18d3ecb8ed7f0d3c9a37037fc32ce76233d369273c64/hf_xet-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0392c79b7cf48418cd61478c1a925246cf10639f4cd9d94368d8ca1e8df9ea07", size = 3672280, upload-time = "2026-03-31T22:40:16.401Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/2e462d34e23a09a74d73785dbed71cc5dbad82a72eee2ad60a72a554155d/hf_xet-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:681c92a07796325778a79d76c67011764ecc9042a8c3579332b61b63ae512075", size = 3528945, upload-time = "2026-03-31T22:40:14.995Z" }, + { url = "https://files.pythonhosted.org/packages/ac/9f/9c23e4a447b8f83120798f9279d0297a4d1360bdbf59ef49ebec78fe2545/hf_xet-1.4.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d0da85329eaf196e03e90b84c2d0aca53bd4573d097a75f99609e80775f98025", size = 3805048, upload-time = "2026-03-31T22:39:53.105Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f8/7aacb8e5f4a7899d39c787b5984e912e6c18b11be136ef13947d7a66d265/hf_xet-1.4.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e23717ce4186b265f69afa66e6f0069fe7efbf331546f5c313d00e123dc84583", size = 3562178, upload-time = "2026-03-31T22:39:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/df/9a/a24b26dc8a65f0ecc0fe5be981a19e61e7ca963b85e062c083f3a9100529/hf_xet-1.4.3-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc360b70c815bf340ed56c7b8c63aacf11762a4b099b2fe2c9bd6d6068668c08", size = 4212320, upload-time = "2026-03-31T22:39:42.922Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/46d493db155d2ee2801b71fb1b0fd67696359047fdd8caee2c914cc50c79/hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39f2d2e9654cd9b4319885733993807aab6de9dfbd34c42f0b78338d6617421f", size = 3991546, upload-time = "2026-03-31T22:39:41.335Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f5/067363e1c96c6b17256910830d1b54099d06287e10f4ec6ec4e7e08371fc/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:49ad8a8cead2b56051aa84d7fce3e1335efe68df3cf6c058f22a65513885baac", size = 4193200, upload-time = "2026-03-31T22:40:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/42/4b/53951592882d9c23080c7644542fda34a3813104e9e11fa1a7d82d419cb8/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7716d62015477a70ea272d2d68cd7cad140f61c52ee452e133e139abfe2c17ba", size = 4429392, upload-time = "2026-03-31T22:40:03.492Z" }, + { url = "https://files.pythonhosted.org/packages/8a/21/75a6c175b4e79662ad8e62f46a40ce341d8d6b206b06b4320d07d55b188c/hf_xet-1.4.3-cp37-abi3-win_amd64.whl", hash = "sha256:6b591fcad34e272a5b02607485e4f2a1334aebf1bc6d16ce8eb1eb8978ac2021", size = 3677359, upload-time = "2026-03-31T22:40:13.619Z" }, + { url = "https://files.pythonhosted.org/packages/8a/7c/44314ecd0e89f8b2b51c9d9e5e7a60a9c1c82024ac471d415860557d3cd8/hf_xet-1.4.3-cp37-abi3-win_arm64.whl", hash = "sha256:7c2c7e20bcfcc946dc67187c203463f5e932e395845d098cc2a93f5b67ca0b47", size = 3533664, upload-time = "2026-03-31T22:40:12.152Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" }, + { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" }, + { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" }, + { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" }, + { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" }, + { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" }, + { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" }, + { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" }, + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" }, + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" }, + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" }, + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" }, + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" }, + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/bb/62c7aa86f63a05e2f9b96642fdef9b94526a23979820b09f5455deff4983/huggingface_hub-1.9.0.tar.gz", hash = "sha256:0ea5be7a56135c91797cae6ad726e38eaeb6eb4b77cefff5c9d38ba0ecf874f7", size = 750326, upload-time = "2026-04-03T08:35:55.888Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/37/0d15d16150e1829f3e90962c99f28257f6de9e526a680b4c6f5acdb54fd2/huggingface_hub-1.9.0-py3-none-any.whl", hash = "sha256:2999328c058d39fd19ab748dd09bd4da2fbaa4f4c1ddea823eab103051e14a1f", size = 637355, upload-time = "2026-04-03T08:35:53.897Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + +[[package]] +name = "instructor" +version = "1.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "docstring-parser" }, + { name = "jinja2" }, + { name = "jiter" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "pydantic-core" }, + { name = "requests" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/a4/832cfb15420360e26d2d85bd9d5fe1e4b839d52587574d389bc31284bf6f/instructor-1.15.1.tar.gz", hash = "sha256:c72406469d9025b742e83cf0c13e914b317db2089d08d889944e74fcd659ef94", size = 69948370, upload-time = "2026-04-03T01:51:30.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/c8/36c5d9b80aaf40ba9a7084a8fc18c967db6bf248a4cc8d0f0816b14284be/instructor-1.15.1-py3-none-any.whl", hash = "sha256:be81d17ba2b154a04ab4720808f24f9d6b598f80992f82eaf9cc79006099cf6c", size = 178156, upload-time = "2026-04-03T01:51:23.098Z" }, +] + +[[package]] +name = "ipykernel" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046, upload-time = "2026-02-06T16:43:27.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788, upload-time = "2026-02-06T16:43:25.149Z" }, +] + +[[package]] +name = "ipython" +version = "9.10.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.12' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.12'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version < '3.12'" }, + { name = "jedi", marker = "python_full_version < '3.12'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.12'" }, + { name = "pexpect", marker = "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.12'" }, + { name = "pygments", marker = "python_full_version < '3.12'" }, + { name = "stack-data", marker = "python_full_version < '3.12'" }, + { name = "traitlets", marker = "python_full_version < '3.12'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/25/daae0e764047b0a2480c7bbb25d48f4f509b5818636562eeac145d06dfee/ipython-9.10.1.tar.gz", hash = "sha256:e170e9b2a44312484415bdb750492699bf329233b03f2557a9692cce6466ada4", size = 4426663, upload-time = "2026-03-27T09:53:26.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/09/ba70f8d662d5671687da55ad2cc0064cf795b15e1eea70907532202e7c97/ipython-9.10.1-py3-none-any.whl", hash = "sha256:82d18ae9fb9164ded080c71ef92a182ee35ee7db2395f67616034bebb020a232", size = 622827, upload-time = "2026-03-27T09:53:24.566Z" }, +] + +[[package]] +name = "ipython" +version = "9.12.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'emscripten'", + "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.12'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.12'" }, + { name = "jedi", marker = "python_full_version >= '3.12'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.12'" }, + { name = "pexpect", marker = "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.12'" }, + { name = "pygments", marker = "python_full_version >= '3.12'" }, + { name = "stack-data", marker = "python_full_version >= '3.12'" }, + { name = "traitlets", marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jiter" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/29/499f8c9eaa8a16751b1c0e45e6f5f1761d180da873d417996cc7bddc8eef/jiter-0.13.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ea026e70a9a28ebbdddcbcf0f1323128a8db66898a06eaad3a4e62d2f554d096", size = 311157, upload-time = "2026-02-02T12:35:37.758Z" }, + { url = "https://files.pythonhosted.org/packages/50/f6/566364c777d2ab450b92100bea11333c64c38d32caf8dc378b48e5b20c46/jiter-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66aa3e663840152d18cc8ff1e4faad3dd181373491b9cfdc6004b92198d67911", size = 319729, upload-time = "2026-02-02T12:35:39.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/560f13ec5e4f116d8ad2658781646cca91b617ae3b8758d4a5076b278f70/jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3524798e70655ff19aec58c7d05adb1f074fecff62da857ea9be2b908b6d701", size = 354766, upload-time = "2026-02-02T12:35:40.662Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0d/061faffcfe94608cbc28a0d42a77a74222bdf5055ccdbe5fd2292b94f510/jiter-0.13.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec7e287d7fbd02cb6e22f9a00dd9c9cd504c40a61f2c61e7e1f9690a82726b4c", size = 362587, upload-time = "2026-02-02T12:35:42.025Z" }, + { url = "https://files.pythonhosted.org/packages/92/c9/c66a7864982fd38a9773ec6e932e0398d1262677b8c60faecd02ffb67bf3/jiter-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47455245307e4debf2ce6c6e65a717550a0244231240dcf3b8f7d64e4c2f22f4", size = 487537, upload-time = "2026-02-02T12:35:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/6c/86/84eb4352cd3668f16d1a88929b5888a3fe0418ea8c1dfc2ad4e7bf6e069a/jiter-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee9da221dca6e0429c2704c1b3655fe7b025204a71d4d9b73390c759d776d165", size = 373717, upload-time = "2026-02-02T12:35:44.928Z" }, + { url = "https://files.pythonhosted.org/packages/6e/09/9fe4c159358176f82d4390407a03f506a8659ed13ca3ac93a843402acecf/jiter-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ab43126d5e05f3d53a36a8e11eb2f23304c6c1117844aaaf9a0aa5e40b5018", size = 362683, upload-time = "2026-02-02T12:35:46.636Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5e/85f3ab9caca0c1d0897937d378b4a515cae9e119730563572361ea0c48ae/jiter-0.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9da38b4fedde4fb528c740c2564628fbab737166a0e73d6d46cb4bb5463ff411", size = 392345, upload-time = "2026-02-02T12:35:48.088Z" }, + { url = "https://files.pythonhosted.org/packages/12/4c/05b8629ad546191939e6f0c2f17e29f542a398f4a52fb987bc70b6d1eb8b/jiter-0.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b34c519e17658ed88d5047999a93547f8889f3c1824120c26ad6be5f27b6cf5", size = 517775, upload-time = "2026-02-02T12:35:49.482Z" }, + { url = "https://files.pythonhosted.org/packages/4d/88/367ea2eb6bc582c7052e4baf5ddf57ebe5ab924a88e0e09830dfb585c02d/jiter-0.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2a6394e6af690d462310a86b53c47ad75ac8c21dc79f120714ea449979cb1d3", size = 551325, upload-time = "2026-02-02T12:35:51.104Z" }, + { url = "https://files.pythonhosted.org/packages/f3/12/fa377ffb94a2f28c41afaed093e0d70cfe512035d5ecb0cad0ae4792d35e/jiter-0.13.0-cp311-cp311-win32.whl", hash = "sha256:0f0c065695f616a27c920a56ad0d4fc46415ef8b806bf8fc1cacf25002bd24e1", size = 204709, upload-time = "2026-02-02T12:35:52.467Z" }, + { url = "https://files.pythonhosted.org/packages/cb/16/8e8203ce92f844dfcd3d9d6a5a7322c77077248dbb12da52d23193a839cd/jiter-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0733312953b909688ae3c2d58d043aa040f9f1a6a75693defed7bc2cc4bf2654", size = 204560, upload-time = "2026-02-02T12:35:53.925Z" }, + { url = "https://files.pythonhosted.org/packages/44/26/97cc40663deb17b9e13c3a5cf29251788c271b18ee4d262c8f94798b8336/jiter-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:5d9b34ad56761b3bf0fbe8f7e55468704107608512350962d3317ffd7a4382d5", size = 189608, upload-time = "2026-02-02T12:35:55.304Z" }, + { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, + { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, + { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, + { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, + { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, + { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, + { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, + { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, + { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, + { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, + { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, + { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, + { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, + { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, + { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, + { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, + { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, + { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, + { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, + { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, + { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, + { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, + { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, + { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, + { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, + { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, + { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, + { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, + { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, + { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, + { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, + { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/3c29819a27178d0e461a8571fb63c6ae38be6dc36b78b3ec2876bbd6a910/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c", size = 307016, upload-time = "2026-02-02T12:37:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/60993e4b07b1ac5ebe46da7aa99fdbb802eb986c38d26e3883ac0125c4e0/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2", size = 305024, upload-time = "2026-02-02T12:37:44.774Z" }, + { url = "https://files.pythonhosted.org/packages/77/fa/2227e590e9cf98803db2811f172b2d6460a21539ab73006f251c66f44b14/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434", size = 339337, upload-time = "2026-02-02T12:37:46.668Z" }, + { url = "https://files.pythonhosted.org/packages/2d/92/015173281f7eb96c0ef580c997da8ef50870d4f7f4c9e03c845a1d62ae04/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d", size = 346395, upload-time = "2026-02-02T12:37:48.09Z" }, + { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, + { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "json-repair" +version = "0.25.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/60/484ee009c1867ddc5ffe0ff2131b82e80bbf13fdb59f3d93834f98e56a9f/json_repair-0.25.3.tar.gz", hash = "sha256:4ee970581a05b0b258b749eb8bcac21de380edda97c3717a4edfafc519ec21a4", size = 20619, upload-time = "2024-07-10T13:42:18.977Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/9e/2ab68cc0ff030e1ef78329d7b933473d3ad2c7d0e66aede6a7c87f74753c/json_repair-0.25.3-py3-none-any.whl", hash = "sha256:f00b510dd21b31ebe72581bdb07e66381df2883d6f640c89605e482882c12b17", size = 12812, upload-time = "2024-07-10T13:42:16.918Z" }, +] + +[[package]] +name = "json5" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/3d/bbe62f3d0c05a689c711cff57b2e3ac3d3e526380adb7c781989f075115c/json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559", size = 48202, upload-time = "2024-11-26T19:56:37.823Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/42/797895b952b682c3dafe23b1834507ee7f02f4d6299b65aaa61425763278/json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa", size = 34049, upload-time = "2024-11-26T19:56:36.649Z" }, +] + +[[package]] +name = "jsonref" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "jupyter-client" +version = "8.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, +] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, +] + +[[package]] +name = "kubernetes" +version = "35.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "durationpy" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "requests-oauthlib" }, + { name = "six" }, + { name = "urllib3" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/8f/85bf51ad4150f64e8c665daf0d9dfe9787ae92005efb9a4d1cba592bd79d/kubernetes-35.0.0.tar.gz", hash = "sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee", size = 1094642, upload-time = "2026-01-16T01:05:27.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/70/05b685ea2dffcb2adbf3cdcea5d8865b7bc66f67249084cf845012a0ff13/kubernetes-35.0.0-py2.py3-none-any.whl", hash = "sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d", size = 2017602, upload-time = "2026-01-16T01:05:25.991Z" }, +] + +[[package]] +name = "lance-namespace" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lance-namespace-urllib3-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/9f/7906ba4117df8d965510285eaf07264a77de2fd283b9d44ec7fc63a4a57a/lance_namespace-0.6.1.tar.gz", hash = "sha256:f0deea442bd3f1056a8e2fed056ae2778e3356517ec2e680db049058b824d131", size = 10666, upload-time = "2026-03-17T17:55:44.977Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/91/aee1c0a04d17f2810173bd304bd444eb78332045df1b0c1b07cebd01f530/lance_namespace-0.6.1-py3-none-any.whl", hash = "sha256:9699c9e3f12236e5e08ea979cc4e036a8e3c67ed2f37ae6f25c5353ab908e1be", size = 12498, upload-time = "2026-03-17T17:55:44.062Z" }, +] + +[[package]] +name = "lance-namespace-urllib3-client" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/a1/8706a2be25bd184acccc411e48f1a42a4cbf3b6556cba15b9fcf4c15cfcc/lance_namespace_urllib3_client-0.6.1.tar.gz", hash = "sha256:31fbd058ce1ea0bf49045cdeaa756360ece0bc61e9e10276f41af6d217debe87", size = 182567, upload-time = "2026-03-17T17:55:46.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/c7/cb9580602dec25f0fdd6005c1c9ba1d4c8c0c3dc8d543107e5a9f248bba8/lance_namespace_urllib3_client-0.6.1-py3-none-any.whl", hash = "sha256:b9c103e1377ad46d2bd70eec894bfec0b1e2133dae0964d7e4de543c6e16293b", size = 317111, upload-time = "2026-03-17T17:55:45.546Z" }, +] + +[[package]] +name = "lancedb" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecation" }, + { name = "lance-namespace" }, + { name = "numpy" }, + { name = "overrides", marker = "python_full_version < '3.12'" }, + { name = "packaging" }, + { name = "pyarrow" }, + { name = "pydantic" }, + { name = "tqdm" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/2f/1577778ad57dba0c55dc13d87230583e14541c82562483ecf8bb2f8e8a00/lancedb-0.30.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:be2a9a43a65c330ccfd08115afb26106cd8d16788522fe7693d3a1f4e01ad321", size = 41959907, upload-time = "2026-03-16T23:03:04.551Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ca/8c2a04ce499a2a97d1a0de2b7e84fa8166f988a9a495e1ada860110489c2/lancedb-0.30.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be6a4ba2a1799a426cbf2ba5ea2559a7389a569e9a31f2409d531ceb59d42f35", size = 43873070, upload-time = "2026-03-16T23:11:01.352Z" }, + { url = "https://files.pythonhosted.org/packages/16/68/e01bf7837454a5ce9e2f6773905e07b09a949bc88136c0773c8166ed7729/lancedb-0.30.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a967ec05f9930770aeb077bc5579769b1bedf559fcd03a592d9644084625918", size = 46891197, upload-time = "2026-03-16T23:14:39.18Z" }, + { url = "https://files.pythonhosted.org/packages/43/d1/9085ad17abd98f3a180d7860df3190b2d76f99f533c76d7c7494cec4139d/lancedb-0.30.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:05c66f40f7d4f6f24208e786c40f84b87b1b8e55505305849dd3fed3b78431a3", size = 43877660, upload-time = "2026-03-16T23:11:00.837Z" }, + { url = "https://files.pythonhosted.org/packages/ea/69/504ee25c57c3f23c80276b5b7b5e4c0f98a5197a7e9e51d3c50500d2b53a/lancedb-0.30.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:bdcd27d98554ed11b6f345b14d1307b0e2332d5654767e9ee2e23d9b2d6513d1", size = 46932144, upload-time = "2026-03-16T23:15:00.474Z" }, + { url = "https://files.pythonhosted.org/packages/2c/85/d5550f22023e672af1945394f7a06a578fcab2980ecc6666acef3428a771/lancedb-0.30.0-cp39-abi3-win_amd64.whl", hash = "sha256:4751ff0446b90be4d4dccfe05f6c105f403a05f3b8531ab99eedc1c656aca950", size = 51121310, upload-time = "2026-03-16T23:43:23.89Z" }, +] + +[[package]] +name = "linkify-it-py" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "uc-micro-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[package.optional-dependencies] +linkify = [ + { name = "linkify-it-py" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, +] + +[[package]] +name = "mcp" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mmh3" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/1a/edb23803a168f070ded7a3014c6d706f63b90c84ccc024f89d794a3b7a6d/mmh3-5.2.1.tar.gz", hash = "sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad", size = 33775, upload-time = "2026-03-05T15:55:57.716Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/d7/3312a59df3c1cdd783f4cf0c4ee8e9decff9c5466937182e4cc7dbbfe6c5/mmh3-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dae0f0bd7d30c0ad61b9a504e8e272cb8391eed3f1587edf933f4f6b33437450", size = 56082, upload-time = "2026-03-05T15:53:59.702Z" }, + { url = "https://files.pythonhosted.org/packages/61/96/6f617baa098ca0d2989bfec6d28b5719532cd8d8848782662f5b755f657f/mmh3-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9aeaf53eaa075dd63e81512522fd180097312fb2c9f476333309184285c49ce0", size = 40458, upload-time = "2026-03-05T15:54:01.548Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b4/9cd284bd6062d711e13d26c04d4778ab3f690c1c38a4563e3c767ec8802e/mmh3-5.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0634581290e6714c068f4aa24020acf7880927d1f0084fa753d9799ae9610082", size = 40079, upload-time = "2026-03-05T15:54:02.743Z" }, + { url = "https://files.pythonhosted.org/packages/f6/09/a806334ce1d3d50bf782b95fcee8b3648e1e170327d4bb7b4bad2ad7d956/mmh3-5.2.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080c0637aea036f35507e803a4778f119a9b436617694ae1c5c366805f1e997", size = 97242, upload-time = "2026-03-05T15:54:04.536Z" }, + { url = "https://files.pythonhosted.org/packages/ee/93/723e317dd9e041c4dc4566a2eb53b01ad94de31750e0b834f1643905e97c/mmh3-5.2.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db0562c5f71d18596dcd45e854cf2eeba27d7543e1a3acdafb7eef728f7fe85d", size = 103082, upload-time = "2026-03-05T15:54:06.387Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/f96121e69cc48696075071531cf574f112e1ffd08059f4bffb41210e6fc5/mmh3-5.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d9f9a3ce559a5267014b04b82956993270f63ec91765e13e9fd73daf2d2738e", size = 106054, upload-time = "2026-03-05T15:54:07.506Z" }, + { url = "https://files.pythonhosted.org/packages/82/49/192b987ec48d0b2aecf8ac285a9b11fbc00030f6b9c694664ae923458dde/mmh3-5.2.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:960b1b3efa39872ac8b6cc3a556edd6fb90ed74f08c9c45e028f1005b26aa55d", size = 112910, upload-time = "2026-03-05T15:54:09.403Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a1/03e91fd334ed0144b83343a76eb11f17434cd08f746401488cfeafb2d241/mmh3-5.2.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d30b650595fdbe32366b94cb14f30bb2b625e512bd4e1df00611f99dc5c27fd4", size = 120551, upload-time = "2026-03-05T15:54:10.587Z" }, + { url = "https://files.pythonhosted.org/packages/93/b9/b89a71d2ff35c3a764d1c066c7313fc62c7cc48fa48a4b3b0304a4a0146f/mmh3-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:82f3802bfc4751f420d591c5c864de538b71cea117fce67e4595c2afede08a15", size = 99096, upload-time = "2026-03-05T15:54:11.76Z" }, + { url = "https://files.pythonhosted.org/packages/36/b5/613772c1c6ed5f7b63df55eb131e887cc43720fec392777b95a79d34e640/mmh3-5.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:915e7a2418f10bd1151b1953df06d896db9783c9cfdb9a8ee1f9b3a4331ab503", size = 98524, upload-time = "2026-03-05T15:54:13.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/0e/1524566fe8eaf871e4f7bc44095929fcd2620488f402822d848df19d679c/mmh3-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fc78739b5ec6e4fb02301984a3d442a91406e7700efbe305071e7fd1c78278f2", size = 106239, upload-time = "2026-03-05T15:54:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/04/94/21adfa7d90a7a697137ad6de33eeff6445420ca55e433a5d4919c79bc3b5/mmh3-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:41aac7002a749f08727cb91babff1daf8deac317c0b1f317adc69be0e6c375d1", size = 109797, upload-time = "2026-03-05T15:54:15.819Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e6/1aacc3a219e1aa62fa65669995d4a3562b35be5200ec03680c7e4bec9676/mmh3-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9d8089d853c7963a8ce87fff93e2a67075c0bc08684a08ea6ad13577c38ffc38", size = 97228, upload-time = "2026-03-05T15:54:16.992Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b9/5e4cca8dcccf298add0a27f3c357bc8cf8baf821d35cdc6165e4bd5a48b0/mmh3-5.2.1-cp311-cp311-win32.whl", hash = "sha256:baeb47635cb33375dee4924cd93d7f5dcaa786c740b08423b0209b824a1ee728", size = 40751, upload-time = "2026-03-05T15:54:18.714Z" }, + { url = "https://files.pythonhosted.org/packages/72/fc/5b11d49247f499bcda591171e9cf3b6ee422b19e70aa2cef2e0ae65ca3b9/mmh3-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:1e4ecee40ba19e6975e1120829796770325841c2f153c0e9aecca927194c6a2a", size = 41517, upload-time = "2026-03-05T15:54:19.764Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5f/2a511ee8a1c2a527c77726d5231685b72312c5a1a1b7639ad66a9652aa84/mmh3-5.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:c302245fd6c33d96bd169c7ccf2513c20f4c1e417c07ce9dce107c8bc3f8411f", size = 39287, upload-time = "2026-03-05T15:54:20.904Z" }, + { url = "https://files.pythonhosted.org/packages/92/94/bc5c3b573b40a328c4d141c20e399039ada95e5e2a661df3425c5165fd84/mmh3-5.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1", size = 56087, upload-time = "2026-03-05T15:54:21.92Z" }, + { url = "https://files.pythonhosted.org/packages/f6/80/64a02cc3e95c3af0aaa2590849d9ed24a9f14bb93537addde688e039b7c3/mmh3-5.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00", size = 40500, upload-time = "2026-03-05T15:54:22.953Z" }, + { url = "https://files.pythonhosted.org/packages/8b/72/e6d6602ce18adf4ddcd0e48f2e13590cc92a536199e52109f46f259d3c46/mmh3-5.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7", size = 40034, upload-time = "2026-03-05T15:54:23.943Z" }, + { url = "https://files.pythonhosted.org/packages/59/c2/bf4537a8e58e21886ef16477041238cab5095c836496e19fafc34b7445d2/mmh3-5.2.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b", size = 97292, upload-time = "2026-03-05T15:54:25.335Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e2/51ed62063b44d10b06d975ac87af287729eeb5e3ed9772f7584a17983e90/mmh3-5.2.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006", size = 103274, upload-time = "2026-03-05T15:54:26.44Z" }, + { url = "https://files.pythonhosted.org/packages/75/ce/12a7524dca59eec92e5b31fdb13ede1e98eda277cf2b786cf73bfbc24e81/mmh3-5.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825", size = 106158, upload-time = "2026-03-05T15:54:28.578Z" }, + { url = "https://files.pythonhosted.org/packages/86/1f/d3ba6dd322d01ab5d44c46c8f0c38ab6bbbf9b5e20e666dfc05bf4a23604/mmh3-5.2.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a", size = 113005, upload-time = "2026-03-05T15:54:29.767Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a9/15d6b6f913294ea41b44d901741298e3718e1cb89ee626b3694625826a43/mmh3-5.2.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b", size = 120744, upload-time = "2026-03-05T15:54:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/70b73923fd0284c439860ff5c871b20210dfdbe9a6b9dd0ee6496d77f174/mmh3-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166", size = 99111, upload-time = "2026-03-05T15:54:32.353Z" }, + { url = "https://files.pythonhosted.org/packages/dd/38/99f7f75cd27d10d8b899a1caafb9d531f3903e4d54d572220e3d8ac35e89/mmh3-5.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16", size = 98623, upload-time = "2026-03-05T15:54:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/fd/68/6e292c0853e204c44d2f03ea5f090be3317a0e2d9417ecb62c9eb27687df/mmh3-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211", size = 106437, upload-time = "2026-03-05T15:54:35.177Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c6/fedd7284c459cfb58721d461fcf5607a4c1f5d9ab195d113d51d10164d16/mmh3-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000", size = 110002, upload-time = "2026-03-05T15:54:36.673Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ac/ca8e0c19a34f5b71390171d2ff0b9f7f187550d66801a731bb68925126a4/mmh3-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5", size = 97507, upload-time = "2026-03-05T15:54:37.804Z" }, + { url = "https://files.pythonhosted.org/packages/df/94/6ebb9094cfc7ac5e7950776b9d13a66bb4a34f83814f32ba2abc9494fc68/mmh3-5.2.1-cp312-cp312-win32.whl", hash = "sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025", size = 40773, upload-time = "2026-03-05T15:54:40.077Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/cd3527198cf159495966551c84a5f36805a10ac17b294f41f67b83f6a4d6/mmh3-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00", size = 41560, upload-time = "2026-03-05T15:54:41.148Z" }, + { url = "https://files.pythonhosted.org/packages/15/96/6fe5ebd0f970a076e3ed5512871ce7569447b962e96c125528a2f9724470/mmh3-5.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc", size = 39313, upload-time = "2026-03-05T15:54:42.171Z" }, + { url = "https://files.pythonhosted.org/packages/25/a5/9daa0508a1569a54130f6198d5462a92deda870043624aa3ea72721aa765/mmh3-5.2.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e", size = 40832, upload-time = "2026-03-05T15:54:43.212Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6b/3230c6d80c1f4b766dedf280a92c2241e99f87c1504ff74205ec8cebe451/mmh3-5.2.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d", size = 41964, upload-time = "2026-03-05T15:54:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/62/fb/648bfddb74a872004b6ee751551bfdda783fe6d70d2e9723bad84dbe5311/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6", size = 39114, upload-time = "2026-03-05T15:54:45.205Z" }, + { url = "https://files.pythonhosted.org/packages/95/c2/ab7901f87af438468b496728d11264cb397b3574d41506e71b92128e0373/mmh3-5.2.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f", size = 39819, upload-time = "2026-03-05T15:54:46.509Z" }, + { url = "https://files.pythonhosted.org/packages/2f/ed/6f88dda0df67de1612f2e130ffea34cf84aaee5bff5b0aff4dbff2babe34/mmh3-5.2.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8", size = 40330, upload-time = "2026-03-05T15:54:47.864Z" }, + { url = "https://files.pythonhosted.org/packages/3d/66/7516d23f53cdf90f43fce24ab80c28f45e6851d78b46bef8c02084edf583/mmh3-5.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6", size = 56078, upload-time = "2026-03-05T15:54:48.9Z" }, + { url = "https://files.pythonhosted.org/packages/bc/34/4d152fdf4a91a132cb226b671f11c6b796eada9ab78080fb5ce1e95adaab/mmh3-5.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9", size = 40498, upload-time = "2026-03-05T15:54:49.942Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4c/8e3af1b6d85a299767ec97bd923f12b06267089c1472c27c1696870d1175/mmh3-5.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03", size = 40033, upload-time = "2026-03-05T15:54:50.994Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/966ea560e32578d453c9e9db53d602cbb1d0da27317e232afa7c38ceba11/mmh3-5.2.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b", size = 97320, upload-time = "2026-03-05T15:54:52.072Z" }, + { url = "https://files.pythonhosted.org/packages/bb/0d/2c5f9893b38aeb6b034d1a44ecd55a010148054f6a516abe53b5e4057297/mmh3-5.2.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5", size = 103299, upload-time = "2026-03-05T15:54:53.569Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fc/2ebaef4a4d4376f89761274dc274035ffd96006ab496b4ee5af9b08f21a9/mmh3-5.2.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593", size = 106222, upload-time = "2026-03-05T15:54:55.092Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/ea7ffe126d0ba0406622602a2d05e1e1a6841cc92fc322eb576c95b27fad/mmh3-5.2.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4", size = 113048, upload-time = "2026-03-05T15:54:56.305Z" }, + { url = "https://files.pythonhosted.org/packages/85/57/9447032edf93a64aa9bef4d9aa596400b1756f40411890f77a284f6293ca/mmh3-5.2.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1", size = 120742, upload-time = "2026-03-05T15:54:57.453Z" }, + { url = "https://files.pythonhosted.org/packages/53/82/a86cc87cc88c92e9e1a598fee509f0409435b57879a6129bf3b3e40513c7/mmh3-5.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104", size = 99132, upload-time = "2026-03-05T15:54:58.583Z" }, + { url = "https://files.pythonhosted.org/packages/54/f7/6b16eb1b40ee89bb740698735574536bc20d6cdafc65ae702ea235578e05/mmh3-5.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d", size = 98686, upload-time = "2026-03-05T15:55:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/e8/88/a601e9f32ad1410f438a6d0544298ea621f989bd34a0731a7190f7dec799/mmh3-5.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f", size = 106479, upload-time = "2026-03-05T15:55:01.532Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5c/ce29ae3dfc4feec4007a437a1b7435fb9507532a25147602cd5b52be86db/mmh3-5.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2", size = 110030, upload-time = "2026-03-05T15:55:02.934Z" }, + { url = "https://files.pythonhosted.org/packages/13/30/ae444ef2ff87c805d525da4fa63d27cda4fe8a48e77003a036b8461cfd5c/mmh3-5.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a", size = 97536, upload-time = "2026-03-05T15:55:04.135Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f9/dc3787ee5c813cc27fe79f45ad4500d9b5437f23a7402435cc34e07c7718/mmh3-5.2.1-cp313-cp313-win32.whl", hash = "sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b", size = 40769, upload-time = "2026-03-05T15:55:05.277Z" }, + { url = "https://files.pythonhosted.org/packages/43/67/850e0b5a1e97799822ebfc4ca0e8c6ece3ed8baf7dcdf64de817dfdda2ca/mmh3-5.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229", size = 41563, upload-time = "2026-03-05T15:55:06.283Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/98c90b28e1da5458e19fbfaf4adb5289208d3bfccd45dd14eab216a2f0bb/mmh3-5.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d", size = 39310, upload-time = "2026-03-05T15:55:07.323Z" }, + { url = "https://files.pythonhosted.org/packages/63/b4/65bc1fb2bb7f83e91c30865023b1847cf89a5f237165575e8c83aa536584/mmh3-5.2.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227", size = 40794, upload-time = "2026-03-05T15:55:09.773Z" }, + { url = "https://files.pythonhosted.org/packages/c4/86/7168b3d83be8eb553897b1fac9da8bbb06568e5cfe555ffc329ebb46f59d/mmh3-5.2.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0", size = 41923, upload-time = "2026-03-05T15:55:10.924Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9b/b653ab611c9060ce8ff0ba25c0226757755725e789292f3ca138a58082cd/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b", size = 39131, upload-time = "2026-03-05T15:55:11.961Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b4/5a2e0d34ab4d33543f01121e832395ea510132ea8e52cdf63926d9d81754/mmh3-5.2.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966", size = 39825, upload-time = "2026-03-05T15:55:13.013Z" }, + { url = "https://files.pythonhosted.org/packages/bd/69/81699a8f39a3f8d368bec6443435c0c392df0d200ad915bf0d222b588e03/mmh3-5.2.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b", size = 40344, upload-time = "2026-03-05T15:55:14.026Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b3/71c8c775807606e8fd8acc5c69016e1caf3200d50b50b6dd4b40ce10b76c/mmh3-5.2.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8", size = 56291, upload-time = "2026-03-05T15:55:15.137Z" }, + { url = "https://files.pythonhosted.org/packages/6f/75/2c24517d4b2ce9e4917362d24f274d3d541346af764430249ddcc4cb3a08/mmh3-5.2.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7", size = 40575, upload-time = "2026-03-05T15:55:16.518Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/e4a360164365ac9f07a25f0f7928e3a66eb9ecc989384060747aa170e6aa/mmh3-5.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e", size = 40052, upload-time = "2026-03-05T15:55:17.735Z" }, + { url = "https://files.pythonhosted.org/packages/97/ca/120d92223a7546131bbbc31c9174168ee7a73b1366f5463ffe69d9e691fe/mmh3-5.2.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74", size = 97311, upload-time = "2026-03-05T15:55:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/b6/71/c1a60c1652b8813ef9de6d289784847355417ee0f2980bca002fe87f4ae5/mmh3-5.2.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc", size = 103279, upload-time = "2026-03-05T15:55:20.448Z" }, + { url = "https://files.pythonhosted.org/packages/48/29/ad97f4be1509cdcb28ae32c15593ce7c415db47ace37f8fad35b493faa9a/mmh3-5.2.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617", size = 106290, upload-time = "2026-03-05T15:55:21.6Z" }, + { url = "https://files.pythonhosted.org/packages/77/29/1f86d22e281bd8827ba373600a4a8b0c0eae5ca6aa55b9a8c26d2a34decc/mmh3-5.2.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2", size = 113116, upload-time = "2026-03-05T15:55:22.826Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7c/339971ea7ed4c12d98f421f13db3ea576a9114082ccb59d2d1a0f00ccac1/mmh3-5.2.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312", size = 120740, upload-time = "2026-03-05T15:55:24.3Z" }, + { url = "https://files.pythonhosted.org/packages/e4/92/3c7c4bdb8e926bb3c972d1e2907d77960c1c4b250b41e8366cf20c6e4373/mmh3-5.2.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb", size = 99143, upload-time = "2026-03-05T15:55:25.456Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/33dd8706e732458c8375eae63c981292de07a406bad4ec03e5269654aa2c/mmh3-5.2.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a", size = 98703, upload-time = "2026-03-05T15:55:26.723Z" }, + { url = "https://files.pythonhosted.org/packages/51/04/76bbce05df76cbc3d396f13b2ea5b1578ef02b6a5187e132c6c33f99d596/mmh3-5.2.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105", size = 106484, upload-time = "2026-03-05T15:55:28.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8f/c6e204a2c70b719c1f62ffd9da27aef2dddcba875ea9c31ca0e87b975a46/mmh3-5.2.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a", size = 110012, upload-time = "2026-03-05T15:55:29.532Z" }, + { url = "https://files.pythonhosted.org/packages/e3/37/7181efd8e39db386c1ebc3e6b7d1f702a09d7c1197a6f2742ed6b5c16597/mmh3-5.2.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd", size = 97508, upload-time = "2026-03-05T15:55:31.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/0f/afa7ca2615fd85e1469474bb860e381443d0b868c083b62b41cb1d7ca32f/mmh3-5.2.1-cp314-cp314-win32.whl", hash = "sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4", size = 41387, upload-time = "2026-03-05T15:55:32.403Z" }, + { url = "https://files.pythonhosted.org/packages/71/0d/46d42a260ee1357db3d486e6c7a692e303c017968e14865e00efa10d09fc/mmh3-5.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb", size = 42101, upload-time = "2026-03-05T15:55:33.646Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7b/848a8378059d96501a41159fca90d6a99e89736b0afbe8e8edffeac8c74b/mmh3-5.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe", size = 39836, upload-time = "2026-03-05T15:55:35.026Z" }, + { url = "https://files.pythonhosted.org/packages/27/61/1dabea76c011ba8547c25d30c91c0ec22544487a8750997a27a0c9e1180b/mmh3-5.2.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba", size = 57727, upload-time = "2026-03-05T15:55:36.162Z" }, + { url = "https://files.pythonhosted.org/packages/b7/32/731185950d1cf2d5e28979cc8593016ba1619a295faba10dda664a4931b5/mmh3-5.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00", size = 41308, upload-time = "2026-03-05T15:55:37.254Z" }, + { url = "https://files.pythonhosted.org/packages/76/aa/66c76801c24b8c9418b4edde9b5e57c75e72c94e29c48f707e3962534f18/mmh3-5.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8", size = 40758, upload-time = "2026-03-05T15:55:38.61Z" }, + { url = "https://files.pythonhosted.org/packages/9e/bb/79a1f638a02f0ae389f706d13891e2fbf7d8c0a22ecde67ba828951bb60a/mmh3-5.2.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc", size = 109670, upload-time = "2026-03-05T15:55:40.13Z" }, + { url = "https://files.pythonhosted.org/packages/26/94/8cd0e187a288985bcfc79bf5144d1d712df9dee74365f59d26e3a1865be6/mmh3-5.2.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f", size = 117399, upload-time = "2026-03-05T15:55:42.076Z" }, + { url = "https://files.pythonhosted.org/packages/42/94/dfea6059bd5c5beda565f58a4096e43f4858fb6d2862806b8bbd12cbb284/mmh3-5.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44", size = 120386, upload-time = "2026-03-05T15:55:43.481Z" }, + { url = "https://files.pythonhosted.org/packages/47/cb/f9c45e62aaa67220179f487772461d891bb582bb2f9783c944832c60efd9/mmh3-5.2.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7", size = 125924, upload-time = "2026-03-05T15:55:44.638Z" }, + { url = "https://files.pythonhosted.org/packages/a5/83/fe54a4a7c11bc9f623dfc1707decd034245602b076dfc1dcc771a4163170/mmh3-5.2.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c", size = 135280, upload-time = "2026-03-05T15:55:45.866Z" }, + { url = "https://files.pythonhosted.org/packages/97/67/fe7e9e9c143daddd210cd22aef89cbc425d58ecf238d2b7d9eb0da974105/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac", size = 110050, upload-time = "2026-03-05T15:55:47.074Z" }, + { url = "https://files.pythonhosted.org/packages/43/c4/6d4b09fcbef80794de447c9378e39eefc047156b290fa3dd2d5257ca8227/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912", size = 111158, upload-time = "2026-03-05T15:55:48.239Z" }, + { url = "https://files.pythonhosted.org/packages/81/a6/ca51c864bdb30524beb055a6d8826db3906af0834ec8c41d097a6e8573d5/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf", size = 116890, upload-time = "2026-03-05T15:55:49.405Z" }, + { url = "https://files.pythonhosted.org/packages/cc/04/5a1fe2e2ad843d03e89af25238cbc4f6840a8bb6c4329a98ab694c71deda/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d", size = 123121, upload-time = "2026-03-05T15:55:50.61Z" }, + { url = "https://files.pythonhosted.org/packages/af/4d/3c820c6f4897afd25905270a9f2330a23f77a207ea7356f7aadace7273c0/mmh3-5.2.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18", size = 110187, upload-time = "2026-03-05T15:55:52.143Z" }, + { url = "https://files.pythonhosted.org/packages/21/54/1d71cd143752361c0aebef16ad3f55926a6faf7b112d355745c1f8a25f7f/mmh3-5.2.1-cp314-cp314t-win32.whl", hash = "sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82", size = 41934, upload-time = "2026-03-05T15:55:53.564Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e4/63a2a88f31d93dea03947cccc2a076946857e799ea4f7acdecbf43b324aa/mmh3-5.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb", size = 43036, upload-time = "2026-03-05T15:55:55.252Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0f/59204bf136d1201f8d7884cfbaf7498c5b4674e87a4c693f9bde63741ce1/mmh3-5.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b", size = 40391, upload-time = "2026-03-05T15:55:56.697Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "narwhals" +version = "2.18.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/96/45218c2fdec4c9f22178f905086e85ef1a6d63862dcc3cd68eb60f1867f5/narwhals-2.18.1.tar.gz", hash = "sha256:652a1fcc9d432bbf114846688884c215f17eb118aa640b7419295d2f910d2a8b", size = 620578, upload-time = "2026-03-24T15:11:25.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl", hash = "sha256:a0a8bb80205323851338888ba3a12b4f65d352362c8a94be591244faf36504ad", size = 444952, upload-time = "2026-03-24T15:11:23.801Z" }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, + { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, + { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, +] + +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + +[[package]] +name = "onnxruntime" +version = "1.24.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "sympy" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/69/6c40720201012c6af9aa7d4ecdd620e521bd806dc6269d636fdd5c5aeebe/onnxruntime-1.24.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bdfce8e9a6497cec584aab407b71bf697dac5e1b7b7974adc50bf7533bdb3a2", size = 17332131, upload-time = "2026-03-17T22:05:49.005Z" }, + { url = "https://files.pythonhosted.org/packages/38/e9/8c901c150ce0c368da38638f44152fb411059c0c7364b497c9e5c957321a/onnxruntime-1.24.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:046ff290045a387676941a02a8ae5c3ebec6b4f551ae228711968c4a69d8f6b7", size = 15152472, upload-time = "2026-03-17T22:03:26.176Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b6/7a4df417cdd01e8f067a509e123ac8b31af450a719fa7ed81787dd6057ec/onnxruntime-1.24.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e54ad52e61d2d4618dcff8fa1480ac66b24ee2eab73331322db1049f11ccf330", size = 17222993, upload-time = "2026-03-17T22:04:34.485Z" }, + { url = "https://files.pythonhosted.org/packages/dd/59/8febe015f391aa1757fa5ba82c759ea4b6c14ef970132efb5e316665ba61/onnxruntime-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b43b63eb24a2bc8fc77a09be67587a570967a412cccb837b6245ccb546691153", size = 12594863, upload-time = "2026-03-17T22:05:38.749Z" }, + { url = "https://files.pythonhosted.org/packages/32/84/4155fcd362e8873eb6ce305acfeeadacd9e0e59415adac474bea3d9281bb/onnxruntime-1.24.4-cp311-cp311-win_arm64.whl", hash = "sha256:e26478356dba25631fb3f20112e345f8e8bf62c499bb497e8a559f7d69cf7e7b", size = 12259895, upload-time = "2026-03-17T22:05:28.812Z" }, + { url = "https://files.pythonhosted.org/packages/d7/38/31db1b232b4ba960065a90c1506ad7a56995cd8482033184e97fadca17cc/onnxruntime-1.24.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cad1c2b3f455c55678ab2a8caa51fb420c25e6e3cf10f4c23653cdabedc8de78", size = 17341875, upload-time = "2026-03-17T22:05:51.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/60/c4d1c8043eb42f8a9aa9e931c8c293d289c48ff463267130eca97d13357f/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a5c5a544b22f90859c88617ecb30e161ee3349fcc73878854f43d77f00558b5", size = 15172485, upload-time = "2026-03-17T22:03:32.182Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ab/5b68110e0460d73fad814d5bd11c7b1ddcce5c37b10177eb264d6a36e331/onnxruntime-1.24.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d640eb9f3782689b55cfa715094474cd5662f2f137be6a6f847a594b6e9705c", size = 17244912, upload-time = "2026-03-17T22:04:37.251Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f4/6b89e297b93704345f0f3f8c62229bee323ef25682a3f9b4f89a39324950/onnxruntime-1.24.4-cp312-cp312-win_amd64.whl", hash = "sha256:535b29475ca42b593c45fbb2152fbf1cdf3f287315bf650e6a724a0a1d065cdb", size = 12596856, upload-time = "2026-03-17T22:05:41.224Z" }, + { url = "https://files.pythonhosted.org/packages/43/06/8b8ec6e9e6a474fcd5d772453f627ad4549dfe3ab8c0bf70af5afcde551b/onnxruntime-1.24.4-cp312-cp312-win_arm64.whl", hash = "sha256:e6214096e14b7b52e3bee1903dc12dc7ca09cb65e26664668a4620cc5e6f9a90", size = 12270275, upload-time = "2026-03-17T22:05:31.132Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f0/8a21ec0a97e40abb7d8da1e8b20fb9e1af509cc6d191f6faa75f73622fb2/onnxruntime-1.24.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e99a48078baaefa2b50fe5836c319499f71f13f76ed32d0211f39109147a49e0", size = 17341922, upload-time = "2026-03-17T22:03:56.364Z" }, + { url = "https://files.pythonhosted.org/packages/8b/25/d7908de8e08cee9abfa15b8aa82349b79733ae5865162a3609c11598805d/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4aaed1e5e1aaacf2343c838a30a7c3ade78f13eeb16817411f929d04040a13", size = 15172290, upload-time = "2026-03-17T22:03:37.124Z" }, + { url = "https://files.pythonhosted.org/packages/7f/72/105ec27a78c5aa0154a7c0cd8c41c19a97799c3b12fc30392928997e3be3/onnxruntime-1.24.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e30c972bc02e072911aabb6891453ec73795386c0af2b761b65444b8a4c4745f", size = 17244738, upload-time = "2026-03-17T22:04:40.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/fb/a592736d968c2f58e12de4d52088dda8e0e724b26ad5c0487263adb45875/onnxruntime-1.24.4-cp313-cp313-win_amd64.whl", hash = "sha256:3b6ba8b0181a3aa88edab00eb01424ffc06f42e71095a91186c2249415fcff93", size = 12597435, upload-time = "2026-03-17T22:05:43.826Z" }, + { url = "https://files.pythonhosted.org/packages/ad/04/ae2479e9841b64bd2eb44f8a64756c62593f896514369a11243b1b86ca5c/onnxruntime-1.24.4-cp313-cp313-win_arm64.whl", hash = "sha256:71d6a5c1821d6e8586a024000ece458db8f2fc0ecd050435d45794827ce81e19", size = 12269852, upload-time = "2026-03-17T22:05:33.353Z" }, + { url = "https://files.pythonhosted.org/packages/b4/af/a479a536c4398ffaf49fbbe755f45d5b8726bdb4335ab31b537f3d7149b8/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1700f559c8086d06b2a4d5de51e62cb4ff5e2631822f71a36db8c72383db71ee", size = 15176861, upload-time = "2026-03-17T22:03:40.143Z" }, + { url = "https://files.pythonhosted.org/packages/be/13/19f5da70c346a76037da2c2851ecbf1266e61d7f0dcdb887c667210d4608/onnxruntime-1.24.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c74e268dc808e61e63784d43f9ddcdaf50a776c2819e8bd1d1b11ef64bf7e36", size = 17247454, upload-time = "2026-03-17T22:04:46.643Z" }, + { url = "https://files.pythonhosted.org/packages/89/db/b30dbbd6037847b205ab75d962bc349bf1e46d02a65b30d7047a6893ffd6/onnxruntime-1.24.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:fbff2a248940e3398ae78374c5a839e49a2f39079b488bc64439fa0ec327a3e4", size = 17343300, upload-time = "2026-03-17T22:03:59.223Z" }, + { url = "https://files.pythonhosted.org/packages/61/88/1746c0e7959961475b84c776d35601a21d445f463c93b1433a409ec3e188/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2b7969e72d8cb53ffc88ab6d49dd5e75c1c663bda7be7eb0ece192f127343d1", size = 15175936, upload-time = "2026-03-17T22:03:43.671Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ba/4699cde04a52cece66cbebc85bd8335a0d3b9ad485abc9a2e15946a1349d/onnxruntime-1.24.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14ed1f197fab812b695a5eaddb536c635e58a2fbbe50a517c78f082cc6ce9177", size = 17246432, upload-time = "2026-03-17T22:04:49.58Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/4590910841bb28bd3b4b388a9efbedf4e2d2cca99ddf0c863642b4e87814/onnxruntime-1.24.4-cp314-cp314-win_amd64.whl", hash = "sha256:311e309f573bf3c12aa5723e23823077f83d5e412a18499d4485c7eb41040858", size = 12903276, upload-time = "2026-03-17T22:05:46.349Z" }, + { url = "https://files.pythonhosted.org/packages/7f/6f/60e2c0acea1e1ac09b3e794b5a19c166eebf91c0b860b3e6db8e74983fda/onnxruntime-1.24.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f0b910e86b759a4732663ec61fd57ac42ee1b0066f68299de164220b660546d", size = 12594365, upload-time = "2026-03-17T22:05:35.795Z" }, + { url = "https://files.pythonhosted.org/packages/cf/68/0c05d10f8f6c40fe0912ebec0d5a33884aaa2af2053507e864dab0883208/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa12ddc54c9c4594073abcaa265cd9681e95fb89dae982a6f508a794ca42e661", size = 15176889, upload-time = "2026-03-17T22:03:48.021Z" }, + { url = "https://files.pythonhosted.org/packages/6c/1d/1666dc64e78d8587d168fec4e3b7922b92eb286a2ddeebcf6acb55c7dc82/onnxruntime-1.24.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1cc6a518255f012134bc791975a6294806be9a3b20c4a54cca25194c90cf731", size = 17247021, upload-time = "2026-03-17T22:04:52.377Z" }, +] + +[[package]] +name = "openai" +version = "2.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/15/52580c8fbc16d0675d516e8749806eda679b16de1e4434ea06fb6feaa610/openai-2.30.0.tar.gz", hash = "sha256:92f7661c990bda4b22a941806c83eabe4896c3094465030dd882a71abe80c885", size = 676084, upload-time = "2026-03-25T22:08:59.96Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/9e/5bfa2270f902d5b92ab7d41ce0475b8630572e71e349b2a4996d14bdda93/openai-2.30.0-py3-none-any.whl", hash = "sha256:9a5ae616888eb2748ec5e0c5b955a51592e0b201a11f4262db920f2a78c5231d", size = 1146656, upload-time = "2026-03-25T22:08:58.2Z" }, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.34.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/5e/94a8cb759e4e409022229418294e098ca7feca00eb3c467bb20cbd329bda/opentelemetry_api-1.34.1.tar.gz", hash = "sha256:64f0bd06d42824843731d05beea88d4d4b6ae59f9fe347ff7dfa2cc14233bbb3", size = 64987, upload-time = "2025-06-10T08:55:19.818Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/3a/2ba85557e8dc024c0842ad22c570418dc02c36cbd1ab4b832a93edf071b8/opentelemetry_api-1.34.1-py3-none-any.whl", hash = "sha256:b7df4cb0830d5a6c29ad0c0691dbae874d8daefa934b8b1d642de48323d32a8c", size = 65767, upload-time = "2025-06-10T08:54:56.717Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.34.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/f0/ff235936ee40db93360233b62da932d4fd9e8d103cd090c6bcb9afaf5f01/opentelemetry_exporter_otlp_proto_common-1.34.1.tar.gz", hash = "sha256:b59a20a927facd5eac06edaf87a07e49f9e4a13db487b7d8a52b37cb87710f8b", size = 20817, upload-time = "2025-06-10T08:55:22.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/e8/8b292a11cc8d8d87ec0c4089ae21b6a58af49ca2e51fa916435bc922fdc7/opentelemetry_exporter_otlp_proto_common-1.34.1-py3-none-any.whl", hash = "sha256:8e2019284bf24d3deebbb6c59c71e6eef3307cd88eff8c633e061abba33f7e87", size = 18834, upload-time = "2025-06-10T08:55:00.806Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.34.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/f7/bb63837a3edb9ca857aaf5760796874e7cecddc88a2571b0992865a48fb6/opentelemetry_exporter_otlp_proto_grpc-1.34.1.tar.gz", hash = "sha256:7c841b90caa3aafcfc4fee58487a6c71743c34c6dc1787089d8b0578bbd794dd", size = 22566, upload-time = "2025-06-10T08:55:23.214Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/42/0a4dd47e7ef54edf670c81fc06a83d68ea42727b82126a1df9dd0477695d/opentelemetry_exporter_otlp_proto_grpc-1.34.1-py3-none-any.whl", hash = "sha256:04bb8b732b02295be79f8a86a4ad28fae3d4ddb07307a98c7aa6f331de18cca6", size = 18615, upload-time = "2025-06-10T08:55:02.214Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.34.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/8f/954bc725961cbe425a749d55c0ba1df46832a5999eae764d1a7349ac1c29/opentelemetry_exporter_otlp_proto_http-1.34.1.tar.gz", hash = "sha256:aaac36fdce46a8191e604dcf632e1f9380c7d5b356b27b3e0edb5610d9be28ad", size = 15351, upload-time = "2025-06-10T08:55:24.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/54/b05251c04e30c1ac70cf4a7c5653c085dfcf2c8b98af71661d6a252adc39/opentelemetry_exporter_otlp_proto_http-1.34.1-py3-none-any.whl", hash = "sha256:5251f00ca85872ce50d871f6d3cc89fe203b94c3c14c964bbdc3883366c705d8", size = 17744, upload-time = "2025-06-10T08:55:03.802Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.34.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/b3/c3158dd012463bb7c0eb7304a85a6f63baeeb5b4c93a53845cf89f848c7e/opentelemetry_proto-1.34.1.tar.gz", hash = "sha256:16286214e405c211fc774187f3e4bbb1351290b8dfb88e8948af209ce85b719e", size = 34344, upload-time = "2025-06-10T08:55:32.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/ab/4591bfa54e946350ce8b3f28e5c658fe9785e7cd11e9c11b1671a867822b/opentelemetry_proto-1.34.1-py3-none-any.whl", hash = "sha256:eb4bb5ac27f2562df2d6857fc557b3a481b5e298bc04f94cc68041f00cebcbd2", size = 55692, upload-time = "2025-06-10T08:55:14.904Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.34.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/41/fe20f9036433da8e0fcef568984da4c1d1c771fa072ecd1a4d98779dccdd/opentelemetry_sdk-1.34.1.tar.gz", hash = "sha256:8091db0d763fcd6098d4781bbc80ff0971f94e260739aa6afe6fd379cdf3aa4d", size = 159441, upload-time = "2025-06-10T08:55:33.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/1b/def4fe6aa73f483cabf4c748f4c25070d5f7604dcc8b52e962983491b29e/opentelemetry_sdk-1.34.1-py3-none-any.whl", hash = "sha256:308effad4059562f1d92163c61c8141df649da24ce361827812c40abb2a1e96e", size = 118477, upload-time = "2025-06-10T08:55:16.02Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.55b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/f0/f33458486da911f47c4aa6db9bda308bb80f3236c111bf848bd870c16b16/opentelemetry_semantic_conventions-0.55b1.tar.gz", hash = "sha256:ef95b1f009159c28d7a7849f5cbc71c4c34c845bb514d66adfdf1b3fff3598b3", size = 119829, upload-time = "2025-06-10T08:55:33.881Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/89/267b0af1b1d0ba828f0e60642b6a5116ac1fd917cde7fc02821627029bd1/opentelemetry_semantic_conventions-0.55b1-py3-none-any.whl", hash = "sha256:5da81dfdf7d52e3d37f8fe88d5e771e191de924cfff5f550ab0b8f7b2409baed", size = 196223, upload-time = "2025-06-10T08:55:17.638Z" }, +] + +[[package]] +name = "orjson" +version = "3.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/41/5aa7fa3b0f4dc6b47dcafc3cea909299c37e40e9972feabc8b6a74e2730d/orjson-3.11.8-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:003646067cc48b7fcab2ae0c562491c9b5d2cbd43f1e5f16d98fd118c5522d34", size = 229229, upload-time = "2026-03-31T16:14:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/0a/d7/57e7f2458e0a2c41694f39fc830030a13053a84f837a5b73423dca1f0938/orjson-3.11.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ed193ce51d77a3830cad399a529cd4ef029968761f43ddc549e1bc62b40d88f8", size = 128871, upload-time = "2026-03-31T16:14:51.888Z" }, + { url = "https://files.pythonhosted.org/packages/53/4a/e0fdb9430983e6c46e0299559275025075568aad5d21dd606faee3703924/orjson-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30491bc4f862aa15744b9738517454f1e46e56c972a2be87d70d727d5b2a8f8", size = 132104, upload-time = "2026-03-31T16:14:53.142Z" }, + { url = "https://files.pythonhosted.org/packages/08/4a/2025a60ff3f5c8522060cda46612d9b1efa653de66ed2908591d8d82f22d/orjson-3.11.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eda5b8b6be91d3f26efb7dc6e5e68ee805bc5617f65a328587b35255f138bf4", size = 130483, upload-time = "2026-03-31T16:14:54.605Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3c/b9cde05bdc7b2385c66014e0620627da638d3d04e4954416ab48c31196c5/orjson-3.11.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8db7bfb6fe03581bbab54d7c4124a6dd6a7f4273a38f7267197890f094675f", size = 135481, upload-time = "2026-03-31T16:14:55.901Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f2/a8238e7734de7cb589fed319857a8025d509c89dc52fdcc88f39c6d03d5a/orjson-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8b5231de76c528a46b57010bbd83fb51e056aa0220a372fd5065e978406f1c", size = 146819, upload-time = "2026-03-31T16:14:57.548Z" }, + { url = "https://files.pythonhosted.org/packages/db/10/dbf1e2a3cafea673b1b4350e371877b759060d6018a998643b7040e5de48/orjson-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a4a208a6fbfdb7a7327b8f201c6014f189f721fd55d047cafc4157af1bc62a", size = 132846, upload-time = "2026-03-31T16:14:58.91Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fc/55e667ec9c85694038fcff00573d221b085d50777368ee3d77f38668bf3c/orjson-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f8952d6d2505c003e8f0224ff7858d341fa4e33fef82b91c4ff0ef070f2393c", size = 133580, upload-time = "2026-03-31T16:15:00.519Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a6/c08c589a9aad0cb46c4831d17de212a2b6901f9d976814321ff8e69e8785/orjson-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0022bb50f90da04b009ce32c512dc1885910daa7cb10b7b0cba4505b16db82a8", size = 142042, upload-time = "2026-03-31T16:15:01.906Z" }, + { url = "https://files.pythonhosted.org/packages/5c/cc/2f78ea241d52b717d2efc38878615fe80425bf2beb6e68c984dde257a766/orjson-3.11.8-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6", size = 423845, upload-time = "2026-03-31T16:15:03.703Z" }, + { url = "https://files.pythonhosted.org/packages/70/07/c17dcf05dd8045457538428a983bf1f1127928df5bf328cb24d2b7cddacb/orjson-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6dbe9a97bdb4d8d9d5367b52a7c32549bba70b2739c58ef74a6964a6d05ae054", size = 147729, upload-time = "2026-03-31T16:15:05.203Z" }, + { url = "https://files.pythonhosted.org/packages/90/6c/0fb6e8a24e682e0958d71711ae6f39110e4b9cd8cab1357e2a89cb8e1951/orjson-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c370674ebabe16c6ccac33ff80c62bf8a6e59439f5e9d40c1f5ab8fd2215b7", size = 136425, upload-time = "2026-03-31T16:15:07.052Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/4d3cc3a3d616035beb51b24a09bb872942dc452cf2df0c1d11ab35046d9f/orjson-3.11.8-cp311-cp311-win32.whl", hash = "sha256:0e32f7154299f42ae66f13488963269e5eccb8d588a65bc839ed986919fc9fac", size = 131870, upload-time = "2026-03-31T16:15:08.678Z" }, + { url = "https://files.pythonhosted.org/packages/13/26/9fe70f81d16b702f8c3a775e8731b50ad91d22dacd14c7599b60a0941cd1/orjson-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:25e0c672a2e32348d2eb33057b41e754091f2835f87222e4675b796b92264f06", size = 127440, upload-time = "2026-03-31T16:15:09.994Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c6/b038339f4145efd2859c1ca53097a52c0bb9cbdd24f947ebe146da1ad067/orjson-3.11.8-cp311-cp311-win_arm64.whl", hash = "sha256:9185589c1f2a944c17e26c9925dcdbc2df061cc4a145395c57f0c51f9b5dbfcd", size = 127399, upload-time = "2026-03-31T16:15:11.412Z" }, + { url = "https://files.pythonhosted.org/packages/01/f6/8d58b32ab32d9215973a1688aebd098252ee8af1766c0e4e36e7831f0295/orjson-3.11.8-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1cd0b77e77c95758f8e1100139844e99f3ccc87e71e6fc8e1c027e55807c549f", size = 229233, upload-time = "2026-03-31T16:15:12.762Z" }, + { url = "https://files.pythonhosted.org/packages/a9/8b/2ffe35e71f6b92622e8ea4607bf33ecf7dfb51b3619dcfabfd36cbe2d0a5/orjson-3.11.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6a3d159d5ffa0e3961f353c4b036540996bf8b9697ccc38261c0eac1fd3347a6", size = 128772, upload-time = "2026-03-31T16:15:14.237Z" }, + { url = "https://files.pythonhosted.org/packages/27/d2/1f8682ae50d5c6897a563cb96bc106da8c9cb5b7b6e81a52e4cc086679b9/orjson-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76070a76e9c5ae661e2d9848f216980d8d533e0f8143e6ed462807b242e3c5e8", size = 131946, upload-time = "2026-03-31T16:15:15.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/4b/5500f76f0eece84226e0689cb48dcde081104c2fa6e2483d17ca13685ffb/orjson-3.11.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54153d21520a71a4c82a0dbb4523e468941d549d221dc173de0f019678cf3813", size = 130368, upload-time = "2026-03-31T16:15:17.066Z" }, + { url = "https://files.pythonhosted.org/packages/da/4e/58b927e08fbe9840e6c920d9e299b051ea667463b1f39a56e668669f8508/orjson-3.11.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:469ac2125611b7c5741a0b3798cd9e5786cbad6345f9f400c77212be89563bec", size = 135540, upload-time = "2026-03-31T16:15:18.404Z" }, + { url = "https://files.pythonhosted.org/packages/56/7c/ba7cb871cba1bcd5cd02ee34f98d894c6cea96353ad87466e5aef2429c60/orjson-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14778ffd0f6896aa613951a7fbf4690229aa7a543cb2bfbe9f358e08aafa9546", size = 146877, upload-time = "2026-03-31T16:15:19.833Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/eb9c25fc1386696c6a342cd361c306452c75e0b55e86ad602dd4827a7fd7/orjson-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea56a955056a6d6c550cf18b3348656a9d9a4f02e2d0c02cabf3c73f1055d506", size = 132837, upload-time = "2026-03-31T16:15:21.282Z" }, + { url = "https://files.pythonhosted.org/packages/37/87/5ddeb7fc1fbd9004aeccab08426f34c81a5b4c25c7061281862b015fce2b/orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a0f57e59a530d18a142f4d4ba6dfc708dc5fdedce45e98ff06b44930a2a48f", size = 133624, upload-time = "2026-03-31T16:15:22.641Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/90048793db94ee4b2fcec4ac8e5ddb077367637d6650be896b3494b79bb7/orjson-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b48e274f8824567d74e2158199e269597edf00823a1b12b63d48462bbf5123e", size = 141904, upload-time = "2026-03-31T16:15:24.435Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cf/eb284847487821a5d415e54149a6449ba9bfc5872ce63ab7be41b8ec401c/orjson-3.11.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3f262401086a3960586af06c054609365e98407151f5ea24a62893a40d80dbbb", size = 423742, upload-time = "2026-03-31T16:15:26.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/09/e12423d327071c851c13e76936f144a96adacfc037394dec35ac3fc8d1e8/orjson-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e8c6218b614badf8e229b697865df4301afa74b791b6c9ade01d19a9953a942", size = 147806, upload-time = "2026-03-31T16:15:27.909Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6d/37c2589ba864e582ffe7611643314785c6afb1f83c701654ef05daa8fcc7/orjson-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:093d489fa039ddade2db541097dbb484999fcc65fc2b0ff9819141e2ab364f25", size = 136485, upload-time = "2026-03-31T16:15:29.749Z" }, + { url = "https://files.pythonhosted.org/packages/be/c9/135194a02ab76b04ed9a10f68624b7ebd238bbe55548878b11ff15a0f352/orjson-3.11.8-cp312-cp312-win32.whl", hash = "sha256:e0950ed1bcb9893f4293fd5c5a7ee10934fbf82c4101c70be360db23ce24b7d2", size = 131966, upload-time = "2026-03-31T16:15:31.687Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9a/9796f8fbe3cf30ce9cb696748dbb535e5c87be4bf4fe2e9ca498ef1fa8cf/orjson-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:3cf17c141617b88ced4536b2135c552490f07799f6ad565948ea07bef0dcb9a6", size = 127441, upload-time = "2026-03-31T16:15:33.333Z" }, + { url = "https://files.pythonhosted.org/packages/cc/47/5aaf54524a7a4a0dd09dd778f3fa65dd2108290615b652e23d944152bc8e/orjson-3.11.8-cp312-cp312-win_arm64.whl", hash = "sha256:48854463b0572cc87dac7d981aa72ed8bf6deedc0511853dc76b8bbd5482d36d", size = 127364, upload-time = "2026-03-31T16:15:34.748Z" }, + { url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180, upload-time = "2026-03-31T16:15:36.426Z" }, + { url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754, upload-time = "2026-03-31T16:15:38.049Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877, upload-time = "2026-03-31T16:15:39.484Z" }, + { url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361, upload-time = "2026-03-31T16:15:41.274Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521, upload-time = "2026-03-31T16:15:42.758Z" }, + { url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862, upload-time = "2026-03-31T16:15:44.341Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847, upload-time = "2026-03-31T16:15:46.368Z" }, + { url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637, upload-time = "2026-03-31T16:15:48.123Z" }, + { url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906, upload-time = "2026-03-31T16:15:49.626Z" }, + { url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722, upload-time = "2026-03-31T16:15:51.176Z" }, + { url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801, upload-time = "2026-03-31T16:15:52.939Z" }, + { url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460, upload-time = "2026-03-31T16:15:54.431Z" }, + { url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956, upload-time = "2026-03-31T16:15:56.081Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410, upload-time = "2026-03-31T16:15:57.54Z" }, + { url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338, upload-time = "2026-03-31T16:15:59.106Z" }, + { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" }, + { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" }, + { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" }, + { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" }, + { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" }, + { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" }, + { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" }, + { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" }, + { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" }, +] + +[[package]] +name = "overrides" +version = "7.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, + { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, + { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, + { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, + { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, + { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, + { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, + { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, + { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, + { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, + { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, + { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, + { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, + { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, + { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, +] + +[[package]] +name = "parso" +version = "0.8.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, +] + +[[package]] +name = "pdfminer-six" +version = "20251230" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "charset-normalizer" }, + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/9a/d79d8fa6d47a0338846bb558b39b9963b8eb2dfedec61867c138c1b17eeb/pdfminer_six-20251230.tar.gz", hash = "sha256:e8f68a14c57e00c2d7276d26519ea64be1b48f91db1cdc776faa80528ca06c1e", size = 8511285, upload-time = "2025-12-30T15:49:13.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/d7/b288ea32deb752a09aab73c75e1e7572ab2a2b56c3124a5d1eb24c62ceb3/pdfminer_six-20251230-py3-none-any.whl", hash = "sha256:9ff2e3466a7dfc6de6fd779478850b6b7c2d9e9405aa2a5869376a822771f485", size = 6591909, upload-time = "2025-12-30T15:49:10.76Z" }, +] + +[[package]] +name = "pdfplumber" +version = "0.11.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pdfminer-six" }, + { name = "pillow" }, + { name = "pypdfium2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/37/9ca3519e92a8434eb93be570b131476cc0a4e840bb39c62ddb7813a39d53/pdfplumber-0.11.9.tar.gz", hash = "sha256:481224b678b2bbdbf376e2c39bf914144eef7c3d301b4a28eebf0f7f6109d6dc", size = 102768, upload-time = "2026-01-05T08:10:29.072Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/c8/cdbc975f5b634e249cfa6597e37c50f3078412474f21c015e508bfbfe3c3/pdfplumber-0.11.9-py3-none-any.whl", hash = "sha256:33ec5580959ba524e9100138746e090879504c42955df1b8a997604dd326c443", size = 60045, upload-time = "2026-01-05T08:10:27.512Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, + { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, + { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, + { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, + { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, + { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "portalocker" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/f8/969e6f280201b40b31bcb62843c619f343dcc351dff83a5891530c9dd60e/portalocker-2.7.0.tar.gz", hash = "sha256:032e81d534a88ec1736d03f780ba073f047a06c478b06e2937486f334e955c51", size = 20183, upload-time = "2023-01-18T23:36:14.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/df/d4f711d168524f5aebd7fb30969eaa31e3048cf8979688cde3b08f6e5eb8/portalocker-2.7.0-py2.py3-none-any.whl", hash = "sha256:a07c5b4f3985c3cf4798369631fb7011adb498e2a46d8440efc75a8f29a0f983", size = 15502, upload-time = "2023-01-18T23:36:12.849Z" }, +] + +[[package]] +name = "posthog" +version = "5.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backoff" }, + { name = "distro" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/20/60ae67bb9d82f00427946218d49e2e7e80fb41c15dc5019482289ec9ce8d/posthog-5.4.0.tar.gz", hash = "sha256:701669261b8d07cdde0276e5bc096b87f9e200e3b9589c5ebff14df658c5893c", size = 88076, upload-time = "2025-06-20T23:19:23.485Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/98/e480cab9a08d1c09b1c59a93dade92c1bb7544826684ff2acbfd10fcfbd4/posthog-5.4.0-py3-none-any.whl", hash = "sha256:284dfa302f64353484420b52d4ad81ff5c2c2d1d607c4e2db602ac72761831bd", size = 105364, upload-time = "2025-06-20T23:19:22.001Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "protobuf" +version = "5.29.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/57/394a763c103e0edf87f0938dafcd918d53b4c011dfc5c8ae80f3b0452dbb/protobuf-5.29.6.tar.gz", hash = "sha256:da9ee6a5424b6b30fd5e45c5ea663aef540ca95f9ad99d1e887e819cdf9b8723", size = 425623, upload-time = "2026-02-04T22:54:40.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/88/9ee58ff7863c479d6f8346686d4636dd4c415b0cbeed7a6a7d0617639c2a/protobuf-5.29.6-cp310-abi3-win32.whl", hash = "sha256:62e8a3114992c7c647bce37dcc93647575fc52d50e48de30c6fcb28a6a291eb1", size = 423357, upload-time = "2026-02-04T22:54:25.805Z" }, + { url = "https://files.pythonhosted.org/packages/1c/66/2dc736a4d576847134fb6d80bd995c569b13cdc7b815d669050bf0ce2d2c/protobuf-5.29.6-cp310-abi3-win_amd64.whl", hash = "sha256:7e6ad413275be172f67fdee0f43484b6de5a904cc1c3ea9804cb6fe2ff366eda", size = 435175, upload-time = "2026-02-04T22:54:28.592Z" }, + { url = "https://files.pythonhosted.org/packages/06/db/49b05966fd208ae3f44dcd33837b6243b4915c57561d730a43f881f24dea/protobuf-5.29.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:b5a169e664b4057183a34bdc424540e86eea47560f3c123a0d64de4e137f9269", size = 418619, upload-time = "2026-02-04T22:54:30.266Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d7/48cbf6b0c3c39761e47a99cb483405f0fde2be22cf00d71ef316ce52b458/protobuf-5.29.6-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:a8866b2cff111f0f863c1b3b9e7572dc7eaea23a7fae27f6fc613304046483e6", size = 320284, upload-time = "2026-02-04T22:54:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dd/cadd6ec43069247d91f6345fa7a0d2858bef6af366dbd7ba8f05d2c77d3b/protobuf-5.29.6-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:e3387f44798ac1106af0233c04fb8abf543772ff241169946f698b3a9a3d3ab9", size = 320478, upload-time = "2026-02-04T22:54:32.909Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cb/e3065b447186cb70aa65acc70c86baf482d82bf75625bf5a2c4f6919c6a3/protobuf-5.29.6-py3-none-any.whl", hash = "sha256:6b9edb641441b2da9fa8f428760fc136a49cf97a52076010cf22a2ff73438a86", size = 173126, upload-time = "2026-02-04T22:54:39.462Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + +[[package]] +name = "pyarrow" +version = "23.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/41/8e6b6ef7e225d4ceead8459427a52afdc23379768f54dd3566014d7618c1/pyarrow-23.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb", size = 34302230, upload-time = "2026-02-16T10:09:03.859Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4a/1472c00392f521fea03ae93408bf445cc7bfa1ab81683faf9bc188e36629/pyarrow-23.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350", size = 35850050, upload-time = "2026-02-16T10:09:11.877Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b2/bd1f2f05ded56af7f54d702c8364c9c43cd6abb91b0e9933f3d77b4f4132/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd", size = 44491918, upload-time = "2026-02-16T10:09:18.144Z" }, + { url = "https://files.pythonhosted.org/packages/0b/62/96459ef5b67957eac38a90f541d1c28833d1b367f014a482cb63f3b7cd2d/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9", size = 47562811, upload-time = "2026-02-16T10:09:25.792Z" }, + { url = "https://files.pythonhosted.org/packages/7d/94/1170e235add1f5f45a954e26cd0e906e7e74e23392dcb560de471f7366ec/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701", size = 48183766, upload-time = "2026-02-16T10:09:34.645Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/39a42af4570377b99774cdb47f63ee6c7da7616bd55b3d5001aa18edfe4f/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78", size = 50607669, upload-time = "2026-02-16T10:09:44.153Z" }, + { url = "https://files.pythonhosted.org/packages/00/ca/db94101c187f3df742133ac837e93b1f269ebdac49427f8310ee40b6a58f/pyarrow-23.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919", size = 27527698, upload-time = "2026-02-16T10:09:50.263Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/4166bb5abbfe6f750fc60ad337c43ecf61340fa52ab386da6e8dbf9e63c4/pyarrow-23.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f", size = 34214575, upload-time = "2026-02-16T10:09:56.225Z" }, + { url = "https://files.pythonhosted.org/packages/e1/da/3f941e3734ac8088ea588b53e860baeddac8323ea40ce22e3d0baa865cc9/pyarrow-23.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7", size = 35832540, upload-time = "2026-02-16T10:10:03.428Z" }, + { url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" }, + { url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" }, + { url = "https://files.pythonhosted.org/packages/f6/70/1fdda42d65b28b078e93d75d371b2185a61da89dda4def8ba6ba41ebdeb4/pyarrow-23.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0", size = 27620678, upload-time = "2026-02-16T10:10:39.31Z" }, + { url = "https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8", size = 34210066, upload-time = "2026-02-16T10:10:45.487Z" }, + { url = "https://files.pythonhosted.org/packages/cb/4f/679fa7e84dadbaca7a65f7cdba8d6c83febbd93ca12fa4adf40ba3b6362b/pyarrow-23.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f", size = 35825526, upload-time = "2026-02-16T10:10:52.266Z" }, + { url = "https://files.pythonhosted.org/packages/f9/63/d2747d930882c9d661e9398eefc54f15696547b8983aaaf11d4a2e8b5426/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677", size = 44473279, upload-time = "2026-02-16T10:11:01.557Z" }, + { url = "https://files.pythonhosted.org/packages/b3/93/10a48b5e238de6d562a411af6467e71e7aedbc9b87f8d3a35f1560ae30fb/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2", size = 47585798, upload-time = "2026-02-16T10:11:09.401Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/476943001c54ef078dbf9542280e22741219a184a0632862bca4feccd666/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37", size = 48179446, upload-time = "2026-02-16T10:11:17.781Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b6/5dd0c47b335fcd8edba9bfab78ad961bd0fd55ebe53468cc393f45e0be60/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2", size = 50623972, upload-time = "2026-02-16T10:11:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/d5/09/a532297c9591a727d67760e2e756b83905dd89adb365a7f6e9c72578bcc1/pyarrow-23.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a", size = 27540749, upload-time = "2026-02-16T10:12:23.297Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8e/38749c4b1303e6ae76b3c80618f84861ae0c55dd3c2273842ea6f8258233/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1", size = 34471544, upload-time = "2026-02-16T10:11:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/a3/73/f237b2bc8c669212f842bcfd842b04fc8d936bfc9d471630569132dc920d/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500", size = 35949911, upload-time = "2026-02-16T10:11:39.813Z" }, + { url = "https://files.pythonhosted.org/packages/0c/86/b912195eee0903b5611bf596833def7d146ab2d301afeb4b722c57ffc966/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41", size = 44520337, upload-time = "2026-02-16T10:11:47.764Z" }, + { url = "https://files.pythonhosted.org/packages/69/c2/f2a717fb824f62d0be952ea724b4f6f9372a17eed6f704b5c9526f12f2f1/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07", size = 47548944, upload-time = "2026-02-16T10:11:56.607Z" }, + { url = "https://files.pythonhosted.org/packages/84/a7/90007d476b9f0dc308e3bc57b832d004f848fd6c0da601375d20d92d1519/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83", size = 48236269, upload-time = "2026-02-16T10:12:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3f/b16fab3e77709856eb6ac328ce35f57a6d4a18462c7ca5186ef31b45e0e0/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125", size = 50604794, upload-time = "2026-02-16T10:12:11.797Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a1/22df0620a9fac31d68397a75465c344e83c3dfe521f7612aea33e27ab6c0/pyarrow-23.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8", size = 27660642, upload-time = "2026-02-16T10:12:17.746Z" }, + { url = "https://files.pythonhosted.org/packages/8d/1b/6da9a89583ce7b23ac611f183ae4843cd3a6cf54f079549b0e8c14031e73/pyarrow-23.0.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca", size = 34238755, upload-time = "2026-02-16T10:12:32.819Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b5/d58a241fbe324dbaeb8df07be6af8752c846192d78d2272e551098f74e88/pyarrow-23.0.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1", size = 35847826, upload-time = "2026-02-16T10:12:38.949Z" }, + { url = "https://files.pythonhosted.org/packages/54/a5/8cbc83f04aba433ca7b331b38f39e000efd9f0c7ce47128670e737542996/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb", size = 44536859, upload-time = "2026-02-16T10:12:45.467Z" }, + { url = "https://files.pythonhosted.org/packages/36/2e/c0f017c405fcdc252dbccafbe05e36b0d0eb1ea9a958f081e01c6972927f/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1", size = 47614443, upload-time = "2026-02-16T10:12:55.525Z" }, + { url = "https://files.pythonhosted.org/packages/af/6b/2314a78057912f5627afa13ba43809d9d653e6630859618b0fd81a4e0759/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886", size = 48232991, upload-time = "2026-02-16T10:13:04.729Z" }, + { url = "https://files.pythonhosted.org/packages/40/f2/1bcb1d3be3460832ef3370d621142216e15a2c7c62602a4ea19ec240dd64/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f", size = 50645077, upload-time = "2026-02-16T10:13:14.147Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3f/b1da7b61cd66566a4d4c8383d376c606d1c34a906c3f1cb35c479f59d1aa/pyarrow-23.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5", size = 28234271, upload-time = "2026-02-16T10:14:09.397Z" }, + { url = "https://files.pythonhosted.org/packages/b5/78/07f67434e910a0f7323269be7bfbf58699bd0c1d080b18a1ab49ba943fe8/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d", size = 34488692, upload-time = "2026-02-16T10:13:21.541Z" }, + { url = "https://files.pythonhosted.org/packages/50/76/34cf7ae93ece1f740a04910d9f7e80ba166b9b4ab9596a953e9e62b90fe1/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f", size = 35964383, upload-time = "2026-02-16T10:13:28.63Z" }, + { url = "https://files.pythonhosted.org/packages/46/90/459b827238936d4244214be7c684e1b366a63f8c78c380807ae25ed92199/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814", size = 44538119, upload-time = "2026-02-16T10:13:35.506Z" }, + { url = "https://files.pythonhosted.org/packages/28/a1/93a71ae5881e99d1f9de1d4554a87be37da11cd6b152239fb5bd924fdc64/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d", size = 47571199, upload-time = "2026-02-16T10:13:42.504Z" }, + { url = "https://files.pythonhosted.org/packages/88/a3/d2c462d4ef313521eaf2eff04d204ac60775263f1fb08c374b543f79f610/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7", size = 48259435, upload-time = "2026-02-16T10:13:49.226Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f1/11a544b8c3d38a759eb3fbb022039117fd633e9a7b19e4841cc3da091915/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690", size = 50629149, upload-time = "2026-02-16T10:13:57.238Z" }, + { url = "https://files.pythonhosted.org/packages/50/f2/c0e76a0b451ffdf0cf788932e182758eb7558953f4f27f1aff8e2518b653/pyarrow-23.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce", size = 28365807, upload-time = "2026-02-16T10:14:03.892Z" }, +] + +[[package]] +name = "pybase64" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/63/21e981e9d3f1f123e0b0ee2130112b1956cad9752309f574862c7ae77c08/pybase64-1.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:70b0d4a4d54e216ce42c2655315378b8903933ecfa32fced453989a92b4317b2", size = 38237, upload-time = "2025-12-06T13:22:52.159Z" }, + { url = "https://files.pythonhosted.org/packages/92/fb/3f448e139516404d2a3963915cc10dc9dde7d3a67de4edba2f827adfef17/pybase64-1.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8127f110cdee7a70e576c5c9c1d4e17e92e76c191869085efbc50419f4ae3c72", size = 31673, upload-time = "2025-12-06T13:22:53.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/bb06a5b9885e7d853ac1e801c4d8abfdb4c8506deee33e53d55aa6690e67/pybase64-1.4.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f9ef0388878bc15a084bd9bf73ec1b2b4ee513d11009b1506375e10a7aae5032", size = 68331, upload-time = "2025-12-06T13:22:54.197Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/8d60b9ec5e658185fc2ee3333e01a6e30d717cf677b24f47cbb3a859d13c/pybase64-1.4.3-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95a57cccf106352a72ed8bc8198f6820b16cc7d55aa3867a16dea7011ae7c218", size = 71370, upload-time = "2025-12-06T13:22:55.517Z" }, + { url = "https://files.pythonhosted.org/packages/ac/29/a3e5c1667cc8c38d025a4636855de0fc117fc62e2afeb033a3c6f12c6a22/pybase64-1.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cd1c47dfceb9c7bd3de210fb4e65904053ed2d7c9dce6d107f041ff6fbd7e21", size = 59834, upload-time = "2025-12-06T13:22:56.682Z" }, + { url = "https://files.pythonhosted.org/packages/a9/00/8ffcf9810bd23f3984698be161cf7edba656fd639b818039a7be1d6405d4/pybase64-1.4.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9fe9922698f3e2f72874b26890d53a051c431d942701bb3a37aae94da0b12107", size = 56652, upload-time = "2025-12-06T13:22:57.724Z" }, + { url = "https://files.pythonhosted.org/packages/81/62/379e347797cdea4ab686375945bc77ad8d039c688c0d4d0cfb09d247beb9/pybase64-1.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:af5f4bd29c86b59bb4375e0491d16ec8a67548fa99c54763aaedaf0b4b5a6632", size = 59382, upload-time = "2025-12-06T13:22:58.758Z" }, + { url = "https://files.pythonhosted.org/packages/c6/f2/9338ffe2f487086f26a2c8ca175acb3baa86fce0a756ff5670a0822bb877/pybase64-1.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c302f6ca7465262908131411226e02100f488f531bb5e64cb901aa3f439bccd9", size = 59990, upload-time = "2025-12-06T13:23:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/85a6142b65b4df8625b337727aa81dc199642de3d09677804141df6ee312/pybase64-1.4.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2f3f439fa4d7fde164ebbbb41968db7d66b064450ab6017c6c95cef0afa2b349", size = 54923, upload-time = "2025-12-06T13:23:02.369Z" }, + { url = "https://files.pythonhosted.org/packages/ac/00/e40215d25624012bf5b7416ca37f168cb75f6dd15acdb91ea1f2ea4dc4e7/pybase64-1.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a23c6866551043f8b681a5e1e0d59469148b2920a3b4fc42b1275f25ea4217a", size = 58664, upload-time = "2025-12-06T13:23:03.378Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/d7e19a63e795c13837f2356268d95dc79d1180e756f57ced742a1e52fdeb/pybase64-1.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:56e6526f8565642abc5f84338cc131ce298a8ccab696b19bdf76fa6d7dc592ef", size = 52338, upload-time = "2025-12-06T13:23:04.458Z" }, + { url = "https://files.pythonhosted.org/packages/f2/32/3c746d7a310b69bdd9df77ffc85c41b80bce00a774717596f869b0d4a20e/pybase64-1.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6a792a8b9d866ffa413c9687d9b611553203753987a3a582d68cbc51cf23da45", size = 68993, upload-time = "2025-12-06T13:23:05.526Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b3/63cec68f9d6f6e4c0b438d14e5f1ef536a5fe63ce14b70733ac5e31d7ab8/pybase64-1.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:62ad29a5026bb22cfcd1ca484ec34b0a5ced56ddba38ceecd9359b2818c9c4f9", size = 58055, upload-time = "2025-12-06T13:23:06.931Z" }, + { url = "https://files.pythonhosted.org/packages/d5/cb/7acf7c3c06f9692093c07f109668725dc37fb9a3df0fa912b50add645195/pybase64-1.4.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11b9d1d2d32ec358c02214363b8fc3651f6be7dd84d880ecd597a6206a80e121", size = 54430, upload-time = "2025-12-06T13:23:07.936Z" }, + { url = "https://files.pythonhosted.org/packages/33/39/4eb33ff35d173bfff4002e184ce8907f5d0a42d958d61cd9058ef3570179/pybase64-1.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0aebaa7f238caa0a0d373616016e2040c6c879ebce3ba7ab3c59029920f13640", size = 56272, upload-time = "2025-12-06T13:23:09.253Z" }, + { url = "https://files.pythonhosted.org/packages/19/97/a76d65c375a254e65b730c6f56bf528feca91305da32eceab8bcc08591e6/pybase64-1.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e504682b20c63c2b0c000e5f98a80ea867f8d97642e042a5a39818e44ba4d599", size = 70904, upload-time = "2025-12-06T13:23:10.336Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/8338b6d3da3c265002839e92af0a80d6db88385c313c73f103dfb800c857/pybase64-1.4.3-cp311-cp311-win32.whl", hash = "sha256:e9a8b81984e3c6fb1db9e1614341b0a2d98c0033d693d90c726677db1ffa3a4c", size = 33639, upload-time = "2025-12-06T13:23:11.9Z" }, + { url = "https://files.pythonhosted.org/packages/39/dc/32efdf2f5927e5449cc341c266a1bbc5fecd5319a8807d9c5405f76e6d02/pybase64-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:a90a8fa16a901fabf20de824d7acce07586e6127dc2333f1de05f73b1f848319", size = 35797, upload-time = "2025-12-06T13:23:13.174Z" }, + { url = "https://files.pythonhosted.org/packages/da/59/eda4f9cb0cbce5a45f0cd06131e710674f8123a4d570772c5b9694f88559/pybase64-1.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:61d87de5bc94d143622e94390ec3e11b9c1d4644fe9be3a81068ab0f91056f59", size = 31160, upload-time = "2025-12-06T13:23:15.696Z" }, + { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, + { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, + { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, + { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, + { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, + { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, + { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, + { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, + { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, + { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, + { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, + { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, + { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, + { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, + { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, + { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, + { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, + { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, + { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, + { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, + { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, + { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, + { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, + { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, + { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, + { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, + { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, + { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, + { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, + { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, + { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, + { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, + { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, + { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, + { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, + { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, + { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, + { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, + { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, + { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, + { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, + { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, + { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, + { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, + { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, + { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, + { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/545fd4935a0e1ddd7147f557bf8157c73eecec9cffd523382fa7af2557de/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:d27c1dfdb0c59a5e758e7a98bd78eaca5983c22f4a811a36f4f980d245df4611", size = 38393, upload-time = "2025-12-06T13:26:19.535Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ca/ae7a96be9ddc96030d4e9dffc43635d4e136b12058b387fd47eb8301b60f/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0f1a0c51d6f159511e3431b73c25db31095ee36c394e26a4349e067c62f434e5", size = 32109, upload-time = "2025-12-06T13:26:20.72Z" }, + { url = "https://files.pythonhosted.org/packages/bf/44/d4b7adc7bf4fd5b52d8d099121760c450a52c390223806b873f0b6a2d551/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a492518f3078a4e3faaef310697d21df9c6bc71908cebc8c2f6fbfa16d7d6b1f", size = 43227, upload-time = "2025-12-06T13:26:21.845Z" }, + { url = "https://files.pythonhosted.org/packages/08/86/2ba2d8734ef7939debeb52cf9952e457ba7aa226cae5c0e6dd631f9b851f/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae1a0f47784fd16df90d8acc32011c8d5fcdd9ab392c9ec49543e5f6a9c43a4", size = 35804, upload-time = "2025-12-06T13:26:23.149Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5b/19c725dc3aaa6281f2ce3ea4c1628d154a40dd99657d1381995f8096768b/pybase64-1.4.3-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:03cea70676ffbd39a1ab7930a2d24c625b416cacc9d401599b1d29415a43ab6a", size = 35880, upload-time = "2025-12-06T13:26:24.663Z" }, + { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, + { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, + { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/160dded493c00d3376d4ad0f38a2119c5345de4a6693419ad39c3565959b/pybase64-1.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:277de6e03cc9090fb359365c686a2a3036d23aee6cd20d45d22b8c89d1247f17", size = 37939, upload-time = "2025-12-06T13:26:41.014Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/a0f10be8d648d6f8f26e560d6e6955efa7df0ff1e009155717454d76f601/pybase64-1.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab1dd8b1ed2d1d750260ed58ab40defaa5ba83f76a30e18b9ebd5646f6247ae5", size = 31466, upload-time = "2025-12-06T13:26:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/22/832a2f9e76cdf39b52e01e40d8feeb6a04cf105494f2c3e3126d0149717f/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:bd4d2293de9fd212e294c136cec85892460b17d24e8c18a6ba18750928037750", size = 40681, upload-time = "2025-12-06T13:26:43.782Z" }, + { url = "https://files.pythonhosted.org/packages/12/d7/6610f34a8972415fab3bb4704c174a1cc477bffbc3c36e526428d0f3957d/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af6d0d3a691911cc4c9a625f3ddcd3af720738c21be3d5c72de05629139d393", size = 41294, upload-time = "2025-12-06T13:26:44.936Z" }, + { url = "https://files.pythonhosted.org/packages/64/25/ed24400948a6c974ab1374a233cb7e8af0a5373cea0dd8a944627d17c34a/pybase64-1.4.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfc8c49a28322d82242088378f8542ce97459866ba73150b062a7073e82629d", size = 35447, upload-time = "2025-12-06T13:26:46.098Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2b/e18ee7c5ee508a82897f021c1981533eca2940b5f072fc6ed0906c03a7a7/pybase64-1.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:debf737e09b8bf832ba86f5ecc3d3dbd0e3021d6cd86ba4abe962d6a5a77adb3", size = 36134, upload-time = "2025-12-06T13:26:47.35Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.11.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/54/ecab642b3bed45f7d5f59b38443dcb36ef50f85af192e6ece103dbfe9587/pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423", size = 788494, upload-time = "2025-10-04T10:40:41.338Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/1f/73c53fcbfb0b5a78f91176df41945ca466e71e9d9d836e5c522abda39ee7/pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a", size = 444823, upload-time = "2025-10-04T10:40:39.055Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, +] + +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240, upload-time = "2024-05-10T15:36:21.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403, upload-time = "2024-05-10T15:36:17.36Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pypdfium2" +version = "5.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/01/be763b9081c7eb823196e7d13d9c145bf75ac43f3c1466de81c21c24b381/pypdfium2-5.6.0.tar.gz", hash = "sha256:bcb9368acfe3547054698abbdae68ba0cbd2d3bda8e8ee437e061deef061976d", size = 270714, upload-time = "2026-03-08T01:05:06.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/b1/129ed0177521a93a892f8a6a215dd3260093e30e77ef7035004bb8af7b6c/pypdfium2-5.6.0-py3-none-android_23_arm64_v8a.whl", hash = "sha256:fb7858c9707708555b4a719b5548a6e7f5d26bc82aef55ae4eb085d7a2190b11", size = 3346059, upload-time = "2026-03-08T01:04:21.37Z" }, + { url = "https://files.pythonhosted.org/packages/86/34/cbdece6886012180a7f2c7b2c360c415cf5e1f83f1973d2c9201dae3506a/pypdfium2-5.6.0-py3-none-android_23_armeabi_v7a.whl", hash = "sha256:6a7e1f4597317786f994bfb947eef480e53933f804a990193ab89eef8243f805", size = 2804418, upload-time = "2026-03-08T01:04:23.384Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f6/9f9e190fe0e5a6b86b82f83bd8b5d3490348766062381140ca5cad8e00b1/pypdfium2-5.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e468c38997573f0e86f03273c2c1fbdea999de52ba43fee96acaa2f6b2ad35f7", size = 3412541, upload-time = "2026-03-08T01:04:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8d/e57492cb2228ba56ed57de1ff044c8ac114b46905f8b1445c33299ba0488/pypdfium2-5.6.0-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:ad3abddc5805424f962e383253ccad6a0d1d2ebd86afa9a9e1b9ca659773cd0d", size = 3592320, upload-time = "2026-03-08T01:04:27.509Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8a/8ab82e33e9c551494cbe1526ea250ca8cc4e9e98d6a4fc6b6f8d959aa1d1/pypdfium2-5.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b5eb9eae5c45076395454522ca26add72ba8bd1fe473e1e4721aa58521470c", size = 3596450, upload-time = "2026-03-08T01:04:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b5/602a792282312ccb158cc63849528079d94b0a11efdc61f2a359edfb41e9/pypdfium2-5.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:258624da8ef45cdc426e11b33e9d83f9fb723c1c201c6e0f4ab5a85966c6b876", size = 3325442, upload-time = "2026-03-08T01:04:30.886Z" }, + { url = "https://files.pythonhosted.org/packages/81/1f/9e48ec05ed8d19d736c2d1f23c1bd0f20673f02ef846a2576c69e237f15d/pypdfium2-5.6.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9367451c8a00931d6612db0822525a18c06f649d562cd323a719e46ac19c9bb", size = 3727434, upload-time = "2026-03-08T01:04:33.619Z" }, + { url = "https://files.pythonhosted.org/packages/33/90/0efd020928b4edbd65f4f3c2af0c84e20b43a3ada8fa6d04f999a97afe7a/pypdfium2-5.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a757869f891eac1cc1372e38a4aa01adac8abc8fe2a8a4e2ebf50595e3bf5937", size = 4139029, upload-time = "2026-03-08T01:04:36.08Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/a640b288a48dab1752281dd9b72c0679fccea107874e80a65a606b00efa9/pypdfium2-5.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:515be355222cc57ae9e62cd5c7c350b8e0c863efc539f80c7d75e2811ba45cb6", size = 3646387, upload-time = "2026-03-08T01:04:38.151Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/a344c19c01021eeb5d830c102e4fc9b1602f19c04aa7d11abbe2d188fd8e/pypdfium2-5.6.0-py3-none-manylinux_2_27_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1c4753c7caf7d004211d7f57a21f10d127f5e0e5510a14d24bc073e7220a3ea", size = 3097212, upload-time = "2026-03-08T01:04:40.776Z" }, + { url = "https://files.pythonhosted.org/packages/50/96/e48e13789ace22aeb9b7510904a1b1493ec588196e11bbacc122da330b3d/pypdfium2-5.6.0-py3-none-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c49729090281fdd85775fb8912c10bd19e99178efaa98f145ab06e7ce68554d2", size = 2965026, upload-time = "2026-03-08T01:04:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/cb/06/3100e44d4935f73af8f5d633d3bd40f0d36d606027085a0ef1f0566a6320/pypdfium2-5.6.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a4a1749a8d4afd62924a8d95cfa4f2e26fc32957ce34ac3b674be6f127ed252e", size = 4131431, upload-time = "2026-03-08T01:04:44.982Z" }, + { url = "https://files.pythonhosted.org/packages/64/ef/d8df63569ce9a66c8496057782eb8af78e0d28667922d62ec958434e3d4b/pypdfium2-5.6.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:36469ebd0fdffb7130ce45ed9c44f8232d91571c89eb851bd1633c64b6f6114f", size = 3747469, upload-time = "2026-03-08T01:04:46.702Z" }, + { url = "https://files.pythonhosted.org/packages/a6/47/fd2c6a67a49fade1acd719fbd11f7c375e7219912923ef2de0ea0ac1544e/pypdfium2-5.6.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da900df09be3cf546b637a127a7b6428fb22d705951d731269e25fd3adef457", size = 4337578, upload-time = "2026-03-08T01:04:49.007Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f5/836c83e54b01e09478c4d6bf4912651d6053c932250fcee953f5c72d8e4a/pypdfium2-5.6.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:45fccd5622233c5ec91a885770ae7dd4004d4320ac05a4ad8fa03a66dea40244", size = 4376104, upload-time = "2026-03-08T01:04:51.04Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7f/b940b6a1664daf8f9bad87c6c99b84effa3611615b8708d10392dc33036c/pypdfium2-5.6.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:282dc030e767cd61bd0299f9d581052b91188e2b87561489057a8e7963e7e0cb", size = 3929824, upload-time = "2026-03-08T01:04:53.544Z" }, + { url = "https://files.pythonhosted.org/packages/88/79/00267d92a6a58c229e364d474f5698efe446e0c7f4f152f58d0138715e99/pypdfium2-5.6.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:a1c1dfe950382c76a7bba1ba160ec5e40df8dd26b04a1124ae268fda55bc4cbe", size = 4270201, upload-time = "2026-03-08T01:04:55.81Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ab/b127f38aba41746bdf9ace15ba08411d7ef6ecba1326d529ba414eb1ed50/pypdfium2-5.6.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:43b0341ca6feb6c92e4b7a9eb4813e5466f5f5e8b6baeb14df0a94d5f312c00b", size = 4180793, upload-time = "2026-03-08T01:04:57.961Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8c/a01c8e4302448b614d25a85c08298b0d3e9dfbdac5bd1b2f32c9b02e83d9/pypdfium2-5.6.0-py3-none-win32.whl", hash = "sha256:9dfcd4ff49a2b9260d00e38539ab28190d59e785e83030b30ffaf7a29c42155d", size = 3596753, upload-time = "2026-03-08T01:05:00.566Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5f/2d871adf46761bb002a62686545da6348afe838d19af03df65d1ece786a2/pypdfium2-5.6.0-py3-none-win_amd64.whl", hash = "sha256:c6bc8dd63d0568f4b592f3e03de756afafc0e44aa1fe8878cc4aba1b11ae7374", size = 3716526, upload-time = "2026-03-08T01:05:02.433Z" }, + { url = "https://files.pythonhosted.org/packages/3a/80/0d9b162098597fbe3ac2b269b1682c0c3e8db9ba87679603fdd9b19afaa6/pypdfium2-5.6.0-py3-none-win_arm64.whl", hash = "sha256:5538417b199bdcb3207370c88df61f2ba3dac7a3253f82e1aa2708e6376b6f90", size = 3515049, upload-time = "2026-03-08T01:05:04.587Z" }, +] + +[[package]] +name = "pypika" +version = "0.51.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/78/cbaebba88e05e2dcda13ca203131b38d3640219f20ebb49676d26714861b/pypika-0.51.1.tar.gz", hash = "sha256:c30c7c1048fbf056fd3920c5a2b88b0c29dd190a9b2bee971fd17e4abe4d0ebe", size = 80919, upload-time = "2026-02-04T11:27:48.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/83/c77dfeed04022e8930b08eedca2b6e5efed256ab3321396fde90066efb65/pypika-0.51.1-py2.py3-none-any.whl", hash = "sha256:77985b4d7ce71b9905255bf12468cf598349e98837c037541cfc240e528aec46", size = 60585, upload-time = "2026-02-04T11:27:46.251Z" }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328, upload-time = "2025-09-08T23:07:45.946Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803, upload-time = "2025-09-08T23:07:47.551Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836, upload-time = "2025-09-08T23:07:49.436Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038, upload-time = "2025-09-08T23:07:51.234Z" }, + { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531, upload-time = "2025-09-08T23:07:52.795Z" }, + { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786, upload-time = "2025-09-08T23:07:55.047Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220, upload-time = "2025-09-08T23:07:57.172Z" }, + { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155, upload-time = "2025-09-08T23:07:59.05Z" }, + { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428, upload-time = "2025-09-08T23:08:00.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497, upload-time = "2025-09-08T23:08:02.15Z" }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" }, + { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265, upload-time = "2025-09-08T23:09:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208, upload-time = "2025-09-08T23:09:51.073Z" }, + { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747, upload-time = "2025-09-08T23:09:52.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371, upload-time = "2025-09-08T23:09:54.563Z" }, + { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "regex" +version = "2026.1.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811, upload-time = "2026-01-14T23:18:02.775Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/c9/0c80c96eab96948363d270143138d671d5731c3a692b417629bf3492a9d6/regex-2026.1.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ae6020fb311f68d753b7efa9d4b9a5d47a5d6466ea0d5e3b5a471a960ea6e4a", size = 488168, upload-time = "2026-01-14T23:14:16.129Z" }, + { url = "https://files.pythonhosted.org/packages/17/f0/271c92f5389a552494c429e5cc38d76d1322eb142fb5db3c8ccc47751468/regex-2026.1.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eddf73f41225942c1f994914742afa53dc0d01a6e20fe14b878a1b1edc74151f", size = 290636, upload-time = "2026-01-14T23:14:17.715Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f9/5f1fd077d106ca5655a0f9ff8f25a1ab55b92128b5713a91ed7134ff688e/regex-2026.1.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e8cd52557603f5c66a548f69421310886b28b7066853089e1a71ee710e1cdc1", size = 288496, upload-time = "2026-01-14T23:14:19.326Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e1/8f43b03a4968c748858ec77f746c286d81f896c2e437ccf050ebc5d3128c/regex-2026.1.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5170907244b14303edc5978f522f16c974f32d3aa92109fabc2af52411c9433b", size = 793503, upload-time = "2026-01-14T23:14:20.922Z" }, + { url = "https://files.pythonhosted.org/packages/8d/4e/a39a5e8edc5377a46a7c875c2f9a626ed3338cb3bb06931be461c3e1a34a/regex-2026.1.15-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2748c1ec0663580b4510bd89941a31560b4b439a0b428b49472a3d9944d11cd8", size = 860535, upload-time = "2026-01-14T23:14:22.405Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1c/9dce667a32a9477f7a2869c1c767dc00727284a9fa3ff5c09a5c6c03575e/regex-2026.1.15-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2f2775843ca49360508d080eaa87f94fa248e2c946bbcd963bb3aae14f333413", size = 907225, upload-time = "2026-01-14T23:14:23.897Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3c/87ca0a02736d16b6262921425e84b48984e77d8e4e572c9072ce96e66c30/regex-2026.1.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9ea2604370efc9a174c1b5dcc81784fb040044232150f7f33756049edfc9026", size = 800526, upload-time = "2026-01-14T23:14:26.039Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/647d5715aeea7c87bdcbd2f578f47b415f55c24e361e639fe8c0cc88878f/regex-2026.1.15-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dcd31594264029b57bf16f37fd7248a70b3b764ed9e0839a8f271b2d22c0785", size = 773446, upload-time = "2026-01-14T23:14:28.109Z" }, + { url = "https://files.pythonhosted.org/packages/af/89/bf22cac25cb4ba0fe6bff52ebedbb65b77a179052a9d6037136ae93f42f4/regex-2026.1.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c08c1f3e34338256732bd6938747daa3c0d5b251e04b6e43b5813e94d503076e", size = 783051, upload-time = "2026-01-14T23:14:29.929Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f4/6ed03e71dca6348a5188363a34f5e26ffd5db1404780288ff0d79513bce4/regex-2026.1.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e43a55f378df1e7a4fa3547c88d9a5a9b7113f653a66821bcea4718fe6c58763", size = 854485, upload-time = "2026-01-14T23:14:31.366Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/8e8560bd78caded8eb137e3e47612430a05b9a772caf60876435192d670a/regex-2026.1.15-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f82110ab962a541737bd0ce87978d4c658f06e7591ba899192e2712a517badbb", size = 762195, upload-time = "2026-01-14T23:14:32.802Z" }, + { url = "https://files.pythonhosted.org/packages/38/6b/61fc710f9aa8dfcd764fe27d37edfaa023b1a23305a0d84fccd5adb346ea/regex-2026.1.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:27618391db7bdaf87ac6c92b31e8f0dfb83a9de0075855152b720140bda177a2", size = 845986, upload-time = "2026-01-14T23:14:34.898Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2e/fbee4cb93f9d686901a7ca8d94285b80405e8c34fe4107f63ffcbfb56379/regex-2026.1.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bfb0d6be01fbae8d6655c8ca21b3b72458606c4aec9bbc932db758d47aba6db1", size = 788992, upload-time = "2026-01-14T23:14:37.116Z" }, + { url = "https://files.pythonhosted.org/packages/ed/14/3076348f3f586de64b1ab75a3fbabdaab7684af7f308ad43be7ef1849e55/regex-2026.1.15-cp311-cp311-win32.whl", hash = "sha256:b10e42a6de0e32559a92f2f8dc908478cc0fa02838d7dbe764c44dca3fa13569", size = 265893, upload-time = "2026-01-14T23:14:38.426Z" }, + { url = "https://files.pythonhosted.org/packages/0f/19/772cf8b5fc803f5c89ba85d8b1870a1ca580dc482aa030383a9289c82e44/regex-2026.1.15-cp311-cp311-win_amd64.whl", hash = "sha256:e9bf3f0bbdb56633c07d7116ae60a576f846efdd86a8848f8d62b749e1209ca7", size = 277840, upload-time = "2026-01-14T23:14:39.785Z" }, + { url = "https://files.pythonhosted.org/packages/78/84/d05f61142709474da3c0853222d91086d3e1372bcdab516c6fd8d80f3297/regex-2026.1.15-cp311-cp311-win_arm64.whl", hash = "sha256:41aef6f953283291c4e4e6850607bd71502be67779586a61472beacb315c97ec", size = 270374, upload-time = "2026-01-14T23:14:41.592Z" }, + { url = "https://files.pythonhosted.org/packages/92/81/10d8cf43c807d0326efe874c1b79f22bfb0fb226027b0b19ebc26d301408/regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1", size = 489398, upload-time = "2026-01-14T23:14:43.741Z" }, + { url = "https://files.pythonhosted.org/packages/90/b0/7c2a74e74ef2a7c32de724658a69a862880e3e4155cba992ba04d1c70400/regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681", size = 291339, upload-time = "2026-01-14T23:14:45.183Z" }, + { url = "https://files.pythonhosted.org/packages/19/4d/16d0773d0c818417f4cc20aa0da90064b966d22cd62a8c46765b5bd2d643/regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f", size = 289003, upload-time = "2026-01-14T23:14:47.25Z" }, + { url = "https://files.pythonhosted.org/packages/c6/e4/1fc4599450c9f0863d9406e944592d968b8d6dfd0d552a7d569e43bceada/regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa", size = 798656, upload-time = "2026-01-14T23:14:48.77Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e6/59650d73a73fa8a60b3a590545bfcf1172b4384a7df2e7fe7b9aab4e2da9/regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804", size = 864252, upload-time = "2026-01-14T23:14:50.528Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ab/1d0f4d50a1638849a97d731364c9a80fa304fec46325e48330c170ee8e80/regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c", size = 912268, upload-time = "2026-01-14T23:14:52.952Z" }, + { url = "https://files.pythonhosted.org/packages/dd/df/0d722c030c82faa1d331d1921ee268a4e8fb55ca8b9042c9341c352f17fa/regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5", size = 803589, upload-time = "2026-01-14T23:14:55.182Z" }, + { url = "https://files.pythonhosted.org/packages/66/23/33289beba7ccb8b805c6610a8913d0131f834928afc555b241caabd422a9/regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3", size = 775700, upload-time = "2026-01-14T23:14:56.707Z" }, + { url = "https://files.pythonhosted.org/packages/e7/65/bf3a42fa6897a0d3afa81acb25c42f4b71c274f698ceabd75523259f6688/regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb", size = 787928, upload-time = "2026-01-14T23:14:58.312Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f5/13bf65864fc314f68cdd6d8ca94adcab064d4d39dbd0b10fef29a9da48fc/regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410", size = 858607, upload-time = "2026-01-14T23:15:00.657Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/040e589834d7a439ee43fb0e1e902bc81bd58a5ba81acffe586bb3321d35/regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4", size = 763729, upload-time = "2026-01-14T23:15:02.248Z" }, + { url = "https://files.pythonhosted.org/packages/9b/84/6921e8129687a427edf25a34a5594b588b6d88f491320b9de5b6339a4fcb/regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d", size = 850697, upload-time = "2026-01-14T23:15:03.878Z" }, + { url = "https://files.pythonhosted.org/packages/8a/87/3d06143d4b128f4229158f2de5de6c8f2485170c7221e61bf381313314b2/regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22", size = 789849, upload-time = "2026-01-14T23:15:06.102Z" }, + { url = "https://files.pythonhosted.org/packages/77/69/c50a63842b6bd48850ebc7ab22d46e7a2a32d824ad6c605b218441814639/regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913", size = 266279, upload-time = "2026-01-14T23:15:07.678Z" }, + { url = "https://files.pythonhosted.org/packages/f2/36/39d0b29d087e2b11fd8191e15e81cce1b635fcc845297c67f11d0d19274d/regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a", size = 277166, upload-time = "2026-01-14T23:15:09.257Z" }, + { url = "https://files.pythonhosted.org/packages/28/32/5b8e476a12262748851fa8ab1b0be540360692325975b094e594dfebbb52/regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056", size = 270415, upload-time = "2026-01-14T23:15:10.743Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164, upload-time = "2026-01-14T23:15:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218, upload-time = "2026-01-14T23:15:15.647Z" }, + { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895, upload-time = "2026-01-14T23:15:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680, upload-time = "2026-01-14T23:15:19.342Z" }, + { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210, upload-time = "2026-01-14T23:15:22.005Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358, upload-time = "2026-01-14T23:15:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583, upload-time = "2026-01-14T23:15:26.526Z" }, + { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782, upload-time = "2026-01-14T23:15:29.352Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978, upload-time = "2026-01-14T23:15:31.295Z" }, + { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550, upload-time = "2026-01-14T23:15:33.558Z" }, + { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747, upload-time = "2026-01-14T23:15:35.206Z" }, + { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615, upload-time = "2026-01-14T23:15:37.523Z" }, + { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951, upload-time = "2026-01-14T23:15:39.582Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275, upload-time = "2026-01-14T23:15:42.108Z" }, + { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145, upload-time = "2026-01-14T23:15:44.244Z" }, + { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411, upload-time = "2026-01-14T23:15:45.858Z" }, + { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068, upload-time = "2026-01-14T23:15:47.616Z" }, + { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756, upload-time = "2026-01-14T23:15:49.307Z" }, + { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114, upload-time = "2026-01-14T23:15:51.484Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524, upload-time = "2026-01-14T23:15:53.102Z" }, + { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455, upload-time = "2026-01-14T23:15:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007, upload-time = "2026-01-14T23:15:57.041Z" }, + { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794, upload-time = "2026-01-14T23:15:58.916Z" }, + { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159, upload-time = "2026-01-14T23:16:00.817Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558, upload-time = "2026-01-14T23:16:03.267Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427, upload-time = "2026-01-14T23:16:05.646Z" }, + { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939, upload-time = "2026-01-14T23:16:07.542Z" }, + { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753, upload-time = "2026-01-14T23:16:10.395Z" }, + { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559, upload-time = "2026-01-14T23:16:12.347Z" }, + { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879, upload-time = "2026-01-14T23:16:14.049Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317, upload-time = "2026-01-14T23:16:15.718Z" }, + { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551, upload-time = "2026-01-14T23:16:17.533Z" }, + { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170, upload-time = "2026-01-14T23:16:19.835Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146, upload-time = "2026-01-14T23:16:21.541Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986, upload-time = "2026-01-14T23:16:23.381Z" }, + { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098, upload-time = "2026-01-14T23:16:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980, upload-time = "2026-01-14T23:16:27.297Z" }, + { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607, upload-time = "2026-01-14T23:16:29.235Z" }, + { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358, upload-time = "2026-01-14T23:16:31.369Z" }, + { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833, upload-time = "2026-01-14T23:16:33.141Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045, upload-time = "2026-01-14T23:16:35.005Z" }, + { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374, upload-time = "2026-01-14T23:16:36.919Z" }, + { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940, upload-time = "2026-01-14T23:16:38.685Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112, upload-time = "2026-01-14T23:16:40.646Z" }, + { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586, upload-time = "2026-01-14T23:16:42.799Z" }, + { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691, upload-time = "2026-01-14T23:16:44.671Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422, upload-time = "2026-01-14T23:16:46.607Z" }, + { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467, upload-time = "2026-01-14T23:16:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073, upload-time = "2026-01-14T23:16:51.154Z" }, + { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757, upload-time = "2026-01-14T23:16:53.08Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122, upload-time = "2026-01-14T23:16:55.383Z" }, + { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761, upload-time = "2026-01-14T23:16:57.251Z" }, + { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538, upload-time = "2026-01-14T23:16:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066, upload-time = "2026-01-14T23:17:01.607Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938, upload-time = "2026-01-14T23:17:04.366Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314, upload-time = "2026-01-14T23:17:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652, upload-time = "2026-01-14T23:17:08.521Z" }, + { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550, upload-time = "2026-01-14T23:17:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981, upload-time = "2026-01-14T23:17:12.871Z" }, + { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780, upload-time = "2026-01-14T23:17:14.837Z" }, + { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778, upload-time = "2026-01-14T23:17:17.369Z" }, + { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, + { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, +] + +[[package]] +name = "requests" +version = "2.33.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, +] + +[[package]] +name = "s3transfer" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/74/8d69dcb7a9efe8baa2046891735e5dfe433ad558ae23d9e3c14c633d1d58/s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125", size = 151547, upload-time = "2025-09-09T19:23:31.089Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712, upload-time = "2025-09-09T19:23:30.041Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "smmap" +version = "5.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/8c/f9290339ef6d79badbc010f067cd769d6601ec11a57d78569c683fb4dd87/sse_starlette-3.3.4.tar.gz", hash = "sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1", size = 32427, upload-time = "2026-03-29T09:00:23.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/7f/3de5402f39890ac5660b86bcf5c03f9d855dad5c4ed764866d7b592b46fd/sse_starlette-3.3.4-py3-none-any.whl", hash = "sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1", size = 14330, upload-time = "2026-03-29T09:00:21.846Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "starlette" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, +] + +[[package]] +name = "streamlit" +version = "1.56.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "watchdog", marker = "sys_platform != 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/85/7c669b3a1336d34ef39fa9760fbd343185f3b15db2ad0838fd78423d1c7f/streamlit-1.56.0.tar.gz", hash = "sha256:1176acfa89ae1318b79078e8efe689a9d02e8d58e325c00fc0e55fa2f3fe8d6a", size = 8559239, upload-time = "2026-03-31T22:29:38.59Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/91/cb6f13a89e376ef179309d74f37a70ea0041d5e4b5ba5c4836dbf6e020ad/streamlit-1.56.0-py3-none-any.whl", hash = "sha256:8677a335734a30a51bc57ad0ec910e365d95f7c456fc02c60032927cd0729dc5", size = 9052089, upload-time = "2026-03-31T22:29:36.342Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, +] + +[[package]] +name = "textual" +version = "8.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", extra = ["linkify"] }, + { name = "mdit-py-plugins" }, + { name = "platformdirs" }, + { name = "pygments" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/b0/a9aedf13af1bfb1bf01cbc645ea5d5a4151b5d77ac1748b85c4f0d777d7d/textual-8.2.2.tar.gz", hash = "sha256:94e85267650cf679ac16ade5ac929055e836dc00798a0e6e3925926a5beee303", size = 1848623, upload-time = "2026-04-03T13:19:06.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/18/4d59eb3f2241db6d346a90f2452fc47a19d61090a38b9cf331afe23e8431/textual-8.2.2-py3-none-any.whl", hash = "sha256:35a8f439875dc6e5b4dc7ee72dc9698a40bd13091c2de5bd5b2d4318522af8df", size = 724078, upload-time = "2026-04-03T13:19:08.115Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tomli" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096, upload-time = "2024-10-02T10:46:13.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237, upload-time = "2024-10-02T10:46:11.806Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/19/b65f1a088ee23e37cdea415b357843eca8b1422a7b11a9eee6e35d4ec273/tomli_w-1.1.0.tar.gz", hash = "sha256:49e847a3a304d516a169a601184932ef0f6b61623fe680f836a2aa7128ed0d33", size = 6929, upload-time = "2024-10-08T11:13:29.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/ac/ce90573ba446a9bbe65838ded066a805234d159b4446ae9f8ec5bbd36cbd/tomli_w-1.1.0-py3-none-any.whl", hash = "sha256:1403179c78193e3184bfaade390ddbd071cba48a32a2e62ba11aae47490c63f7", size = 6440, upload-time = "2024-10-08T11:13:27.897Z" }, +] + +[[package]] +name = "tornado" +version = "6.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006, upload-time = "2026-03-10T21:31:02.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983, upload-time = "2026-03-10T21:30:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246, upload-time = "2026-03-10T21:30:46.571Z" }, + { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229, upload-time = "2026-03-10T21:30:48.273Z" }, + { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192, upload-time = "2026-03-10T21:30:51.22Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039, upload-time = "2026-03-10T21:30:53.52Z" }, + { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445, upload-time = "2026-03-10T21:30:55.541Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582, upload-time = "2026-03-10T21:30:57.142Z" }, + { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990, upload-time = "2026-03-10T21:30:58.857Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016, upload-time = "2026-03-10T21:31:00.43Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, +] + +[[package]] +name = "typer" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/07/b822e1b307d40e263e8253d2384cf98c51aa2368cc7ba9a07e523a1d964b/typer-0.23.1.tar.gz", hash = "sha256:2070374e4d31c83e7b61362fd859aa683576432fd5b026b060ad6b4cd3b86134", size = 120047, upload-time = "2026-02-13T10:04:30.984Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/91/9b286ab899c008c2cb05e8be99814807e7fbbd33f0c0c960470826e5ac82/typer-0.23.1-py3-none-any.whl", hash = "sha256:3291ad0d3c701cbf522012faccfbb29352ff16ad262db2139e6b01f15781f14e", size = 56813, upload-time = "2026-02-13T10:04:32.008Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, +] + +[[package]] +name = "uc-micro-py" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "uv" +version = "0.9.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/a0/63cea38fe839fb89592728b91928ee6d15705f1376a7940fee5bbc77fea0/uv-0.9.30.tar.gz", hash = "sha256:03ebd4b22769e0a8d825fa09d038e31cbab5d3d48edf755971cb0cec7920ab95", size = 3846526, upload-time = "2026-02-04T21:45:37.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/3c/71be72f125f0035348b415468559cc3b335ec219376d17a3d242d2bd9b23/uv-0.9.30-py3-none-linux_armv6l.whl", hash = "sha256:a5467dddae1cd5f4e093f433c0f0d9a0df679b92696273485ec91bbb5a8620e6", size = 21927585, upload-time = "2026-02-04T21:46:14.935Z" }, + { url = "https://files.pythonhosted.org/packages/0f/fd/8070b5423a77d4058d14e48a970aa075762bbff4c812dda3bb3171543e44/uv-0.9.30-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ec38ae29aa83a37c6e50331707eac8ecc90cf2b356d60ea6382a94de14973be", size = 21050392, upload-time = "2026-02-04T21:45:55.649Z" }, + { url = "https://files.pythonhosted.org/packages/42/5f/3ccc9415ef62969ed01829572338ea7bdf4c5cf1ffb9edc1f8cb91b571f3/uv-0.9.30-py3-none-macosx_11_0_arm64.whl", hash = "sha256:777ecd117cf1d8d6bb07de8c9b7f6c5f3e802415b926cf059d3423699732eb8c", size = 19817085, upload-time = "2026-02-04T21:45:40.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3f/76b44e2a224f4c4a8816fc92686ef6d4c2656bc5fc9d4f673816162c994d/uv-0.9.30-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:93049ba3c41fa2cc38b467cb78ef61b2ddedca34b6be924a5481d7750c8111c6", size = 21620537, upload-time = "2026-02-04T21:45:47.846Z" }, + { url = "https://files.pythonhosted.org/packages/60/2a/50f7e8c6d532af8dd327f77bdc75ce4652322ac34f5e29f79a8e04ea3cc8/uv-0.9.30-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:f295604fee71224ebe2685a0f1f4ff7a45c77211a60bd57133a4a02056d7c775", size = 21550855, upload-time = "2026-02-04T21:46:26.269Z" }, + { url = "https://files.pythonhosted.org/packages/0e/10/f823d4af1125fae559194b356757dc7d4a8ac79d10d11db32c2d4c9e2f63/uv-0.9.30-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2faf84e1f3b6fc347a34c07f1291d11acf000b0dd537a61d541020f22b17ccd9", size = 21516576, upload-time = "2026-02-04T21:46:03.494Z" }, + { url = "https://files.pythonhosted.org/packages/91/f3/64b02db11f38226ed34458c7fbdb6f16b6d4fd951de24c3e51acf02b30f8/uv-0.9.30-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b3b3700ecf64a09a07fd04d10ec35f0973ec15595d38bbafaa0318252f7e31f", size = 22718097, upload-time = "2026-02-04T21:45:51.875Z" }, + { url = "https://files.pythonhosted.org/packages/28/21/a48d1872260f04a68bb5177b0f62ddef62ab892d544ed1922f2d19fd2b00/uv-0.9.30-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b176fc2937937dd81820445cb7e7e2e3cd1009a003c512f55fa0ae10064c8a38", size = 24107844, upload-time = "2026-02-04T21:46:19.032Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c6/d7e5559bfe1ab7a215a7ad49c58c8a5701728f2473f7f436ef00b4664e88/uv-0.9.30-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:180e8070b8c438b9a3fb3fde8a37b365f85c3c06e17090f555dc68fdebd73333", size = 23685378, upload-time = "2026-02-04T21:46:07.166Z" }, + { url = "https://files.pythonhosted.org/packages/a8/bf/b937bbd50d14c6286e353fd4c7bdc09b75f6b3a26bd4e2f3357e99891f28/uv-0.9.30-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4125a9aa2a751e1589728f6365cfe204d1be41499148ead44b6180b7df576f27", size = 22848471, upload-time = "2026-02-04T21:45:18.728Z" }, + { url = "https://files.pythonhosted.org/packages/6a/57/12a67c569e69b71508ad669adad266221f0b1d374be88eaf60109f551354/uv-0.9.30-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4366dd740ac9ad3ec50a58868a955b032493bb7d7e6ed368289e6ced8bbc70f3", size = 22774258, upload-time = "2026-02-04T21:46:10.798Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b8/a26cc64685dddb9fb13f14c3dc1b12009f800083405f854f84eb8c86b494/uv-0.9.30-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:33e50f208e01a0c20b3c5f87d453356a5cbcfd68f19e47a28b274cd45618881c", size = 21699573, upload-time = "2026-02-04T21:45:44.365Z" }, + { url = "https://files.pythonhosted.org/packages/c8/59/995af0c5f0740f8acb30468e720269e720352df1d204e82c2d52d9a8c586/uv-0.9.30-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5e7a6fa7a3549ce893cf91fe4b06629e3e594fc1dca0a6050aba2ea08722e964", size = 22460799, upload-time = "2026-02-04T21:45:26.658Z" }, + { url = "https://files.pythonhosted.org/packages/bb/0b/6affe815ecbaebf38b35d6230fbed2f44708c67d5dd5720f81f2ec8f96ff/uv-0.9.30-py3-none-musllinux_1_1_i686.whl", hash = "sha256:62d7e408d41e392b55ffa4cf9b07f7bbd8b04e0929258a42e19716c221ac0590", size = 22001777, upload-time = "2026-02-04T21:45:34.656Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b6/47a515171c891b0d29f8e90c8a1c0e233e4813c95a011799605cfe04c74c/uv-0.9.30-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6dc65c24f5b9cdc78300fa6631368d3106e260bbffa66fb1e831a318374da2df", size = 22968416, upload-time = "2026-02-04T21:45:22.863Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3a/c1df8615385138bb7c43342586431ca32b77466c5fb086ac0ed14ab6ca28/uv-0.9.30-py3-none-win32.whl", hash = "sha256:74e94c65d578657db94a753d41763d0364e5468ec0d368fb9ac8ddab0fb6e21f", size = 20889232, upload-time = "2026-02-04T21:46:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a8/e8761c8414a880d70223723946576069e042765475f73b4436d78b865dba/uv-0.9.30-py3-none-win_amd64.whl", hash = "sha256:88a2190810684830a1ba4bb1cf8fb06b0308988a1589559404259d295260891c", size = 23432208, upload-time = "2026-02-04T21:45:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/49/e8/6f2ebab941ec559f97110bbbae1279cd0333d6bc352b55f6fa3fefb020d9/uv-0.9.30-py3-none-win_arm64.whl", hash = "sha256:7fde83a5b5ea027315223c33c30a1ab2f2186910b933d091a1b7652da879e230", size = 21887273, upload-time = "2026-02-04T21:45:59.787Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.43.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/f2/368268300fb8af33743508d738ef7bb4d56afdb46c6d9c0fa3dd515df171/uvicorn-0.43.0.tar.gz", hash = "sha256:ab1652d2fb23abf124f36ccc399828558880def222c3cb3d98d24021520dc6e8", size = 85686, upload-time = "2026-04-03T18:37:48.984Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/df/0cf5b0c451602748fdc7a702d4667f6e209bf96aa6e3160d754234445f2a/uvicorn-0.43.0-py3-none-any.whl", hash = "sha256:46fac64f487fd968cd999e5e49efbbe64bd231b5bd8b4a0b482a23ebce499620", size = 68591, upload-time = "2026-04-03T18:37:47.64Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, + { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, + { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, + { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, + { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, + { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, + { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, + { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, + { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, + { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, + { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, + { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, + { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, + { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +]

NS=y#)ZfTYFiS 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 literal 0 HcmV?d00001 diff --git a/eval/rescore.py b/eval/rescore.py deleted file mode 100644 index 15c8176..0000000 --- a/eval/rescore.py +++ /dev/null @@ -1,340 +0,0 @@ -""" -rescore.py -========== -Re-scores an existing per_question.json against the updated scorer.py -without making any LLM or retrieval calls. - -Usage ------ -python rescore.py \ - --results results//per_question.json \ - --questions /path/to/hf_dataset/questions/questions-00000.parquet \ - --scorer scorer.py - -Writes updated per_question.json and summary.json to the same directory, -and appends/updates leaderboard.json + leaderboard.csv. -""" - -from __future__ import annotations - -import argparse -import json -import sys -import types -from collections import defaultdict -from datetime import datetime, timezone -from importlib.machinery import SourceFileLoader -from pathlib import Path -from typing import Dict, List - - -# ── Scoring helpers (duplicated from eval_e2e to keep this script standalone) ─ - - -def _mean(vals: List[float]) -> float: - return round(sum(vals) / len(vals), 4) if vals else 0.0 - - -def aggregate(per_question: List[dict]) -> dict: - by_type: Dict[str, list] = defaultdict(list) - by_diff: Dict[str, list] = defaultdict(list) - - for r in per_question: - by_type[r.get("question_type", "UNKNOWN")].append(r) - by_diff[r.get("difficulty", "unknown")].append(r) - - def _agg(rows): - mrr = [r["scores"]["retrieval_mrr"] for r in rows] - rec = [r["scores"]["retrieval_recall"] for r in rows] - score = [ - r["scores"]["answer_score"] - for r in rows - if r["scores"]["answer_score"] is not None - ] - corr = [ - r["scores"]["correct"] for r in rows if r["scores"]["correct"] is not None - ] - return { - "n": len(rows), - "mrr_at_10": _mean(mrr), - "recall_at_10": _mean(rec), - "answer_score": _mean(score) if score else None, - "accuracy": _mean([float(v) for v in corr]) if corr else None, - } - - return { - "overall": _agg(per_question), - "by_type": {k: _agg(v) for k, v in sorted(by_type.items())}, - "by_difficulty": {k: _agg(v) for k, v in sorted(by_diff.items())}, - } - - -def _print_summary(summary: dict, run_id: str) -> None: - overall = summary.get("overall", {}) - - def _f(v): - return f"{v:.4f}" if v is not None else " n/a " - - print(f"\n{'=' * 64}") - print(f" Re-scored run: {run_id}") - print(f"{'=' * 64}") - print( - f" {'Type':<18} {'MRR@10':>8} {'Recall@10':>10} {'Score':>8} {'Acc':>6} {'N':>4}" - ) - print(f" {'-' * 58}") - print( - f" {'OVERALL':<18} {_f(overall.get('mrr_at_10')):>8} " - f"{_f(overall.get('recall_at_10')):>10} " - f"{_f(overall.get('answer_score')):>8} " - f"{_f(overall.get('accuracy')):>6} " - f"{overall.get('n', 0):>4}" - ) - print(f" {'-' * 58}") - for qtype, m in sorted(summary.get("by_type", {}).items()): - print( - f" {qtype:<18} {_f(m.get('mrr_at_10')):>8} " - f"{_f(m.get('recall_at_10')):>10} " - f"{_f(m.get('answer_score')):>8} " - f"{_f(m.get('accuracy')):>6} " - f"{m.get('n', 0):>4}" - ) - print(f"{'=' * 64}\n") - - -# ── Loader ──────────────────────────────────────────────────────────────────── - - -def load_scorer(scorer_path: str): - p = Path(scorer_path) - if not p.exists(): - raise FileNotFoundError(f"scorer.py not found: {p}") - mod = types.ModuleType("orgforge_scorer") - mod.__file__ = str(p) - sys.modules["orgforge_scorer"] = mod - SourceFileLoader("orgforge_scorer", str(p)).exec_module(mod) - return mod.OrgForgeScorer() - - -def load_questions(parquet_path: str) -> Dict[str, dict]: - """Returns {question_id: question_dict} with ground_truth deserialised.""" - import pandas as pd - - df = pd.read_parquet(parquet_path) - questions = {} - for row in df.to_dict("records"): - for field in ("ground_truth", "evidence_chain"): - val = row.get(field) - if isinstance(val, str): - try: - row[field] = json.loads(val) - except Exception: - pass - questions[row["question_id"]] = row - return questions - - -# ── Main ────────────────────────────────────────────────────────────────────── - - -def rescore(args: argparse.Namespace) -> None: - results_path = Path(args.results) - run_dir = results_path.parent - - print(f"Loading results from {results_path}") - per_question = json.loads(results_path.read_text()) - - print(f"Loading questions from {args.questions}") - questions = load_questions(args.questions) - - print(f"Loading scorer from {args.scorer}") - scorer = load_scorer(args.scorer) - - # Re-score each question - changed = 0 - for entry in per_question: - qid = entry["question_id"] - q = questions.get(qid) - if q is None: - print(f" WARNING: {qid} not found in questions parquet — skipping") - continue - - agent_answer = entry["agent_answer"] - - # Inject retrieved IDs for evidence scoring if missing - if not agent_answer.get("retrieved_artifact_ids"): - agent_answer["retrieved_artifact_ids"] = entry.get("top_k_ids", []) - - try: - result = scorer.score(q, agent_answer) - new_score = round(result.score, 4) - except Exception as exc: - print(f" WARNING: scorer failed on {qid}: {exc}") - new_score = None - - old_score = entry["scores"].get("answer_score") - if old_score != new_score: - changed += 1 - print(f" {qid} ({entry['question_type']}): {old_score} → {new_score}") - - entry["scores"]["answer_score"] = new_score - entry["scores"]["correct"] = ( - (new_score >= 0.9) if new_score is not None else None - ) - - print(f"\n{changed} scores changed out of {len(per_question)}") - - # Write updated per_question.json - results_path.write_text(json.dumps(per_question, indent=2)) - print(f"Updated {results_path}") - - # Write updated summary.json - summary = aggregate(per_question) - summary_path = run_dir / "summary.json" - summary_path.write_text(json.dumps(summary, indent=2)) - print(f"Updated {summary_path}") - - # Print table - run_id = run_dir.name - _print_summary(summary, run_id) - - # Update leaderboard if requested - if args.leaderboard: - _update_leaderboard(run_id, run_dir, summary, Path(args.leaderboard)) - - -def _update_leaderboard( - run_id: str, run_dir: Path, summary: dict, leaderboard_dir: Path -) -> None: - import csv - - lb_json = leaderboard_dir / "leaderboard.json" - lb_csv = leaderboard_dir / "leaderboard.csv" - - # Parse retriever and generator from run_id. - # New format: ____ - # Old format: __bedrock__ (legacy, generator unknown) - parts = run_id.split("__") - retriever = parts[0] if len(parts) > 0 else "unknown" - generator = parts[1].replace("-", "/", 1) if len(parts) > 1 else "unknown" - tier = "1" if generator == "none" else "1+2" - - overall = summary.get("overall", {}) - - # Load existing leaderboard - leaderboard = [] - if lb_json.exists(): - leaderboard = json.loads(lb_json.read_text()) - - new_row = { - "run_id": run_id, - "timestamp": datetime.now(timezone.utc).isoformat(), - "tier": tier, - "retriever": retriever, - "generator": generator, - "n": overall.get("n"), - "mrr_at_10": overall.get("mrr_at_10"), - "recall_at_10": overall.get("recall_at_10"), - "answer_score": overall.get("answer_score"), - "accuracy": overall.get("accuracy"), - "by_type": { - qtype: { - "mrr_at_10": m.get("mrr_at_10"), - "answer_score": m.get("answer_score"), - } - for qtype, m in summary.get("by_type", {}).items() - }, - } - - leaderboard = [r for r in leaderboard if r.get("run_id") != run_id] - leaderboard.append(new_row) - leaderboard.sort( - key=lambda r: ( - 0 if r.get("tier") == "1+2" else 1, - -(r.get("answer_score") or 0.0), - -(r.get("mrr_at_10") or 0.0), - ) - ) - - lb_json.write_text(json.dumps(leaderboard, indent=2)) - print(f"Updated {lb_json}") - - # CSV — flatten by_type into columns - _QTYPES = [ - "CAUSAL", - "ESCALATION", - "GAP_DETECTION", - "PLAN", - "RETRIEVAL", - "ROUTING", - "TEMPORAL", - ] - - def _f(v): - return "" if v is None else v - - fieldnames = [ - "run_id", - "timestamp", - "tier", - "retriever", - "generator", - "n", - "mrr_at_10", - "recall_at_10", - "answer_score", - "accuracy", - ] - for qt in _QTYPES: - fieldnames += [f"mrr_{qt}", f"score_{qt}"] - - with open(lb_csv, "w", newline="") as f: - writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore") - writer.writeheader() - for row in leaderboard: - flat = { - k: _f(row.get(k)) - for k in [ - "run_id", - "timestamp", - "tier", - "retriever", - "generator", - "n", - "mrr_at_10", - "recall_at_10", - "answer_score", - "accuracy", - ] - } - by_type = row.get("by_type", {}) - for qt in _QTYPES: - m = by_type.get(qt, {}) - flat[f"mrr_{qt}"] = _f(m.get("mrr_at_10")) - flat[f"score_{qt}"] = _f(m.get("answer_score")) - writer.writerow(flat) - - print(f"Updated {lb_csv}") - - -def _parse_args() -> argparse.Namespace: - p = argparse.ArgumentParser( - description="Re-score existing eval results with updated scorer.py" - ) - p.add_argument("--results", required=True, help="Path to per_question.json") - p.add_argument("--questions", required=True, help="Path to questions-00000.parquet") - p.add_argument("--scorer", required=True, help="Path to scorer.py") - p.add_argument( - "--leaderboard", - default=None, - metavar="DIR", - help="Directory containing leaderboard.json/csv to update (optional). " - "Defaults to the directory where you run this script.", - ) - return p.parse_args() - - -if __name__ == "__main__": - args = _parse_args() - if args.leaderboard is None: - args.leaderboard = str(Path.cwd()) - rescore(args) diff --git a/eval/retrieval_extensions.py b/eval/retrieval_extensions.py deleted file mode 100644 index 4d283ba..0000000 --- a/eval/retrieval_extensions.py +++ /dev/null @@ -1,457 +0,0 @@ -""" -retrieval_extensions.py -======================= -Drop-in retrieval extensions for eval_e2e.py. - -Provides two new Retriever subclasses that slot directly into -build_retriever() and the existing eval loop: - - RRFRetriever - ------------ - Reciprocal Rank Fusion over any 2-N sub-retrievers. - Fuses BM25 (lexical) with a dense retriever (Cohere / OpenAI / Bedrock) - by default, producing a ranked list whose score is: - - RRF(d) = Σ_r 1 / (k + rank_r(d)) - - where k=60 is the standard smoothing constant. - - Usage (eval_e2e.py CLI addition): - python eval_e2e.py --retriever rrf --generator claude - python eval_e2e.py --retriever rrf-openai --generator claude - python eval_e2e.py --retriever rrf-bedrock --generator claude - - GraphAugmentedRetriever - ----------------------- - Wraps any base Retriever and expands results by walking artifact - relationship edges that are embedded in the corpus itself. - - The graph expander: - 1. Indexes all edges at index() time → O(|corpus|) build - 2. At retrieve() time, takes the base retriever's top-K results, - adds 1-hop neighbors from the edge graph, re-ranks the combined - pool by (base_score + neighbour_boost), and returns top-K. - - Neighbor boost decays with hop distance: - boost(d, hop) = NEIGHBOUR_BOOST_BASE ** hop (default: 0.5 per hop) - - Usage: - python eval_e2e.py --retriever graph-bm25 --generator claude - python eval_e2e.py --retriever graph-cohere --generator claude - python eval_e2e.py --retriever graph-rrf --generator claude -""" - -from __future__ import annotations - -import json -import logging -import re -from collections import defaultdict -from typing import Dict, List, Set, Tuple - - -logger = logging.getLogger("orgforge.retrieval_extensions") - -# ── Artifact-ID pattern: covers ORG-42, CONF-ENG-007, EMAIL-003, -# SLACK-THREAD-9, PR-12, ZD-456, etc. -_ARTIFACT_ID_RE = re.compile( - r"\b(?:ORG|CONF|EMAIL|SLACK(?:-THREAD)?|PR|ZD|SF|DD|JIRA)[-_][\w-]+", - re.IGNORECASE, -) - -# Fields in corpus docs that may carry related artifact IDs (JSON-encoded or plain list). -_RELATION_FIELDS = ( - "related_ids", - "causal_chain", - "artifact_ids", - "evidence_chain", - "downstream_artifacts", - "linked_artifacts", -) - -# RRF smoothing constant (Cormack et al. 2009 recommend k=60). -RRF_K: int = 60 - -# Graph expansion: score boost applied to 1-hop neighbors. -# Each additional hop multiplies by this factor (geometric decay). -NEIGHBOUR_BOOST_BASE: float = 0.5 - -# Maximum graph hops to expand. Keep at 1-2 to avoid noise amplification. -MAX_HOPS: int = 2 - - -# ───────────────────────────────────────────────────────────────────────────── -# RECIPROCAL RANK FUSION -# ───────────────────────────────────────────────────────────────────────────── - - -class RRFRetriever: - """ - Fuse ranked lists from two or more Retriever instances using - Reciprocal Rank Fusion (Cormack, Clarke & Buettcher, SIGIR 2009). - - RRF score for document d across ranker set R: - - rrf(d) = Σ_{r ∈ R} 1 / (k + rank_r(d)) - - Documents not ranked by a given retriever are assigned rank = infinity - (contributing 0 to the sum), which naturally deprioritises them without - discarding them entirely. - - Parameters - ---------- - retrievers : list of Retriever - At least two Retriever instances. All must be indexable with the same - corpus. Mixing BM25 + dense gives the best lexical/semantic coverage. - k : int - RRF smoothing constant. Default 60 matches the canonical paper. - candidate_k : int - How many candidates each sub-retriever fetches before fusion. - Should be ≥ final top_k; larger values improve recall at the cost of - extra embedding lookups on dense retrievers. - """ - - name = "rrf" - - def __init__( - self, - retrievers: List, - k: int = RRF_K, - candidate_k: int = 50, - ) -> None: - if len(retrievers) < 2: - raise ValueError("RRFRetriever requires at least two sub-retrievers.") - self._retrievers = retrievers - self._k = k - self._candidate_k = candidate_k - # Build a human-readable name from the sub-retriever names. - sub_names = "+".join(r.name for r in retrievers) - self.name = f"rrf({sub_names})" - - # ------------------------------------------------------------------ - # Retriever protocol - # ------------------------------------------------------------------ - - def index(self, corpus: List[dict]) -> None: - """Index every sub-retriever with the same corpus.""" - for r in self._retrievers: - logger.info(f" [RRF] Indexing sub-retriever: {r.name}") - r.index(corpus) - logger.info(f" [RRF] All {len(self._retrievers)} sub-retrievers indexed.") - - def retrieve(self, query: str, top_k: int = 10) -> List[str]: - """ - Fetch candidates from each sub-retriever, apply RRF scoring, - and return the top_k doc_ids ordered by descending RRF score. - """ - candidate_k = max(self._candidate_k, top_k * 3) - - # Collect per-retriever ranked lists. - ranked_lists: List[List[str]] = [] - for r in self._retrievers: - try: - ranked = r.retrieve(query, top_k=candidate_k) - except Exception as exc: - logger.warning(f" [RRF] Sub-retriever {r.name} failed: {exc}") - ranked = [] - ranked_lists.append(ranked) - - # Compute RRF scores. - rrf_scores: Dict[str, float] = defaultdict(float) - for ranked in ranked_lists: - for rank, doc_id in enumerate(ranked, start=1): - rrf_scores[doc_id] += 1.0 / (self._k + rank) - - # Sort by descending RRF score. - sorted_docs = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True) - return [doc_id for doc_id, _ in sorted_docs[:top_k]] - - # ------------------------------------------------------------------ - # Helpers - # ------------------------------------------------------------------ - - def per_retriever_ranks( - self, query: str, candidate_k: int = 50 - ) -> Dict[str, Dict[str, int]]: - """ - Diagnostic helper: returns {retriever_name: {doc_id: rank}} for a query. - Useful for understanding which sub-retriever contributed each result. - """ - out: Dict[str, Dict[str, int]] = {} - for r in self._retrievers: - ranked = r.retrieve(query, top_k=candidate_k) - out[r.name] = {doc_id: rank + 1 for rank, doc_id in enumerate(ranked)} - return out - - -# ───────────────────────────────────────────────────────────────────────────── -# GRAPH-AUGMENTED RETRIEVAL -# ───────────────────────────────────────────────────────────────────────────── - - -class _ArtifactGraph: - """ - Bidirectional adjacency list extracted from corpus metadata. - - Edges are collected from: - 1. Structured relation fields (RELATION_FIELDS) — parsed as JSON or - plain lists of artifact ID strings. - 2. Artifact-ID tokens embedded in the body / title text. - - All edges are bidirectional: if doc A references doc B, we add both - A → B and B → A so that graph traversal works in both directions. - """ - - def __init__(self) -> None: - self._adj: Dict[str, Set[str]] = defaultdict(set) - - # ------------------------------------------------------------------ - # Build - # ------------------------------------------------------------------ - - def build(self, corpus: List[dict]) -> None: - """Populate the adjacency list from the corpus.""" - doc_ids: Set[str] = {r["doc_id"] for r in corpus} - - for doc in corpus: - src = doc["doc_id"] - neighbors: Set[str] = set() - - # 1. Structured relation fields. - for field in _RELATION_FIELDS: - val = doc.get(field) - if val is None: - continue - # Might be a JSON-encoded string (e.g. stored as text in parquet). - if isinstance(val, str): - try: - val = json.loads(val) - except (json.JSONDecodeError, ValueError): - # Try to extract IDs inline from the raw string. - neighbors.update(self._extract_ids_from_text(val, doc_ids)) - continue - # Might be a dict (artifact_ids maps type → id). - if isinstance(val, dict): - for v in val.values(): - if isinstance(v, str) and v in doc_ids: - neighbors.add(v) - elif isinstance(v, list): - neighbors.update(x for x in v if x in doc_ids) - elif isinstance(val, list): - for item in val: - if isinstance(item, str) and item in doc_ids: - neighbors.add(item) - - # 2. Inline artifact-ID tokens in body / title. - for text_field in ("body", "content", "title"): - text = doc.get(text_field) or "" - neighbors.update(self._extract_ids_from_text(text, doc_ids)) - - # Remove self-loops. - neighbors.discard(src) - - # Register bidirectional edges. - for tgt in neighbors: - self._adj[src].add(tgt) - self._adj[tgt].add(src) - - total_edges = sum(len(v) for v in self._adj.values()) // 2 - logger.info( - f" [Graph] Artifact graph built: " - f"{len(self._adj)} nodes, ~{total_edges} undirected edges" - ) - - # ------------------------------------------------------------------ - # Query - # ------------------------------------------------------------------ - - def neighbors(self, doc_id: str) -> Set[str]: - """Return all direct neighbors of doc_id.""" - return set(self._adj.get(doc_id, set())) - - def expand( - self, - seed_ids: List[str], - max_hops: int = MAX_HOPS, - ) -> Dict[str, int]: - """ - BFS from seed_ids up to max_hops away. - - Returns {doc_id: hop_distance} for every reachable node, - excluding the seeds themselves (hop 0). - """ - visited: Dict[str, int] = {} - frontier: Set[str] = set(seed_ids) - current_hop = 0 - - while frontier and current_hop < max_hops: - current_hop += 1 - next_frontier: Set[str] = set() - for node in frontier: - for nbr in self.neighbors(node): - if nbr not in visited and nbr not in set(seed_ids): - visited[nbr] = current_hop - next_frontier.add(nbr) - frontier = next_frontier - - return visited - - # ------------------------------------------------------------------ - # Internal helpers - # ------------------------------------------------------------------ - - @staticmethod - def _extract_ids_from_text(text: str, doc_ids: Set[str]) -> Set[str]: - """Extract all artifact-ID tokens from free text that exist in the corpus.""" - tokens = _ARTIFACT_ID_RE.findall(text) - return {t.upper() for t in tokens if t.upper() in doc_ids} - - -class GraphAugmentedRetriever: - """ - Wraps any base Retriever and expands its results by one or more hops - along an artifact relationship graph built from corpus metadata. - - Algorithm - --------- - retrieve(query, top_k): - 1. Ask base retriever for `candidate_k` docs → seeded set S. - 2. Expand S up to `max_hops` hops in the artifact graph. - Each hop-N neighbour receives a boost of: - boost = base_score(nearest_seed) * NEIGHBOUR_BOOST_BASE^N - where base_score is approximated as 1 / rank for the nearest - seed in S that reaches this neighbour. - 3. Merge seed scores and neighbour boosts, normalise, return top_k. - - This is particularly valuable for OrgForge's CAUSAL and TEMPORAL - question types, which require multi-artifact evidence chains that a - single-document retriever may miss. - - Parameters - ---------- - base_retriever : Retriever - Any indexable retriever (BM25, Cohere, OpenAI, RRFRetriever, …). - max_hops : int - Graph expansion depth. 1–2 recommended; ≥3 adds noise. - neighbour_boost_base : float - Multiplicative decay per hop. 0.5 means 1-hop neighbors get 50% - of the connecting seed's score, 2-hop get 25%, etc. - candidate_k : int - Seeds fetched from base retriever before graph expansion. - Should be larger than final top_k so the graph has richer seeds. - """ - - def __init__( - self, - base_retriever, - max_hops: int = MAX_HOPS, - neighbour_boost_base: float = NEIGHBOUR_BOOST_BASE, - candidate_k: int = 30, - ) -> None: - self._base = base_retriever - self._max_hops = max_hops - self._neighbour_boost_base = neighbour_boost_base - self._candidate_k = candidate_k - self._graph = _ArtifactGraph() - self.name = f"graph({base_retriever.name},hops={max_hops})" - - # ------------------------------------------------------------------ - # Retriever protocol - # ------------------------------------------------------------------ - - def index(self, corpus: List[dict]) -> None: - """Index the base retriever and build the artifact graph.""" - logger.info(f" [Graph] Indexing base retriever: {self._base.name}") - self._base.index(corpus) - logger.info(" [Graph] Building artifact relationship graph …") - self._graph.build(corpus) - - def retrieve(self, query: str, top_k: int = 10) -> List[str]: - """ - Retrieve top_k documents by combining base retriever scores with - graph-neighbour boost scores. - """ - candidate_k = max(self._candidate_k, top_k * 2) - - # Step 1: seed retrieval. - seeds: List[str] = self._base.retrieve(query, top_k=candidate_k) - if not seeds: - return [] - - # Approximate base score as 1/(rank) — monotone proxy for relevance. - seed_scores: Dict[str, float] = { - doc_id: 1.0 / (rank + 1) for rank, doc_id in enumerate(seeds) - } - - # Step 2: graph expansion. - # For each neighbour, find its minimum hop distance across all seeds, - # and use the highest-scoring seed that reaches it for the boost. - neighbour_scores: Dict[str, float] = {} - for seed_id, seed_score in seed_scores.items(): - reachable = self._graph.expand([seed_id], max_hops=self._max_hops) - for nbr_id, hop in reachable.items(): - boost = seed_score * (self._neighbour_boost_base**hop) - if boost > neighbour_scores.get(nbr_id, 0.0): - neighbour_scores[nbr_id] = boost - - # Step 3: merge seed + neighbour scores. - combined: Dict[str, float] = dict(seed_scores) - for doc_id, boost in neighbour_scores.items(): - if doc_id in combined: - # Already in seed set — add boost on top. - combined[doc_id] += boost - else: - combined[doc_id] = boost - - # Step 4: sort by descending combined score and return top_k. - sorted_docs = sorted(combined.items(), key=lambda x: x[1], reverse=True) - return [doc_id for doc_id, _ in sorted_docs[:top_k]] - - # ------------------------------------------------------------------ - # Diagnostic helpers - # ------------------------------------------------------------------ - - def explain(self, query: str, top_k: int = 10) -> List[dict]: - """ - Returns a list of dicts with retrieval provenance for each result: - { - "doc_id": str, - "combined_score": float, - "from_base": bool, # was it in the seed set? - "hop_distance": int, # 0 = seed, N = N hops away - "base_rank": int | None, # rank in base retriever (1-indexed) - } - Useful for offline debugging of graph expansion. - """ - candidate_k = max(self._candidate_k, top_k * 2) - seeds = self._base.retrieve(query, top_k=candidate_k) - seed_scores = {doc_id: 1.0 / (rank + 1) for rank, doc_id in enumerate(seeds)} - seed_rank = {doc_id: rank + 1 for rank, doc_id in enumerate(seeds)} - - neighbour_info: Dict[str, Tuple[float, int]] = {} # doc_id → (boost, hop) - for seed_id, seed_score in seed_scores.items(): - reachable = self._graph.expand([seed_id], max_hops=self._max_hops) - for nbr_id, hop in reachable.items(): - boost = seed_score * (self._neighbour_boost_base**hop) - if boost > neighbour_info.get(nbr_id, (0.0, 999))[0]: - neighbour_info[nbr_id] = (boost, hop) - - combined: Dict[str, float] = dict(seed_scores) - for doc_id, (boost, _) in neighbour_info.items(): - combined[doc_id] = combined.get(doc_id, 0.0) + boost - - sorted_docs = sorted(combined.items(), key=lambda x: x[1], reverse=True)[:top_k] - - results = [] - for doc_id, score in sorted_docs: - hop = 0 if doc_id in seed_scores else neighbour_info.get(doc_id, (0, -1))[1] - results.append( - { - "doc_id": doc_id, - "combined_score": round(score, 6), - "from_base": doc_id in seed_scores, - "hop_distance": hop, - "base_rank": seed_rank.get(doc_id), - } - ) - return results diff --git a/eval/scorer.py b/eval/scorer.py deleted file mode 100644 index f49706f..0000000 --- a/eval/scorer.py +++ /dev/null @@ -1,1276 +0,0 @@ -""" -scorer.py -========= -Per-question-type scoring for the OrgForge eval dataset. - -Design principles ------------------ - 1. Scores are always in [0.0, 1.0] — comparable across question types. - 2. Partial credit is supported everywhere via evidence_chain overlap. - 3. Each scorer returns a ScorerResult so callers can aggregate, filter, - or report by type independently. - 4. No LLM involvement — scoring is deterministic and reproducible. - -Question types handled ----------------------- - RETRIEVAL Exact artifact_id match + optional timestamp proximity bonus. - CAUSAL Artifact match AND event_type match required for full credit. - TEMPORAL Boolean match routed by temporal_category (knowledge_gap/ - point_in_time/stress_state/propagation). Ground truth field - is had_knowledge, was_true, or knew_before per sub-category. - GAP_DETECTION Boolean was_actioned + downstream artifact overlap. - ROUTING first_recipient exact match. - PLAN dept + theme match (theme uses substring matching for LLM prose). - ESCALATION escalation_actors set match, partial credit for overlap. - KNOWLEDGE_GAP gap_areas set match, partial credit for overlap. - ZD_RESOLUTION resolved boolean + duration_days exact match; escalated bonus. - SF_RISK incident_id match + at_risk_accounts set overlap. - NPS_SCORE nps_score exact + classification match; escalated_tickets bonus. - INVOICE_SLA breach_duration_days exact + sla_credit_per_org within 5%. - -Partial credit via evidence_chain ----------------------------------- -All question types award partial credit if the agent retrieved relevant -artifacts from evidence_chain even when the final answer is wrong. -This separates retrieval quality from reasoning quality, which is useful -for diagnosing whether failures come from the retriever or the reader. - -Usage ------ - from scorer import OrgForgeScorer, ScorerResult - - scorer = OrgForgeScorer() - result = scorer.score(question, agent_answer) - - # Batch - results = scorer.score_all(questions, agent_answers) - report = scorer.report(results) - -Agent answer format (per question type) ----------------------------------------- -RETRIEVAL: - { - "artifact_id": "ORG-42", # required - "artifact_type": "jira", # optional - "timestamp": "2024-01-15T10:32:00", # optional — used for proximity - "retrieved_artifact_ids": ["ORG-42", ...] # optional — evidence credit - } - -CAUSAL: - { - "artifact_id": "CONF-ENG-007", - "event_type": "confluence_created", - "actors": ["Alice", "Bob"], - "retrieved_artifact_ids": [...] - } - -TEMPORAL: - # knowledge_gap sub-category (default): - { - "had_knowledge": true, - "person": "Alice", - "domain": "auth-service", - "departure_day": null, # null if agent thinks no departure - "reasoning": "..." # free text — not scored - } - # point_in_time / stress_state sub-categories: - { - "was_true": false, - "reasoning": "..." - } - # propagation sub-category: - { - "knew_before": true, - "reasoning": "..." - } - -GAP_DETECTION: - { - "was_actioned": false, - "artifact_id": "EMAIL-003", - "downstream_artifacts": [], - "retrieved_artifact_ids": [...] - } - -ROUTING: - { - "first_recipient": "Alice", - "retrieved_artifact_ids": [...] - } - -PLAN: - { - "dept": "Engineering_Backend", - "theme": "Stabilize sensor ingest and Kafka reliability", - "retrieved_artifact_ids": [...] # optional — evidence credit - } - -ESCALATION: - { - "escalation_actors": ["Jax", "Chloe"], # order-insensitive - "retrieved_artifact_ids": [...] - } - -KNOWLEDGE_GAP: - { - "gap_areas": ["auth-service", "redis-cache"], # order-insensitive - "retrieved_artifact_ids": [...] - } -""" - -from __future__ import annotations - -import json -import logging -from dataclasses import dataclass, field, asdict -from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple - -logger = logging.getLogger("orgforge.scorer") - -# ── Weights ─────────────────────────────────────────────────────────────────── -# Final score = primary_score * PRIMARY_WEIGHT + evidence_score * EVIDENCE_WEIGHT -# Evidence credit is always secondary so a lucky retrieval can't mask a bad answer. -PRIMARY_WEIGHT = 0.80 -EVIDENCE_WEIGHT = 0.20 - -# Temporal proximity bonus: awarded when predicted timestamp is within this -# many minutes of the ground truth. Adds up to PROXIMITY_BONUS to primary score -# before weighting (capped at 1.0). -PROXIMITY_BONUS = 0.10 -PROXIMITY_WINDOW_MIN = 30 # minutes - - -# ───────────────────────────────────────────────────────────────────────────── -# RESULT DATA CLASS -# ───────────────────────────────────────────────────────────────────────────── - - -@dataclass -class ScorerResult: - question_id: str - question_type: str - difficulty: str - score: float # [0.0, 1.0] - primary_score: float # [0.0, 1.0] — main answer correctness - evidence_score: float # [0.0, 1.0] — retrieved right artifacts? - correct: bool # True if score >= 0.9 - partial: bool # True if 0.2 <= score < 0.9 - failure_reason: Optional[str] # populated when score < 0.9 - meta: Dict[str, Any] = field(default_factory=dict) - - def to_dict(self) -> Dict: - return asdict(self) - - -# ───────────────────────────────────────────────────────────────────────────── -# INDIVIDUAL SCORERS -# ───────────────────────────────────────────────────────────────────────────── - - -class _BaseScorer: - """Shared helpers for all question-type scorers.""" - - def _evidence_overlap( - self, - ground_truth_chain: List[str], - agent_retrieved: List[str], - ) -> float: - """Recall of ground-truth evidence chain in agent-retrieved IDs. - - Using recall (hits / |chain|) rather than Jaccard because retrievers - always return top-K docs — penalising for retrieving non-chain documents - structurally caps scores below 0.9 for short evidence chains, making - accuracy always 0 regardless of answer quality. - """ - if not ground_truth_chain: - return 1.0 - gt_set = set(ground_truth_chain) - agent_set = set(agent_retrieved or []) - if not agent_set: - return 0.0 - hits = len(gt_set & agent_set) - return hits / len(gt_set) - - def _timestamp_proximity( - self, - gt_ts: Optional[str], - agent_ts: Optional[str], - ) -> float: - """ - Returns PROXIMITY_BONUS if the predicted timestamp is within - PROXIMITY_WINDOW_MIN of ground truth, else 0.0. - """ - if not gt_ts or not agent_ts: - return 0.0 - try: - gt = datetime.fromisoformat(str(gt_ts)) - agent = datetime.fromisoformat(str(agent_ts)) - delta = abs((gt - agent).total_seconds()) / 60 - return PROXIMITY_BONUS if delta <= PROXIMITY_WINDOW_MIN else 0.0 - except (ValueError, TypeError): - return 0.0 - - def _combine(self, primary: float, evidence: float) -> float: - return min(1.0, primary * PRIMARY_WEIGHT + evidence * EVIDENCE_WEIGHT) - - -class RetrievalScorer(_BaseScorer): - """ - RETRIEVAL — "Which artifact first documented X?" - - Full credit (1.0): artifact_id matches ground truth exactly. - Partial credit: evidence_chain overlap when artifact_id is wrong. - Timestamp proximity bonus if agent also provides a timestamp. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_id = gt.get("artifact_id", "") - agent_id = agent_answer.get("artifact_id", "") - - primary = 1.0 if agent_id == gt_id else 0.0 - primary += self._timestamp_proximity( - gt.get("timestamp"), agent_answer.get("timestamp") - ) - primary = min(1.0, primary) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - - failure = ( - None - if primary >= 1.0 - else (f"Expected artifact_id={gt_id!r}, got {agent_id!r}") - ) - return primary, evidence, failure - - -class CausalScorer(_BaseScorer): - """ - CAUSAL — "What happened immediately after X?" - - Full credit requires matching artifact_id AND event_type. - Partial credit for artifact match without event_type match (0.5 primary). - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - # POSTMORTEM questions are typed CAUSAL but use postmortem_confluence_id - gt_id = gt.get("artifact_id") or gt.get("postmortem_confluence_id") or "" - gt_etype = gt.get("event_type", "") - agent_id = agent_answer.get("artifact_id", "") - agent_et = agent_answer.get("event_type", "") - - id_correct = agent_id == gt_id - et_correct = agent_et == gt_etype - - if id_correct and et_correct: - primary = 1.0 - failure = None - elif id_correct: - primary = 0.6 - failure = f"Correct artifact but wrong event_type: expected {gt_etype!r}, got {agent_et!r}" - else: - primary = 0.0 - failure = f"Expected artifact_id={gt_id!r}, got {agent_id!r}" - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class TemporalScorer(_BaseScorer): - """ - TEMPORAL — multi-sub-category scorer routed by temporal_category. - - Sub-categories and their ground truth boolean fields: - knowledge_gap (default) — had_knowledge + optional departure_day - point_in_time — was_true (no secondary date field) - stress_state — was_true (no secondary date field) - propagation — knew_before (no secondary date field) - - Full credit logic per sub-category: - knowledge_gap: boolean matches AND departure_day matches (±1 day). - Partial (0.6) when boolean correct but departure_day wrong. - point_in_time / stress_state / propagation: boolean match only → 1.0 or 0.0. - """ - - # Maps temporal_category → (gt_field, agent_field) - _BOOL_FIELDS = { - "knowledge_gap": ("had_knowledge", "had_knowledge"), - "point_in_time": ("was_true", "was_true"), - "stress_state": ("was_true", "was_true"), - "propagation": ("knew_before", "knew_before"), - } - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - category = question.get("temporal_category", "knowledge_gap") - - gt_field, agent_field = self._BOOL_FIELDS.get( - category, ("had_knowledge", "had_knowledge") - ) - gt_bool = gt.get(gt_field) - agent_bool = agent_answer.get(agent_field) - - bool_match = agent_bool == gt_bool - - if not bool_match: - primary = 0.0 - failure = f"{gt_field} expected {gt_bool}, got {agent_bool}" - elif category == "knowledge_gap": - # knowledge_gap questions carry an optional departure_day for - # additional precision credit. - gt_dep_day = gt.get("departure_day") # int or None - agent_dep_day = agent_answer.get("departure_day") - - if gt_dep_day is None and agent_dep_day is None: - # Both agree no departure relevant — full credit. - # Note: when the dataset has had_knowledge=True for all questions - # (e.g. short sim runs where no incident touches a departed - # employee's domains), an agent that always returns - # {"had_knowledge": true, "departure_day": null} will score 1.0 - # here. This is a known dataset limitation, not a scorer bug — - # disclosed in the accompanying paper. - primary = 1.0 - failure = None - elif gt_dep_day is not None and agent_dep_day is not None: - day_delta = abs(int(gt_dep_day) - int(agent_dep_day)) - if day_delta <= 1: - primary = 1.0 - failure = None - else: - primary = 0.6 - failure = f"Departure day off by {day_delta} days (gt={gt_dep_day}, agent={agent_dep_day})" - else: - primary = 0.6 - failure = ( - f"Agent missed departure day (expected {gt_dep_day})" - if gt_dep_day is not None - else "Agent reported a departure day that doesn't exist" - ) - else: - # point_in_time, stress_state, propagation — boolean match is - # sufficient for full credit; no secondary date field to check. - primary = 1.0 - failure = None - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class MultiHopScorer(_BaseScorer): - """ - MULTI_HOP — full customer complaint → resolution chain traversal. - - Scored as a weighted checklist across four hops. Each hop is worth - 0.25 of the primary score — partial credit scales with how far the - agent traced the chain before losing it. - - Hop 1 (0.25): correct email_id / source identified - Hop 2 (0.25): correct slack_thread_id (internal relay) - Hop 3 (0.25): correct ticket_id + assignee - Hop 4 (0.25): correct reply_id OR correct resolved_same_day boolean - - This structure means an agent that traces email→slack→jira but misses - the reply scores 0.75 rather than 0 — which correctly reflects that - it found 3 of 4 artifacts. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - failures = [] - hop_scores = [] - - # Hop 1: source / email identified - gt_email = gt.get("email_id", "") - agent_email = agent_answer.get("email_id", "") - hop1 = 1.0 if agent_email == gt_email else 0.0 - if not hop1: - failures.append( - f"Hop 1: email_id expected {gt_email!r}, got {agent_email!r}" - ) - hop_scores.append(hop1) - - # Hop 2: internal relay (Slack thread) - gt_slack = gt.get("slack_thread_id", "") - agent_slack = agent_answer.get("slack_thread_id", "") - hop2 = ( - 1.0 - if (gt_slack and agent_slack == gt_slack) - else (0.5 if (gt_slack and agent_slack) else (1.0 if not gt_slack else 0.0)) - ) - if gt_slack and agent_slack != gt_slack: - failures.append( - f"Hop 2: slack_thread expected {gt_slack!r}, got {agent_slack!r}" - ) - hop_scores.append(hop2) - - # Hop 3: ticket + assignee - gt_ticket = gt.get("ticket_id", "") - agent_ticket = agent_answer.get("ticket_id", "") - gt_assignee = gt.get("assignee", "").lower() - agent_assignee = agent_answer.get("assignee", "").lower() - ticket_match = agent_ticket == gt_ticket - assignee_match = agent_assignee == gt_assignee - hop3 = ( - 1.0 if (ticket_match and assignee_match) else (0.6 if ticket_match else 0.0) - ) - if not ticket_match: - failures.append( - f"Hop 3: ticket expected {gt_ticket!r}, got {agent_ticket!r}" - ) - elif not assignee_match: - failures.append( - f"Hop 3: assignee expected {gt_assignee!r}, got {agent_assignee!r}" - ) - hop_scores.append(hop3) - - # Hop 4: reply sent + same-day resolution - gt_reply = gt.get("reply_id", "") - gt_same_day = gt.get("resolved_same_day", False) - agent_reply = agent_answer.get("reply_id", "") - agent_same_day = agent_answer.get("resolved_same_day") - reply_match = (not gt_reply) or (agent_reply == gt_reply) - same_day_match = agent_same_day == gt_same_day - hop4 = ( - 1.0 - if (reply_match and same_day_match) - else (0.5 if (reply_match or same_day_match) else 0.0) - ) - if not reply_match: - failures.append( - f"Hop 4: reply_id expected {gt_reply!r}, got {agent_reply!r}" - ) - if not same_day_match: - failures.append( - f"Hop 4: resolved_same_day expected {gt_same_day}, got {agent_same_day}" - ) - hop_scores.append(hop4) - - primary = sum(hop_scores) / len(hop_scores) - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - failure = "; ".join(failures) if failures else None - return primary, evidence, failure - - -class GapDetectionScorer(_BaseScorer): - """ - GAP_DETECTION — "Was this email ever actioned?" - - Full credit: was_actioned boolean matches AND (if actioned=True) - downstream artifact overlap is ≥ 0.5. - Partial: boolean correct but poor downstream recall. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_bool = gt.get("was_actioned") - gt_downstream = gt.get("downstream_artifacts", []) - agent_bool = agent_answer.get("was_actioned") - agent_downstream = agent_answer.get("downstream_artifacts", []) - - if agent_bool != gt_bool: - primary = 0.0 - failure = f"was_actioned expected {gt_bool}, got {agent_bool}" - elif not gt_bool: - primary = 1.0 - failure = None - else: - # was_actioned=True: reward boolean correctness with a 0.6 floor, - # then scale the remaining 0.4 by downstream artifact recall. - # INTENTIONAL ASYMMETRY: a correct True boolean with zero downstream - # overlap yields primary=0.6, which after PRIMARY_WEIGHT produces a - # combined floor of ~0.48+ (plus any evidence credit). This is - # deliberate — correctly identifying that an email was actioned is - # meaningful signal even when the agent can't enumerate the artifacts. - # If you want a stricter floor, lower 0.6 here (e.g. to 0.4) or make - # it conditional on ds_overlap > 0. - ds_overlap = self._evidence_overlap(gt_downstream, agent_downstream) - primary = 0.6 + 0.4 * ds_overlap - failure = ( - None - if ds_overlap >= 0.5 - else ( - f"Correct boolean but poor downstream artifact recall ({ds_overlap:.2f})" - ) - ) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class RoutingScorer(_BaseScorer): - """ - ROUTING — "Who was the first internal person to receive this email?" - Full credit: first_recipient matches. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_recipient = gt.get("first_recipient", "") - agent_recipient = agent_answer.get("first_recipient", "") - - recipient_match = ( - agent_recipient.strip().lower() == gt_recipient.strip().lower() - ) - - if recipient_match: - primary = 1.0 - failure = None - else: - primary = 0.0 - failure = ( - f"Expected first_recipient={gt_recipient!r}, got {agent_recipient!r}" - ) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class PlanScorer(_BaseScorer): - """ - PLAN — "What was department X's focus on Day N?" - - Full credit: dept AND theme both match. - Partial: dept correct but theme wrong (0.5). - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_dept = gt.get("dept", "").lower() - gt_theme = gt.get("theme", "").lower() - agent_dept = agent_answer.get("dept", "").lower() - agent_theme = agent_answer.get("theme", "").lower() - - dept_match = agent_dept == gt_dept - # Theme is LLM-generated prose — use substring match rather than exact. - # Minimum length guard prevents trivially short strings (e.g. "stable") - # from matching any ground truth that happens to contain them. - _MIN_THEME_LEN = 5 - if len(gt_theme) < _MIN_THEME_LEN or len(agent_theme) < _MIN_THEME_LEN: - theme_match = gt_theme == agent_theme - else: - theme_match = gt_theme in agent_theme or agent_theme in gt_theme - - if dept_match and theme_match: - primary = 1.0 - failure = None - elif dept_match: - primary = 0.5 - failure = f"Correct dept but theme mismatch: expected {gt_theme!r}, got {agent_theme!r}" - else: - primary = 0.0 - failure = f"Expected dept={gt_dept!r}, got {agent_dept!r}" - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class EscalationScorer(_BaseScorer): - """ - ESCALATION — "Who was in the escalation chain for ticket X?" - - Full credit: all escalation_actors match (order-insensitive). - Partial: at least one actor correct (scaled by overlap). - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_actors = [a.lower() for a in gt.get("escalation_actors", [])] - agent_actors = [a.lower() for a in agent_answer.get("escalation_actors", [])] - - if not gt_actors: - primary = 1.0 - failure = None - else: - gt_set = set(gt_actors) - agent_set = set(agent_actors) - overlap = len(gt_set & agent_set) / len(gt_set) - - if overlap == 1.0: - # All ground-truth actors retrieved — full credit regardless of - # extra actors returned. Penalising for extras would unfairly - # punish agents that recall correctly but over-enumerate slightly. - primary = 1.0 - failure = None - elif overlap > 0: - primary = round(0.4 + 0.6 * overlap, 4) # 0.4 floor for partial - failure = ( - f"Partial actor match ({len(gt_set & agent_set)}/{len(gt_set)}): " - f"missing {gt_set - agent_set}" - ) - else: - primary = 0.0 - failure = f"No escalation actors matched. Expected {gt_actors}" - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class PostmortemScorer(_BaseScorer): - """ - POSTMORTEM — "Which Confluence doc contains the postmortem for incident X?" - - Ground truth uses postmortem_confluence_id (not artifact_id). - Full credit: artifact_id matches postmortem_confluence_id. - Partial credit: evidence_chain overlap (incident + confluence doc present). - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_id = gt.get("postmortem_confluence_id", "") - agent_id = agent_answer.get("artifact_id", "") - - primary = 1.0 if agent_id == gt_id else 0.0 - failure = ( - None - if primary == 1.0 - else f"Expected postmortem_confluence_id={gt_id!r}, got {agent_id!r}" - ) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class KnowledgeGapScorer(_BaseScorer): - """ - KNOWLEDGE_GAP — "Which domain was undocumented during incident X?" - - Full credit: all gap_areas matched (order-insensitive). - Partial: at least one gap area correct (scaled by overlap). - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_gaps = [g.lower() for g in gt.get("gap_areas", [])] - agent_gaps = [g.lower() for g in agent_answer.get("gap_areas", [])] - - if not gt_gaps: - primary = 1.0 - failure = None - else: - gt_set = set(gt_gaps) - agent_set = set(agent_gaps) - overlap = len(gt_set & agent_set) / len(gt_set) - - if overlap == 1.0: - # Full recall of ground-truth gap areas — full credit regardless - # of any additional areas the agent returns. - primary = 1.0 - failure = None - elif overlap > 0: - primary = round(0.4 + 0.6 * overlap, 4) - failure = ( - f"Partial gap match ({len(gt_set & agent_set)}/{len(gt_set)}): " - f"missing {gt_set - agent_set}" - ) - else: - primary = 0.0 - failure = f"No gap areas matched. Expected {gt_gaps}" - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class PRReviewScorer(_BaseScorer): - """ - PR_REVIEW — "Who reviewed PR-X and what was the verdict?" - - Full credit: pr_id matches AND verdict matches. - Partial: pr_id correct but verdict wrong (0.5). - reviewer correct adds 0.15 bonus on top, capped at 1.0. - - Verdict is case-insensitive and normalised so "LGTM" / "approve" / - "approved" all resolve to "approved", and "changes" / "request changes" - resolve to "changes_requested" before comparison. - """ - - _APPROVE_ALIASES = {"approved", "approve", "lgtm", "merged", "merge"} - _CHANGES_ALIASES = { - "changes_requested", - "changes requested", - "request changes", - "needs changes", - "needs work", - } - - @staticmethod - def _normalise_verdict(raw: str) -> str: - v = raw.strip().lower() - if v in PRReviewScorer._APPROVE_ALIASES: - return "approved" - if v in PRReviewScorer._CHANGES_ALIASES: - return "changes_requested" - return v # return as-is — will fail comparison cleanly - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_pr = gt.get("pr_id", "") - gt_verdict = self._normalise_verdict(gt.get("verdict", "")) - gt_reviewer = gt.get("reviewer", "").strip().lower() - - agent_pr = agent_answer.get("pr_id", "") - agent_verdict = self._normalise_verdict(agent_answer.get("verdict", "")) - agent_reviewer = agent_answer.get("reviewer", "").strip().lower() - - pr_match = agent_pr == gt_pr - - if not pr_match: - primary = 0.0 - failure = f"Expected pr_id={gt_pr!r}, got {agent_pr!r}" - elif agent_verdict == gt_verdict: - primary = 1.0 - failure = None - else: - primary = 0.5 - failure = ( - f"Correct PR but wrong verdict: expected {gt_verdict!r}, " - f"got {agent_verdict!r}" - ) - - # Reviewer identification bonus - if gt_reviewer and agent_reviewer == gt_reviewer: - primary = min(1.0, primary + 0.15) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class ZDResolutionScorer(_BaseScorer): - """ - ZD_RESOLUTION — "Was Zendesk ticket X resolved and how long did it take?" - - Full credit: resolved boolean matches AND duration_days matches exactly. - Partial credit: boolean correct but duration wrong or missing (0.6). - escalated flag correct adds 0.1 bonus on top of partial. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_resolved = gt.get("resolved") - gt_duration = gt.get("duration_days") - gt_escalated = gt.get("escalated", False) - - agent_resolved = agent_answer.get("resolved") - agent_duration = agent_answer.get("duration_days") - agent_escalated = agent_answer.get("escalated") - - if agent_resolved != gt_resolved: - primary = 0.0 - failure = f"resolved expected {gt_resolved}, got {agent_resolved}" - else: - # Resolution boolean correct — check duration - if gt_duration is None: - # Ticket unresolved: correct if agent also has no duration - primary = 1.0 if agent_duration is None else 0.7 - failure = ( - None - if agent_duration is None - else "Correctly identified unresolved but reported a duration" - ) - elif agent_duration is not None and int(agent_duration) == int(gt_duration): - primary = 1.0 - failure = None - elif agent_duration is not None: - primary = 0.6 - failure = ( - f"Duration off: expected {gt_duration}d, got {agent_duration}d" - ) - else: - primary = 0.6 - failure = f"Correct resolution status but missing duration (expected {gt_duration}d)" - - # Escalation awareness bonus (+0.1, capped at 1.0) - if agent_escalated is not None and agent_escalated == gt_escalated: - primary = min(1.0, primary + 0.1) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class SFRiskScorer(_BaseScorer): - """ - SF_RISK — "Which Salesforce accounts were flagged at-risk after incident X?" - - Full credit: all at_risk_accounts matched (order-insensitive) AND - incident_id correct. - Partial: incident_id correct but incomplete account list (scaled by overlap). - 0.3 floor for incident match with zero account overlap. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_incident = gt.get("incident_id", "") - gt_accounts = [a.lower() for a in gt.get("at_risk_accounts", [])] - - agent_incident = agent_answer.get("incident_id", "") - agent_accounts = [a.lower() for a in agent_answer.get("at_risk_accounts", [])] - - incident_match = agent_incident == gt_incident - - if not gt_accounts: - primary = 1.0 if incident_match else 0.0 - failure = ( - None - if incident_match - else f"Expected incident_id={gt_incident!r}, got {agent_incident!r}" - ) - elif not incident_match: - primary = 0.0 - failure = f"Expected incident_id={gt_incident!r}, got {agent_incident!r}" - else: - gt_set = set(gt_accounts) - agent_set = set(agent_accounts) - overlap = len(gt_set & agent_set) / len(gt_set) if gt_set else 1.0 - - if overlap == 1.0 and len(agent_set) == len(gt_set): - primary = 1.0 - failure = None - elif overlap > 0: - primary = round(0.3 + 0.7 * overlap, 4) - failure = ( - f"Partial account match ({len(gt_set & agent_set)}/{len(gt_set)}): " - f"missing {gt_set - agent_set}" - ) - else: - primary = 0.3 # floor for correct incident identification - failure = ( - f"Correct incident but no accounts matched. Expected {gt_accounts}" - ) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class NPSScoreScorer(_BaseScorer): - """ - NPS_SCORE — "What NPS score did customer X give and what drove it?" - - Full credit: nps_score exact match AND classification correct. - Partial: classification correct but score wrong (0.6). - escalated_tickets count correct adds 0.1 bonus. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_score = gt.get("nps_score") - gt_class = gt.get("classification", "").lower() - gt_escalated = gt.get("escalated_tickets", 0) - - agent_score = agent_answer.get("nps_score") - agent_class = agent_answer.get("classification", "").lower() - agent_escalated = agent_answer.get("escalated_tickets") - - class_match = agent_class == gt_class - score_match = agent_score is not None and int(agent_score) == int(gt_score) - - if class_match and score_match: - primary = 1.0 - failure = None - elif class_match: - primary = 0.6 - failure = f"Correct classification but wrong score: expected {gt_score}, got {agent_score}" - else: - primary = 0.0 - failure = ( - f"Classification wrong: expected {gt_class!r}, got {agent_class!r}" - ) - - # Escalation count awareness bonus - if agent_escalated is not None and int(agent_escalated) == int(gt_escalated): - primary = min(1.0, primary + 0.1) - - evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - return primary, evidence, failure - - -class InvoiceSLAScorer(_BaseScorer): - """ - INVOICE_SLA — "What SLA credit appeared on the invoice for customers - affected by incident X?" - - Full credit: breach_duration_days exact AND sla_credit_per_org within 5%. - Partial: incident identified correctly but wrong duration/credit (0.5). - Evidence: affected_orgs overlap used as secondary evidence score. - """ - - def score( - self, question: dict, agent_answer: dict - ) -> Tuple[float, float, Optional[str]]: - gt = question["ground_truth"] - gt_incident = gt.get("incident_id", "") - gt_duration = gt.get("breach_duration_days") - gt_credit = gt.get("sla_credit_per_org") - gt_orgs = [o.lower() for o in gt.get("affected_orgs", [])] - - agent_incident = agent_answer.get("incident_id", "") - agent_duration = agent_answer.get("breach_duration_days") - agent_credit = agent_answer.get("sla_credit_per_org") - agent_orgs = [o.lower() for o in agent_answer.get("affected_orgs", [])] - - if agent_incident != gt_incident: - primary = 0.0 - failure = f"Expected incident_id={gt_incident!r}, got {agent_incident!r}" - else: - duration_ok = agent_duration is not None and int(agent_duration) == int( - gt_duration - ) - credit_ok = False - if agent_credit is not None and gt_credit: - try: - ratio = abs(float(agent_credit) - float(gt_credit)) / float( - gt_credit - ) - credit_ok = ratio <= 0.05 - except (TypeError, ZeroDivisionError): - pass - - if duration_ok and credit_ok: - primary = 1.0 - failure = None - elif duration_ok: - primary = 0.7 - failure = f"Correct duration but credit off: expected {gt_credit}, got {agent_credit}" - elif credit_ok: - primary = 0.7 - failure = f"Correct credit but duration off: expected {gt_duration}d, got {agent_duration}d" - else: - primary = 0.4 # floor for correct incident identification - failure = ( - f"Correct incident but duration ({agent_duration} vs {gt_duration}) " - f"and credit ({agent_credit} vs {gt_credit}) both wrong" - ) - - # Evidence: blend evidence_chain retrieval (did the agent find the invoice?) - # with affected_orgs overlap (did it identify the right customers?). - # evidence_chain recall is weighted more heavily (0.7) because it - # directly reflects whether the invoice artifact was retrieved; org - # overlap (0.3) is a secondary signal of answer quality. - chain_evidence = self._evidence_overlap( - question.get("evidence_chain", []), - agent_answer.get("retrieved_artifact_ids", []), - ) - if gt_orgs: - org_overlap = self._evidence_overlap(gt_orgs, agent_orgs) - evidence = round(0.7 * chain_evidence + 0.3 * org_overlap, 4) - else: - evidence = chain_evidence - return primary, evidence, failure - - -_SCORERS: Dict[str, _BaseScorer] = { - "RETRIEVAL": RetrievalScorer(), - "CAUSAL": CausalScorer(), - "TEMPORAL": TemporalScorer(), - "GAP_DETECTION": GapDetectionScorer(), - "ROUTING": RoutingScorer(), - "PLAN": PlanScorer(), - "ESCALATION": EscalationScorer(), - "KNOWLEDGE_GAP": KnowledgeGapScorer(), - "POSTMORTEM": PostmortemScorer(), - "STANDUP": RetrievalScorer(), - "CUSTOMER_ESC": CausalScorer(), - "ZD_RESOLUTION": ZDResolutionScorer(), - "ZD_ESCALATION": CausalScorer(), - "SF_RISK": SFRiskScorer(), - "NPS_SCORE": NPSScoreScorer(), - "INVOICE_SLA": InvoiceSLAScorer(), - "DATADOG_ALERT": RetrievalScorer(), - "PR_REVIEW": PRReviewScorer(), - "MULTI_HOP": MultiHopScorer(), -} - - -class OrgForgeScorer: - """ - Entry point for scoring OrgForge eval questions. - - scorer = OrgForgeScorer() - result = scorer.score(question_dict, agent_answer_dict) - """ - - def score(self, question: dict, agent_answer: dict) -> ScorerResult: - qtype = question.get("question_type", "UNKNOWN") - qid = question.get("question_id", "") - - # POSTMORTEM questions are stored with question_type=CAUSAL but use a - # different ground_truth schema (postmortem_confluence_id, not artifact_id). - # Route by question_id prefix so they get the right scorer. - if qid.startswith("postmortem_"): - qtype = "POSTMORTEM" - - scorer_impl = _SCORERS.get(qtype) - - if scorer_impl is None: - logger.warning(f"No scorer for question type: {qtype!r}. Returning 0.") - return ScorerResult( - question_id=question.get("question_id", "?"), - question_type=qtype, - difficulty=question.get("difficulty", "unknown"), - score=0.0, - primary_score=0.0, - evidence_score=0.0, - correct=False, - partial=False, - failure_reason=f"No scorer registered for type {qtype!r}", - ) - - primary, evidence, failure = scorer_impl.score(question, agent_answer) - combined = scorer_impl._combine(primary, evidence) - - return ScorerResult( - question_id=question.get("question_id", "?"), - question_type=qtype, - difficulty=question.get("difficulty", "unknown"), - score=round(combined, 4), - primary_score=round(primary, 4), - evidence_score=round(evidence, 4), - correct=combined >= 0.90, - partial=0.20 <= combined < 0.90, - failure_reason=failure, - meta={ - "requires_reasoning": question.get("requires_reasoning", False), - "chain_id": question.get("chain_id"), - }, - ) - - def score_all( - self, - questions: List[dict], - agent_answers: Dict[str, dict], # {question_id: answer_dict} - ) -> List[ScorerResult]: - """ - Score every question. Questions without a matching answer receive 0. - """ - results = [] - for q in questions: - qid = q.get("question_id", "") - answer = agent_answers.get(qid, {}) - if not answer: - logger.debug(f"No answer provided for {qid!r} — scoring as 0.") - results.append(self.score(q, answer)) - return results - - def report(self, results: List[ScorerResult]) -> Dict[str, Any]: - """ - Aggregate statistics over a scored result set. - Returns a dict suitable for JSON serialisation or dataset-card reporting. - """ - if not results: - return {} - - def _mean(vals): - return round(sum(vals) / len(vals), 4) if vals else 0.0 - - by_type: Dict[str, List[float]] = {} - by_difficulty: Dict[str, List[float]] = {} - - for r in results: - by_type.setdefault(r.question_type, []).append(r.score) - by_difficulty.setdefault(r.difficulty, []).append(r.score) - - total = len(results) - all_scores = [r.score for r in results] - - return { - "total_questions": total, - "overall_score": _mean(all_scores), - "accuracy": round(sum(r.correct for r in results) / total, 4), - "partial_rate": round(sum(r.partial for r in results) / total, 4), - "by_type": { - qtype: { - "n": len(scores), - "mean_score": _mean(scores), - "accuracy": round( - sum( - r.score >= 0.90 for r in results if r.question_type == qtype - ) - / len(scores), - 4, - ), - } - for qtype, scores in by_type.items() - }, - "by_difficulty": { - diff: { - "n": len(scores), - "mean_score": _mean(scores), - } - for diff, scores in by_difficulty.items() - }, - "reasoning_vs_direct": { - "requires_reasoning": _mean( - [r.score for r in results if r.meta.get("requires_reasoning")] - ), - "direct": _mean( - [r.score for r in results if not r.meta.get("requires_reasoning")] - ), - }, - } - - -if __name__ == "__main__": - import json - import pathlib - import sys - - eval_path = pathlib.Path("export/eval/eval_questions.json") - if not eval_path.exists(): - print("No eval_questions.json found. Run eval_harness.py first.") - sys.exit(1) - - data = json.loads(eval_path.read_text()) - questions = data.get("questions", []) - - mock_answers = {} - for q in questions: - gt = q["ground_truth"] - qid = q["question_id"] - qtype = q["question_type"] - if qtype == "RETRIEVAL": - mock_answers[qid] = {"artifact_id": gt.get("artifact_id", "")} - elif qtype == "CAUSAL": - mock_answers[qid] = { - "artifact_id": gt.get("artifact_id", ""), - "event_type": "wrong_type", # intentionally wrong to demonstrate partial credit scoring - } - elif qtype == "TEMPORAL": - mock_answers[qid] = {"had_knowledge": gt.get("had_knowledge")} - elif qtype == "GAP_DETECTION": - mock_answers[qid] = {"was_actioned": gt.get("was_actioned")} - elif qtype == "ROUTING": - mock_answers[qid] = {"first_recipient": gt.get("first_recipient", "")} - elif qtype == "PLAN": - mock_answers[qid] = { - "dept": gt.get("dept", ""), - "theme": gt.get("theme", ""), - } - elif qtype == "ESCALATION": - mock_answers[qid] = { - "escalation_actors": gt.get("escalation_actors", []), - } - elif qtype == "KNOWLEDGE_GAP": - mock_answers[qid] = { - "gap_areas": gt.get("gap_areas", []), - } - elif qtype == "ZD_RESOLUTION": - mock_answers[qid] = { - "resolved": gt.get("resolved"), - "duration_days": gt.get("duration_days"), - "escalated": gt.get("escalated"), - } - elif qtype == "ZD_ESCALATION": - mock_answers[qid] = { - "artifact_id": gt.get("artifact_id", ""), - "event_type": gt.get("event_type", ""), - } - elif qtype == "SF_RISK": - mock_answers[qid] = { - "incident_id": gt.get("incident_id", ""), - "at_risk_accounts": gt.get("at_risk_accounts", []), - } - elif qtype == "NPS_SCORE": - mock_answers[qid] = { - "nps_score": gt.get("nps_score"), - "classification": gt.get("classification", ""), - "escalated_tickets": gt.get("escalated_tickets", 0), - } - elif qtype == "INVOICE_SLA": - mock_answers[qid] = { - "incident_id": gt.get("incident_id", ""), - "breach_duration_days": gt.get("breach_duration_days"), - "sla_credit_per_org": gt.get("sla_credit_per_org"), - "affected_orgs": gt.get("affected_orgs", []), - } - elif qtype == "DATADOG_ALERT": - mock_answers[qid] = {"artifact_id": gt.get("artifact_id", "")} - elif qtype == "PR_REVIEW": - mock_answers[qid] = { - "pr_id": gt.get("pr_id", ""), - "verdict": gt.get("verdict", ""), - "reviewer": gt.get("reviewer", ""), - } - elif qtype == "MULTI_HOP": - mock_answers[qid] = { - "email_id": gt.get("email_id", ""), - "slack_thread_id": gt.get("slack_thread_id", ""), - "ticket_id": gt.get("ticket_id", ""), - "assignee": gt.get("assignee", ""), - "reply_id": gt.get("reply_id", ""), - "resolved_same_day": gt.get("resolved_same_day"), - } - - scorer = OrgForgeScorer() - results = scorer.score_all(questions, mock_answers) - report = scorer.report(results) - print(json.dumps(report, indent=2)) diff --git a/orgforge_hero.png b/orgforge_hero.png new file mode 100644 index 0000000000000000000000000000000000000000..b55bc197eb2168eecd8e34c29b578227ccd7475e GIT binary patch literal 161322 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