VibecoderMcSwaggins Mario commited on
Commit
3aa91e9
·
1 Parent(s): 17bd211

feat: Integrate Mario's Modal code execution + LlamaIndex RAG

Browse files

Cherry-picked from mario-dev branch:
- src/tools/code_execution.py: Modal sandbox for LLM-generated code
- src/services/llamaindex_rag.py: LlamaIndex + ChromaDB RAG service
- src/agents/analysis_agent.py: Statistical analysis agent
- examples/modal_demo/: Demo scripts for code execution
- docs/workflow-diagrams.md: Architecture documentation

Dependencies added as optional [modal] group:
- modal>=0.63.0, llama-index, chromadb

Config additions:
- MODAL_TOKEN_ID, MODAL_TOKEN_SECRET, CHROMA_DB_PATH

Co-authored-by: Mario <mario@contributor>

.env.example CHANGED
@@ -11,3 +11,12 @@ HF_TOKEN=hf_your-token-here
11
  # Agent Config
12
  MAX_ITERATIONS=10
13
  LOG_LEVEL=INFO
 
 
 
 
 
 
 
 
 
 
11
  # Agent Config
12
  MAX_ITERATIONS=10
13
  LOG_LEVEL=INFO
14
+
15
+ # ============== PARTNER SERVICES (Mario's Modal Integration) ==============
16
+
17
+ # Modal (Code Execution)
18
+ MODAL_TOKEN_ID=ak-your-modal-token-id-here
19
+ MODAL_TOKEN_SECRET=your-modal-token-secret-here
20
+
21
+ # Vector Database
22
+ CHROMA_DB_PATH=./chroma_db
docs/workflow-diagrams.md ADDED
@@ -0,0 +1,662 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeepCritical Workflow - Simplified Magentic Architecture
2
+
3
+ > **Architecture Pattern**: Microsoft Magentic Orchestration
4
+ > **Design Philosophy**: Simple, dynamic, manager-driven coordination
5
+ > **Key Innovation**: Intelligent manager replaces rigid sequential phases
6
+
7
+ ---
8
+
9
+ ## 1. High-Level Magentic Workflow
10
+
11
+ ```mermaid
12
+ flowchart TD
13
+ Start([User Query]) --> Manager[Magentic Manager<br/>Plan • Select • Assess • Adapt]
14
+
15
+ Manager -->|Plans| Task1[Task Decomposition]
16
+ Task1 --> Manager
17
+
18
+ Manager -->|Selects & Executes| HypAgent[Hypothesis Agent]
19
+ Manager -->|Selects & Executes| SearchAgent[Search Agent]
20
+ Manager -->|Selects & Executes| AnalysisAgent[Analysis Agent]
21
+ Manager -->|Selects & Executes| ReportAgent[Report Agent]
22
+
23
+ HypAgent -->|Results| Manager
24
+ SearchAgent -->|Results| Manager
25
+ AnalysisAgent -->|Results| Manager
26
+ ReportAgent -->|Results| Manager
27
+
28
+ Manager -->|Assesses Quality| Decision{Good Enough?}
29
+ Decision -->|No - Refine| Manager
30
+ Decision -->|No - Different Agent| Manager
31
+ Decision -->|No - Stalled| Replan[Reset Plan]
32
+ Replan --> Manager
33
+
34
+ Decision -->|Yes| Synthesis[Synthesize Final Result]
35
+ Synthesis --> Output([Research Report])
36
+
37
+ style Start fill:#e1f5e1
38
+ style Manager fill:#ffe6e6
39
+ style HypAgent fill:#fff4e6
40
+ style SearchAgent fill:#fff4e6
41
+ style AnalysisAgent fill:#fff4e6
42
+ style ReportAgent fill:#fff4e6
43
+ style Decision fill:#ffd6d6
44
+ style Synthesis fill:#d4edda
45
+ style Output fill:#e1f5e1
46
+ ```
47
+
48
+ ## 2. Magentic Manager: The 6-Phase Cycle
49
+
50
+ ```mermaid
51
+ flowchart LR
52
+ P1[1. Planning<br/>Analyze task<br/>Create strategy] --> P2[2. Agent Selection<br/>Pick best agent<br/>for subtask]
53
+ P2 --> P3[3. Execution<br/>Run selected<br/>agent with tools]
54
+ P3 --> P4[4. Assessment<br/>Evaluate quality<br/>Check progress]
55
+ P4 --> Decision{Quality OK?<br/>Progress made?}
56
+ Decision -->|Yes| P6[6. Synthesis<br/>Combine results<br/>Generate report]
57
+ Decision -->|No| P5[5. Iteration<br/>Adjust plan<br/>Try again]
58
+ P5 --> P2
59
+ P6 --> Done([Complete])
60
+
61
+ style P1 fill:#fff4e6
62
+ style P2 fill:#ffe6e6
63
+ style P3 fill:#e6f3ff
64
+ style P4 fill:#ffd6d6
65
+ style P5 fill:#fff3cd
66
+ style P6 fill:#d4edda
67
+ style Done fill:#e1f5e1
68
+ ```
69
+
70
+ ## 3. Simplified Agent Architecture
71
+
72
+ ```mermaid
73
+ graph TB
74
+ subgraph "Orchestration Layer"
75
+ Manager[Magentic Manager<br/>• Plans workflow<br/>• Selects agents<br/>• Assesses quality<br/>• Adapts strategy]
76
+ SharedContext[(Shared Context<br/>• Hypotheses<br/>• Search Results<br/>• Analysis<br/>• Progress)]
77
+ Manager <--> SharedContext
78
+ end
79
+
80
+ subgraph "Specialist Agents"
81
+ HypAgent[Hypothesis Agent<br/>• Domain understanding<br/>• Hypothesis generation<br/>• Testability refinement]
82
+ SearchAgent[Search Agent<br/>• Multi-source search<br/>• RAG retrieval<br/>• Result ranking]
83
+ AnalysisAgent[Analysis Agent<br/>• Evidence extraction<br/>• Statistical analysis<br/>• Code execution]
84
+ ReportAgent[Report Agent<br/>• Report assembly<br/>• Visualization<br/>• Citation formatting]
85
+ end
86
+
87
+ subgraph "MCP Tools"
88
+ WebSearch[Web Search<br/>PubMed • arXiv • bioRxiv]
89
+ CodeExec[Code Execution<br/>Sandboxed Python]
90
+ RAG[RAG Retrieval<br/>Vector DB • Embeddings]
91
+ Viz[Visualization<br/>Charts • Graphs]
92
+ end
93
+
94
+ Manager -->|Selects & Directs| HypAgent
95
+ Manager -->|Selects & Directs| SearchAgent
96
+ Manager -->|Selects & Directs| AnalysisAgent
97
+ Manager -->|Selects & Directs| ReportAgent
98
+
99
+ HypAgent --> SharedContext
100
+ SearchAgent --> SharedContext
101
+ AnalysisAgent --> SharedContext
102
+ ReportAgent --> SharedContext
103
+
104
+ SearchAgent --> WebSearch
105
+ SearchAgent --> RAG
106
+ AnalysisAgent --> CodeExec
107
+ ReportAgent --> CodeExec
108
+ ReportAgent --> Viz
109
+
110
+ style Manager fill:#ffe6e6
111
+ style SharedContext fill:#ffe6f0
112
+ style HypAgent fill:#fff4e6
113
+ style SearchAgent fill:#fff4e6
114
+ style AnalysisAgent fill:#fff4e6
115
+ style ReportAgent fill:#fff4e6
116
+ style WebSearch fill:#e6f3ff
117
+ style CodeExec fill:#e6f3ff
118
+ style RAG fill:#e6f3ff
119
+ style Viz fill:#e6f3ff
120
+ ```
121
+
122
+ ## 4. Dynamic Workflow Example
123
+
124
+ ```mermaid
125
+ sequenceDiagram
126
+ participant User
127
+ participant Manager
128
+ participant HypAgent
129
+ participant SearchAgent
130
+ participant AnalysisAgent
131
+ participant ReportAgent
132
+
133
+ User->>Manager: "Research protein folding in Alzheimer's"
134
+
135
+ Note over Manager: PLAN: Generate hypotheses → Search → Analyze → Report
136
+
137
+ Manager->>HypAgent: Generate 3 hypotheses
138
+ HypAgent-->>Manager: Returns 3 hypotheses
139
+ Note over Manager: ASSESS: Good quality, proceed
140
+
141
+ Manager->>SearchAgent: Search literature for hypothesis 1
142
+ SearchAgent-->>Manager: Returns 15 papers
143
+ Note over Manager: ASSESS: Good results, continue
144
+
145
+ Manager->>SearchAgent: Search for hypothesis 2
146
+ SearchAgent-->>Manager: Only 2 papers found
147
+ Note over Manager: ASSESS: Insufficient, refine search
148
+
149
+ Manager->>SearchAgent: Refined query for hypothesis 2
150
+ SearchAgent-->>Manager: Returns 12 papers
151
+ Note over Manager: ASSESS: Better, proceed
152
+
153
+ Manager->>AnalysisAgent: Analyze evidence for all hypotheses
154
+ AnalysisAgent-->>Manager: Returns analysis with code
155
+ Note over Manager: ASSESS: Complete, generate report
156
+
157
+ Manager->>ReportAgent: Create comprehensive report
158
+ ReportAgent-->>Manager: Returns formatted report
159
+ Note over Manager: SYNTHESIZE: Combine all results
160
+
161
+ Manager->>User: Final Research Report
162
+ ```
163
+
164
+ ## 5. Manager Decision Logic
165
+
166
+ ```mermaid
167
+ flowchart TD
168
+ Start([Manager Receives Task]) --> Plan[Create Initial Plan]
169
+
170
+ Plan --> Select[Select Agent for Next Subtask]
171
+ Select --> Execute[Execute Agent]
172
+ Execute --> Collect[Collect Results]
173
+
174
+ Collect --> Assess[Assess Quality & Progress]
175
+
176
+ Assess --> Q1{Quality Sufficient?}
177
+ Q1 -->|No| Q2{Same Agent Can Fix?}
178
+ Q2 -->|Yes| Feedback[Provide Specific Feedback]
179
+ Feedback --> Execute
180
+ Q2 -->|No| Different[Try Different Agent]
181
+ Different --> Select
182
+
183
+ Q1 -->|Yes| Q3{Task Complete?}
184
+ Q3 -->|No| Q4{Making Progress?}
185
+ Q4 -->|Yes| Select
186
+ Q4 -->|No - Stalled| Replan[Reset Plan & Approach]
187
+ Replan --> Plan
188
+
189
+ Q3 -->|Yes| Synth[Synthesize Final Result]
190
+ Synth --> Done([Return Report])
191
+
192
+ style Start fill:#e1f5e1
193
+ style Plan fill:#fff4e6
194
+ style Select fill:#ffe6e6
195
+ style Execute fill:#e6f3ff
196
+ style Assess fill:#ffd6d6
197
+ style Q1 fill:#ffe6e6
198
+ style Q2 fill:#ffe6e6
199
+ style Q3 fill:#ffe6e6
200
+ style Q4 fill:#ffe6e6
201
+ style Synth fill:#d4edda
202
+ style Done fill:#e1f5e1
203
+ ```
204
+
205
+ ## 6. Hypothesis Agent Workflow
206
+
207
+ ```mermaid
208
+ flowchart LR
209
+ Input[Research Query] --> Domain[Identify Domain<br/>& Key Concepts]
210
+ Domain --> Context[Retrieve Background<br/>Knowledge]
211
+ Context --> Generate[Generate 3-5<br/>Initial Hypotheses]
212
+ Generate --> Refine[Refine for<br/>Testability]
213
+ Refine --> Rank[Rank by<br/>Quality Score]
214
+ Rank --> Output[Return Top<br/>Hypotheses]
215
+
216
+ Output --> Struct[Hypothesis Structure:<br/>• Statement<br/>• Rationale<br/>• Testability Score<br/>• Data Requirements<br/>• Expected Outcomes]
217
+
218
+ style Input fill:#e1f5e1
219
+ style Output fill:#fff4e6
220
+ style Struct fill:#e6f3ff
221
+ ```
222
+
223
+ ## 7. Search Agent Workflow
224
+
225
+ ```mermaid
226
+ flowchart TD
227
+ Input[Hypotheses] --> Strategy[Formulate Search<br/>Strategy per Hypothesis]
228
+
229
+ Strategy --> Multi[Multi-Source Search]
230
+
231
+ Multi --> PubMed[PubMed Search<br/>via MCP]
232
+ Multi --> ArXiv[arXiv Search<br/>via MCP]
233
+ Multi --> BioRxiv[bioRxiv Search<br/>via MCP]
234
+
235
+ PubMed --> Aggregate[Aggregate Results]
236
+ ArXiv --> Aggregate
237
+ BioRxiv --> Aggregate
238
+
239
+ Aggregate --> Filter[Filter & Rank<br/>by Relevance]
240
+ Filter --> Dedup[Deduplicate<br/>Cross-Reference]
241
+ Dedup --> Embed[Embed Documents<br/>via MCP]
242
+ Embed --> Vector[(Vector DB)]
243
+ Vector --> RAGRetrieval[RAG Retrieval<br/>Top-K per Hypothesis]
244
+ RAGRetrieval --> Output[Return Contextualized<br/>Search Results]
245
+
246
+ style Input fill:#fff4e6
247
+ style Multi fill:#ffe6e6
248
+ style Vector fill:#ffe6f0
249
+ style Output fill:#e6f3ff
250
+ ```
251
+
252
+ ## 8. Analysis Agent Workflow
253
+
254
+ ```mermaid
255
+ flowchart TD
256
+ Input1[Hypotheses] --> Extract
257
+ Input2[Search Results] --> Extract[Extract Evidence<br/>per Hypothesis]
258
+
259
+ Extract --> Methods[Determine Analysis<br/>Methods Needed]
260
+
261
+ Methods --> Branch{Requires<br/>Computation?}
262
+ Branch -->|Yes| GenCode[Generate Python<br/>Analysis Code]
263
+ Branch -->|No| Qual[Qualitative<br/>Synthesis]
264
+
265
+ GenCode --> Execute[Execute Code<br/>via MCP Sandbox]
266
+ Execute --> Interpret1[Interpret<br/>Results]
267
+ Qual --> Interpret2[Interpret<br/>Findings]
268
+
269
+ Interpret1 --> Synthesize[Synthesize Evidence<br/>Across Sources]
270
+ Interpret2 --> Synthesize
271
+
272
+ Synthesize --> Verdict[Determine Verdict<br/>per Hypothesis]
273
+ Verdict --> Support[• Supported<br/>• Refuted<br/>• Inconclusive]
274
+ Support --> Gaps[Identify Knowledge<br/>Gaps & Limitations]
275
+ Gaps --> Output[Return Analysis<br/>Report]
276
+
277
+ style Input1 fill:#fff4e6
278
+ style Input2 fill:#e6f3ff
279
+ style Execute fill:#ffe6e6
280
+ style Output fill:#e6ffe6
281
+ ```
282
+
283
+ ## 9. Report Agent Workflow
284
+
285
+ ```mermaid
286
+ flowchart TD
287
+ Input1[Query] --> Assemble
288
+ Input2[Hypotheses] --> Assemble
289
+ Input3[Search Results] --> Assemble
290
+ Input4[Analysis] --> Assemble[Assemble Report<br/>Sections]
291
+
292
+ Assemble --> Exec[Executive Summary]
293
+ Assemble --> Intro[Introduction]
294
+ Assemble --> Methods[Methods]
295
+ Assemble --> Results[Results per<br/>Hypothesis]
296
+ Assemble --> Discussion[Discussion]
297
+ Assemble --> Future[Future Directions]
298
+ Assemble --> Refs[References]
299
+
300
+ Results --> VizCheck{Needs<br/>Visualization?}
301
+ VizCheck -->|Yes| GenViz[Generate Viz Code]
302
+ GenViz --> ExecViz[Execute via MCP<br/>Create Charts]
303
+ ExecViz --> Combine
304
+ VizCheck -->|No| Combine[Combine All<br/>Sections]
305
+
306
+ Exec --> Combine
307
+ Intro --> Combine
308
+ Methods --> Combine
309
+ Discussion --> Combine
310
+ Future --> Combine
311
+ Refs --> Combine
312
+
313
+ Combine --> Format[Format Output]
314
+ Format --> MD[Markdown]
315
+ Format --> PDF[PDF]
316
+ Format --> JSON[JSON]
317
+
318
+ MD --> Output[Return Final<br/>Report]
319
+ PDF --> Output
320
+ JSON --> Output
321
+
322
+ style Input1 fill:#e1f5e1
323
+ style Input2 fill:#fff4e6
324
+ style Input3 fill:#e6f3ff
325
+ style Input4 fill:#e6ffe6
326
+ style Output fill:#d4edda
327
+ ```
328
+
329
+ ## 10. Data Flow & Event Streaming
330
+
331
+ ```mermaid
332
+ flowchart TD
333
+ User[👤 User] -->|Research Query| UI[Gradio UI]
334
+ UI -->|Submit| Manager[Magentic Manager]
335
+
336
+ Manager -->|Event: Planning| UI
337
+ Manager -->|Select Agent| HypAgent[Hypothesis Agent]
338
+ HypAgent -->|Event: Delta/Message| UI
339
+ HypAgent -->|Hypotheses| Context[(Shared Context)]
340
+
341
+ Context -->|Retrieved by| Manager
342
+ Manager -->|Select Agent| SearchAgent[Search Agent]
343
+ SearchAgent -->|MCP Request| WebSearch[Web Search Tool]
344
+ WebSearch -->|Results| SearchAgent
345
+ SearchAgent -->|Event: Delta/Message| UI
346
+ SearchAgent -->|Documents| Context
347
+ SearchAgent -->|Embeddings| VectorDB[(Vector DB)]
348
+
349
+ Context -->|Retrieved by| Manager
350
+ Manager -->|Select Agent| AnalysisAgent[Analysis Agent]
351
+ AnalysisAgent -->|MCP Request| CodeExec[Code Execution Tool]
352
+ CodeExec -->|Results| AnalysisAgent
353
+ AnalysisAgent -->|Event: Delta/Message| UI
354
+ AnalysisAgent -->|Analysis| Context
355
+
356
+ Context -->|Retrieved by| Manager
357
+ Manager -->|Select Agent| ReportAgent[Report Agent]
358
+ ReportAgent -->|MCP Request| CodeExec
359
+ ReportAgent -->|Event: Delta/Message| UI
360
+ ReportAgent -->|Report| Context
361
+
362
+ Manager -->|Event: Final Result| UI
363
+ UI -->|Display| User
364
+
365
+ style User fill:#e1f5e1
366
+ style UI fill:#e6f3ff
367
+ style Manager fill:#ffe6e6
368
+ style Context fill:#ffe6f0
369
+ style VectorDB fill:#ffe6f0
370
+ style WebSearch fill:#f0f0f0
371
+ style CodeExec fill:#f0f0f0
372
+ ```
373
+
374
+ ## 11. MCP Tool Architecture
375
+
376
+ ```mermaid
377
+ graph TB
378
+ subgraph "Agent Layer"
379
+ Manager[Magentic Manager]
380
+ HypAgent[Hypothesis Agent]
381
+ SearchAgent[Search Agent]
382
+ AnalysisAgent[Analysis Agent]
383
+ ReportAgent[Report Agent]
384
+ end
385
+
386
+ subgraph "MCP Protocol Layer"
387
+ Registry[MCP Tool Registry<br/>• Discovers tools<br/>• Routes requests<br/>• Manages connections]
388
+ end
389
+
390
+ subgraph "MCP Servers"
391
+ Server1[Web Search Server<br/>localhost:8001<br/>• PubMed<br/>• arXiv<br/>• bioRxiv]
392
+ Server2[Code Execution Server<br/>localhost:8002<br/>• Sandboxed Python<br/>• Package management]
393
+ Server3[RAG Server<br/>localhost:8003<br/>• Vector embeddings<br/>• Similarity search]
394
+ Server4[Visualization Server<br/>localhost:8004<br/>• Chart generation<br/>• Plot rendering]
395
+ end
396
+
397
+ subgraph "External Services"
398
+ PubMed[PubMed API]
399
+ ArXiv[arXiv API]
400
+ BioRxiv[bioRxiv API]
401
+ Docker[Docker Sandbox]
402
+ ChromaDB[(ChromaDB)]
403
+ end
404
+
405
+ SearchAgent -->|Request| Registry
406
+ AnalysisAgent -->|Request| Registry
407
+ ReportAgent -->|Request| Registry
408
+
409
+ Registry --> Server1
410
+ Registry --> Server2
411
+ Registry --> Server3
412
+ Registry --> Server4
413
+
414
+ Server1 --> PubMed
415
+ Server1 --> ArXiv
416
+ Server1 --> BioRxiv
417
+ Server2 --> Docker
418
+ Server3 --> ChromaDB
419
+
420
+ style Manager fill:#ffe6e6
421
+ style Registry fill:#fff4e6
422
+ style Server1 fill:#e6f3ff
423
+ style Server2 fill:#e6f3ff
424
+ style Server3 fill:#e6f3ff
425
+ style Server4 fill:#e6f3ff
426
+ ```
427
+
428
+ ## 12. Progress Tracking & Stall Detection
429
+
430
+ ```mermaid
431
+ stateDiagram-v2
432
+ [*] --> Initialization: User Query
433
+
434
+ Initialization --> Planning: Manager starts
435
+
436
+ Planning --> AgentExecution: Select agent
437
+
438
+ AgentExecution --> Assessment: Collect results
439
+
440
+ Assessment --> QualityCheck: Evaluate output
441
+
442
+ QualityCheck --> AgentExecution: Poor quality<br/>(retry < max_rounds)
443
+ QualityCheck --> Planning: Poor quality<br/>(try different agent)
444
+ QualityCheck --> NextAgent: Good quality<br/>(task incomplete)
445
+ QualityCheck --> Synthesis: Good quality<br/>(task complete)
446
+
447
+ NextAgent --> AgentExecution: Select next agent
448
+
449
+ state StallDetection <<choice>>
450
+ Assessment --> StallDetection: Check progress
451
+ StallDetection --> Planning: No progress<br/>(stall count < max)
452
+ StallDetection --> ErrorRecovery: No progress<br/>(max stalls reached)
453
+
454
+ ErrorRecovery --> PartialReport: Generate partial results
455
+ PartialReport --> [*]
456
+
457
+ Synthesis --> FinalReport: Combine all outputs
458
+ FinalReport --> [*]
459
+
460
+ note right of QualityCheck
461
+ Manager assesses:
462
+ • Output completeness
463
+ • Quality metrics
464
+ • Progress made
465
+ end note
466
+
467
+ note right of StallDetection
468
+ Stall = no new progress
469
+ after agent execution
470
+ Triggers plan reset
471
+ end note
472
+ ```
473
+
474
+ ## 13. Gradio UI Integration
475
+
476
+ ```mermaid
477
+ graph TD
478
+ App[Gradio App<br/>DeepCritical Research Agent]
479
+
480
+ App --> Input[Input Section]
481
+ App --> Status[Status Section]
482
+ App --> Output[Output Section]
483
+
484
+ Input --> Query[Research Question<br/>Text Area]
485
+ Input --> Controls[Controls]
486
+ Controls --> MaxHyp[Max Hypotheses: 1-10]
487
+ Controls --> MaxRounds[Max Rounds: 5-20]
488
+ Controls --> Submit[Start Research Button]
489
+
490
+ Status --> Log[Real-time Event Log<br/>• Manager planning<br/>• Agent selection<br/>• Execution updates<br/>• Quality assessment]
491
+ Status --> Progress[Progress Tracker<br/>• Current agent<br/>• Round count<br/>• Stall count]
492
+
493
+ Output --> Tabs[Tabbed Results]
494
+ Tabs --> Tab1[Hypotheses Tab<br/>Generated hypotheses with scores]
495
+ Tabs --> Tab2[Search Results Tab<br/>Papers & sources found]
496
+ Tabs --> Tab3[Analysis Tab<br/>Evidence & verdicts]
497
+ Tabs --> Tab4[Report Tab<br/>Final research report]
498
+ Tab4 --> Download[Download Report<br/>MD / PDF / JSON]
499
+
500
+ Submit -.->|Triggers| Workflow[Magentic Workflow]
501
+ Workflow -.->|MagenticOrchestratorMessageEvent| Log
502
+ Workflow -.->|MagenticAgentDeltaEvent| Log
503
+ Workflow -.->|MagenticAgentMessageEvent| Log
504
+ Workflow -.->|MagenticFinalResultEvent| Tab4
505
+
506
+ style App fill:#e1f5e1
507
+ style Input fill:#fff4e6
508
+ style Status fill:#e6f3ff
509
+ style Output fill:#e6ffe6
510
+ style Workflow fill:#ffe6e6
511
+ ```
512
+
513
+ ## 14. Complete System Context
514
+
515
+ ```mermaid
516
+ graph LR
517
+ User[👤 Researcher<br/>Asks research questions] -->|Submits query| DC[DeepCritical<br/>Magentic Workflow]
518
+
519
+ DC -->|Literature search| PubMed[PubMed API<br/>Medical papers]
520
+ DC -->|Preprint search| ArXiv[arXiv API<br/>Scientific preprints]
521
+ DC -->|Biology search| BioRxiv[bioRxiv API<br/>Biology preprints]
522
+ DC -->|Agent reasoning| Claude[Claude API<br/>Sonnet 4 / Opus]
523
+ DC -->|Code execution| Docker[Docker Sandbox<br/>Safe Python env]
524
+ DC -->|Vector storage| Chroma[ChromaDB<br/>Embeddings & RAG]
525
+
526
+ DC -->|Deployed on| HF[HuggingFace Spaces<br/>Gradio 6.0]
527
+
528
+ PubMed -->|Results| DC
529
+ ArXiv -->|Results| DC
530
+ BioRxiv -->|Results| DC
531
+ Claude -->|Responses| DC
532
+ Docker -->|Output| DC
533
+ Chroma -->|Context| DC
534
+
535
+ DC -->|Research report| User
536
+
537
+ style User fill:#e1f5e1
538
+ style DC fill:#ffe6e6
539
+ style PubMed fill:#e6f3ff
540
+ style ArXiv fill:#e6f3ff
541
+ style BioRxiv fill:#e6f3ff
542
+ style Claude fill:#ffd6d6
543
+ style Docker fill:#f0f0f0
544
+ style Chroma fill:#ffe6f0
545
+ style HF fill:#d4edda
546
+ ```
547
+
548
+ ## 15. Workflow Timeline (Simplified)
549
+
550
+ ```mermaid
551
+ gantt
552
+ title DeepCritical Magentic Workflow - Typical Execution
553
+ dateFormat mm:ss
554
+ axisFormat %M:%S
555
+
556
+ section Manager Planning
557
+ Initial planning :p1, 00:00, 10s
558
+
559
+ section Hypothesis Agent
560
+ Generate hypotheses :h1, after p1, 30s
561
+ Manager assessment :h2, after h1, 5s
562
+
563
+ section Search Agent
564
+ Search hypothesis 1 :s1, after h2, 20s
565
+ Search hypothesis 2 :s2, after s1, 20s
566
+ Search hypothesis 3 :s3, after s2, 20s
567
+ RAG processing :s4, after s3, 15s
568
+ Manager assessment :s5, after s4, 5s
569
+
570
+ section Analysis Agent
571
+ Evidence extraction :a1, after s5, 15s
572
+ Code generation :a2, after a1, 20s
573
+ Code execution :a3, after a2, 25s
574
+ Synthesis :a4, after a3, 20s
575
+ Manager assessment :a5, after a4, 5s
576
+
577
+ section Report Agent
578
+ Report assembly :r1, after a5, 30s
579
+ Visualization :r2, after r1, 15s
580
+ Formatting :r3, after r2, 10s
581
+
582
+ section Manager Synthesis
583
+ Final synthesis :f1, after r3, 10s
584
+ ```
585
+
586
+ ---
587
+
588
+ ## Key Differences from Original Design
589
+
590
+ | Aspect | Original (Judge-in-Loop) | New (Magentic) |
591
+ |--------|-------------------------|----------------|
592
+ | **Control Flow** | Fixed sequential phases | Dynamic agent selection |
593
+ | **Quality Control** | Separate Judge Agent | Manager assessment built-in |
594
+ | **Retry Logic** | Phase-level with feedback | Agent-level with adaptation |
595
+ | **Flexibility** | Rigid 4-phase pipeline | Adaptive workflow |
596
+ | **Complexity** | 5 agents (including Judge) | 4 agents (no Judge) |
597
+ | **Progress Tracking** | Manual state management | Built-in round/stall detection |
598
+ | **Agent Coordination** | Sequential handoff | Manager-driven dynamic selection |
599
+ | **Error Recovery** | Retry same phase | Try different agent or replan |
600
+
601
+ ---
602
+
603
+ ## Simplified Design Principles
604
+
605
+ 1. **Manager is Intelligent**: LLM-powered manager handles planning, selection, and quality assessment
606
+ 2. **No Separate Judge**: Manager's assessment phase replaces dedicated Judge Agent
607
+ 3. **Dynamic Workflow**: Agents can be called multiple times in any order based on need
608
+ 4. **Built-in Safety**: max_round_count (15) and max_stall_count (3) prevent infinite loops
609
+ 5. **Event-Driven UI**: Real-time streaming updates to Gradio interface
610
+ 6. **MCP-Powered Tools**: All external capabilities via Model Context Protocol
611
+ 7. **Shared Context**: Centralized state accessible to all agents
612
+ 8. **Progress Awareness**: Manager tracks what's been done and what's needed
613
+
614
+ ---
615
+
616
+ ## Legend
617
+
618
+ - 🔴 **Red/Pink**: Manager, orchestration, decision-making
619
+ - 🟡 **Yellow/Orange**: Specialist agents, processing
620
+ - 🔵 **Blue**: Data, tools, MCP services
621
+ - 🟣 **Purple/Pink**: Storage, databases, state
622
+ - 🟢 **Green**: User interactions, final outputs
623
+ - ⚪ **Gray**: External services, APIs
624
+
625
+ ---
626
+
627
+ ## Implementation Highlights
628
+
629
+ **Simple 4-Agent Setup:**
630
+ ```python
631
+ workflow = (
632
+ MagenticBuilder()
633
+ .participants(
634
+ hypothesis=HypothesisAgent(tools=[background_tool]),
635
+ search=SearchAgent(tools=[web_search, rag_tool]),
636
+ analysis=AnalysisAgent(tools=[code_execution]),
637
+ report=ReportAgent(tools=[code_execution, visualization])
638
+ )
639
+ .with_standard_manager(
640
+ chat_client=AnthropicClient(model="claude-sonnet-4"),
641
+ max_round_count=15, # Prevent infinite loops
642
+ max_stall_count=3 # Detect stuck workflows
643
+ )
644
+ .build()
645
+ )
646
+ ```
647
+
648
+ **Manager handles quality assessment in its instructions:**
649
+ - Checks hypothesis quality (testable, novel, clear)
650
+ - Validates search results (relevant, authoritative, recent)
651
+ - Assesses analysis soundness (methodology, evidence, conclusions)
652
+ - Ensures report completeness (all sections, proper citations)
653
+
654
+ No separate Judge Agent needed - manager does it all!
655
+
656
+ ---
657
+
658
+ **Document Version**: 2.0 (Magentic Simplified)
659
+ **Last Updated**: 2025-11-24
660
+ **Architecture**: Microsoft Magentic Orchestration Pattern
661
+ **Agents**: 4 (Hypothesis, Search, Analysis, Report) + 1 Manager
662
+ **License**: MIT
examples/modal_demo/test_code_execution.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Demo script to test Modal code execution integration.
2
+
3
+ Run with: uv run python examples/modal_demo/test_code_execution.py
4
+ """
5
+
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ # Add src to path
10
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
11
+
12
+ from src.tools.code_execution import CodeExecutionError, get_code_executor
13
+
14
+
15
+ def test_basic_execution():
16
+ """Test basic code execution."""
17
+ print("\n=== Test 1: Basic Execution ===")
18
+ executor = get_code_executor()
19
+
20
+ code = """
21
+ print("Hello from Modal sandbox!")
22
+ result = 2 + 2
23
+ print(f"2 + 2 = {result}")
24
+ """
25
+
26
+ result = executor.execute(code)
27
+ print(f"Success: {result['success']}")
28
+ print(f"Stdout:\n{result['stdout']}")
29
+ if result["stderr"]:
30
+ print(f"Stderr:\n{result['stderr']}")
31
+
32
+
33
+ def test_scientific_computing():
34
+ """Test scientific computing libraries."""
35
+ print("\n=== Test 2: Scientific Computing ===")
36
+ executor = get_code_executor()
37
+
38
+ code = """
39
+ import pandas as pd
40
+ import numpy as np
41
+
42
+ # Create sample data
43
+ data = {
44
+ 'drug': ['DrugA', 'DrugB', 'DrugC'],
45
+ 'efficacy': [0.75, 0.82, 0.68],
46
+ 'sample_size': [100, 150, 120]
47
+ }
48
+
49
+ df = pd.DataFrame(data)
50
+
51
+ # Calculate weighted average
52
+ weighted_avg = np.average(df['efficacy'], weights=df['sample_size'])
53
+
54
+ print(f"Drugs tested: {len(df)}")
55
+ print(f"Weighted average efficacy: {weighted_avg:.3f}")
56
+ print("\\nDataFrame:")
57
+ print(df.to_string())
58
+ """
59
+
60
+ result = executor.execute(code)
61
+ print(f"Success: {result['success']}")
62
+ print(f"Output:\n{result['stdout']}")
63
+
64
+
65
+ def test_statistical_analysis():
66
+ """Test statistical analysis."""
67
+ print("\n=== Test 3: Statistical Analysis ===")
68
+ executor = get_code_executor()
69
+
70
+ code = """
71
+ import numpy as np
72
+ from scipy import stats
73
+
74
+ # Simulate two treatment groups
75
+ np.random.seed(42)
76
+ control_group = np.random.normal(100, 15, 50)
77
+ treatment_group = np.random.normal(110, 15, 50)
78
+
79
+ # Perform t-test
80
+ t_stat, p_value = stats.ttest_ind(treatment_group, control_group)
81
+
82
+ print(f"Control mean: {np.mean(control_group):.2f}")
83
+ print(f"Treatment mean: {np.mean(treatment_group):.2f}")
84
+ print(f"T-statistic: {t_stat:.3f}")
85
+ print(f"P-value: {p_value:.4f}")
86
+
87
+ if p_value < 0.05:
88
+ print("Result: Statistically significant difference")
89
+ else:
90
+ print("Result: No significant difference")
91
+ """
92
+
93
+ result = executor.execute(code)
94
+ print(f"Success: {result['success']}")
95
+ print(f"Output:\n{result['stdout']}")
96
+
97
+
98
+ def test_with_return_value():
99
+ """Test execute_with_return method."""
100
+ print("\n=== Test 4: Return Value ===")
101
+ executor = get_code_executor()
102
+
103
+ code = """
104
+ import numpy as np
105
+
106
+ # Calculate something
107
+ data = np.array([1, 2, 3, 4, 5])
108
+ result = {
109
+ 'mean': float(np.mean(data)),
110
+ 'std': float(np.std(data)),
111
+ 'sum': int(np.sum(data))
112
+ }
113
+ """
114
+
115
+ try:
116
+ result = executor.execute_with_return(code)
117
+ print(f"Returned result: {result}")
118
+ print(f"Mean: {result['mean']}")
119
+ print(f"Std: {result['std']}")
120
+ print(f"Sum: {result['sum']}")
121
+ except CodeExecutionError as e:
122
+ print(f"Error: {e}")
123
+
124
+
125
+ def test_error_handling():
126
+ """Test error handling."""
127
+ print("\n=== Test 5: Error Handling ===")
128
+ executor = get_code_executor()
129
+
130
+ code = """
131
+ # This will fail
132
+ x = 1 / 0
133
+ """
134
+
135
+ result = executor.execute(code)
136
+ print(f"Success: {result['success']}")
137
+ print(f"Error: {result['error']}")
138
+
139
+
140
+ def main():
141
+ """Run all tests."""
142
+ print("=" * 60)
143
+ print("Modal Code Execution Demo")
144
+ print("=" * 60)
145
+
146
+ tests = [
147
+ test_basic_execution,
148
+ test_scientific_computing,
149
+ test_statistical_analysis,
150
+ test_with_return_value,
151
+ test_error_handling,
152
+ ]
153
+
154
+ for test in tests:
155
+ try:
156
+ test()
157
+ except Exception as e:
158
+ print(f"\n❌ Test failed: {e}")
159
+ import traceback
160
+
161
+ traceback.print_exc()
162
+
163
+ print("\n" + "=" * 60)
164
+ print("Demo completed!")
165
+ print("=" * 60)
166
+
167
+
168
+ if __name__ == "__main__":
169
+ main()
examples/modal_demo/verify_sandbox.py ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Verification script to prove code is running in Modal sandboxes, not locally.
2
+
3
+ This script runs tests that would behave differently in a sandbox vs local execution.
4
+ """
5
+
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
10
+
11
+ from src.tools.code_execution import get_code_executor
12
+
13
+
14
+ def test_1_hostname_check():
15
+ """Test 1: Check hostname - should be different in sandbox."""
16
+ print("\n" + "=" * 60)
17
+ print("TEST 1: Hostname Check")
18
+ print("=" * 60)
19
+
20
+ executor = get_code_executor()
21
+
22
+ # Get local hostname
23
+ import socket
24
+
25
+ local_hostname = socket.gethostname()
26
+ print(f"Local hostname: {local_hostname}")
27
+
28
+ # Get sandbox hostname
29
+ code = """
30
+ import socket
31
+ hostname = socket.gethostname()
32
+ print(f"Sandbox hostname: {hostname}")
33
+ """
34
+
35
+ result = executor.execute(code)
36
+ print(f"\n{result['stdout']}")
37
+
38
+ if local_hostname in result["stdout"]:
39
+ print("⚠️ WARNING: Hostnames match - might be running locally!")
40
+ return False
41
+ else:
42
+ print("✅ SUCCESS: Different hostnames - running in sandbox!")
43
+ return True
44
+
45
+
46
+ def test_2_file_system_isolation():
47
+ """Test 2: Try to access local files - should fail in sandbox."""
48
+ print("\n" + "=" * 60)
49
+ print("TEST 2: File System Isolation")
50
+ print("=" * 60)
51
+
52
+ executor = get_code_executor()
53
+
54
+ # Try to read our own source file
55
+ local_file = Path(__file__).resolve()
56
+ print(f"Local file exists: {local_file}")
57
+ print(f"Can read locally: {local_file.exists()}")
58
+
59
+ # Try to access it from sandbox
60
+ code = f"""
61
+ from pathlib import Path
62
+ file_path = Path("{local_file}")
63
+ exists = file_path.exists()
64
+ print(f"File exists in sandbox: {{exists}}")
65
+ if exists:
66
+ print("⚠️ Can access local filesystem!")
67
+ else:
68
+ print("✅ Filesystem is isolated!")
69
+ """
70
+
71
+ result = executor.execute(code)
72
+ print(f"\n{result['stdout']}")
73
+
74
+ if "File exists in sandbox: True" in result["stdout"]:
75
+ print("\n⚠️ WARNING: Can access local files - not properly sandboxed!")
76
+ return False
77
+ else:
78
+ print("\n✅ SUCCESS: Cannot access local files - properly sandboxed!")
79
+ return True
80
+
81
+
82
+ def test_3_process_information():
83
+ """Test 3: Check process and container info."""
84
+ print("\n" + "=" * 60)
85
+ print("TEST 3: Process Information")
86
+ print("=" * 60)
87
+
88
+ executor = get_code_executor()
89
+
90
+ code = """
91
+ import os
92
+ import sys
93
+ import platform
94
+
95
+ print(f"Python version: {sys.version}")
96
+ print(f"Platform: {platform.platform()}")
97
+ print(f"Machine: {platform.machine()}")
98
+ print(f"Process ID: {os.getpid()}")
99
+ print(f"User: {os.getenv('USER', 'unknown')}")
100
+ print(f"Home: {os.getenv('HOME', 'unknown')}")
101
+ print(f"Working directory: {os.getcwd()}")
102
+
103
+ # Check if running in container
104
+ in_container = os.path.exists('/.dockerenv') or os.path.exists('/run/.containerenv')
105
+ print(f"In container: {in_container}")
106
+ """
107
+
108
+ result = executor.execute(code)
109
+ print(f"\n{result['stdout']}")
110
+
111
+ if "In container: True" in result["stdout"]:
112
+ print("\n✅ SUCCESS: Running in containerized environment!")
113
+ return True
114
+ else:
115
+ print("\n⚠️ WARNING: Not detecting container environment")
116
+ return False
117
+
118
+
119
+ def test_4_library_versions():
120
+ """Test 4: Check if scientific libraries match Modal image specs."""
121
+ print("\n" + "=" * 60)
122
+ print("TEST 4: Library Versions (Should match Modal image)")
123
+ print("=" * 60)
124
+
125
+ executor = get_code_executor()
126
+
127
+ code = """
128
+ import pandas as pd
129
+ import numpy as np
130
+ import scipy
131
+ import matplotlib
132
+ import sklearn
133
+ import statsmodels
134
+
135
+ print(f"pandas: {pd.__version__}")
136
+ print(f"numpy: {np.__version__}")
137
+ print(f"scipy: {scipy.__version__}")
138
+ print(f"matplotlib: {matplotlib.__version__}")
139
+ print(f"scikit-learn: {sklearn.__version__}")
140
+ print(f"statsmodels: {statsmodels.__version__}")
141
+ """
142
+
143
+ result = executor.execute(code)
144
+ print(f"\n{result['stdout']}")
145
+
146
+ # Check if versions match what we specified in code_execution.py
147
+ expected_versions = {
148
+ "pandas: 2.2.0": True,
149
+ "numpy: 1.26.4": True,
150
+ "scipy: 1.11.4": True,
151
+ }
152
+
153
+ matches = 0
154
+ for expected in expected_versions:
155
+ if expected in result["stdout"]:
156
+ matches += 1
157
+ print(f"✅ {expected}")
158
+
159
+ if matches >= 2:
160
+ print(f"\n✅ SUCCESS: Library versions match Modal image spec ({matches}/3)")
161
+ return True
162
+ else:
163
+ print(f"\n⚠️ WARNING: Library versions don't match ({matches}/3)")
164
+ return False
165
+
166
+
167
+ def test_5_destructive_operations():
168
+ """Test 5: Try destructive operations that would be dangerous locally."""
169
+ print("\n" + "=" * 60)
170
+ print("TEST 5: Destructive Operations (Safe in sandbox)")
171
+ print("=" * 60)
172
+
173
+ executor = get_code_executor()
174
+
175
+ code = """
176
+ import os
177
+ import tempfile
178
+
179
+ # Try to write to /tmp (should work)
180
+ tmp_file = "/tmp/test_modal_sandbox.txt"
181
+ try:
182
+ with open(tmp_file, 'w') as f:
183
+ f.write("Test write to /tmp")
184
+ print(f"✅ Can write to /tmp: {tmp_file}")
185
+ os.remove(tmp_file)
186
+ print("✅ Can delete from /tmp")
187
+ except Exception as e:
188
+ print(f"❌ Error with /tmp: {e}")
189
+
190
+ # Try to write to /root (might fail due to permissions)
191
+ try:
192
+ test_file = "/root/test.txt"
193
+ with open(test_file, 'w') as f:
194
+ f.write("Test")
195
+ print(f"✅ Can write to /root (running as root in container)")
196
+ os.remove(test_file)
197
+ except Exception as e:
198
+ print(f"⚠️ Cannot write to /root: {e}")
199
+
200
+ # Check what user we're running as
201
+ print(f"Running as UID: {os.getuid()}")
202
+ print(f"Running as GID: {os.getgid()}")
203
+ """
204
+
205
+ result = executor.execute(code)
206
+ print(f"\n{result['stdout']}")
207
+
208
+ if "Can write to /tmp" in result["stdout"]:
209
+ print("\n✅ SUCCESS: Sandbox has expected filesystem permissions!")
210
+ return True
211
+ else:
212
+ print("\n⚠️ WARNING: Unexpected filesystem behavior")
213
+ return False
214
+
215
+
216
+ def test_6_network_isolation():
217
+ """Test 6: Check network access (should be allowed by default in our config)."""
218
+ print("\n" + "=" * 60)
219
+ print("TEST 6: Network Access Check")
220
+ print("=" * 60)
221
+
222
+ executor = get_code_executor()
223
+
224
+ code = """
225
+ import socket
226
+
227
+ # Try to resolve a hostname
228
+ try:
229
+ ip = socket.gethostbyname('google.com')
230
+ print(f"✅ Can resolve DNS: google.com -> {ip}")
231
+ print("(Network is enabled - can be disabled for security)")
232
+ except Exception as e:
233
+ print(f"❌ Cannot resolve DNS: {e}")
234
+ print("(Network is blocked)")
235
+ """
236
+
237
+ result = executor.execute(code)
238
+ print(f"\n{result['stdout']}")
239
+
240
+ return True # Either result is valid
241
+
242
+
243
+ def main():
244
+ """Run all verification tests."""
245
+ print("\n" + "=" * 70)
246
+ print(" " * 15 + "MODAL SANDBOX VERIFICATION")
247
+ print("=" * 70)
248
+ print("\nThese tests verify code is running in Modal sandboxes, not locally.")
249
+ print("=" * 70)
250
+
251
+ tests = [
252
+ ("Hostname Isolation", test_1_hostname_check),
253
+ ("Filesystem Isolation", test_2_file_system_isolation),
254
+ ("Container Detection", test_3_process_information),
255
+ ("Library Versions", test_4_library_versions),
256
+ ("Destructive Operations", test_5_destructive_operations),
257
+ ("Network Access", test_6_network_isolation),
258
+ ]
259
+
260
+ results = []
261
+ for name, test_func in tests:
262
+ try:
263
+ passed = test_func()
264
+ results.append((name, passed))
265
+ except Exception as e:
266
+ print(f"\n❌ Test failed with exception: {e}")
267
+ import traceback
268
+
269
+ traceback.print_exc()
270
+ results.append((name, False))
271
+
272
+ # Summary
273
+ print("\n" + "=" * 70)
274
+ print(" " * 25 + "SUMMARY")
275
+ print("=" * 70)
276
+
277
+ passed = sum(1 for _, result in results if result)
278
+ total = len(results)
279
+
280
+ for name, result in results:
281
+ status = "✅ PASS" if result else "❌ FAIL"
282
+ print(f"{status} - {name}")
283
+
284
+ print("=" * 70)
285
+ print(f"\nResults: {passed}/{total} tests passed")
286
+
287
+ if passed >= 4:
288
+ print("\n🎉 Modal sandboxing is working correctly!")
289
+ elif passed >= 2:
290
+ print("\n⚠️ Some tests failed - review output above")
291
+ else:
292
+ print("\n❌ Modal sandboxing may not be working - check configuration")
293
+
294
+ print("=" * 70)
295
+
296
+
297
+ if __name__ == "__main__":
298
+ main()
pyproject.toml CHANGED
@@ -47,6 +47,15 @@ embeddings = [
47
  "chromadb>=0.4.0",
48
  "sentence-transformers>=2.2.0",
49
  ]
 
 
 
 
 
 
 
 
 
50
 
51
  [build-system]
52
  requires = ["hatchling"]
@@ -75,6 +84,9 @@ select = [
75
  ignore = [
76
  "PLR0913", # Too many arguments (agents need many params)
77
  "PLR0912", # Too many branches (complex orchestrator logic)
 
 
 
78
  "PLC0415", # Lazy imports for optional dependencies
79
  "E402", # Module level import not at top (needed for pytest.importorskip)
80
  "RUF100", # Unused noqa (version differences between local/CI)
 
47
  "chromadb>=0.4.0",
48
  "sentence-transformers>=2.2.0",
49
  ]
50
+ modal = [
51
+ # Mario's Modal code execution + LlamaIndex RAG
52
+ "modal>=0.63.0",
53
+ "llama-index>=0.11.0",
54
+ "llama-index-llms-openai",
55
+ "llama-index-embeddings-openai",
56
+ "llama-index-vector-stores-chroma",
57
+ "chromadb>=0.4.0",
58
+ ]
59
 
60
  [build-system]
61
  requires = ["hatchling"]
 
84
  ignore = [
85
  "PLR0913", # Too many arguments (agents need many params)
86
  "PLR0912", # Too many branches (complex orchestrator logic)
87
+ "PLR0911", # Too many return statements (complex agent logic)
88
+ "PLR2004", # Magic values (statistical constants like p-values)
89
+ "PLW0603", # Global statement (singleton pattern for Modal)
90
  "PLC0415", # Lazy imports for optional dependencies
91
  "E402", # Module level import not at top (needed for pytest.importorskip)
92
  "RUF100", # Unused noqa (version differences between local/CI)
src/agents/analysis_agent.py ADDED
@@ -0,0 +1,365 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Analysis agent for statistical analysis using Modal code execution."""
2
+
3
+ from collections.abc import AsyncIterable
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ from agent_framework import (
7
+ AgentRunResponse,
8
+ AgentRunResponseUpdate,
9
+ AgentThread,
10
+ BaseAgent,
11
+ ChatMessage,
12
+ Role,
13
+ )
14
+ from pydantic import BaseModel, Field
15
+ from pydantic_ai import Agent
16
+
17
+ from src.agent_factory.judges import get_model
18
+ from src.tools.code_execution import CodeExecutionError, get_code_executor
19
+ from src.utils.models import Evidence
20
+
21
+ if TYPE_CHECKING:
22
+ from src.services.embeddings import EmbeddingService
23
+
24
+
25
+ class AnalysisResult(BaseModel):
26
+ """Result of statistical analysis."""
27
+
28
+ verdict: str = Field(
29
+ description="SUPPORTED, REFUTED, or INCONCLUSIVE",
30
+ )
31
+ confidence: float = Field(ge=0.0, le=1.0, description="Confidence in verdict (0-1)")
32
+ statistical_evidence: str = Field(
33
+ description="Summary of statistical findings from code execution"
34
+ )
35
+ code_generated: str = Field(description="Python code that was executed")
36
+ execution_output: str = Field(description="Output from code execution")
37
+ key_findings: list[str] = Field(default_factory=list, description="Key takeaways from analysis")
38
+ limitations: list[str] = Field(default_factory=list, description="Limitations of the analysis")
39
+
40
+
41
+ class AnalysisAgent(BaseAgent): # type: ignore[misc]
42
+ """Performs statistical analysis using Modal code execution.
43
+
44
+ This agent:
45
+ 1. Retrieves relevant evidence using RAG (if available)
46
+ 2. Generates Python code for statistical analysis
47
+ 3. Executes code in Modal sandbox
48
+ 4. Interprets results
49
+ 5. Returns verdict (SUPPORTED/REFUTED/INCONCLUSIVE)
50
+ """
51
+
52
+ def __init__(
53
+ self,
54
+ evidence_store: dict[str, Any],
55
+ embedding_service: "EmbeddingService | None" = None,
56
+ ) -> None:
57
+ super().__init__(
58
+ name="AnalysisAgent",
59
+ description="Performs statistical analysis of evidence using secure code execution",
60
+ )
61
+ self._evidence_store = evidence_store
62
+ self._embeddings = embedding_service
63
+ self._code_executor = get_code_executor()
64
+ self._agent: Agent[None, str] | None = None # LLM for code generation
65
+
66
+ def _get_agent(self) -> Agent[None, str]:
67
+ """Lazy initialization of LLM agent."""
68
+ if self._agent is None:
69
+ self._agent = Agent(
70
+ model=get_model(),
71
+ output_type=str, # Returns code as string
72
+ system_prompt=self._get_system_prompt(),
73
+ )
74
+ return self._agent
75
+
76
+ def _get_system_prompt(self) -> str:
77
+ """System prompt for code generation."""
78
+ return """You are a biomedical data scientist specializing in statistical analysis.
79
+
80
+ Your task: Generate Python code to analyze research evidence and test hypotheses.
81
+
82
+ Guidelines:
83
+ 1. Use pandas, numpy, scipy.stats for analysis
84
+ 2. Generate code that prints clear, interpretable results
85
+ 3. Include statistical tests (t-tests, chi-square, meta-analysis, etc.)
86
+ 4. Calculate effect sizes and confidence intervals
87
+ 5. Print summary statistics and test results
88
+ 6. Keep code concise (<50 lines)
89
+ 7. Set a variable called 'result' with final verdict
90
+
91
+ Available libraries:
92
+ - pandas==2.2.0
93
+ - numpy==1.26.4
94
+ - scipy==1.11.4
95
+ - matplotlib==3.8.2
96
+ - scikit-learn==1.4.0
97
+ - statsmodels==0.14.1
98
+
99
+ Output format:
100
+ Return ONLY executable Python code, no explanations or markdown.
101
+ """
102
+
103
+ async def run(
104
+ self,
105
+ messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
106
+ *,
107
+ thread: AgentThread | None = None,
108
+ **kwargs: Any,
109
+ ) -> AgentRunResponse:
110
+ """Analyze evidence and return verdict."""
111
+ # Extract query and hypothesis
112
+ query = self._extract_query(messages)
113
+ hypotheses = self._evidence_store.get("hypotheses", [])
114
+ evidence = self._evidence_store.get("current", [])
115
+
116
+ if not hypotheses:
117
+ return self._error_response("No hypotheses available. Run HypothesisAgent first.")
118
+
119
+ if not evidence:
120
+ return self._error_response("No evidence available. Run SearchAgent first.")
121
+
122
+ # Get primary hypothesis
123
+ primary = hypotheses[0] if hypotheses else None
124
+ if not primary:
125
+ return self._error_response("No primary hypothesis found.")
126
+
127
+ # Retrieve relevant evidence using RAG (if available)
128
+ relevant_evidence = await self._retrieve_relevant_evidence(primary, evidence)
129
+
130
+ # Generate analysis code
131
+ code_prompt = self._create_code_generation_prompt(query, primary, relevant_evidence)
132
+
133
+ try:
134
+ # Generate code using LLM
135
+ agent = self._get_agent()
136
+ code_result = await agent.run(code_prompt)
137
+ generated_code = code_result.output
138
+
139
+ # Execute code in Modal sandbox
140
+ execution_result = self._code_executor.execute(generated_code, timeout=120)
141
+
142
+ if not execution_result["success"]:
143
+ return self._error_response(f"Code execution failed: {execution_result['error']}")
144
+
145
+ # Interpret results
146
+ analysis_result = await self._interpret_results(
147
+ query, primary, generated_code, execution_result
148
+ )
149
+
150
+ # Store analysis in shared context
151
+ self._evidence_store["analysis"] = analysis_result.model_dump()
152
+
153
+ # Format response
154
+ response_text = self._format_response(analysis_result)
155
+
156
+ return AgentRunResponse(
157
+ messages=[ChatMessage(role=Role.ASSISTANT, text=response_text)],
158
+ response_id=f"analysis-{analysis_result.verdict.lower()}",
159
+ additional_properties={"analysis": analysis_result.model_dump()},
160
+ )
161
+
162
+ except CodeExecutionError as e:
163
+ return self._error_response(f"Analysis failed: {e}")
164
+ except Exception as e:
165
+ return self._error_response(f"Unexpected error: {e}")
166
+
167
+ async def _retrieve_relevant_evidence(
168
+ self, hypothesis: Any, all_evidence: list[Evidence]
169
+ ) -> list[Evidence]:
170
+ """Retrieve most relevant evidence using RAG (if available)."""
171
+ if not self._embeddings:
172
+ # No RAG available, return top N evidence
173
+ return all_evidence[:10]
174
+
175
+ # Use embeddings to find relevant evidence
176
+ # TODO: Implement semantic search with embeddings service
177
+ # For now, just return all evidence
178
+ return all_evidence[:10]
179
+
180
+ def _create_code_generation_prompt(
181
+ self, query: str, hypothesis: Any, evidence: list[Evidence]
182
+ ) -> str:
183
+ """Create prompt for code generation."""
184
+ # Extract data from evidence
185
+ evidence_summary = self._summarize_evidence(evidence)
186
+
187
+ prompt = f"""Generate Python code to statistically analyze the following hypothesis:
188
+
189
+ **Original Question**: {query}
190
+
191
+ **Hypothesis**: {hypothesis.drug} → {hypothesis.target} → {hypothesis.pathway} → {hypothesis.effect}
192
+ **Confidence**: {hypothesis.confidence:.0%}
193
+
194
+ **Evidence Summary**:
195
+ {evidence_summary}
196
+
197
+ **Task**:
198
+ 1. Parse the evidence data
199
+ 2. Perform appropriate statistical tests
200
+ 3. Calculate effect sizes and confidence intervals
201
+ 4. Determine verdict: SUPPORTED, REFUTED, or INCONCLUSIVE
202
+ 5. Set result variable to verdict string
203
+
204
+ Generate executable Python code only (no markdown, no explanations).
205
+ """
206
+ return prompt
207
+
208
+ def _summarize_evidence(self, evidence: list[Evidence]) -> str:
209
+ """Summarize evidence for code generation prompt."""
210
+ if not evidence:
211
+ return "No evidence available."
212
+
213
+ lines = []
214
+ for i, ev in enumerate(evidence[:5], 1): # Top 5 most relevant
215
+ lines.append(f"{i}. {ev.content[:200]}...")
216
+ lines.append(f" Source: {ev.citation.title}")
217
+ lines.append(f" Relevance: {ev.relevance:.0%}\n")
218
+
219
+ return "\n".join(lines)
220
+
221
+ async def _interpret_results(
222
+ self,
223
+ query: str,
224
+ hypothesis: Any,
225
+ code: str,
226
+ execution_result: dict[str, Any],
227
+ ) -> AnalysisResult:
228
+ """Interpret code execution results using LLM."""
229
+ # Extract verdict from output
230
+ stdout = execution_result["stdout"]
231
+ verdict = "INCONCLUSIVE" # Default
232
+
233
+ # Simple heuristic: look for verdict in output
234
+ if "SUPPORTED" in stdout.upper():
235
+ verdict = "SUPPORTED"
236
+ elif "REFUTED" in stdout.upper():
237
+ verdict = "REFUTED"
238
+ elif "INCONCLUSIVE" in stdout.upper():
239
+ verdict = "INCONCLUSIVE"
240
+
241
+ # Parse key findings from output
242
+ key_findings = self._extract_findings(stdout)
243
+
244
+ # Calculate confidence based on statistical significance
245
+ confidence = self._calculate_confidence(stdout)
246
+
247
+ return AnalysisResult(
248
+ verdict=verdict,
249
+ confidence=confidence,
250
+ statistical_evidence=stdout.strip(),
251
+ code_generated=code,
252
+ execution_output=stdout,
253
+ key_findings=key_findings,
254
+ limitations=[
255
+ "Analysis based on summary data only",
256
+ "Limited to available evidence",
257
+ "Statistical tests assume data independence",
258
+ ],
259
+ )
260
+
261
+ def _extract_findings(self, output: str) -> list[str]:
262
+ """Extract key findings from code output."""
263
+ findings = []
264
+
265
+ # Look for common statistical patterns
266
+ lines = output.split("\n")
267
+ for line in lines:
268
+ line_lower = line.lower()
269
+ if any(
270
+ keyword in line_lower
271
+ for keyword in ["p-value", "significant", "effect size", "correlation", "mean"]
272
+ ):
273
+ findings.append(line.strip())
274
+
275
+ return findings[:5] # Top 5 findings
276
+
277
+ def _calculate_confidence(self, output: str) -> float:
278
+ """Calculate confidence based on statistical results."""
279
+ # Look for p-values
280
+ import re
281
+
282
+ p_values = re.findall(r"p[-\s]?value[:\s]+(\d+\.?\d*)", output.lower())
283
+
284
+ if p_values:
285
+ try:
286
+ min_p = min(float(p) for p in p_values)
287
+ # Higher confidence for lower p-values
288
+ if min_p < 0.001:
289
+ return 0.95
290
+ elif min_p < 0.01:
291
+ return 0.90
292
+ elif min_p < 0.05:
293
+ return 0.80
294
+ else:
295
+ return 0.60
296
+ except ValueError:
297
+ pass
298
+
299
+ # Default medium confidence
300
+ return 0.70
301
+
302
+ def _format_response(self, result: AnalysisResult) -> str:
303
+ """Format analysis result as markdown."""
304
+ lines = [
305
+ "## Statistical Analysis Complete\n",
306
+ f"### Verdict: **{result.verdict}**",
307
+ f"**Confidence**: {result.confidence:.0%}\n",
308
+ "### Key Findings",
309
+ ]
310
+
311
+ for finding in result.key_findings:
312
+ lines.append(f"- {finding}")
313
+
314
+ lines.extend(
315
+ [
316
+ "\n### Statistical Evidence",
317
+ "```",
318
+ result.statistical_evidence,
319
+ "```",
320
+ "\n### Generated Code",
321
+ "```python",
322
+ result.code_generated,
323
+ "```",
324
+ "\n### Limitations",
325
+ ]
326
+ )
327
+
328
+ for limitation in result.limitations:
329
+ lines.append(f"- {limitation}")
330
+
331
+ return "\n".join(lines)
332
+
333
+ def _error_response(self, message: str) -> AgentRunResponse:
334
+ """Create error response."""
335
+ return AgentRunResponse(
336
+ messages=[ChatMessage(role=Role.ASSISTANT, text=f"❌ **Error**: {message}")],
337
+ response_id="analysis-error",
338
+ )
339
+
340
+ def _extract_query(
341
+ self, messages: str | ChatMessage | list[str] | list[ChatMessage] | None
342
+ ) -> str:
343
+ """Extract query from messages."""
344
+ if isinstance(messages, str):
345
+ return messages
346
+ elif isinstance(messages, ChatMessage):
347
+ return messages.text or ""
348
+ elif isinstance(messages, list):
349
+ for msg in reversed(messages):
350
+ if isinstance(msg, ChatMessage) and msg.role == Role.USER:
351
+ return msg.text or ""
352
+ elif isinstance(msg, str):
353
+ return msg
354
+ return ""
355
+
356
+ async def run_stream(
357
+ self,
358
+ messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
359
+ *,
360
+ thread: AgentThread | None = None,
361
+ **kwargs: Any,
362
+ ) -> AsyncIterable[AgentRunResponseUpdate]:
363
+ """Streaming wrapper."""
364
+ result = await self.run(messages, thread=thread, **kwargs)
365
+ yield AgentRunResponseUpdate(messages=result.messages, response_id=result.response_id)
src/services/llamaindex_rag.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """LlamaIndex RAG service for evidence retrieval and indexing."""
2
+
3
+ from typing import Any
4
+
5
+ import chromadb
6
+ import structlog
7
+ from llama_index.core import Document, Settings, StorageContext, VectorStoreIndex
8
+ from llama_index.core.retrievers import VectorIndexRetriever
9
+ from llama_index.embeddings.openai import OpenAIEmbedding
10
+ from llama_index.llms.openai import OpenAI
11
+ from llama_index.vector_stores.chroma import ChromaVectorStore
12
+
13
+ from src.utils.config import settings
14
+ from src.utils.models import Evidence
15
+
16
+ logger = structlog.get_logger()
17
+
18
+
19
+ class LlamaIndexRAGService:
20
+ """RAG service using LlamaIndex with ChromaDB vector store."""
21
+
22
+ def __init__(
23
+ self,
24
+ collection_name: str = "deepcritical_evidence",
25
+ persist_dir: str | None = None,
26
+ embedding_model: str = "text-embedding-3-small",
27
+ similarity_top_k: int = 5,
28
+ ) -> None:
29
+ """
30
+ Initialize LlamaIndex RAG service.
31
+
32
+ Args:
33
+ collection_name: Name of the ChromaDB collection
34
+ persist_dir: Directory to persist ChromaDB data
35
+ embedding_model: OpenAI embedding model to use
36
+ similarity_top_k: Number of top results to retrieve
37
+ """
38
+ self.collection_name = collection_name
39
+ self.persist_dir = persist_dir or settings.chroma_db_path
40
+ self.similarity_top_k = similarity_top_k
41
+
42
+ # Configure LlamaIndex settings
43
+ Settings.llm = OpenAI(
44
+ model="gpt-4o-mini",
45
+ api_key=settings.openai_api_key,
46
+ )
47
+ Settings.embed_model = OpenAIEmbedding(
48
+ model=embedding_model,
49
+ api_key=settings.openai_api_key,
50
+ )
51
+
52
+ # Initialize ChromaDB client
53
+ self.chroma_client = chromadb.PersistentClient(path=self.persist_dir)
54
+
55
+ # Get or create collection
56
+ try:
57
+ self.collection = self.chroma_client.get_collection(self.collection_name)
58
+ logger.info("loaded_existing_collection", name=self.collection_name)
59
+ except Exception:
60
+ self.collection = self.chroma_client.create_collection(self.collection_name)
61
+ logger.info("created_new_collection", name=self.collection_name)
62
+
63
+ # Initialize vector store and index
64
+ self.vector_store = ChromaVectorStore(chroma_collection=self.collection)
65
+ self.storage_context = StorageContext.from_defaults(vector_store=self.vector_store)
66
+
67
+ # Try to load existing index, or create empty one
68
+ try:
69
+ self.index = VectorStoreIndex.from_vector_store(
70
+ vector_store=self.vector_store,
71
+ storage_context=self.storage_context,
72
+ )
73
+ logger.info("loaded_existing_index")
74
+ except Exception:
75
+ self.index = VectorStoreIndex([], storage_context=self.storage_context)
76
+ logger.info("created_new_index")
77
+
78
+ def ingest_evidence(self, evidence_list: list[Evidence]) -> None:
79
+ """
80
+ Ingest evidence into the vector store.
81
+
82
+ Args:
83
+ evidence_list: List of Evidence objects to ingest
84
+ """
85
+ if not evidence_list:
86
+ logger.warning("no_evidence_to_ingest")
87
+ return
88
+
89
+ # Convert Evidence objects to LlamaIndex Documents
90
+ documents = []
91
+ for evidence in evidence_list:
92
+ metadata = {
93
+ "source": evidence.citation.source,
94
+ "title": evidence.citation.title,
95
+ "url": evidence.citation.url,
96
+ "date": evidence.citation.date,
97
+ "authors": ", ".join(evidence.citation.authors),
98
+ }
99
+
100
+ doc = Document(
101
+ text=evidence.content,
102
+ metadata=metadata,
103
+ doc_id=evidence.citation.url, # Use URL as unique ID
104
+ )
105
+ documents.append(doc)
106
+
107
+ # Insert documents into index
108
+ try:
109
+ for doc in documents:
110
+ self.index.insert(doc)
111
+ logger.info("ingested_evidence", count=len(documents))
112
+ except Exception as e:
113
+ logger.error("failed_to_ingest_evidence", error=str(e))
114
+ raise
115
+
116
+ def ingest_documents(self, documents: list[Document]) -> None:
117
+ """
118
+ Ingest raw LlamaIndex Documents.
119
+
120
+ Args:
121
+ documents: List of LlamaIndex Document objects
122
+ """
123
+ if not documents:
124
+ logger.warning("no_documents_to_ingest")
125
+ return
126
+
127
+ try:
128
+ for doc in documents:
129
+ self.index.insert(doc)
130
+ logger.info("ingested_documents", count=len(documents))
131
+ except Exception as e:
132
+ logger.error("failed_to_ingest_documents", error=str(e))
133
+ raise
134
+
135
+ def retrieve(self, query: str, top_k: int | None = None) -> list[dict[str, Any]]:
136
+ """
137
+ Retrieve relevant documents for a query.
138
+
139
+ Args:
140
+ query: Query string
141
+ top_k: Number of results to return (defaults to similarity_top_k)
142
+
143
+ Returns:
144
+ List of retrieved documents with metadata and scores
145
+ """
146
+ k = top_k or self.similarity_top_k
147
+
148
+ # Create retriever
149
+ retriever = VectorIndexRetriever(
150
+ index=self.index,
151
+ similarity_top_k=k,
152
+ )
153
+
154
+ try:
155
+ # Retrieve nodes
156
+ nodes = retriever.retrieve(query)
157
+
158
+ # Convert to dict format
159
+ results = []
160
+ for node in nodes:
161
+ results.append(
162
+ {
163
+ "text": node.node.text,
164
+ "score": node.score,
165
+ "metadata": node.node.metadata,
166
+ }
167
+ )
168
+
169
+ logger.info("retrieved_documents", query=query[:50], count=len(results))
170
+ return results
171
+
172
+ except Exception as e:
173
+ logger.error("failed_to_retrieve", error=str(e), query=query[:50])
174
+ return []
175
+
176
+ def query(self, query_str: str, top_k: int | None = None) -> str:
177
+ """
178
+ Query the RAG system and get a synthesized response.
179
+
180
+ Args:
181
+ query_str: Query string
182
+ top_k: Number of results to use (defaults to similarity_top_k)
183
+
184
+ Returns:
185
+ Synthesized response string
186
+ """
187
+ k = top_k or self.similarity_top_k
188
+
189
+ # Create query engine
190
+ query_engine = self.index.as_query_engine(
191
+ similarity_top_k=k,
192
+ )
193
+
194
+ try:
195
+ response = query_engine.query(query_str)
196
+ logger.info("generated_response", query=query_str[:50])
197
+ return str(response)
198
+
199
+ except Exception as e:
200
+ logger.error("failed_to_query", error=str(e), query=query_str[:50])
201
+ return f"Error generating response: {e}"
202
+
203
+ def clear_collection(self) -> None:
204
+ """Clear all documents from the collection."""
205
+ try:
206
+ self.chroma_client.delete_collection(self.collection_name)
207
+ self.collection = self.chroma_client.create_collection(self.collection_name)
208
+ self.vector_store = ChromaVectorStore(chroma_collection=self.collection)
209
+ self.storage_context = StorageContext.from_defaults(vector_store=self.vector_store)
210
+ self.index = VectorStoreIndex([], storage_context=self.storage_context)
211
+ logger.info("cleared_collection", name=self.collection_name)
212
+ except Exception as e:
213
+ logger.error("failed_to_clear_collection", error=str(e))
214
+ raise
215
+
216
+
217
+ def get_rag_service(
218
+ collection_name: str = "deepcritical_evidence",
219
+ **kwargs: Any,
220
+ ) -> LlamaIndexRAGService:
221
+ """
222
+ Get or create a RAG service instance.
223
+
224
+ Args:
225
+ collection_name: Name of the ChromaDB collection
226
+ **kwargs: Additional arguments for LlamaIndexRAGService
227
+
228
+ Returns:
229
+ Configured LlamaIndexRAGService instance
230
+ """
231
+ return LlamaIndexRAGService(collection_name=collection_name, **kwargs)
src/tools/code_execution.py CHANGED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Modal-based secure code execution tool for statistical analysis.
2
+
3
+ This module provides sandboxed Python code execution using Modal's serverless infrastructure.
4
+ It's designed for running LLM-generated statistical analysis code safely.
5
+ """
6
+
7
+ import os
8
+ from typing import Any
9
+
10
+ import structlog
11
+
12
+ logger = structlog.get_logger(__name__)
13
+
14
+
15
+ class CodeExecutionError(Exception):
16
+ """Raised when code execution fails."""
17
+
18
+ pass
19
+
20
+
21
+ class ModalCodeExecutor:
22
+ """Execute Python code securely using Modal sandboxes.
23
+
24
+ This class provides a safe environment for executing LLM-generated code,
25
+ particularly for scientific computing and statistical analysis tasks.
26
+
27
+ Features:
28
+ - Sandboxed execution (isolated from host system)
29
+ - Pre-installed scientific libraries (numpy, scipy, pandas, matplotlib)
30
+ - Network isolation for security
31
+ - Timeout protection
32
+ - Stdout/stderr capture
33
+
34
+ Example:
35
+ >>> executor = ModalCodeExecutor()
36
+ >>> result = executor.execute('''
37
+ ... import pandas as pd
38
+ ... df = pd.DataFrame({'a': [1, 2, 3]})
39
+ ... result = df['a'].sum()
40
+ ... ''')
41
+ >>> print(result['stdout'])
42
+ 6
43
+ """
44
+
45
+ def __init__(self) -> None:
46
+ """Initialize Modal code executor.
47
+
48
+ Raises:
49
+ ConfigurationError: If Modal credentials are not configured.
50
+ """
51
+ # Check for Modal credentials
52
+ self.modal_token_id = os.getenv("MODAL_TOKEN_ID")
53
+ self.modal_token_secret = os.getenv("MODAL_TOKEN_SECRET")
54
+
55
+ if not self.modal_token_id or not self.modal_token_secret:
56
+ logger.warning(
57
+ "Modal credentials not found. Code execution will fail unless modal setup is run."
58
+ )
59
+
60
+ def execute(self, code: str, timeout: int = 60, allow_network: bool = False) -> dict[str, Any]:
61
+ """Execute Python code in a Modal sandbox.
62
+
63
+ Args:
64
+ code: Python code to execute
65
+ timeout: Maximum execution time in seconds (default: 60)
66
+ allow_network: Whether to allow network access (default: False for security)
67
+
68
+ Returns:
69
+ Dictionary containing:
70
+ - stdout: Standard output from code execution
71
+ - stderr: Standard error from code execution
72
+ - success: Boolean indicating if execution succeeded
73
+ - error: Error message if execution failed
74
+
75
+ Raises:
76
+ CodeExecutionError: If execution fails or times out
77
+ """
78
+ try:
79
+ import modal
80
+ except ImportError as e:
81
+ raise CodeExecutionError(
82
+ "Modal SDK not installed. Run: uv sync or pip install modal>=0.63.0"
83
+ ) from e
84
+
85
+ logger.info("executing_code", code_length=len(code), timeout=timeout)
86
+
87
+ try:
88
+ # Create or lookup Modal app
89
+ app = modal.App.lookup("deepcritical-code-execution", create_if_missing=True)
90
+
91
+ # Define scientific computing image with common libraries
92
+ scientific_image = modal.Image.debian_slim(python_version="3.11").uv_pip_install(
93
+ "pandas==2.2.0",
94
+ "numpy==1.26.4",
95
+ "scipy==1.11.4",
96
+ "matplotlib==3.8.2",
97
+ "scikit-learn==1.4.0",
98
+ "statsmodels==0.14.1",
99
+ )
100
+
101
+ # Create sandbox with security restrictions
102
+ sandbox = modal.Sandbox.create(
103
+ app=app,
104
+ image=scientific_image,
105
+ timeout=timeout,
106
+ # Security settings based on Modal docs for untrusted code
107
+ )
108
+
109
+ # Execute the code
110
+ # Wrap code to capture result
111
+ wrapped_code = f"""
112
+ import sys
113
+ import io
114
+ from contextlib import redirect_stdout, redirect_stderr
115
+
116
+ stdout_io = io.StringIO()
117
+ stderr_io = io.StringIO()
118
+
119
+ try:
120
+ with redirect_stdout(stdout_io), redirect_stderr(stderr_io):
121
+ {self._indent_code(code, 8)}
122
+ print("__EXECUTION_SUCCESS__")
123
+ except Exception as e:
124
+ print(f"__EXECUTION_ERROR__: {{type(e).__name__}}: {{e}}", file=sys.stderr)
125
+
126
+ print("__STDOUT_START__")
127
+ print(stdout_io.getvalue())
128
+ print("__STDOUT_END__")
129
+ print("__STDERR_START__")
130
+ print(stderr_io.getvalue(), file=sys.stderr)
131
+ print("__STDERR_END__", file=sys.stderr)
132
+ """
133
+
134
+ # Run the wrapped code
135
+ process = sandbox.exec("python", "-c", wrapped_code, timeout=timeout)
136
+
137
+ # Read output
138
+ stdout_raw = process.stdout.read()
139
+ stderr_raw = process.stderr.read()
140
+
141
+ # Terminate sandbox
142
+ sandbox.terminate()
143
+
144
+ # Parse output
145
+ success = "__EXECUTION_SUCCESS__" in stdout_raw
146
+
147
+ # Extract actual stdout/stderr
148
+ stdout = self._extract_output(stdout_raw, "__STDOUT_START__", "__STDOUT_END__")
149
+ stderr = self._extract_output(stderr_raw, "__STDERR_START__", "__STDERR_END__")
150
+
151
+ result = {
152
+ "stdout": stdout,
153
+ "stderr": stderr,
154
+ "success": success,
155
+ "error": stderr if not success else None,
156
+ }
157
+
158
+ logger.info(
159
+ "code_execution_completed",
160
+ success=success,
161
+ stdout_length=len(stdout),
162
+ stderr_length=len(stderr),
163
+ )
164
+
165
+ return result
166
+
167
+ except Exception as e:
168
+ logger.error("code_execution_failed", error=str(e), error_type=type(e).__name__)
169
+ raise CodeExecutionError(f"Code execution failed: {e}") from e
170
+
171
+ def execute_with_return(self, code: str, timeout: int = 60) -> Any:
172
+ """Execute code and return the value of the 'result' variable.
173
+
174
+ Convenience method that executes code and extracts a return value.
175
+ The code should assign its final result to a variable named 'result'.
176
+
177
+ Args:
178
+ code: Python code to execute (must set 'result' variable)
179
+ timeout: Maximum execution time in seconds
180
+
181
+ Returns:
182
+ The value of the 'result' variable from the executed code
183
+
184
+ Example:
185
+ >>> executor.execute_with_return("result = 2 + 2")
186
+ 4
187
+ """
188
+ # Modify code to print result as JSON
189
+ wrapped = f"""
190
+ import json
191
+ {code}
192
+ print(json.dumps({{"__RESULT__": result}}))
193
+ """
194
+
195
+ execution_result = self.execute(wrapped, timeout=timeout)
196
+
197
+ if not execution_result["success"]:
198
+ raise CodeExecutionError(f"Execution failed: {execution_result['error']}")
199
+
200
+ # Parse result from stdout
201
+ import json
202
+
203
+ try:
204
+ output = execution_result["stdout"].strip()
205
+ if "__RESULT__" in output:
206
+ # Extract JSON line
207
+ for line in output.split("\n"):
208
+ if "__RESULT__" in line:
209
+ data = json.loads(line)
210
+ return data["__RESULT__"]
211
+ raise ValueError("Result not found in output")
212
+ except (json.JSONDecodeError, ValueError) as e:
213
+ logger.warning(
214
+ "failed_to_parse_result", error=str(e), stdout=execution_result["stdout"]
215
+ )
216
+ return execution_result["stdout"]
217
+
218
+ def _indent_code(self, code: str, spaces: int) -> str:
219
+ """Indent code by specified number of spaces."""
220
+ indent = " " * spaces
221
+ return "\n".join(indent + line if line.strip() else line for line in code.split("\n"))
222
+
223
+ def _extract_output(self, text: str, start_marker: str, end_marker: str) -> str:
224
+ """Extract content between markers."""
225
+ try:
226
+ start_idx = text.index(start_marker) + len(start_marker)
227
+ end_idx = text.index(end_marker)
228
+ return text[start_idx:end_idx].strip()
229
+ except ValueError:
230
+ # Markers not found, return original text
231
+ return text.strip()
232
+
233
+
234
+ # Singleton instance for easy import
235
+ _executor: ModalCodeExecutor | None = None
236
+
237
+
238
+ def get_code_executor() -> ModalCodeExecutor:
239
+ """Get or create singleton code executor instance."""
240
+ global _executor
241
+ if _executor is None:
242
+ _executor = ModalCodeExecutor()
243
+ return _executor
src/utils/config.py CHANGED
@@ -41,6 +41,11 @@ class Settings(BaseSettings):
41
  # Logging
42
  log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO"
43
 
 
 
 
 
 
44
  def get_api_key(self) -> str:
45
  """Get the API key for the configured provider."""
46
  if self.llm_provider == "openai":
 
41
  # Logging
42
  log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO"
43
 
44
+ # Partner Service Configuration (Mario's Modal Integration)
45
+ modal_token_id: str | None = Field(default=None, description="Modal token ID")
46
+ modal_token_secret: str | None = Field(default=None, description="Modal token secret")
47
+ chroma_db_path: str = Field(default="./chroma_db", description="ChromaDB storage path")
48
+
49
  def get_api_key(self) -> str:
50
  """Get the API key for the configured provider."""
51
  if self.llm_provider == "openai":
uv.lock CHANGED
@@ -174,6 +174,18 @@ wheels = [
174
  { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 },
175
  ]
176
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  [[package]]
178
  name = "annotated-doc"
179
  version = "0.0.4"
@@ -358,6 +370,22 @@ wheels = [
358
  { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181 },
359
  ]
360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  [[package]]
362
  name = "bcrypt"
363
  version = "5.0.0"
@@ -549,6 +577,47 @@ wheels = [
549
  { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503 },
550
  ]
551
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
  [[package]]
553
  name = "certifi"
554
  version = "2025.11.12"
@@ -974,6 +1043,19 @@ wheels = [
974
  { url = "https://files.pythonhosted.org/packages/b6/00/a9b81bdba88e2904602e970e46ffd18b6a833d902f18d91bdce6fc271c49/cyclopts-4.2.5-py3-none-any.whl", hash = "sha256:361be316ce7f6ce674cad8d34bf6c5e39c34daaeceae40632a55b599472975c7", size = 185196 },
975
  ]
976
 
 
 
 
 
 
 
 
 
 
 
 
 
 
977
  [[package]]
978
  name = "deepcritical"
979
  version = "0.1.0"
@@ -1013,6 +1095,14 @@ embeddings = [
1013
  magentic = [
1014
  { name = "agent-framework-core" },
1015
  ]
 
 
 
 
 
 
 
 
1016
 
1017
  [package.metadata]
1018
  requires-dist = [
@@ -1020,8 +1110,14 @@ requires-dist = [
1020
  { name = "anthropic", specifier = ">=0.18.0" },
1021
  { name = "beautifulsoup4", specifier = ">=4.12" },
1022
  { name = "chromadb", marker = "extra == 'embeddings'", specifier = ">=0.4.0" },
 
1023
  { name = "gradio", specifier = ">=5.0" },
1024
  { name = "httpx", specifier = ">=0.27" },
 
 
 
 
 
1025
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10" },
1026
  { name = "openai", specifier = ">=1.0.0" },
1027
  { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.7" },
@@ -1042,7 +1138,37 @@ requires-dist = [
1042
  { name = "tenacity", specifier = ">=8.2" },
1043
  { name = "xmltodict", specifier = ">=0.13" },
1044
  ]
1045
- provides-extras = ["dev", "magentic", "embeddings"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1046
 
1047
  [[package]]
1048
  name = "diskcache"
@@ -1250,6 +1376,15 @@ wheels = [
1250
  { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 },
1251
  ]
1252
 
 
 
 
 
 
 
 
 
 
1253
  [[package]]
1254
  name = "flatbuffers"
1255
  version = "25.9.23"
@@ -1486,6 +1621,56 @@ wheels = [
1486
  { url = "https://files.pythonhosted.org/packages/be/8a/f2a47134c5b5a7f3bad27eae749589a80d81efaaad8f59af47c136712bf6/gradio_client-1.14.0-py3-none-any.whl", hash = "sha256:9a2f5151978411e0f8b55a2d38cddd0a94491851149d14db4af96f5a09774825", size = 325555 },
1487
  ]
1488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1489
  [[package]]
1490
  name = "griffe"
1491
  version = "1.15.0"
@@ -1575,6 +1760,19 @@ wheels = [
1575
  { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462 },
1576
  ]
1577
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1578
  [[package]]
1579
  name = "h11"
1580
  version = "0.16.0"
@@ -1584,6 +1782,19 @@ wheels = [
1584
  { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 },
1585
  ]
1586
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1587
  [[package]]
1588
  name = "hf-xet"
1589
  version = "1.2.0"
@@ -1613,6 +1824,15 @@ wheels = [
1613
  { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 },
1614
  ]
1615
 
 
 
 
 
 
 
 
 
 
1616
  [[package]]
1617
  name = "httpcore"
1618
  version = "1.0.9"
@@ -1722,6 +1942,15 @@ wheels = [
1722
  { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 },
1723
  ]
1724
 
 
 
 
 
 
 
 
 
 
1725
  [[package]]
1726
  name = "identify"
1727
  version = "2.6.15"
@@ -2020,6 +2249,231 @@ wheels = [
2020
  { url = "https://files.pythonhosted.org/packages/ca/ec/65f7d563aa4a62dd58777e8f6aa882f15db53b14eb29aba0c28a20f7eb26/kubernetes-34.1.0-py2.py3-none-any.whl", hash = "sha256:bffba2272534e224e6a7a74d582deb0b545b7c9879d2cd9e4aae9481d1f2cc2a", size = 2008380 },
2021
  ]
2022
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2023
  [[package]]
2024
  name = "logfire"
2025
  version = "4.15.1"
@@ -2138,6 +2592,18 @@ wheels = [
2138
  { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 },
2139
  ]
2140
 
 
 
 
 
 
 
 
 
 
 
 
 
2141
  [[package]]
2142
  name = "mcp"
2143
  version = "1.22.0"
@@ -2291,6 +2757,31 @@ wheels = [
2291
  { url = "https://files.pythonhosted.org/packages/6a/fc/0e61d9a4e29c8679356795a40e48f647b4aad58d71bfc969f0f8f56fb912/mmh3-5.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e7884931fe5e788163e7b3c511614130c2c59feffdc21112290a194487efb2e9", size = 40455 },
2292
  ]
2293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2294
  [[package]]
2295
  name = "more-itertools"
2296
  version = "10.8.0"
@@ -2499,6 +2990,15 @@ wheels = [
2499
  { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 },
2500
  ]
2501
 
 
 
 
 
 
 
 
 
 
2502
  [[package]]
2503
  name = "networkx"
2504
  version = "3.6"
@@ -2520,6 +3020,21 @@ wheels = [
2520
  { url = "https://files.pythonhosted.org/packages/bf/2f/9e9d0dcaa4c6ffa22b7aa31069a8a264c753ff8027b36af602cce038c92f/nexus_rpc-1.1.0-py3-none-any.whl", hash = "sha256:d1b007af2aba186a27e736f8eaae39c03aed05b488084ff6c3d1785c9ba2ad38", size = 27743 },
2521
  ]
2522
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2523
  [[package]]
2524
  name = "nodeenv"
2525
  version = "1.9.1"
@@ -3053,7 +3568,7 @@ wheels = [
3053
 
3054
  [[package]]
3055
  name = "pandas"
3056
- version = "2.3.3"
3057
  source = { registry = "https://pypi.org/simple" }
3058
  dependencies = [
3059
  { name = "numpy" },
@@ -3061,48 +3576,35 @@ dependencies = [
3061
  { name = "pytz" },
3062
  { name = "tzdata" },
3063
  ]
3064
- sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 }
3065
- wheels = [
3066
- { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790 },
3067
- { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831 },
3068
- { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267 },
3069
- { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281 },
3070
- { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453 },
3071
- { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361 },
3072
- { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702 },
3073
- { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846 },
3074
- { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618 },
3075
- { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212 },
3076
- { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693 },
3077
- { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002 },
3078
- { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971 },
3079
- { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722 },
3080
- { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 },
3081
- { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 },
3082
- { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 },
3083
- { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 },
3084
- { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 },
3085
- { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 },
3086
- { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 },
3087
- { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 },
3088
- { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 },
3089
- { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 },
3090
- { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 },
3091
- { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 },
3092
- { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 },
3093
- { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635 },
3094
- { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079 },
3095
- { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049 },
3096
- { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638 },
3097
- { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834 },
3098
- { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925 },
3099
- { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071 },
3100
- { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504 },
3101
- { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702 },
3102
- { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535 },
3103
- { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582 },
3104
- { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963 },
3105
- { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175 },
3106
  ]
3107
 
3108
  [[package]]
@@ -3883,6 +4385,15 @@ crypto = [
3883
  { name = "cryptography" },
3884
  ]
3885
 
 
 
 
 
 
 
 
 
 
3886
  [[package]]
3887
  name = "pyperclip"
3888
  version = "1.11.0"
@@ -4666,6 +5177,48 @@ wheels = [
4666
  { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 },
4667
  ]
4668
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4669
  [[package]]
4670
  name = "sse-starlette"
4671
  version = "3.0.3"
@@ -4691,6 +5244,15 @@ wheels = [
4691
  { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 },
4692
  ]
4693
 
 
 
 
 
 
 
 
 
 
4694
  [[package]]
4695
  name = "structlog"
4696
  version = "25.5.0"
@@ -4712,6 +5274,18 @@ wheels = [
4712
  { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 },
4713
  ]
4714
 
 
 
 
 
 
 
 
 
 
 
 
 
4715
  [[package]]
4716
  name = "temporalio"
4717
  version = "1.19.0"
@@ -4757,6 +5331,60 @@ wheels = [
4757
  { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 },
4758
  ]
4759
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4760
  [[package]]
4761
  name = "tokenizers"
4762
  version = "0.22.1"
@@ -4782,6 +5410,15 @@ wheels = [
4782
  { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684 },
4783
  ]
4784
 
 
 
 
 
 
 
 
 
 
4785
  [[package]]
4786
  name = "tomli"
4787
  version = "2.3.0"
@@ -4957,6 +5594,15 @@ wheels = [
4957
  { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028 },
4958
  ]
4959
 
 
 
 
 
 
 
 
 
 
4960
  [[package]]
4961
  name = "types-protobuf"
4962
  version = "6.32.1.20251105"
@@ -4978,6 +5624,15 @@ wheels = [
4978
  { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658 },
4979
  ]
4980
 
 
 
 
 
 
 
 
 
 
4981
  [[package]]
4982
  name = "typing-extensions"
4983
  version = "4.15.0"
@@ -4987,6 +5642,19 @@ wheels = [
4987
  { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 },
4988
  ]
4989
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4990
  [[package]]
4991
  name = "typing-inspection"
4992
  version = "0.4.2"
 
174
  { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 },
175
  ]
176
 
177
+ [[package]]
178
+ name = "aiosqlite"
179
+ version = "0.21.0"
180
+ source = { registry = "https://pypi.org/simple" }
181
+ dependencies = [
182
+ { name = "typing-extensions" },
183
+ ]
184
+ sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454 }
185
+ wheels = [
186
+ { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792 },
187
+ ]
188
+
189
  [[package]]
190
  name = "annotated-doc"
191
  version = "0.0.4"
 
370
  { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181 },
371
  ]
372
 
373
+ [[package]]
374
+ name = "banks"
375
+ version = "2.2.0"
376
+ source = { registry = "https://pypi.org/simple" }
377
+ dependencies = [
378
+ { name = "deprecated" },
379
+ { name = "griffe" },
380
+ { name = "jinja2" },
381
+ { name = "platformdirs" },
382
+ { name = "pydantic" },
383
+ ]
384
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/f8/25ef24814f77f3fd7f0fd3bd1ef3749e38a9dbd23502fbb53034de49900c/banks-2.2.0.tar.gz", hash = "sha256:d1446280ce6e00301e3e952dd754fd8cee23ff277d29ed160994a84d0d7ffe62", size = 179052 }
385
+ wheels = [
386
+ { url = "https://files.pythonhosted.org/packages/b4/d6/f9168956276934162ec8d48232f9920f2985ee45aa7602e3c6b4bc203613/banks-2.2.0-py3-none-any.whl", hash = "sha256:963cd5c85a587b122abde4f4064078def35c50c688c1b9d36f43c92503854e7d", size = 29244 },
387
+ ]
388
+
389
  [[package]]
390
  name = "bcrypt"
391
  version = "5.0.0"
 
577
  { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503 },
578
  ]
579
 
580
+ [[package]]
581
+ name = "cbor2"
582
+ version = "5.7.1"
583
+ source = { registry = "https://pypi.org/simple" }
584
+ sdist = { url = "https://files.pythonhosted.org/packages/a2/b8/c0f6a7d46f816cb18b1fda61a2fe648abe16039f1ff93ea720a6e9fb3cee/cbor2-5.7.1.tar.gz", hash = "sha256:7a405a1d7c8230ee9acf240aad48ae947ef584e8af05f169f3c1bde8f01f8b71", size = 102467 }
585
+ wheels = [
586
+ { url = "https://files.pythonhosted.org/packages/52/67/319baac9c51de0053f58fa74a9548f93f3629aa3adeebd7d2c99d1379370/cbor2-5.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2b1efbe6e82721be44b9faf47d0fd97b0150213eb6a4ba554f4947442bc4e13f", size = 67894 },
587
+ { url = "https://files.pythonhosted.org/packages/2c/53/d23d0a234a4a098b019ac1cadd33631c973142fc947a68c4a38ca47aa5dc/cbor2-5.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb94bab27e00283bdd8f160e125e17dbabec4c9e6ffc8da91c36547ec1eb707f", size = 68444 },
588
+ { url = "https://files.pythonhosted.org/packages/3a/a2/a6fa59e1c23b0bc77628d64153eb9fc69ac8dde5f8ed41a7d5316fcd0bcd/cbor2-5.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29f22266b5e08e0e4152e87ba185e04d3a84a4fd545b99ae3ebe42c658c66a53", size = 261600 },
589
+ { url = "https://files.pythonhosted.org/packages/3d/cb/e0fa066aa7a09b15b8f56bafef6b2be19d9db31310310b0a5601af5c0128/cbor2-5.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25d4c7554d6627da781c9bd1d0dd0709456eecb71f605829f98961bb98487dda", size = 254904 },
590
+ { url = "https://files.pythonhosted.org/packages/2c/d5/b1fb4a3828c440e100a4b2658dd2e8f422faf08f4fcc8e2c92b240656b44/cbor2-5.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1e15c3a08008cf13ce1dfc64d17c960df5d66d935788d28ec7df54bf0ffb0ef", size = 257388 },
591
+ { url = "https://files.pythonhosted.org/packages/34/d5/252657bc5af964fc5f19c0e0e82031b4c32eba5d3ed4098e963e0e8c47a6/cbor2-5.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9f6cdf7eb604ea0e7ef34e3f0b5447da0029ecd3ab7b2dc70e43fa5f7bcfca89", size = 251494 },
592
+ { url = "https://files.pythonhosted.org/packages/8a/3a/503ea4c2977411858ca287808d077fdb4bb1fafdb4b39177b8ce3d5619ac/cbor2-5.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:dd25cbef8e8e6dbf69f0de95311aecaca7217230cda83ae99fdc37cd20d99250", size = 68147 },
593
+ { url = "https://files.pythonhosted.org/packages/49/9e/fe4c9703fd444da193f892787110c5da2a85c16d26917fcb2584f5d00077/cbor2-5.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:40cc9c67242a7abac5a4e062bc4d1d2376979878c0565a4b2f08fd9ed9212945", size = 64126 },
594
+ { url = "https://files.pythonhosted.org/packages/56/54/48426472f0c051982c647331441aed09b271a0500356ae0b7054c813d174/cbor2-5.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bd5ca44891c06f6b85d440836c967187dc1d30b15f86f315d55c675d3a841078", size = 69031 },
595
+ { url = "https://files.pythonhosted.org/packages/d3/68/1dd58c7706e9752188358223db58c83f3c48e07f728aa84221ffd244652f/cbor2-5.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:537d73ef930ccc1a7b6a2e8d2cbf81407d270deb18e40cda5eb511bd70f71078", size = 68825 },
596
+ { url = "https://files.pythonhosted.org/packages/09/4e/380562fe9f9995a1875fb5ec26fd041e19d61f4630cb690a98c5195945fc/cbor2-5.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:edbf814dd7763b6eda27a5770199f6ccd55bd78be8f4367092460261bfbf19d0", size = 286222 },
597
+ { url = "https://files.pythonhosted.org/packages/7c/bb/9eccdc1ea3c4d5c7cdb2e49b9de49534039616be5455ce69bd64c0b2efe2/cbor2-5.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fc81da8c0e09beb42923e455e477b36ff14a03b9ca18a8a2e9b462de9a953e8", size = 285688 },
598
+ { url = "https://files.pythonhosted.org/packages/59/8c/4696d82f5bd04b3d45d9a64ec037fa242630c134e3218d6c252b4f59b909/cbor2-5.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e4a7d660d428911a3aadb7105e94438d7671ab977356fdf647a91aab751033bd", size = 277063 },
599
+ { url = "https://files.pythonhosted.org/packages/95/50/6538e44ca970caaad2fa376b81701d073d84bf597aac07a59d0a253b1a7f/cbor2-5.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:228e0af9c0a9ddf6375b6ae010eaa1942a1901d403f134ac9ee6a76a322483f9", size = 278334 },
600
+ { url = "https://files.pythonhosted.org/packages/64/a9/156ccd2207fb26b5b61d23728b4dbdc595d1600125aa79683a4a8ddc9313/cbor2-5.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:2d08a6c0d9ed778448e185508d870f4160ba74f59bb17a966abd0d14d0ff4dd3", size = 68404 },
601
+ { url = "https://files.pythonhosted.org/packages/4f/49/adc53615e9dd32c4421f6935dfa2235013532c6e6b28ee515bbdd92618be/cbor2-5.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:752506cfe72da0f4014b468b30191470ee8919a64a0772bd3b36a4fccf5fcefc", size = 64047 },
602
+ { url = "https://files.pythonhosted.org/packages/16/b1/51fb868fe38d893c570bb90b38d365ff0f00421402c1ae8f63b31b25d665/cbor2-5.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:59d5da59fffe89692d5bd1530eef4d26e4eb7aa794aaa1f4e192614786409009", size = 69068 },
603
+ { url = "https://files.pythonhosted.org/packages/b9/db/5abc62ec456f552f617aac3359a5d7114b23be9c4d886169592cd5f074b9/cbor2-5.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:533117918d518e01348f8cd0331271c207e7224b9a1ed492a0ff00847f28edc8", size = 68927 },
604
+ { url = "https://files.pythonhosted.org/packages/9a/c2/58d787395c99874d2a2395b3a22c9d48a3cfc5a7dcd5817bf74764998b75/cbor2-5.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8d6d9436ff3c3323ea5863ecf7ae1139590991685b44b9eb6b7bb1734a594af6", size = 285185 },
605
+ { url = "https://files.pythonhosted.org/packages/d0/9c/b680b264a8f4b9aa59c95e166c816275a13138cbee92dd2917f58bca47b9/cbor2-5.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:661b871ca754a619fcd98c13a38b4696b2b57dab8b24235c00b0ba322c040d24", size = 284440 },
606
+ { url = "https://files.pythonhosted.org/packages/1f/59/68183c655d6226d0eee10027f52516882837802a8d5746317a88362ed686/cbor2-5.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8065aa90d715fd9bb28727b2d774ee16e695a0e1627ae76e54bf19f9d99d63f", size = 276876 },
607
+ { url = "https://files.pythonhosted.org/packages/ee/a2/1964e0a569d2b81e8f4862753fee7701ae5773c22e45492a26f92f62e75a/cbor2-5.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb1b7047d73590cfe8e373e2c804fa99be47e55b1b6186602d0f86f384cecec1", size = 278216 },
608
+ { url = "https://files.pythonhosted.org/packages/00/78/9b566d68cb88bb1ecebe354765625161c9d6060a16e55008006d6359f776/cbor2-5.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:31d511df7ebd6624fdb4cecdafb4ffb9a205f9ff8c8d98edd1bef0d27f944d74", size = 68451 },
609
+ { url = "https://files.pythonhosted.org/packages/db/85/7a6a922d147d027fd5d8fd5224b39e8eaf152a42e8cf16351458096d3d62/cbor2-5.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:f5d37f7b0f84394d2995bd8722cb01c86a885c4821a864a34b7b4d9950c5e26e", size = 64111 },
610
+ { url = "https://files.pythonhosted.org/packages/5f/f0/f220222a57371e33434ba7bdc25de31d611cbc0ade2a868e03c3553305e7/cbor2-5.7.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e5826e4fa4c33661960073f99cf67c82783895524fb66f3ebdd635c19b5a7d68", size = 69002 },
611
+ { url = "https://files.pythonhosted.org/packages/c7/3c/34b62ba5173541659f248f005d13373530f02fb997b78fde00bf01ede4f4/cbor2-5.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f19a00d6ac9a77cb611073250b06bf4494b41ba78a1716704f7008e0927d9366", size = 69177 },
612
+ { url = "https://files.pythonhosted.org/packages/77/fd/2400d820d9733df00a5c18aa74201e51d710fb91588687eb594f4a7688ea/cbor2-5.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2113aea044cd172f199da3520bc4401af69eae96c5180ca7eb660941928cb89", size = 284259 },
613
+ { url = "https://files.pythonhosted.org/packages/42/65/280488ef196c1d71ba123cd406ea47727bb3a0e057767a733d9793fcc428/cbor2-5.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f17eacea2d28fecf28ac413c1d7927cde0a11957487d2630655d6b5c9c46a0b", size = 281958 },
614
+ { url = "https://files.pythonhosted.org/packages/42/82/bcdd3fdc73bd5f4194fdb08c808112010add9530bae1dcfdb1e2b2ceae19/cbor2-5.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d65deea39cae533a629561e7da672402c46731122b6129ed7c8eaa1efe04efce", size = 276025 },
615
+ { url = "https://files.pythonhosted.org/packages/ae/a8/a6065dd6a157b877d7d8f3fe96f410fb191a2db1e6588f4d20b5f9a507c2/cbor2-5.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:57d8cc29ec1fd20500748e0e767ff88c13afcee839081ba4478c41fcda6ee18b", size = 275978 },
616
+ { url = "https://files.pythonhosted.org/packages/62/f4/37934045174af9e4253a340b43f07197af54002070cb80fae82d878f1f14/cbor2-5.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:94fb939d0946f80c49ba45105ca3a3e13e598fc9abd63efc6661b02d4b4d2c50", size = 70269 },
617
+ { url = "https://files.pythonhosted.org/packages/0b/fd/933416643e7f5540ae818691fb23fa4189010c6efa39a12c4f59d825da28/cbor2-5.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fd7225ac820bbb9f03bd16bc1a7efb6c4d1c451f22c0a153ff4ec46495c59c5", size = 66182 },
618
+ { url = "https://files.pythonhosted.org/packages/d5/7d/383bafeabb54c17fe5b6d5aca4e863e6b7df10bcc833b34aa169e9dfce1a/cbor2-5.7.1-py3-none-any.whl", hash = "sha256:68834e4eff2f56629ce6422b0634bc3f74c5a4269de5363f5265fe452c706ba7", size = 23829 },
619
+ ]
620
+
621
  [[package]]
622
  name = "certifi"
623
  version = "2025.11.12"
 
1043
  { url = "https://files.pythonhosted.org/packages/b6/00/a9b81bdba88e2904602e970e46ffd18b6a833d902f18d91bdce6fc271c49/cyclopts-4.2.5-py3-none-any.whl", hash = "sha256:361be316ce7f6ce674cad8d34bf6c5e39c34daaeceae40632a55b599472975c7", size = 185196 },
1044
  ]
1045
 
1046
+ [[package]]
1047
+ name = "dataclasses-json"
1048
+ version = "0.6.7"
1049
+ source = { registry = "https://pypi.org/simple" }
1050
+ dependencies = [
1051
+ { name = "marshmallow" },
1052
+ { name = "typing-inspect" },
1053
+ ]
1054
+ sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227 }
1055
+ wheels = [
1056
+ { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 },
1057
+ ]
1058
+
1059
  [[package]]
1060
  name = "deepcritical"
1061
  version = "0.1.0"
 
1095
  magentic = [
1096
  { name = "agent-framework-core" },
1097
  ]
1098
+ modal = [
1099
+ { name = "chromadb" },
1100
+ { name = "llama-index" },
1101
+ { name = "llama-index-embeddings-openai" },
1102
+ { name = "llama-index-llms-openai" },
1103
+ { name = "llama-index-vector-stores-chroma" },
1104
+ { name = "modal" },
1105
+ ]
1106
 
1107
  [package.metadata]
1108
  requires-dist = [
 
1110
  { name = "anthropic", specifier = ">=0.18.0" },
1111
  { name = "beautifulsoup4", specifier = ">=4.12" },
1112
  { name = "chromadb", marker = "extra == 'embeddings'", specifier = ">=0.4.0" },
1113
+ { name = "chromadb", marker = "extra == 'modal'", specifier = ">=0.4.0" },
1114
  { name = "gradio", specifier = ">=5.0" },
1115
  { name = "httpx", specifier = ">=0.27" },
1116
+ { name = "llama-index", marker = "extra == 'modal'", specifier = ">=0.11.0" },
1117
+ { name = "llama-index-embeddings-openai", marker = "extra == 'modal'" },
1118
+ { name = "llama-index-llms-openai", marker = "extra == 'modal'" },
1119
+ { name = "llama-index-vector-stores-chroma", marker = "extra == 'modal'" },
1120
+ { name = "modal", marker = "extra == 'modal'", specifier = ">=0.63.0" },
1121
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10" },
1122
  { name = "openai", specifier = ">=1.0.0" },
1123
  { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.7" },
 
1138
  { name = "tenacity", specifier = ">=8.2" },
1139
  { name = "xmltodict", specifier = ">=0.13" },
1140
  ]
1141
+ provides-extras = ["dev", "magentic", "embeddings", "modal"]
1142
+
1143
+ [[package]]
1144
+ name = "defusedxml"
1145
+ version = "0.7.1"
1146
+ source = { registry = "https://pypi.org/simple" }
1147
+ sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 }
1148
+ wheels = [
1149
+ { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 },
1150
+ ]
1151
+
1152
+ [[package]]
1153
+ name = "deprecated"
1154
+ version = "1.2.18"
1155
+ source = { registry = "https://pypi.org/simple" }
1156
+ dependencies = [
1157
+ { name = "wrapt" },
1158
+ ]
1159
+ sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 }
1160
+ wheels = [
1161
+ { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 },
1162
+ ]
1163
+
1164
+ [[package]]
1165
+ name = "dirtyjson"
1166
+ version = "1.0.8"
1167
+ source = { registry = "https://pypi.org/simple" }
1168
+ sdist = { url = "https://files.pythonhosted.org/packages/db/04/d24f6e645ad82ba0ef092fa17d9ef7a21953781663648a01c9371d9e8e98/dirtyjson-1.0.8.tar.gz", hash = "sha256:90ca4a18f3ff30ce849d100dcf4a003953c79d3a2348ef056f1d9c22231a25fd", size = 30782 }
1169
+ wheels = [
1170
+ { url = "https://files.pythonhosted.org/packages/68/69/1bcf70f81de1b4a9f21b3a62ec0c83bdff991c88d6cc2267d02408457e88/dirtyjson-1.0.8-py3-none-any.whl", hash = "sha256:125e27248435a58acace26d5c2c4c11a1c0de0a9c5124c5a94ba78e517d74f53", size = 25197 },
1171
+ ]
1172
 
1173
  [[package]]
1174
  name = "diskcache"
 
1376
  { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 },
1377
  ]
1378
 
1379
+ [[package]]
1380
+ name = "filetype"
1381
+ version = "1.2.0"
1382
+ source = { registry = "https://pypi.org/simple" }
1383
+ sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 }
1384
+ wheels = [
1385
+ { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 },
1386
+ ]
1387
+
1388
  [[package]]
1389
  name = "flatbuffers"
1390
  version = "25.9.23"
 
1621
  { url = "https://files.pythonhosted.org/packages/be/8a/f2a47134c5b5a7f3bad27eae749589a80d81efaaad8f59af47c136712bf6/gradio_client-1.14.0-py3-none-any.whl", hash = "sha256:9a2f5151978411e0f8b55a2d38cddd0a94491851149d14db4af96f5a09774825", size = 325555 },
1622
  ]
1623
 
1624
+ [[package]]
1625
+ name = "greenlet"
1626
+ version = "3.2.4"
1627
+ source = { registry = "https://pypi.org/simple" }
1628
+ sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260 }
1629
+ wheels = [
1630
+ { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305 },
1631
+ { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472 },
1632
+ { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646 },
1633
+ { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519 },
1634
+ { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707 },
1635
+ { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684 },
1636
+ { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647 },
1637
+ { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073 },
1638
+ { url = "https://files.pythonhosted.org/packages/67/24/28a5b2fa42d12b3d7e5614145f0bd89714c34c08be6aabe39c14dd52db34/greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c", size = 1548385 },
1639
+ { url = "https://files.pythonhosted.org/packages/6a/05/03f2f0bdd0b0ff9a4f7b99333d57b53a7709c27723ec8123056b084e69cd/greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5", size = 1613329 },
1640
+ { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100 },
1641
+ { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079 },
1642
+ { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997 },
1643
+ { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185 },
1644
+ { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926 },
1645
+ { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839 },
1646
+ { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586 },
1647
+ { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281 },
1648
+ { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142 },
1649
+ { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846 },
1650
+ { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814 },
1651
+ { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899 },
1652
+ { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814 },
1653
+ { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073 },
1654
+ { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191 },
1655
+ { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516 },
1656
+ { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169 },
1657
+ { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497 },
1658
+ { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662 },
1659
+ { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210 },
1660
+ { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759 },
1661
+ { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288 },
1662
+ { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685 },
1663
+ { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586 },
1664
+ { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346 },
1665
+ { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218 },
1666
+ { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659 },
1667
+ { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355 },
1668
+ { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512 },
1669
+ { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508 },
1670
+ { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760 },
1671
+ { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425 },
1672
+ ]
1673
+
1674
  [[package]]
1675
  name = "griffe"
1676
  version = "1.15.0"
 
1760
  { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462 },
1761
  ]
1762
 
1763
+ [[package]]
1764
+ name = "grpclib"
1765
+ version = "0.4.8"
1766
+ source = { registry = "https://pypi.org/simple" }
1767
+ dependencies = [
1768
+ { name = "h2" },
1769
+ { name = "multidict" },
1770
+ ]
1771
+ sdist = { url = "https://files.pythonhosted.org/packages/19/75/0f0d3524b38b35e5cd07334b754aa9bd0570140ad982131b04ebfa3b0374/grpclib-0.4.8.tar.gz", hash = "sha256:d8823763780ef94fed8b2c562f7485cf0bbee15fc7d065a640673667f7719c9a", size = 62793 }
1772
+ wheels = [
1773
+ { url = "https://files.pythonhosted.org/packages/03/8b/ad381ec1b8195fa4a9a693cb8087e031b99530c0d6b8ad036dcb99e144c4/grpclib-0.4.8-py3-none-any.whl", hash = "sha256:a5047733a7acc1c1cee6abf3c841c7c6fab67d2844a45a853b113fa2e6cd2654", size = 76311 },
1774
+ ]
1775
+
1776
  [[package]]
1777
  name = "h11"
1778
  version = "0.16.0"
 
1782
  { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 },
1783
  ]
1784
 
1785
+ [[package]]
1786
+ name = "h2"
1787
+ version = "4.3.0"
1788
+ source = { registry = "https://pypi.org/simple" }
1789
+ dependencies = [
1790
+ { name = "hpack" },
1791
+ { name = "hyperframe" },
1792
+ ]
1793
+ sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026 }
1794
+ wheels = [
1795
+ { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779 },
1796
+ ]
1797
+
1798
  [[package]]
1799
  name = "hf-xet"
1800
  version = "1.2.0"
 
1824
  { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 },
1825
  ]
1826
 
1827
+ [[package]]
1828
+ name = "hpack"
1829
+ version = "4.1.0"
1830
+ source = { registry = "https://pypi.org/simple" }
1831
+ sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 }
1832
+ wheels = [
1833
+ { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 },
1834
+ ]
1835
+
1836
  [[package]]
1837
  name = "httpcore"
1838
  version = "1.0.9"
 
1942
  { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 },
1943
  ]
1944
 
1945
+ [[package]]
1946
+ name = "hyperframe"
1947
+ version = "6.1.0"
1948
+ source = { registry = "https://pypi.org/simple" }
1949
+ sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 }
1950
+ wheels = [
1951
+ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 },
1952
+ ]
1953
+
1954
  [[package]]
1955
  name = "identify"
1956
  version = "2.6.15"
 
2249
  { url = "https://files.pythonhosted.org/packages/ca/ec/65f7d563aa4a62dd58777e8f6aa882f15db53b14eb29aba0c28a20f7eb26/kubernetes-34.1.0-py2.py3-none-any.whl", hash = "sha256:bffba2272534e224e6a7a74d582deb0b545b7c9879d2cd9e4aae9481d1f2cc2a", size = 2008380 },
2250
  ]
2251
 
2252
+ [[package]]
2253
+ name = "llama-cloud"
2254
+ version = "0.1.35"
2255
+ source = { registry = "https://pypi.org/simple" }
2256
+ dependencies = [
2257
+ { name = "certifi" },
2258
+ { name = "httpx" },
2259
+ { name = "pydantic" },
2260
+ ]
2261
+ sdist = { url = "https://files.pythonhosted.org/packages/9b/72/816e6e900448e1b4a8137d90e65876b296c5264a23db6ae888bd3e6660ba/llama_cloud-0.1.35.tar.gz", hash = "sha256:200349d5d57424d7461f304cdb1355a58eea3e6ca1e6b0d75c66b2e937216983", size = 106403 }
2262
+ wheels = [
2263
+ { url = "https://files.pythonhosted.org/packages/1d/d2/8d18a021ab757cea231428404f21fe3186bf1ebaac3f57a73c379483fd3f/llama_cloud-0.1.35-py3-none-any.whl", hash = "sha256:b7abab4423118e6f638d2f326749e7a07c6426543bea6da99b623c715b22af71", size = 303280 },
2264
+ ]
2265
+
2266
+ [[package]]
2267
+ name = "llama-cloud-services"
2268
+ version = "0.6.54"
2269
+ source = { registry = "https://pypi.org/simple" }
2270
+ dependencies = [
2271
+ { name = "click" },
2272
+ { name = "llama-cloud" },
2273
+ { name = "llama-index-core" },
2274
+ { name = "platformdirs" },
2275
+ { name = "pydantic" },
2276
+ { name = "python-dotenv" },
2277
+ { name = "tenacity" },
2278
+ ]
2279
+ sdist = { url = "https://files.pythonhosted.org/packages/8a/0c/8ca87d33bea0340a8ed791f36390112aeb29fd3eebfd64b6aef6204a03f0/llama_cloud_services-0.6.54.tar.gz", hash = "sha256:baf65d9bffb68f9dca98ac6e22908b6675b2038b021e657ead1ffc0e43cbd45d", size = 53468 }
2280
+ wheels = [
2281
+ { url = "https://files.pythonhosted.org/packages/7f/48/4e295e3f791b279885a2e584f71e75cbe4ac84e93bba3c36e2668f60a8ac/llama_cloud_services-0.6.54-py3-none-any.whl", hash = "sha256:07f595f7a0ba40c6a1a20543d63024ca7600fe65c4811d1951039977908997be", size = 63874 },
2282
+ ]
2283
+
2284
+ [[package]]
2285
+ name = "llama-index"
2286
+ version = "0.14.8"
2287
+ source = { registry = "https://pypi.org/simple" }
2288
+ dependencies = [
2289
+ { name = "llama-index-cli" },
2290
+ { name = "llama-index-core" },
2291
+ { name = "llama-index-embeddings-openai" },
2292
+ { name = "llama-index-indices-managed-llama-cloud" },
2293
+ { name = "llama-index-llms-openai" },
2294
+ { name = "llama-index-readers-file" },
2295
+ { name = "llama-index-readers-llama-parse" },
2296
+ { name = "nltk" },
2297
+ ]
2298
+ sdist = { url = "https://files.pythonhosted.org/packages/ce/ff/f230bd1b27f77a10de0d4c6a4942b31f5947fc3e170b6c49e3cc2c8a4352/llama_index-0.14.8.tar.gz", hash = "sha256:b4f5fcf5376e10acf691a2c8bdd019df189f1635b831bbc35894ecf1042e3743", size = 8462 }
2299
+ wheels = [
2300
+ { url = "https://files.pythonhosted.org/packages/78/d8/0da07894f6388206fdadcd0ef8655b45d31176fb5052bcce9fede5d3cb63/llama_index-0.14.8-py3-none-any.whl", hash = "sha256:26755032aefb2ca355c5e263eaa3d0c5ed955afa9e95781340c9068916826397", size = 7447 },
2301
+ ]
2302
+
2303
+ [[package]]
2304
+ name = "llama-index-cli"
2305
+ version = "0.5.3"
2306
+ source = { registry = "https://pypi.org/simple" }
2307
+ dependencies = [
2308
+ { name = "llama-index-core" },
2309
+ { name = "llama-index-embeddings-openai" },
2310
+ { name = "llama-index-llms-openai" },
2311
+ ]
2312
+ sdist = { url = "https://files.pythonhosted.org/packages/67/84/41e820efffbe327c38228d3b37fe42512a37e0c3ee4ff6bf97a394e9577a/llama_index_cli-0.5.3.tar.gz", hash = "sha256:ebaf39e785efbfa8d50d837f60cb0f95125c04bf73ed1f92092a2a5f506172f8", size = 24821 }
2313
+ wheels = [
2314
+ { url = "https://files.pythonhosted.org/packages/54/81/b7b3778aa8662913760fbbee77578daf4407aeaa677ccbf0125c4cfa2e67/llama_index_cli-0.5.3-py3-none-any.whl", hash = "sha256:7deb1e953e582bd885443881ce8bd6ab2817b594fef00079dce9993c47d990f7", size = 28173 },
2315
+ ]
2316
+
2317
+ [[package]]
2318
+ name = "llama-index-core"
2319
+ version = "0.14.8"
2320
+ source = { registry = "https://pypi.org/simple" }
2321
+ dependencies = [
2322
+ { name = "aiohttp" },
2323
+ { name = "aiosqlite" },
2324
+ { name = "banks" },
2325
+ { name = "dataclasses-json" },
2326
+ { name = "deprecated" },
2327
+ { name = "dirtyjson" },
2328
+ { name = "filetype" },
2329
+ { name = "fsspec" },
2330
+ { name = "httpx" },
2331
+ { name = "llama-index-workflows" },
2332
+ { name = "nest-asyncio" },
2333
+ { name = "networkx" },
2334
+ { name = "nltk" },
2335
+ { name = "numpy" },
2336
+ { name = "pillow" },
2337
+ { name = "platformdirs" },
2338
+ { name = "pydantic" },
2339
+ { name = "pyyaml" },
2340
+ { name = "requests" },
2341
+ { name = "setuptools" },
2342
+ { name = "sqlalchemy", extra = ["asyncio"] },
2343
+ { name = "tenacity" },
2344
+ { name = "tiktoken" },
2345
+ { name = "tqdm" },
2346
+ { name = "typing-extensions" },
2347
+ { name = "typing-inspect" },
2348
+ { name = "wrapt" },
2349
+ ]
2350
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/3f68cfac0eba7085aaeede5d179d31097d8351cbeae333859aabd0e20ed7/llama_index_core-0.14.8.tar.gz", hash = "sha256:8865a8546746b8c5f0684085ccfcf900aeb04862ebfe2077ad522374cf71e58e", size = 11579082 }
2351
+ wheels = [
2352
+ { url = "https://files.pythonhosted.org/packages/27/61/bdc7d7e818f07ae8208786581608043c95a510f3f0906d7f6f53a69a1712/llama_index_core-0.14.8-py3-none-any.whl", hash = "sha256:e54046f09507aeb2fad51d8e9686dc0fa41c77d3925892e55c3756ca31f1c0c3", size = 11920547 },
2353
+ ]
2354
+
2355
+ [[package]]
2356
+ name = "llama-index-embeddings-openai"
2357
+ version = "0.5.1"
2358
+ source = { registry = "https://pypi.org/simple" }
2359
+ dependencies = [
2360
+ { name = "llama-index-core" },
2361
+ { name = "openai" },
2362
+ ]
2363
+ sdist = { url = "https://files.pythonhosted.org/packages/10/36/90336d054a5061a3f5bc17ac2c18ef63d9d84c55c14d557de484e811ea4d/llama_index_embeddings_openai-0.5.1.tar.gz", hash = "sha256:1c89867a48b0d0daa3d2d44f5e76b394b2b2ef9935932daf921b9e77939ccda8", size = 7020 }
2364
+ wheels = [
2365
+ { url = "https://files.pythonhosted.org/packages/23/4a/8ab11026cf8deff8f555aa73919be0bac48332683111e5fc4290f352dc50/llama_index_embeddings_openai-0.5.1-py3-none-any.whl", hash = "sha256:a2fcda3398bbd987b5ce3f02367caee8e84a56b930fdf43cc1d059aa9fd20ca5", size = 7011 },
2366
+ ]
2367
+
2368
+ [[package]]
2369
+ name = "llama-index-indices-managed-llama-cloud"
2370
+ version = "0.9.4"
2371
+ source = { registry = "https://pypi.org/simple" }
2372
+ dependencies = [
2373
+ { name = "deprecated" },
2374
+ { name = "llama-cloud" },
2375
+ { name = "llama-index-core" },
2376
+ ]
2377
+ sdist = { url = "https://files.pythonhosted.org/packages/61/4a/79044fcb3209583d1ffe0c2a7c19dddfb657a03faeb9fe0cf5a74027e646/llama_index_indices_managed_llama_cloud-0.9.4.tar.gz", hash = "sha256:b5e00752ab30564abf19c57595a2107f5697c3b03b085817b4fca84a38ebbd59", size = 15146 }
2378
+ wheels = [
2379
+ { url = "https://files.pythonhosted.org/packages/a6/6a/0e33245df06afc9766c46a1fe92687be8a09da5d0d0128bc08d84a9f5efa/llama_index_indices_managed_llama_cloud-0.9.4-py3-none-any.whl", hash = "sha256:535a08811046803ca6ab7f8e9d510e926aa5306608b02201ad3d9d21701383bc", size = 17005 },
2380
+ ]
2381
+
2382
+ [[package]]
2383
+ name = "llama-index-instrumentation"
2384
+ version = "0.4.2"
2385
+ source = { registry = "https://pypi.org/simple" }
2386
+ dependencies = [
2387
+ { name = "deprecated" },
2388
+ { name = "pydantic" },
2389
+ ]
2390
+ sdist = { url = "https://files.pythonhosted.org/packages/af/b9/a7a74de6d8aacf4be329329495983d78d96b1a6e69b6d9fcf4a233febd4b/llama_index_instrumentation-0.4.2.tar.gz", hash = "sha256:dc4957b64da0922060690e85a6be9698ac08e34e0f69e90b01364ddec4f3de7f", size = 46146 }
2391
+ wheels = [
2392
+ { url = "https://files.pythonhosted.org/packages/40/54/df8063b0441242e250e03d1e31ebde5dffbe24e1af32b025cb1a4544150c/llama_index_instrumentation-0.4.2-py3-none-any.whl", hash = "sha256:b4989500e6454059ab3f3c4a193575d47ab1fadb730c2e8f2b962649ae88b70b", size = 15411 },
2393
+ ]
2394
+
2395
+ [[package]]
2396
+ name = "llama-index-llms-openai"
2397
+ version = "0.6.9"
2398
+ source = { registry = "https://pypi.org/simple" }
2399
+ dependencies = [
2400
+ { name = "llama-index-core" },
2401
+ { name = "openai" },
2402
+ ]
2403
+ sdist = { url = "https://files.pythonhosted.org/packages/2b/6a/3b0225298b10ace4b5c08f564dfc5ddcdbd1792b027097628c120a00d0af/llama_index_llms_openai-0.6.9.tar.gz", hash = "sha256:4c8c91a5e5b0e59b44ecd8fe43156e93c6abf1c9ca8e1e4ad3e4834c84f51199", size = 25535 }
2404
+ wheels = [
2405
+ { url = "https://files.pythonhosted.org/packages/76/74/103b4f967f589f92e43b55f4dcfa5ad3544a475ff9103e15dcfc57e3c28e/llama_index_llms_openai-0.6.9-py3-none-any.whl", hash = "sha256:02eec6ef46ac67be76607ea28c6fbe4c95a6713adc2da629e3682a6b3802cf38", size = 26532 },
2406
+ ]
2407
+
2408
+ [[package]]
2409
+ name = "llama-index-readers-file"
2410
+ version = "0.5.5"
2411
+ source = { registry = "https://pypi.org/simple" }
2412
+ dependencies = [
2413
+ { name = "beautifulsoup4" },
2414
+ { name = "defusedxml" },
2415
+ { name = "llama-index-core" },
2416
+ { name = "pandas" },
2417
+ { name = "pypdf" },
2418
+ { name = "striprtf" },
2419
+ ]
2420
+ sdist = { url = "https://files.pythonhosted.org/packages/99/13/5bb486c57832efdd8dddc28fc04535f74dcc6ea07847b1ab2075a9d9c6e8/llama_index_readers_file-0.5.5.tar.gz", hash = "sha256:024b841cfdf6d37e4e24cea6c89edfac5f0eaffcbcfc4904e4b0b169847c10e5", size = 32563 }
2421
+ wheels = [
2422
+ { url = "https://files.pythonhosted.org/packages/19/f6/0dc2a15f8d821721cc958873c8c83367d7fbbd411c512d0262b64c055ce3/llama_index_readers_file-0.5.5-py3-none-any.whl", hash = "sha256:948adef8d0aff839c99c95ae876c3ae57a58b319e3dd0862d68932dbd408a022", size = 51826 },
2423
+ ]
2424
+
2425
+ [[package]]
2426
+ name = "llama-index-readers-llama-parse"
2427
+ version = "0.5.1"
2428
+ source = { registry = "https://pypi.org/simple" }
2429
+ dependencies = [
2430
+ { name = "llama-index-core" },
2431
+ { name = "llama-parse" },
2432
+ ]
2433
+ sdist = { url = "https://files.pythonhosted.org/packages/b3/77/5bfaab20e6ec8428dbf2352e18be550c957602723d69383908176b5686cd/llama_index_readers_llama_parse-0.5.1.tar.gz", hash = "sha256:2b78b73faa933e30e6c69df351e4e9f36dfe2ae142e2ab3969ddd2ac48930e37", size = 3858 }
2434
+ wheels = [
2435
+ { url = "https://files.pythonhosted.org/packages/68/81/52410c7245dcbf1a54756a9ce3892cdd167ec0b884d696de1304ca3f452e/llama_index_readers_llama_parse-0.5.1-py3-none-any.whl", hash = "sha256:0d41450ed29b0c49c024e206ef6c8e662b1854e77a1c5faefed3b958be54f880", size = 3203 },
2436
+ ]
2437
+
2438
+ [[package]]
2439
+ name = "llama-index-vector-stores-chroma"
2440
+ version = "0.5.3"
2441
+ source = { registry = "https://pypi.org/simple" }
2442
+ dependencies = [
2443
+ { name = "chromadb" },
2444
+ { name = "llama-index-core" },
2445
+ ]
2446
+ sdist = { url = "https://files.pythonhosted.org/packages/ce/d3/2a1c3a4d3f32b35875c6ab4f33d2390acbf2346d87633126643109c51e20/llama_index_vector_stores_chroma-0.5.3.tar.gz", hash = "sha256:a066b9ee3dc5110a0e13b9dfeb1f4ae0b48931c04644ca7f534a69043c29149a", size = 7948 }
2447
+ wheels = [
2448
+ { url = "https://files.pythonhosted.org/packages/aa/8e/699ffdf950f5c04200ed5129e244f1ea92ff4f2651d16eaeb6fec0e3d320/llama_index_vector_stores_chroma-0.5.3-py3-none-any.whl", hash = "sha256:f958553e421068681f5bc5581dfa63c768922f26280717dfeaf86677c6d583ee", size = 7751 },
2449
+ ]
2450
+
2451
+ [[package]]
2452
+ name = "llama-index-workflows"
2453
+ version = "2.11.5"
2454
+ source = { registry = "https://pypi.org/simple" }
2455
+ dependencies = [
2456
+ { name = "llama-index-instrumentation" },
2457
+ { name = "pydantic" },
2458
+ { name = "typing-extensions" },
2459
+ ]
2460
+ sdist = { url = "https://files.pythonhosted.org/packages/d4/14/b15613ece1d7bfbe3f3b1a7f76cf9eaa603b55dd6b5921e81de2ee21f580/llama_index_workflows-2.11.5.tar.gz", hash = "sha256:defba6c5169a9c986fe06dd9f3ee5e1a6cc5f58c776605c0458baa0a55f07643", size = 70743 }
2461
+ wheels = [
2462
+ { url = "https://files.pythonhosted.org/packages/90/ac/e911594a2f10445717ea45b61b3a93f3bb91594320745fe1bb796c2dc87a/llama_index_workflows-2.11.5-py3-none-any.whl", hash = "sha256:3c5a419129114bb0b1bd83b88aa5f653f84181b2e39e33473e8747ec6e88538e", size = 91982 },
2463
+ ]
2464
+
2465
+ [[package]]
2466
+ name = "llama-parse"
2467
+ version = "0.6.54"
2468
+ source = { registry = "https://pypi.org/simple" }
2469
+ dependencies = [
2470
+ { name = "llama-cloud-services" },
2471
+ ]
2472
+ sdist = { url = "https://files.pythonhosted.org/packages/08/f6/93b5d123c480bc8c93e6dc3ea930f4f8df8da27f829bb011100ba3ce23dc/llama_parse-0.6.54.tar.gz", hash = "sha256:c707b31152155c9bae84e316fab790bbc8c85f4d8825ce5ee386ebeb7db258f1", size = 3577 }
2473
+ wheels = [
2474
+ { url = "https://files.pythonhosted.org/packages/05/50/c5ccd2a50daa0a10c7f3f7d4e6992392454198cd8a7d99fcb96cb60d0686/llama_parse-0.6.54-py3-none-any.whl", hash = "sha256:c66c8d51cf6f29a44eaa8595a595de5d2598afc86e5a33a4cebe5fe228036920", size = 4879 },
2475
+ ]
2476
+
2477
  [[package]]
2478
  name = "logfire"
2479
  version = "4.15.1"
 
2592
  { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 },
2593
  ]
2594
 
2595
+ [[package]]
2596
+ name = "marshmallow"
2597
+ version = "3.26.1"
2598
+ source = { registry = "https://pypi.org/simple" }
2599
+ dependencies = [
2600
+ { name = "packaging" },
2601
+ ]
2602
+ sdist = { url = "https://files.pythonhosted.org/packages/ab/5e/5e53d26b42ab75491cda89b871dab9e97c840bf12c63ec58a1919710cd06/marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6", size = 221825 }
2603
+ wheels = [
2604
+ { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878 },
2605
+ ]
2606
+
2607
  [[package]]
2608
  name = "mcp"
2609
  version = "1.22.0"
 
2757
  { url = "https://files.pythonhosted.org/packages/6a/fc/0e61d9a4e29c8679356795a40e48f647b4aad58d71bfc969f0f8f56fb912/mmh3-5.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e7884931fe5e788163e7b3c511614130c2c59feffdc21112290a194487efb2e9", size = 40455 },
2758
  ]
2759
 
2760
+ [[package]]
2761
+ name = "modal"
2762
+ version = "1.2.4"
2763
+ source = { registry = "https://pypi.org/simple" }
2764
+ dependencies = [
2765
+ { name = "aiohttp" },
2766
+ { name = "cbor2" },
2767
+ { name = "certifi" },
2768
+ { name = "click" },
2769
+ { name = "grpclib" },
2770
+ { name = "protobuf" },
2771
+ { name = "rich" },
2772
+ { name = "synchronicity" },
2773
+ { name = "toml" },
2774
+ { name = "typer" },
2775
+ { name = "types-certifi" },
2776
+ { name = "types-toml" },
2777
+ { name = "typing-extensions" },
2778
+ { name = "watchfiles" },
2779
+ ]
2780
+ sdist = { url = "https://files.pythonhosted.org/packages/91/b1/7bd589a3e1cc1ffc3fc2c05d1fab4b02459552d1ed416e00f19969e54f32/modal-1.2.4.tar.gz", hash = "sha256:5acb4a57a4bc857944579a3cf36e93f38d39499837628e9acf591d45d0c88c89", size = 645018 }
2781
+ wheels = [
2782
+ { url = "https://files.pythonhosted.org/packages/ad/5a/a6bb9d01111109398bad8405587dde4f65088604b958c4f5e8cc5b212460/modal-1.2.4-py3-none-any.whl", hash = "sha256:cf4f01081bd9e5e1ec844d87a2c6a5805fd7c8f4deff5671c20d3b1505899aa8", size = 742291 },
2783
+ ]
2784
+
2785
  [[package]]
2786
  name = "more-itertools"
2787
  version = "10.8.0"
 
2990
  { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 },
2991
  ]
2992
 
2993
+ [[package]]
2994
+ name = "nest-asyncio"
2995
+ version = "1.6.0"
2996
+ source = { registry = "https://pypi.org/simple" }
2997
+ sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 }
2998
+ wheels = [
2999
+ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 },
3000
+ ]
3001
+
3002
  [[package]]
3003
  name = "networkx"
3004
  version = "3.6"
 
3020
  { url = "https://files.pythonhosted.org/packages/bf/2f/9e9d0dcaa4c6ffa22b7aa31069a8a264c753ff8027b36af602cce038c92f/nexus_rpc-1.1.0-py3-none-any.whl", hash = "sha256:d1b007af2aba186a27e736f8eaae39c03aed05b488084ff6c3d1785c9ba2ad38", size = 27743 },
3021
  ]
3022
 
3023
+ [[package]]
3024
+ name = "nltk"
3025
+ version = "3.9.2"
3026
+ source = { registry = "https://pypi.org/simple" }
3027
+ dependencies = [
3028
+ { name = "click" },
3029
+ { name = "joblib" },
3030
+ { name = "regex" },
3031
+ { name = "tqdm" },
3032
+ ]
3033
+ sdist = { url = "https://files.pythonhosted.org/packages/f9/76/3a5e4312c19a028770f86fd7c058cf9f4ec4321c6cf7526bab998a5b683c/nltk-3.9.2.tar.gz", hash = "sha256:0f409e9b069ca4177c1903c3e843eef90c7e92992fa4931ae607da6de49e1419", size = 2887629 }
3034
+ wheels = [
3035
+ { url = "https://files.pythonhosted.org/packages/60/90/81ac364ef94209c100e12579629dc92bf7a709a84af32f8c551b02c07e94/nltk-3.9.2-py3-none-any.whl", hash = "sha256:1e209d2b3009110635ed9709a67a1a3e33a10f799490fa71cf4bec218c11c88a", size = 1513404 },
3036
+ ]
3037
+
3038
  [[package]]
3039
  name = "nodeenv"
3040
  version = "1.9.1"
 
3568
 
3569
  [[package]]
3570
  name = "pandas"
3571
+ version = "2.2.3"
3572
  source = { registry = "https://pypi.org/simple" }
3573
  dependencies = [
3574
  { name = "numpy" },
 
3576
  { name = "pytz" },
3577
  { name = "tzdata" },
3578
  ]
3579
+ sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 }
3580
+ wheels = [
3581
+ { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 },
3582
+ { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 },
3583
+ { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 },
3584
+ { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 },
3585
+ { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 },
3586
+ { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 },
3587
+ { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 },
3588
+ { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 },
3589
+ { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 },
3590
+ { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 },
3591
+ { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 },
3592
+ { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 },
3593
+ { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 },
3594
+ { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 },
3595
+ { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 },
3596
+ { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 },
3597
+ { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 },
3598
+ { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 },
3599
+ { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 },
3600
+ { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 },
3601
+ { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 },
3602
+ { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 },
3603
+ { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 },
3604
+ { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 },
3605
+ { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 },
3606
+ { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 },
3607
+ { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 },
 
 
 
 
 
 
 
 
 
 
 
 
 
3608
  ]
3609
 
3610
  [[package]]
 
4385
  { name = "cryptography" },
4386
  ]
4387
 
4388
+ [[package]]
4389
+ name = "pypdf"
4390
+ version = "6.4.0"
4391
+ source = { registry = "https://pypi.org/simple" }
4392
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/01/f7510cc6124f494cfbec2e8d3c2e1a20d4f6c18622b0c03a3a70e968bacb/pypdf-6.4.0.tar.gz", hash = "sha256:4769d471f8ddc3341193ecc5d6560fa44cf8cd0abfabf21af4e195cc0c224072", size = 5276661 }
4393
+ wheels = [
4394
+ { url = "https://files.pythonhosted.org/packages/cd/f2/9c9429411c91ac1dd5cd66780f22b6df20c64c3646cdd1e6d67cf38579c4/pypdf-6.4.0-py3-none-any.whl", hash = "sha256:55ab9837ed97fd7fcc5c131d52fcc2223bc5c6b8a1488bbf7c0e27f1f0023a79", size = 329497 },
4395
+ ]
4396
+
4397
  [[package]]
4398
  name = "pyperclip"
4399
  version = "1.11.0"
 
5177
  { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 },
5178
  ]
5179
 
5180
+ [[package]]
5181
+ name = "sqlalchemy"
5182
+ version = "2.0.44"
5183
+ source = { registry = "https://pypi.org/simple" }
5184
+ dependencies = [
5185
+ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
5186
+ { name = "typing-extensions" },
5187
+ ]
5188
+ sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830 }
5189
+ wheels = [
5190
+ { url = "https://files.pythonhosted.org/packages/e3/81/15d7c161c9ddf0900b076b55345872ed04ff1ed6a0666e5e94ab44b0163c/sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd", size = 2140517 },
5191
+ { url = "https://files.pythonhosted.org/packages/d4/d5/4abd13b245c7d91bdf131d4916fd9e96a584dac74215f8b5bc945206a974/sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa", size = 2130738 },
5192
+ { url = "https://files.pythonhosted.org/packages/cb/3c/8418969879c26522019c1025171cefbb2a8586b6789ea13254ac602986c0/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e", size = 3304145 },
5193
+ { url = "https://files.pythonhosted.org/packages/94/2d/fdb9246d9d32518bda5d90f4b65030b9bf403a935cfe4c36a474846517cb/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e", size = 3304511 },
5194
+ { url = "https://files.pythonhosted.org/packages/7d/fb/40f2ad1da97d5c83f6c1269664678293d3fe28e90ad17a1093b735420549/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399", size = 3235161 },
5195
+ { url = "https://files.pythonhosted.org/packages/95/cb/7cf4078b46752dca917d18cf31910d4eff6076e5b513c2d66100c4293d83/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b", size = 3261426 },
5196
+ { url = "https://files.pythonhosted.org/packages/f8/3b/55c09b285cb2d55bdfa711e778bdffdd0dc3ffa052b0af41f1c5d6e582fa/sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3", size = 2105392 },
5197
+ { url = "https://files.pythonhosted.org/packages/c7/23/907193c2f4d680aedbfbdf7bf24c13925e3c7c292e813326c1b84a0b878e/sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5", size = 2130293 },
5198
+ { url = "https://files.pythonhosted.org/packages/62/c4/59c7c9b068e6813c898b771204aad36683c96318ed12d4233e1b18762164/sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250", size = 2139675 },
5199
+ { url = "https://files.pythonhosted.org/packages/d6/ae/eeb0920537a6f9c5a3708e4a5fc55af25900216bdb4847ec29cfddf3bf3a/sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29", size = 2127726 },
5200
+ { url = "https://files.pythonhosted.org/packages/d8/d5/2ebbabe0379418eda8041c06b0b551f213576bfe4c2f09d77c06c07c8cc5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44", size = 3327603 },
5201
+ { url = "https://files.pythonhosted.org/packages/45/e5/5aa65852dadc24b7d8ae75b7efb8d19303ed6ac93482e60c44a585930ea5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1", size = 3337842 },
5202
+ { url = "https://files.pythonhosted.org/packages/41/92/648f1afd3f20b71e880ca797a960f638d39d243e233a7082c93093c22378/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7", size = 3264558 },
5203
+ { url = "https://files.pythonhosted.org/packages/40/cf/e27d7ee61a10f74b17740918e23cbc5bc62011b48282170dc4c66da8ec0f/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d", size = 3301570 },
5204
+ { url = "https://files.pythonhosted.org/packages/3b/3d/3116a9a7b63e780fb402799b6da227435be878b6846b192f076d2f838654/sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4", size = 2103447 },
5205
+ { url = "https://files.pythonhosted.org/packages/25/83/24690e9dfc241e6ab062df82cc0df7f4231c79ba98b273fa496fb3dd78ed/sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e", size = 2130912 },
5206
+ { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479 },
5207
+ { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212 },
5208
+ { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353 },
5209
+ { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222 },
5210
+ { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614 },
5211
+ { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248 },
5212
+ { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275 },
5213
+ { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901 },
5214
+ { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718 },
5215
+ ]
5216
+
5217
+ [package.optional-dependencies]
5218
+ asyncio = [
5219
+ { name = "greenlet" },
5220
+ ]
5221
+
5222
  [[package]]
5223
  name = "sse-starlette"
5224
  version = "3.0.3"
 
5244
  { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033 },
5245
  ]
5246
 
5247
+ [[package]]
5248
+ name = "striprtf"
5249
+ version = "0.0.26"
5250
+ source = { registry = "https://pypi.org/simple" }
5251
+ sdist = { url = "https://files.pythonhosted.org/packages/25/20/3d419008265346452d09e5dadfd5d045b64b40d8fc31af40588e6c76997a/striprtf-0.0.26.tar.gz", hash = "sha256:fdb2bba7ac440072d1c41eab50d8d74ae88f60a8b6575c6e2c7805dc462093aa", size = 6258 }
5252
+ wheels = [
5253
+ { url = "https://files.pythonhosted.org/packages/a3/cf/0fea4f4ba3fc2772ac2419278aa9f6964124d4302117d61bc055758e000c/striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb", size = 6914 },
5254
+ ]
5255
+
5256
  [[package]]
5257
  name = "structlog"
5258
  version = "25.5.0"
 
5274
  { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 },
5275
  ]
5276
 
5277
+ [[package]]
5278
+ name = "synchronicity"
5279
+ version = "0.10.4"
5280
+ source = { registry = "https://pypi.org/simple" }
5281
+ dependencies = [
5282
+ { name = "typing-extensions" },
5283
+ ]
5284
+ sdist = { url = "https://files.pythonhosted.org/packages/9e/92/2abaf9f4d846c2b7c240e9ce3c9198abf6660265bc1031640cbca5365351/synchronicity-0.10.4.tar.gz", hash = "sha256:3a9ac19f9a58cad64fcb3729812b828b77e54e0a90ced4439e09d3d9c19a90f0", size = 66903 }
5285
+ wheels = [
5286
+ { url = "https://files.pythonhosted.org/packages/01/c6/a3631d119c9979816c0ed0354aa9fb829a14f53a43337f263dc3329b3a6e/synchronicity-0.10.4-py3-none-any.whl", hash = "sha256:0e3f00b2123cf2a77a8bb3b65fbeccad04adea682bfbd50c01637b75a168c73b", size = 39652 },
5287
+ ]
5288
+
5289
  [[package]]
5290
  name = "temporalio"
5291
  version = "1.19.0"
 
5331
  { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 },
5332
  ]
5333
 
5334
+ [[package]]
5335
+ name = "tiktoken"
5336
+ version = "0.12.0"
5337
+ source = { registry = "https://pypi.org/simple" }
5338
+ dependencies = [
5339
+ { name = "regex" },
5340
+ { name = "requests" },
5341
+ ]
5342
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 }
5343
+ wheels = [
5344
+ { url = "https://files.pythonhosted.org/packages/de/46/21ea696b21f1d6d1efec8639c204bdf20fde8bafb351e1355c72c5d7de52/tiktoken-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e227c7f96925003487c33b1b32265fad2fbcec2b7cf4817afb76d416f40f6bb", size = 1051565 },
5345
+ { url = "https://files.pythonhosted.org/packages/c9/d9/35c5d2d9e22bb2a5f74ba48266fb56c63d76ae6f66e02feb628671c0283e/tiktoken-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c06cf0fcc24c2cb2adb5e185c7082a82cba29c17575e828518c2f11a01f445aa", size = 995284 },
5346
+ { url = "https://files.pythonhosted.org/packages/01/84/961106c37b8e49b9fdcf33fe007bb3a8fdcc380c528b20cc7fbba80578b8/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f18f249b041851954217e9fd8e5c00b024ab2315ffda5ed77665a05fa91f42dc", size = 1129201 },
5347
+ { url = "https://files.pythonhosted.org/packages/6a/d0/3d9275198e067f8b65076a68894bb52fd253875f3644f0a321a720277b8a/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:47a5bc270b8c3db00bb46ece01ef34ad050e364b51d406b6f9730b64ac28eded", size = 1152444 },
5348
+ { url = "https://files.pythonhosted.org/packages/78/db/a58e09687c1698a7c592e1038e01c206569b86a0377828d51635561f8ebf/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:508fa71810c0efdcd1b898fda574889ee62852989f7c1667414736bcb2b9a4bd", size = 1195080 },
5349
+ { url = "https://files.pythonhosted.org/packages/9e/1b/a9e4d2bf91d515c0f74afc526fd773a812232dd6cda33ebea7f531202325/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1af81a6c44f008cba48494089dd98cccb8b313f55e961a52f5b222d1e507967", size = 1255240 },
5350
+ { url = "https://files.pythonhosted.org/packages/9d/15/963819345f1b1fb0809070a79e9dd96938d4ca41297367d471733e79c76c/tiktoken-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e68e3e593637b53e56f7237be560f7a394451cb8c11079755e80ae64b9e6def", size = 879422 },
5351
+ { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728 },
5352
+ { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049 },
5353
+ { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008 },
5354
+ { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665 },
5355
+ { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230 },
5356
+ { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688 },
5357
+ { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694 },
5358
+ { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802 },
5359
+ { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995 },
5360
+ { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948 },
5361
+ { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986 },
5362
+ { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222 },
5363
+ { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097 },
5364
+ { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117 },
5365
+ { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309 },
5366
+ { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712 },
5367
+ { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725 },
5368
+ { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875 },
5369
+ { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451 },
5370
+ { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794 },
5371
+ { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777 },
5372
+ { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188 },
5373
+ { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978 },
5374
+ { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271 },
5375
+ { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216 },
5376
+ { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860 },
5377
+ { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567 },
5378
+ { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067 },
5379
+ { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473 },
5380
+ { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855 },
5381
+ { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022 },
5382
+ { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736 },
5383
+ { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908 },
5384
+ { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706 },
5385
+ { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667 },
5386
+ ]
5387
+
5388
  [[package]]
5389
  name = "tokenizers"
5390
  version = "0.22.1"
 
5410
  { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684 },
5411
  ]
5412
 
5413
+ [[package]]
5414
+ name = "toml"
5415
+ version = "0.10.2"
5416
+ source = { registry = "https://pypi.org/simple" }
5417
+ sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
5418
+ wheels = [
5419
+ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
5420
+ ]
5421
+
5422
  [[package]]
5423
  name = "tomli"
5424
  version = "2.3.0"
 
5594
  { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028 },
5595
  ]
5596
 
5597
+ [[package]]
5598
+ name = "types-certifi"
5599
+ version = "2021.10.8.3"
5600
+ source = { registry = "https://pypi.org/simple" }
5601
+ sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095 }
5602
+ wheels = [
5603
+ { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136 },
5604
+ ]
5605
+
5606
  [[package]]
5607
  name = "types-protobuf"
5608
  version = "6.32.1.20251105"
 
5624
  { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658 },
5625
  ]
5626
 
5627
+ [[package]]
5628
+ name = "types-toml"
5629
+ version = "0.10.8.20240310"
5630
+ source = { registry = "https://pypi.org/simple" }
5631
+ sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 }
5632
+ wheels = [
5633
+ { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 },
5634
+ ]
5635
+
5636
  [[package]]
5637
  name = "typing-extensions"
5638
  version = "4.15.0"
 
5642
  { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 },
5643
  ]
5644
 
5645
+ [[package]]
5646
+ name = "typing-inspect"
5647
+ version = "0.9.0"
5648
+ source = { registry = "https://pypi.org/simple" }
5649
+ dependencies = [
5650
+ { name = "mypy-extensions" },
5651
+ { name = "typing-extensions" },
5652
+ ]
5653
+ sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825 }
5654
+ wheels = [
5655
+ { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827 },
5656
+ ]
5657
+
5658
  [[package]]
5659
  name = "typing-inspection"
5660
  version = "0.4.2"