aixtools 0.1.10__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aixtools might be problematic. Click here for more details.

Files changed (62) hide show
  1. aixtools/_version.py +2 -2
  2. aixtools/agents/agent.py +26 -7
  3. aixtools/agents/print_nodes.py +54 -0
  4. aixtools/agents/prompt.py +2 -2
  5. aixtools/compliance/private_data.py +1 -1
  6. aixtools/evals/discovery.py +174 -0
  7. aixtools/evals/evals.py +74 -0
  8. aixtools/evals/run_evals.py +110 -0
  9. aixtools/logging/log_objects.py +24 -23
  10. aixtools/mcp/client.py +148 -2
  11. aixtools/server/__init__.py +0 -6
  12. aixtools/server/path.py +88 -31
  13. aixtools/testing/aix_test_model.py +9 -1
  14. aixtools/tools/doctor/mcp_tool_doctor.py +79 -0
  15. aixtools/tools/doctor/tool_doctor.py +4 -0
  16. aixtools/tools/doctor/tool_recommendation.py +5 -0
  17. aixtools/utils/config.py +0 -1
  18. {aixtools-0.1.10.dist-info → aixtools-0.2.0.dist-info}/METADATA +186 -30
  19. {aixtools-0.1.10.dist-info → aixtools-0.2.0.dist-info}/RECORD +23 -55
  20. aixtools-0.2.0.dist-info/entry_points.txt +4 -0
  21. aixtools-0.2.0.dist-info/top_level.txt +1 -0
  22. aixtools/server/workspace_privacy.py +0 -65
  23. aixtools-0.1.10.dist-info/entry_points.txt +0 -2
  24. aixtools-0.1.10.dist-info/top_level.txt +0 -5
  25. docker/mcp-base/Dockerfile +0 -33
  26. docker/mcp-base/zscaler.crt +0 -28
  27. notebooks/example_faulty_mcp_server.ipynb +0 -74
  28. notebooks/example_mcp_server_stdio.ipynb +0 -76
  29. notebooks/example_raw_mcp_client.ipynb +0 -84
  30. notebooks/example_tool_doctor.ipynb +0 -65
  31. scripts/config.sh +0 -28
  32. scripts/lint.sh +0 -32
  33. scripts/log_view.sh +0 -18
  34. scripts/run_example_mcp_server.sh +0 -14
  35. scripts/run_faulty_mcp_server.sh +0 -13
  36. scripts/run_server.sh +0 -29
  37. scripts/test.sh +0 -30
  38. tests/unit/__init__.py +0 -0
  39. tests/unit/a2a/__init__.py +0 -0
  40. tests/unit/a2a/google_sdk/__init__.py +0 -0
  41. tests/unit/a2a/google_sdk/pydantic_ai_adapter/__init__.py +0 -0
  42. tests/unit/a2a/google_sdk/pydantic_ai_adapter/test_agent_executor.py +0 -188
  43. tests/unit/a2a/google_sdk/pydantic_ai_adapter/test_storage.py +0 -156
  44. tests/unit/a2a/google_sdk/test_card.py +0 -114
  45. tests/unit/a2a/google_sdk/test_remote_agent_connection.py +0 -413
  46. tests/unit/a2a/google_sdk/test_utils.py +0 -208
  47. tests/unit/agents/__init__.py +0 -0
  48. tests/unit/agents/test_prompt.py +0 -363
  49. tests/unit/compliance/test_private_data.py +0 -329
  50. tests/unit/google/__init__.py +0 -1
  51. tests/unit/google/test_client.py +0 -233
  52. tests/unit/mcp/__init__.py +0 -0
  53. tests/unit/mcp/test_client.py +0 -242
  54. tests/unit/server/__init__.py +0 -0
  55. tests/unit/server/test_path.py +0 -225
  56. tests/unit/server/test_utils.py +0 -362
  57. tests/unit/utils/__init__.py +0 -0
  58. tests/unit/utils/test_files.py +0 -146
  59. tests/unit/vault/__init__.py +0 -0
  60. tests/unit/vault/test_vault.py +0 -246
  61. {tests → aixtools/evals}/__init__.py +0 -0
  62. {aixtools-0.1.10.dist-info → aixtools-0.2.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aixtools
3
- Version: 0.1.10
3
+ Version: 0.2.0
4
4
  Summary: Tools for AI exploration and debugging
5
5
  Requires-Python: >=3.11.2
6
6
  Description-Content-Type: text/markdown
@@ -16,8 +16,10 @@ Requires-Dist: langchain-chroma>=0.2.3
16
16
  Requires-Dist: langchain-ollama>=0.3.2
17
17
  Requires-Dist: langchain-openai>=0.3.14
18
18
  Requires-Dist: mcp>=1.11.0
19
+ Requires-Dist: mypy>=1.18.2
19
20
  Requires-Dist: pandas>=2.2.3
20
- Requires-Dist: pydantic-ai>=0.4.10
21
+ Requires-Dist: pydantic-evals>=0.4.10
22
+ Requires-Dist: pydantic-ai>=1.0.9
21
23
  Requires-Dist: pylint>=3.3.7
22
24
  Requires-Dist: rich>=14.0.0
23
25
  Requires-Dist: ruff>=0.11.6
@@ -44,20 +46,16 @@ A2A
44
46
  - Google SDK Integration for A2A - `aixtools/a2a/google_sdk/`
45
47
  - PydanticAI Adapter for Google SDK - `aixtools/a2a/google_sdk/pydantic_ai_adapter/`
46
48
 
47
- Databases
48
- - Database Integration - `aixtools/db/`
49
- - Vector Database Support - `aixtools/db/vector_db.py`
50
-
51
49
  Logging & Debugging
52
- - Log Viewing Application - `aixtools/log_view/`
50
+ - Log Viewing Web Application - `log_view`
53
51
  - Object Logging System - `aixtools/logging/`
54
52
  - Model Patch Logging - `aixtools/logging/model_patch_logging.py`
55
53
  - Log Filtering System - `aixtools/logfilters/`
56
54
  - FastMCP Logging - `aixtools/mcp/fast_mcp_log.py`
57
- - Command Line Interface for Log Viewing - Entry point: `log_view`
58
55
  - MCP (Model Context Protocol) Support - `aixtools/logging/mcp_log_models.py`, `aixtools/logging/mcp_logger.py`
59
56
 
60
- Testing & Tools
57
+ Testing Tools & Evals
58
+ - Evaluations - `aixtools/evals/` - Entry point: `evals`
61
59
  - Testing Utilities - `aixtools/testing/`
62
60
  - Mock Tool System - `aixtools/testing/mock_tool.py`
63
61
  - Model Patch Caching - `aixtools/testing/model_patch_cache.py`
@@ -65,6 +63,10 @@ Testing & Tools
65
63
  - Tool Recommendation Engine - `aixtools/tools/doctor/tool_recommendation.py`
66
64
  - FaultyMCP - `aixtools/mcp/faulty_mcp.py`
67
65
 
66
+ Databases
67
+ - Database Integration - `aixtools/db/`
68
+ - Vector Database Support - `aixtools/db/vector_db.py`
69
+
68
70
  Chainlit & HTTP Server
69
71
  - Chainlit Integration - `aixtools/app.py`, `aixtools/chainlit.md`
70
72
  - Chainlit Utilities - `aixtools/utils/chainlit/`
@@ -80,28 +82,11 @@ Programming utils
80
82
 
81
83
  ## Installation
82
84
 
83
- ### From GitHub
84
-
85
85
  ```bash
86
86
  uv add aixtools
87
87
  ```
88
88
 
89
- ### Development Setup
90
-
91
- ```bash
92
- # Create a new project
93
- uv init MyNewProject
94
- cd MyNewProject
95
-
96
- # Add virtual environment and activate it
97
- uv venv .venv
98
- source .venv/bin/activate
99
-
100
- # Add this package
101
- uv add aixtools
102
- ```
103
-
104
- ### Updating
89
+ **Updating**
105
90
 
106
91
  ```bash
107
92
  uv add --upgrade aixtools
@@ -113,6 +98,7 @@ AIXtools requires environment variables for model providers.
113
98
 
114
99
  **IMPORTANT:** Create a `.env` file based on [`.env_template`](./.env_template):
115
100
 
101
+ Here is an example configuration:
116
102
  ```bash
117
103
  # Model family (azure, openai, or ollama)
118
104
  MODEL_FAMILY=azure
@@ -160,6 +146,33 @@ agent = get_agent(system_prompt="You are a helpful assistant.")
160
146
  result, nodes = await run_agent(agent, "Tell me about AI")
161
147
  ```
162
148
 
149
+ ### Node Debugging and Visualization
150
+
151
+ The [`print_nodes`](aixtools/agents/print_nodes.py) module provides a clean, indented output for easy reading of the node from agent execution.
152
+
153
+ ```python
154
+ from aixtools.agents.print_nodes import print_nodes, print_node
155
+ from aixtools.agents.agent import get_agent, run_agent
156
+
157
+ agent = get_agent(system_prompt="You are a helpful assistant.")
158
+ result, nodes = await run_agent(agent, "Explain quantum computing")
159
+ # Print all execution nodes for debugging
160
+ print_nodes(nodes)
161
+ ```
162
+
163
+ **Features:**
164
+ - **Node Type Detection**: Automatically handles different node types (`UserPromptNode`, `CallToolsNode`, `ModelRequestNode`, `End`)
165
+ - **Formatted Output**: Provides clean, indented output for easy reading
166
+ - **Tool Call Visualization**: Shows tool names and arguments for tool calls
167
+ - **Text Content Display**: Formats text parts with proper indentation
168
+ - **Model Request Summary**: Shows character count for model requests to avoid verbose output
169
+
170
+ **Node Types Supported:**
171
+ - `UserPromptNode` - Displays user prompts with indentation
172
+ - `CallToolsNode` - Shows tool calls with names and arguments
173
+ - `ModelRequestNode` - Summarizes model requests with character count
174
+ - `End` - Marks the end of execution (output suppressed by default)
175
+
163
176
  ### Agent Batch Processing
164
177
 
165
178
  Process multiple agent queries simultaneously with built-in concurrency control and result aggregation.
@@ -298,9 +311,39 @@ with ObjectLogger() as logger:
298
311
  logger.log(agent_response)
299
312
  ```
300
313
 
301
- ### MCP Logger
314
+ ### MCP logging
315
+
316
+ AIXtools provides MCP support for both client and server implementations with easier logging for debugging pourposes.
317
+
318
+ **Example:**
319
+
320
+ Let's assume we have an MCP server that runs an agent tool.
321
+
322
+ Note that the `ctx: Context` parameter is passed to `run_agent`, this will enable logging to the MCP client.
323
+
324
+ ```python
325
+ @mcp.tool
326
+ async def my_tool_with_agent(query: str, ctx: Context) -> str:
327
+ """ A tool that uses an gents to process the query """
328
+ agent = get_agent()
329
+ async with get_qb_agent() as agent:
330
+ ret, nodes = await run_agent(agent=agent, prompt=query, ctx=ctx) # Enable MCP logging
331
+ return str(ret)
332
+ ```
333
+
334
+ On the client side, you can create an agent connected to the MCP server, the "nodes" from the MCP server will show on the STDOUT so you can see what's going on the MCP server's agent loop
335
+
336
+ ```python
337
+ mcp = get_mcp_client("http://localhost:8000") # Get an MCP client with a default log handler that prints to STDOUT
338
+ agent = get_agent(toolsets=[mcp])
339
+ async with agent:
340
+ # The messages from the MCP server will be printed to the STDOUT
341
+ ret, nodes = await run_agent(agent, prompt="...")
342
+ ```
343
+
344
+ #### MCP Server Logging
302
345
 
303
- This is an MCP server that can log MCP requests and responses.
346
+ Create MCP servers with built-in logging capabilities.
304
347
 
305
348
  ```python
306
349
  from aixtools.mcp.fast_mcp_log import FastMcpLog
@@ -370,6 +413,77 @@ By default, the "FaultyMCP" includes several tools you can use in your tests:
370
413
  - `freeze_server(seconds)` - Simulates server freeze
371
414
  - `throw_404_exception()` - Throws HTTP 404 error
372
415
 
416
+ ### Evals
417
+
418
+ Run comprehensive Agent/LLM evaluations using the built-in evaluation discovery based on Pydantic-AI framework.
419
+
420
+ ```bash
421
+ # Run all evaluations
422
+ evals
423
+
424
+ # Run evaluations with filtering
425
+ evals --filter "specific_test"
426
+
427
+ # Run with verbose output and detailed reporting
428
+ evals --verbose --include-input --include-output --include-reasons
429
+
430
+ # Specify custom evaluations directory
431
+ evals --evals-dir /path/to/evals
432
+
433
+ # Set minimum assertions threshold
434
+ evals --min-assertions 0.8
435
+ ```
436
+
437
+ **Command Line Options:**
438
+ - `--evals-dir` - Directory containing eval_*.py files (default: evals)
439
+ - `--filter` - Filter to run only matching evaluations
440
+ - `--include-input` - Include input in report output
441
+ - `--include-output` - Include output in report output
442
+ - `--include-evaluator-failures` - Include evaluator failures in report
443
+ - `--include-reasons` - Include reasons in report output
444
+ - `--min-assertions` - Minimum assertions average required for success (default: 1.0)
445
+ - `--verbose` - Print detailed information about discovery and processing
446
+
447
+ The evaluation system discovers and runs all Dataset objects from eval_*.py files in the specified directory, similar to test runners but specifically designed for LLM evaluations using pydantic_evals.
448
+
449
+ **Discovery Mechanism:**
450
+
451
+ The evaluation framework uses an automatic discovery system that:
452
+
453
+ 1. **File Discovery**: Scans the specified directory for files matching the pattern `eval_*.py`
454
+ 2. **Dataset Discovery**: Within each file, looks for variables named `dataset_*` that are instances of `pydantic_evals.Dataset`
455
+ 3. **Target Function Discovery**: Automatically finds the first async function in each module that doesn't start with an underscore (`_`) to use as the evaluation target
456
+ 4. **Filtering**: Supports filtering by module name, file name, dataset name, or fully qualified name
457
+
458
+ **Example Evaluation File Structure:**
459
+ ```python
460
+ # eval_math_operations.py
461
+ from pydantic_evals import Dataset, Case
462
+
463
+ # This dataset will be discovered automatically
464
+ dataset_addition = Dataset(
465
+ name="Addition Tests",
466
+ cases=[
467
+ Case(input="What is 2 + 2?", expected="4"),
468
+ Case(input="What is 10 + 5?", expected="15"),
469
+ ],
470
+ evaluators=[...]
471
+ )
472
+
473
+ # This function will be used as the evaluation target
474
+ async def evaluate_math_agent(input_text: str) -> str:
475
+ # Your agent evaluation logic here
476
+ agent = get_agent(system_prompt="You are a math assistant.")
477
+ result, _ = await run_agent(agent, input_text)
478
+ return result
479
+ ```
480
+
481
+ The discovery system will:
482
+ - Find `eval_math_operations.py` in the evals directory
483
+ - Discover `dataset_addition` as an evaluation dataset
484
+ - Use `evaluate_math_agent` as the target function for evaluation
485
+ - Run each case through the target function and evaluate results
486
+
373
487
  ## Testing & Tools
374
488
 
375
489
  AIXtools provides comprehensive testing utilities and diagnostic tools for AI agent development and debugging.
@@ -396,7 +510,49 @@ test_model = AixTestModel()
396
510
 
397
511
  ### Tool Doctor System
398
512
 
399
- Automated tool analysis and recommendation system for optimizing agent tool usage.
513
+ Automated tool analysis and recommendation system for optimizing agent tool usage and analyzing MCP servers.
514
+
515
+ #### MCP Tool Doctor
516
+
517
+ Analyze tools from MCP (Model Context Protocol) servers and receive AI-powered recommendations for improvement.
518
+
519
+ ```python
520
+ from aixtools.tools.doctor.mcp_tool_doctor import tool_doctor_mcp
521
+ from pydantic_ai.mcp import MCPServerStreamableHTTP, MCPServerStdio
522
+
523
+ # Analyze HTTP MCP server
524
+ recommendations = await tool_doctor_mcp(mcp_url='http://127.0.0.1:8000/mcp')
525
+ for rec in recommendations:
526
+ print(rec)
527
+
528
+ # Analyze STDIO MCP server
529
+ server = MCPServerStdio(command='fastmcp', args=['run', 'my_server.py'])
530
+ recommendations = await tool_doctor_mcp(mcp_server=server, verbose=True)
531
+ ```
532
+
533
+ **Command Line Usage:**
534
+
535
+ ```bash
536
+ # Analyze HTTP MCP server (default)
537
+ tool_doctor_mcp
538
+
539
+ # Analyze specific HTTP MCP server
540
+ tool_doctor_mcp --mcp-url http://localhost:9000/mcp --verbose
541
+
542
+ # Analyze STDIO MCP server
543
+ tool_doctor_mcp --stdio-command fastmcp --stdio-args run my_server.py --debug
544
+
545
+ # Available options:
546
+ # --mcp-url URL URL of HTTP MCP server (default: http://127.0.0.1:8000/mcp)
547
+ # --stdio-command CMD Command to run STDIO MCP server
548
+ # --stdio-args ARGS Arguments for STDIO MCP server command
549
+ # --verbose Enable verbose output
550
+ # --debug Enable debug output
551
+ ```
552
+
553
+ #### Traditional Tool Doctor
554
+
555
+ Analyze tool usage patterns from agent logs and get optimization recommendations.
400
556
 
401
557
  ```python
402
558
  from aixtools.tools.doctor.tool_doctor import ToolDoctor
@@ -1,5 +1,5 @@
1
1
  aixtools/__init__.py,sha256=9NGHm7LjsQmsvjTZvw6QFJexSvAU4bCoN_KBk9SCa00,260
2
- aixtools/_version.py,sha256=uf7mpKSLRNNF3RxSXHssYzKadEgmCS7IlRw4lFPAcUg,706
2
+ aixtools/_version.py,sha256=Dg8AmJomLVpjKL6prJylOONZAPRtB86LOce7dorQS_A,704
3
3
  aixtools/app.py,sha256=JzQ0nrv_bjDQokllIlGHOV0HEb-V8N6k_nGQH-TEsVU,5227
4
4
  aixtools/chainlit.md,sha256=yC37Ly57vjKyiIvK4oUvf4DYxZCwH7iocTlx7bLeGLU,761
5
5
  aixtools/context.py,sha256=I_MD40ZnvRm5WPKAKqBUAdXIf8YaurkYUUHSVVy-QvU,598
@@ -26,14 +26,19 @@ aixtools/a2a/google_sdk/utils.py,sha256=hjrNRZywJEUxHHaOttJFQU0FLzteg0Ggtm3qAeXM
26
26
  aixtools/a2a/google_sdk/pydantic_ai_adapter/agent_executor.py,sha256=MMbhbEnUL6NwSYnisJrDdHW8zJoSyJ3Pzzkt8jqwNdI,7066
27
27
  aixtools/a2a/google_sdk/pydantic_ai_adapter/storage.py,sha256=nGoVL7MPoZJW7iVR71laqpUYP308yFKZIifJtvUgpiU,878
28
28
  aixtools/agents/__init__.py,sha256=MAW196S2_G7uGqv-VNjvlOETRfuV44WlU1leO7SiR0A,282
29
- aixtools/agents/agent.py,sha256=E1zu70t53RqIbcLI_R09wUtsiYZR1bTnElCQ5PrsrKw,6127
29
+ aixtools/agents/agent.py,sha256=tceQByn-RGBIhW8BOjKoP0yhNzZLwAa6CxwhPhRe3PU,7270
30
30
  aixtools/agents/agent_batch.py,sha256=0Zu9yNCRPAQZPjXQ-dIUAmP1uGTVbxVt7xvnMpoJMjU,2251
31
- aixtools/agents/prompt.py,sha256=VCOVSnhNKsPIT347ouzwM1PH4I9UTm2cSnTh3ZpjRwk,3391
31
+ aixtools/agents/print_nodes.py,sha256=wVTngNfqM0As845WTRz6G3Rei_Gr3HuBlvu-G_eXuig,1665
32
+ aixtools/agents/prompt.py,sha256=p9OYnyJ4-MyGXwHPrQeJBhZ2a3RV2HqhtdUUCrTMsAQ,3361
32
33
  aixtools/compliance/__init__.py,sha256=vnw0zEdySIJWvDAJ8DCRRaWmY_agEOz1qlpAdhmtiuo,191
33
- aixtools/compliance/private_data.py,sha256=56VzAZ76lCq_fpfllkdm9ok8AQeEeIqrC-pqcfMjGpY,5342
34
+ aixtools/compliance/private_data.py,sha256=OOM9mIp3_w0fNgj3VAEWBl7-jrPc19_Ls1pC5dfF5UY,5323
34
35
  aixtools/db/__init__.py,sha256=b8vRhme3egV-aUZbAntnOaDkSXB8UT0Xy5oqQhU_z0Q,399
35
36
  aixtools/db/database.py,sha256=caWe95GlxZYlxn2ubDmR-_cQUW0ulkpR3BHunKIaOsw,3369
36
37
  aixtools/db/vector_db.py,sha256=be4JGyXj3o8VEfy9L6SO1aAoDET_zazMJkYfjlYHTYQ,4133
38
+ aixtools/evals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ aixtools/evals/discovery.py,sha256=nKBMHuM3Q87GFY4U0QXvU-zXmJjR-bnmlwf5cfp5E9s,5907
40
+ aixtools/evals/evals.py,sha256=3oJ6_HjojLOzG8XxdfdTYFk-gxep41nk_viHTsUwFNo,2738
41
+ aixtools/evals/run_evals.py,sha256=oJpGIPF5avq1r275Yd_RJyxUiUgOd00LItdKXfGlAbA,3910
37
42
  aixtools/google/client.py,sha256=8yuv_zEZKlmUTI-zRxAb3vjLUrfiwrBhcpNe0hYsO0g,1078
38
43
  aixtools/log_view/__init__.py,sha256=0fWLCq9BMo8GoH3Z5WDgvf0-J2TP0XWqtef0f28SHBA,405
39
44
  aixtools/log_view/app.py,sha256=DZp3PUM_iS3DpMHqHfFXVACvbZ9PItbOCNMkDjIOfTc,6595
@@ -45,33 +50,33 @@ aixtools/log_view/node_summary.py,sha256=EJjnBqdBWI-_bI-4nfTxwaost3mtiufb5cK7T54
45
50
  aixtools/logfilters/__init__.py,sha256=pTD8ujCqjPWBCeB7yv7lmCtnA2KXOnkIv0HExDagkXs,129
46
51
  aixtools/logfilters/context_filter.py,sha256=zR3Bnv3fCqXLeb7bCFTmlnWhC6dFIvUb-u712tOnUPk,2259
47
52
  aixtools/logging/__init__.py,sha256=b5oYyGQDUHHxhRtzqKUaQPv8hQeWw54rzDXSV8lDY1w,613
48
- aixtools/logging/log_objects.py,sha256=hKMLIsWZEw9Q0JT3GCn9mS8O7DJCzsgMZTL7mlMObpM,6828
53
+ aixtools/logging/log_objects.py,sha256=ooxa9ue3mFq1tOqAGeRBiM_4TsteVdDzR7qKM-j6Ln8,6904
49
54
  aixtools/logging/logging_config.py,sha256=LvxV3C75-I0096PpcCIbgM-Cp998LzWXeMM14HYbU20,4985
50
55
  aixtools/logging/mcp_log_models.py,sha256=7-H2GJXiiyLhpImuyLLftAGG4skxJal8Swax0ob04MY,3463
51
56
  aixtools/logging/mcp_logger.py,sha256=d2I5l4t0d6rQH17w23FpE1IUD8Ax-mSaKfByCH86q4I,6257
52
57
  aixtools/logging/model_patch_logging.py,sha256=MY2EvR7ZSctC4hJxNMe8iACeVayUJ2V5In2GAnKdgOo,2880
53
58
  aixtools/logging/open_telemetry.py,sha256=fJjF1ou_8GyfNfbyWDQPGK6JAUrUaPwURYPHhXEtDBE,1121
54
59
  aixtools/mcp/__init__.py,sha256=tLo2KZ1Ojo-rgEEJBGtZfUw-iOoopWoHDnYQTq3IzfE,163
55
- aixtools/mcp/client.py,sha256=yUcurbNQ8bRaPc8-gK_ESpcPb26d5ckDOEDQrG_YW0c,11823
60
+ aixtools/mcp/client.py,sha256=aCDLc_AfrjV8JtZLWnKCTnScvFdNoMJZiGQhBUO8t8w,18289
56
61
  aixtools/mcp/example_client.py,sha256=QCFGP3NCNJMOKWjUOnFwjnbJhUSb879IA1ZYmwjRnmc,889
57
62
  aixtools/mcp/example_server.py,sha256=1SWCyrLWsAnOa81HC4QbPJo_lBVu0b3SZBWI-qDh1vQ,458
58
63
  aixtools/mcp/fast_mcp_log.py,sha256=XYOS406dVjn5YTHyGRsRvVNQ0SKlRObfrKj6EeLFjHg,1057
59
64
  aixtools/mcp/faulty_mcp.py,sha256=uU9vlNGCS_i2k20wocVMaDHTlYjMQxuzjILad9O1cjA,12807
60
65
  aixtools/model_patch/model_patch.py,sha256=JT-oHubIn2LeoSwWbzEQ5vLH7crJmFUecHyQfaAFHa0,1813
61
- aixtools/server/__init__.py,sha256=37ADJrGLzsmjFsM2ZKUoM9cevH8rBn359WesDxIwoco,585
66
+ aixtools/server/__init__.py,sha256=rwPx020YpOzCnrxA80Lc4yLLcIp-Mpe9hNqVO9wDPv0,448
62
67
  aixtools/server/app_mounter.py,sha256=0tJ0tC140ezAjnYdlhpLJQjY-TO8NVw7D8LseYCCVY8,3336
63
- aixtools/server/path.py,sha256=SaIJxvmhJy3kzx5zJ6d4cKP6kKu2wFFciQkOLGTA4gg,3056
68
+ aixtools/server/path.py,sha256=nI4yRQcE6gjKx5GG3PmHf7iT1FelT6Q8Xhw4ol9O1e0,5219
64
69
  aixtools/server/utils.py,sha256=tZWITIx6M-luV9yve4j3rPtYGSSA6zWS0JWEAySne_M,2276
65
- aixtools/server/workspace_privacy.py,sha256=grcj82eHSd7gFbb5f_w9nv4TWp50QyU952l0iIPoChM,2375
66
70
  aixtools/testing/__init__.py,sha256=mlmaAR2gmS4SbsYNCxnIprmFpFp-syjgVUkpUszo3mE,166
67
- aixtools/testing/aix_test_model.py,sha256=dlI3sdyvmu4fUs_K4-oazs_a7cE6V-gnI6RQ0_fPVxg,5925
71
+ aixtools/testing/aix_test_model.py,sha256=i0xBdmpKoEfJHle6JDmcoJLUENN8Eqt181_WZ7XtDdU,6240
68
72
  aixtools/testing/mock_tool.py,sha256=4I0LxxSkLhGIKM2YxCP3cnYI8IYJjdKhfwGZ3dioXsM,2465
69
73
  aixtools/testing/model_patch_cache.py,sha256=238gKC_gSpR3BkeejhetObOkpOR1l2Iz3A6B_eUTRNc,10158
70
74
  aixtools/tools/doctor/__init__.py,sha256=FPwYzC1eJyw8IH0-BP0wgxSprLy6Y_4yXCek7496f2k,64
71
- aixtools/tools/doctor/tool_doctor.py,sha256=flp00mbFwVI0-Ii_xC4YDW6Vrn-EAExA1TtQkY6cOZE,2583
72
- aixtools/tools/doctor/tool_recommendation.py,sha256=t-l5bm6kwnXs1NH-ZZVTWhVrEAmWa460M44bi_Bip4g,1463
75
+ aixtools/tools/doctor/mcp_tool_doctor.py,sha256=sX2q5GfNkmUYxnXrqMpeGIwGfeL1LpYJR7YTMEP2KUA,2878
76
+ aixtools/tools/doctor/tool_doctor.py,sha256=EY1pshjLGLD0j6cc1ZFtbc0G19I5IbOZwHFDqypE49Q,2661
77
+ aixtools/tools/doctor/tool_recommendation.py,sha256=LYyVOSXdAorWiY4P-ucSA1vLlV5BTEfX4GzBXNE_X0M,1569
73
78
  aixtools/utils/__init__.py,sha256=xT6almZBQYMfj4h7Hq9QXDHyVXbOOTxqLsmJsxYYnSw,757
74
- aixtools/utils/config.py,sha256=pNWVMC1V9Hn2KEqaXaLbhxCI_iwQfGiVKKaZLrRM4ug,4820
79
+ aixtools/utils/config.py,sha256=JeUbGls1womGZWIp6gPBT0IoAfrljpscKEoKx2eBXjw,4819
75
80
  aixtools/utils/config_util.py,sha256=3Ya4Qqhj1RJ1qtTTykQ6iayf5uxlpigPXgEJlTi1wn4,2229
76
81
  aixtools/utils/enum_with_description.py,sha256=zjSzWxG74eR4x7dpmb74pLTYCWNSMvauHd7_9LpDYIw,1088
77
82
  aixtools/utils/files.py,sha256=8JnxwHJRJcjWCdFpjzWmo0po2fRg8esj4H7sOxElYXU,517
@@ -81,45 +86,8 @@ aixtools/utils/chainlit/cl_agent_show.py,sha256=vaRuowp4BRvhxEr5hw0zHEJ7iaSF_5bo
81
86
  aixtools/utils/chainlit/cl_utils.py,sha256=fxaxdkcZg6uHdM8uztxdPowg3a2f7VR7B26VPY4t-3c,5738
82
87
  aixtools/vault/__init__.py,sha256=fsr_NuX3GZ9WZ7dGfe0gp_5-z3URxAfwVRXw7Xyc0dU,141
83
88
  aixtools/vault/vault.py,sha256=9dZLWdZQk9qN_Q9Djkofw9LUKnJqnrX5H0fGusVLBhA,6037
84
- docker/mcp-base/Dockerfile,sha256=uislVoTEgRF--AAiyX24sBxlDdfA1ZU5rDM94XYFqvI,1388
85
- docker/mcp-base/zscaler.crt,sha256=fCUNiOfJlWTA7R4zV1Xyb-XC1_nMHPoYTFGvBj1oz6s,1732
86
- notebooks/example_faulty_mcp_server.ipynb,sha256=b2Cy3GXfj-gOBZ7SoUzj25F1rxp5u-32EWPHWQ-sxn8,1729
87
- notebooks/example_mcp_server_stdio.ipynb,sha256=ya4dRKNFU2vQxob-uIhKHGAzINXGQ6MehgKVmSCpHLk,1634
88
- notebooks/example_raw_mcp_client.ipynb,sha256=uchaG-LuuwEpE2oIkmhZ2s1EDb19AgT1fUv2Jxtjgu8,1795
89
- notebooks/example_tool_doctor.ipynb,sha256=bWTlPNI1ZQStwMfr-KSkTGYckJuJmRG_e112Gr4KZ0I,1339
90
- scripts/config.sh,sha256=xnA_S4p2w8fuIEPB4MiTWZdyIlCh5m4XuHY_fhE68kg,820
91
- scripts/lint.sh,sha256=YmPcjfFVe2s-xSaddgSxOsSm9dmHnsmbiLZnuaPgXmY,744
92
- scripts/log_view.sh,sha256=bp8oXFRRbbHpyvHAN85wfDHTVK7vMJOYsBx_-bgECQc,511
93
- scripts/run_example_mcp_server.sh,sha256=f7m7h7O_wo6-nAsYlOXVWIASCOh3Qbuu0XWizlxMhl8,355
94
- scripts/run_faulty_mcp_server.sh,sha256=u_-8NbPDnJQt6IinNSjh8tc2ed-_MjGyipJXrUXaGR8,291
95
- scripts/run_server.sh,sha256=5iiB9bB5M2MuOgxVQqu7Oa_tBVtJpt0uB4z9uLu2J50,720
96
- scripts/test.sh,sha256=KxXWkVqctFRNP8hItJr8K27nDHEkfwNWb1UFhpBQDOk,865
97
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
- tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
99
- tests/unit/a2a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- tests/unit/a2a/google_sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
- tests/unit/a2a/google_sdk/test_card.py,sha256=g8OUIX9BCtApM9y8l_nL1Q7sm3yezxu5yBxrpy76mo4,4359
102
- tests/unit/a2a/google_sdk/test_remote_agent_connection.py,sha256=nIY8eg32w96BAddaQ25mT-lr0ozPb6UrG-_Vpqx5RMY,17492
103
- tests/unit/a2a/google_sdk/test_utils.py,sha256=-eHmIk2GJH57W2bAdTzfRrUUb5jnd9Pf-QSXJogN3g8,8312
104
- tests/unit/a2a/google_sdk/pydantic_ai_adapter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
- tests/unit/a2a/google_sdk/pydantic_ai_adapter/test_agent_executor.py,sha256=PcyCw0N3y-txu2KJzufzbCjs7ZfoBBCVjpZuRBqTmOw,7722
106
- tests/unit/a2a/google_sdk/pydantic_ai_adapter/test_storage.py,sha256=tb67pFfvyWSaDfKaiPDNBQfl6-o17WtCMZh3lQHrYxY,5468
107
- tests/unit/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
- tests/unit/agents/test_prompt.py,sha256=YWFZdH_F774hxw79gsWoTWBPVs8UjOAtJOgNXJ8N9gs,15384
109
- tests/unit/compliance/test_private_data.py,sha256=GjH7NCp54Bz1S-CmH_mUe53lb53kllOOJEm448OniRI,13693
110
- tests/unit/google/__init__.py,sha256=eRYHldBi5cFWL7oo2_t5TErI8ESmIjNvBZIcp-w8hSA,45
111
- tests/unit/google/test_client.py,sha256=fXR4Cozea7bdL2prM-1s9IqUQ9AheklQnHpN-4YM3gg,11005
112
- tests/unit/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
- tests/unit/mcp/test_client.py,sha256=n9sZvmzNzJfozvxoHweAg4M5ZLNhEizq16IjcZHGdj0,8838
114
- tests/unit/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
- tests/unit/server/test_path.py,sha256=1QKiKLLRga9GNxmaUEt_wEZ9U14yzB-7PIhAOgB4wwo,9523
116
- tests/unit/server/test_utils.py,sha256=kvhzdgNfsJl5tqcRBWg2yTR5GPpyrFCOmEIOuHb3904,14848
117
- tests/unit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
- tests/unit/utils/test_files.py,sha256=AKFmXQqXstyKd2PreE4EmQyhQYeqOmu1Sp80MwHrf_Q,5782
119
- tests/unit/vault/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
- tests/unit/vault/test_vault.py,sha256=T9V2Opxl3N5sJPftw0Q4lnVOs6urGpAmffe0cz6PUfw,10445
121
- aixtools-0.1.10.dist-info/METADATA,sha256=BvV1AzDpgiy7rjt6AFyUdAiDOwt_mN8CmmEo0JkM4C4,18570
122
- aixtools-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
123
- aixtools-0.1.10.dist-info/entry_points.txt,sha256=dHoutULEZx7xXSqJrZdViSVjfInJibfLibi2nRXL3SE,56
124
- aixtools-0.1.10.dist-info/top_level.txt,sha256=ee4eF-0pqu45zCUVml0mWIhnXQgqMQper2-49BBVHLY,40
125
- aixtools-0.1.10.dist-info/RECORD,,
89
+ aixtools-0.2.0.dist-info/METADATA,sha256=madwumRizYzwL-VJ5yVkuj0qFWlCBziHlf3RXSTNcUE,24951
90
+ aixtools-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
91
+ aixtools-0.2.0.dist-info/entry_points.txt,sha256=q8412TG4T0S8K0SKeWp2vkVPIDYQs0jNoHqcQ7qxOiA,155
92
+ aixtools-0.2.0.dist-info/top_level.txt,sha256=wBn-rw9bCtxrR4AYEYgjilNCUVmKY0LWby9Zan2PRJM,9
93
+ aixtools-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ evals = aixtools.evals.evals:main
3
+ log_view = aixtools.log_view:main_cli
4
+ tool_doctor_mcp = aixtools.tools.doctor.mcp_tool_doctor:main_cli
@@ -0,0 +1 @@
1
+ aixtools
@@ -1,65 +0,0 @@
1
- """
2
- Workspace privacy utilities for managing session-level privacy flags.
3
- """
4
-
5
- from pathlib import Path
6
-
7
- from fastmcp import Context
8
-
9
- from aixtools.logging.logging_config import get_logger
10
- from aixtools.server.path import get_workspace_path
11
-
12
- logger = get_logger(__name__)
13
-
14
- PRIVACY_FLAG_FILENAME = ".private_data_indicator"
15
-
16
-
17
- def set_session_private(ctx: Context | tuple[str, str] | None = None) -> bool:
18
- """
19
- Set the current session as private by creating a privacy flag file.
20
-
21
- Creates an empty file in the session workspace directory
22
- and sets it as read-only to prevent accidental removal
23
-
24
- Args:
25
- ctx: FastMCP context for user/session identification.
26
- If None, uses current FastMCP request context from HTTP headers.
27
- If tuple, first part is a user id (username), second part is session id (aka conversation id)
28
-
29
- Returns:
30
- bool: True if privacy flag was successfully created, False otherwise.
31
- """
32
- try:
33
- workspace_path = Path(get_workspace_path(ctx=ctx))
34
- privacy_file = workspace_path / PRIVACY_FLAG_FILENAME
35
- workspace_path.mkdir(parents=True, exist_ok=True)
36
- privacy_file.touch(exist_ok=True)
37
- privacy_file.chmod(0o444)
38
- logger.warning("Session marked as private")
39
- return True
40
- except (OSError, ValueError, RuntimeError) as e:
41
- logger.error("Set current session as private: %s", str(e))
42
- return False
43
-
44
-
45
- def is_session_private(ctx: Context | tuple[str, str] | None = None) -> bool:
46
- """
47
- Check if the current session is marked as private.
48
-
49
- Args:
50
- ctx: FastMCP context for user/session identification.
51
- If None, uses current FastMCP request context from HTTP headers.
52
- If tuple, first part is a user id (username), second part is session id (aka conversation id)
53
-
54
- Returns:
55
- bool: True if workspace is private (flag file exists), False otherwise.
56
- """
57
- try:
58
- workspace_path = Path(get_workspace_path(ctx=ctx))
59
- privacy_file = workspace_path / PRIVACY_FLAG_FILENAME
60
- is_private = privacy_file.exists()
61
- logger.info("Session privacy check, is private: %s", str(is_private))
62
- return is_private
63
- except (OSError, ValueError, RuntimeError) as e:
64
- logger.error("Check session privacy: %s, assuming not private!", str(e))
65
- return False
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- log_view = aixtools.log_view:main_cli
@@ -1,5 +0,0 @@
1
- aixtools
2
- docker
3
- notebooks
4
- scripts
5
- tests
@@ -1,33 +0,0 @@
1
- FROM ubuntu:22.04
2
-
3
- RUN apt-get -y update && \
4
- apt-get -y install ca-certificates curl gcc git libcap2-bin sudo
5
- RUN mv /usr/bin/sudo /usr/sbin
6
-
7
- # Add Zscaler CA certificate
8
- COPY ./zscaler.crt /usr/local/share/ca-certificates/
9
- RUN update-ca-certificates
10
- ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
11
- ENV CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
12
-
13
- # Install `uv` Python package manager
14
- RUN bash -o pipefail -c "curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh"
15
-
16
- # Add a user matching the sandbox user so that the files and folders created by the MCP server
17
- # are writable by the user in the sandbox containers (UID=1000 must match!)
18
- ENV USER=mcp_user
19
- RUN useradd -m -s /bin/bash -u 1000 ${USER} && \
20
- echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USER} && chmod 0440 /etc/sudoers.d/${USER}
21
- USER ${USER}
22
-
23
- # Build argument: GITHUB_PAT (optional)
24
- # If provided, this GitHub Personal Access Token will be used to authenticate git
25
- # operations against github.com during the build. This is useful for accessing private repositories such as `aixtools`.
26
- # If not set, git will use unauthenticated access (public repositories only).
27
- ARG GITHUB_PAT
28
- RUN if [ -n "$GITHUB_PAT" ]; then \
29
- git config --global url."https://x-access-token:${GITHUB_PAT}@github.com/".insteadOf "https://github.com/"; \
30
- fi
31
-
32
- WORKDIR /app
33
- RUN mkdir data
@@ -1,28 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIE0zCCA7ugAwIBAgIJANu+mC2Jt3uTMA0GCSqGSIb3DQEBCwUAMIGhMQswCQYD
3
- VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIU2FuIEpvc2Ux
4
- FTATBgNVBAoTDFpzY2FsZXIgSW5jLjEVMBMGA1UECxMMWnNjYWxlciBJbmMuMRgw
5
- FgYDVQQDEw9ac2NhbGVyIFJvb3QgQ0ExIjAgBgkqhkiG9w0BCQEWE3N1cHBvcnRA
6
- enNjYWxlci5jb20wHhcNMTQxMjE5MDAyNzU1WhcNNDIwNTA2MDAyNzU1WjCBoTEL
7
- MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBK
8
- b3NlMRUwEwYDVQQKEwxac2NhbGVyIEluYy4xFTATBgNVBAsTDFpzY2FsZXIgSW5j
9
- LjEYMBYGA1UEAxMPWnNjYWxlciBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNzdXBw
10
- b3J0QHpzY2FsZXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
11
- qT7STSxZRTgEFFf6doHajSc1vk5jmzmM6BWuOo044EsaTc9eVEV/HjH/1DWzZtcr
12
- fTj+ni205apMTlKBW3UYR+lyLHQ9FoZiDXYXK8poKSV5+Tm0Vls/5Kb8mkhVVqv7
13
- LgYEmvEY7HPY+i1nEGZCa46ZXCOohJ0mBEtB9JVlpDIO+nN0hUMAYYdZ1KZWCMNf
14
- 5J/aTZiShsorN2A38iSOhdd+mcRM4iNL3gsLu99XhKnRqKoHeH83lVdfu1XBeoQz
15
- z5V6gA3kbRvhDwoIlTBeMa5l4yRdJAfdpkbFzqiwSgNdhbxTHnYYorDzKfr2rEFM
16
- dsMU0DHdeAZf711+1CunuQIDAQABo4IBCjCCAQYwHQYDVR0OBBYEFLm33UrNww4M
17
- hp1d3+wcBGnFTpjfMIHWBgNVHSMEgc4wgcuAFLm33UrNww4Mhp1d3+wcBGnFTpjf
18
- oYGnpIGkMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8G
19
- A1UEBxMIU2FuIEpvc2UxFTATBgNVBAoTDFpzY2FsZXIgSW5jLjEVMBMGA1UECxMM
20
- WnNjYWxlciBJbmMuMRgwFgYDVQQDEw9ac2NhbGVyIFJvb3QgQ0ExIjAgBgkqhkiG
21
- 9w0BCQEWE3N1cHBvcnRAenNjYWxlci5jb22CCQDbvpgtibd7kzAMBgNVHRMEBTAD
22
- AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw0NdJh8w3NsJu4KHuVZUrmZgIohnTm0j+
23
- RTmYQ9IKA/pvxAcA6K1i/LO+Bt+tCX+C0yxqB8qzuo+4vAzoY5JEBhyhBhf1uK+P
24
- /WVWFZN/+hTgpSbZgzUEnWQG2gOVd24msex+0Sr7hyr9vn6OueH+jj+vCMiAm5+u
25
- kd7lLvJsBu3AO3jGWVLyPkS3i6Gf+rwAp1OsRrv3WnbkYcFf9xjuaf4z0hRCrLN2
26
- xFNjavxrHmsH8jPHVvgc1VD0Opja0l/BRVauTrUaoW6tE+wFG5rEcPGS80jjHK4S
27
- pB5iDj2mUZH1T8lzYtuZy0ZPirxmtsk3135+CKNa2OCAhhFjE0xd
28
- -----END CERTIFICATE-----
@@ -1,74 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "markdown",
5
- "id": "57d36f62",
6
- "metadata": {},
7
- "source": [
8
- "# Example \"Faulty MCP Server\"\n",
9
- "\n",
10
- "Run the server using the script:\n",
11
- "\n",
12
- "```bash\n",
13
- "./scripts/run_faulty_mcp_server.sh\n",
14
- "``` "
15
- ]
16
- },
17
- {
18
- "cell_type": "code",
19
- "execution_count": null,
20
- "id": "ebf6d915",
21
- "metadata": {},
22
- "outputs": [],
23
- "source": [
24
- "from aixtools.agents.agent import get_agent, run_agent\n",
25
- "from pydantic_ai.mcp import MCPServerStreamableHTTP"
26
- ]
27
- },
28
- {
29
- "cell_type": "code",
30
- "execution_count": null,
31
- "id": "97d1e607",
32
- "metadata": {},
33
- "outputs": [],
34
- "source": [
35
- "server = MCPServerStreamableHTTP(\"http://localhost:9999/mcp/\")\n",
36
- "agent = get_agent(mcp_servers=[server])"
37
- ]
38
- },
39
- {
40
- "cell_type": "code",
41
- "execution_count": null,
42
- "id": "fc560d25",
43
- "metadata": {},
44
- "outputs": [],
45
- "source": [
46
- "async with agent:\n",
47
- " # ret = await run_agent(agent, \"What is the add of 40123456789 and 2123456789?\", verbose=True, debug=True)\n",
48
- " # ret = await run_agent(agent, \"Invoke the always_error tool\", verbose=True, debug=True)\n",
49
- " ret = await run_agent(agent, \"Invoke the throw_404_exception tool\", verbose=True, debug=True)"
50
- ]
51
- }
52
- ],
53
- "metadata": {
54
- "kernelspec": {
55
- "display_name": ".venv",
56
- "language": "python",
57
- "name": "python3"
58
- },
59
- "language_info": {
60
- "codemirror_mode": {
61
- "name": "ipython",
62
- "version": 3
63
- },
64
- "file_extension": ".py",
65
- "mimetype": "text/x-python",
66
- "name": "python",
67
- "nbconvert_exporter": "python",
68
- "pygments_lexer": "ipython3",
69
- "version": "3.12.2"
70
- }
71
- },
72
- "nbformat": 4,
73
- "nbformat_minor": 5
74
- }