scorebook 0.0.13__py3-none-any.whl → 0.0.15__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.
Files changed (96) hide show
  1. scorebook/__init__.py +12 -5
  2. scorebook/cli/auth.py +1 -1
  3. scorebook/dashboard/__init__.py +1 -0
  4. scorebook/dashboard/create_project.py +91 -0
  5. scorebook/{trismik → dashboard}/credentials.py +57 -12
  6. scorebook/{trismik → dashboard}/upload_results.py +1 -1
  7. scorebook/eval_datasets/__init__.py +0 -4
  8. scorebook/eval_datasets/eval_dataset.py +4 -2
  9. scorebook/evaluate/__init__.py +1 -15
  10. scorebook/evaluate/_async/evaluate_async.py +36 -19
  11. scorebook/evaluate/_sync/evaluate.py +36 -19
  12. scorebook/evaluate/evaluate_helpers.py +4 -3
  13. scorebook/inference/__init__.py +1 -11
  14. scorebook/inference/clients/__init__.py +1 -8
  15. scorebook/inference/inference_pipeline.py +1 -1
  16. scorebook/metrics/README.md +121 -0
  17. scorebook/metrics/__init__.py +7 -16
  18. scorebook/metrics/accuracy.py +2 -6
  19. scorebook/metrics/bertscore.py +50 -0
  20. scorebook/metrics/bleu.py +82 -0
  21. scorebook/metrics/core/__init__.py +1 -0
  22. scorebook/metrics/{metric_base.py → core/metric_base.py} +1 -2
  23. scorebook/metrics/core/metric_registry.py +195 -0
  24. scorebook/metrics/exactmatch.py +95 -0
  25. scorebook/metrics/f1.py +96 -0
  26. scorebook/metrics/precision.py +84 -9
  27. scorebook/metrics/recall.py +94 -0
  28. scorebook/metrics/rouge.py +85 -0
  29. scorebook/score/__init__.py +0 -5
  30. scorebook/score/_async/score_async.py +3 -2
  31. scorebook/score/_sync/score.py +3 -2
  32. scorebook/score/score_helpers.py +29 -12
  33. scorebook/types.py +3 -3
  34. scorebook/utils/__init__.py +0 -22
  35. scorebook/utils/common_helpers.py +1 -1
  36. scorebook/utils/mock_llm/__init__.py +41 -0
  37. scorebook/utils/mock_llm/data/mock_llm_data.json +21970 -0
  38. scorebook/utils/progress_bars.py +58 -786
  39. scorebook-0.0.15.dist-info/METADATA +300 -0
  40. scorebook-0.0.15.dist-info/RECORD +110 -0
  41. {scorebook-0.0.13.dist-info → scorebook-0.0.15.dist-info}/WHEEL +1 -1
  42. tutorials/README.md +147 -0
  43. tutorials/__init__.py +5 -0
  44. tutorials/examples/1-score/1-scoring_model_accuracy.py +47 -0
  45. tutorials/examples/1-score/2-scoring_model_bleu.py +46 -0
  46. tutorials/examples/1-score/3-scoring_model_f1.py +64 -0
  47. tutorials/examples/1-score/4-scoring_model_rouge.py +64 -0
  48. tutorials/examples/1-score/5-scoring_model_exact_match.py +84 -0
  49. tutorials/examples/1-score/6-scoring_with_bertscore.py +57 -0
  50. tutorials/examples/1-score/__init__.py +0 -0
  51. tutorials/examples/2-evaluate/1-evaluating_local_models.py +106 -0
  52. tutorials/examples/2-evaluate/2-evaluating_local_models_with_batching.py +108 -0
  53. tutorials/examples/2-evaluate/3-evaluating_cloud_models.py +109 -0
  54. tutorials/examples/2-evaluate/4-evaluating_cloud_models_with_batching.py +170 -0
  55. tutorials/examples/2-evaluate/5-hyperparameter_sweeps.py +122 -0
  56. tutorials/examples/2-evaluate/6-inference_pipelines.py +141 -0
  57. tutorials/examples/3-evaluation_datasets/1-evaluation_datasets_from_files.py +110 -0
  58. tutorials/examples/3-evaluation_datasets/2-evaluation_datasets_from_huggingface.py +101 -0
  59. tutorials/examples/3-evaluation_datasets/3-evaluation_datasets_from_huggingface_with_yaml_configs.py +110 -0
  60. tutorials/examples/3-evaluation_datasets/example_datasets/basic_questions.csv +11 -0
  61. tutorials/examples/3-evaluation_datasets/example_datasets/basic_questions.json +42 -0
  62. tutorials/examples/3-evaluation_datasets/example_yaml_configs/Cais-MMLU.yaml +19 -0
  63. tutorials/examples/3-evaluation_datasets/example_yaml_configs/TIGER-Lab-MMLU-Pro.yaml +18 -0
  64. tutorials/examples/4-adaptive_evaluations/1-adaptive_evaluation.py +114 -0
  65. tutorials/examples/4-adaptive_evaluations/2-adaptive_dataset_splits.py +106 -0
  66. tutorials/examples/5-upload_results/1-uploading_score_results.py +92 -0
  67. tutorials/examples/5-upload_results/2-uploading_evaluate_results.py +117 -0
  68. tutorials/examples/5-upload_results/3-uploading_your_results.py +153 -0
  69. tutorials/examples/6-providers/aws/__init__.py +1 -0
  70. tutorials/examples/6-providers/aws/batch_example.py +219 -0
  71. tutorials/examples/6-providers/portkey/__init__.py +1 -0
  72. tutorials/examples/6-providers/portkey/batch_example.py +120 -0
  73. tutorials/examples/6-providers/portkey/messages_example.py +121 -0
  74. tutorials/examples/6-providers/vertex/__init__.py +1 -0
  75. tutorials/examples/6-providers/vertex/batch_example.py +166 -0
  76. tutorials/examples/6-providers/vertex/messages_example.py +142 -0
  77. tutorials/examples/__init__.py +0 -0
  78. tutorials/notebooks/1-scoring.ipynb +162 -0
  79. tutorials/notebooks/2-evaluating.ipynb +316 -0
  80. tutorials/notebooks/3.1-adaptive_evaluation_phi.ipynb +354 -0
  81. tutorials/notebooks/3.2-adaptive_evaluation_gpt.ipynb +243 -0
  82. tutorials/notebooks/4-uploading_results.ipynb +175 -0
  83. tutorials/quickstarts/adaptive_evaluations/adaptive_evaluation_openai_demo.ipynb +229 -0
  84. tutorials/quickstarts/adaptive_evaluations/adaptive_evaluation_qwen_demo.ipynb +256 -0
  85. tutorials/quickstarts/classical_evaluations/classical_evaluation_demo.ipynb +277 -0
  86. tutorials/quickstarts/getting_started.ipynb +197 -0
  87. tutorials/utils/__init__.py +35 -0
  88. tutorials/utils/args_parser.py +132 -0
  89. tutorials/utils/output.py +23 -0
  90. tutorials/utils/setup.py +98 -0
  91. scorebook/metrics/metric_registry.py +0 -105
  92. scorebook/trismik/__init__.py +0 -10
  93. scorebook-0.0.13.dist-info/METADATA +0 -389
  94. scorebook-0.0.13.dist-info/RECORD +0 -50
  95. {scorebook-0.0.13.dist-info → scorebook-0.0.15.dist-info}/entry_points.txt +0 -0
  96. {scorebook-0.0.13.dist-info → scorebook-0.0.15.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,166 @@
1
+ """
2
+ Google Cloud Vertex AI Batch Inference Example.
3
+
4
+ This example demonstrates how to leverage Google Cloud Vertex AI's Batch API for
5
+ cost-effective, large-scale model evaluation using Scorebook. It uses Gemini models
6
+ for batch processing with automatic GCS upload/download and job management.
7
+
8
+ This example requires Google Cloud SDK (gsutil) to be installed and authenticated,
9
+ and a Google Cloud project with Vertex AI enabled. Set the project ID in the
10
+ GOOGLE_CLOUD_PROJECT environment variable or pass it as a command line argument.
11
+
12
+ Compare with the Portkey batch example to understand the differences
13
+ between different cloud providers' batch processing approaches.
14
+ """
15
+
16
+ import json
17
+ import os
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from dotenv import load_dotenv
22
+
23
+ from scorebook import EvalDataset, InferencePipeline, evaluate
24
+ from scorebook.inference.clients.vertex import batch
25
+ from scorebook.metrics.accuracy import Accuracy
26
+
27
+
28
+ def main() -> None:
29
+ """Run the Vertex AI batch inference example."""
30
+ # Load environment variables from .env file for configuration
31
+ load_dotenv()
32
+
33
+ output_dir, model_name, input_bucket, output_bucket, project_id = setup_arguments()
34
+
35
+ # Step 1: Load the evaluation dataset
36
+ dataset = EvalDataset.from_json(
37
+ "examples/example_datasets/dataset.json", label="answer", metrics=[Accuracy]
38
+ )
39
+
40
+ # Step 2: Define the preprocessing function for Vertex AI Batch API
41
+ def preprocessor(eval_item: dict) -> list:
42
+ """Pre-process dataset items into Vertex AI Batch API format."""
43
+ prompt = eval_item["question"]
44
+
45
+ # Create the batch API request messages format for Vertex AI
46
+ messages = [
47
+ {
48
+ "role": "system",
49
+ "content": "Answer the question directly and concisely as a single word",
50
+ },
51
+ {"role": "user", "content": prompt},
52
+ ]
53
+
54
+ return messages
55
+
56
+ # Step 3: Define the postprocessing function
57
+ def postprocessor(response: str) -> str:
58
+ """Post-process Vertex AI batch response to extract the answer."""
59
+ # The batch function returns the message content directly
60
+ return response.strip()
61
+
62
+ # Step 4: Create the inference pipeline for batch processing
63
+
64
+ async def inference_function(items: list, **hyperparams: Any) -> Any: # noqa
65
+ return await batch(
66
+ items,
67
+ model=model_name,
68
+ project_id=project_id,
69
+ input_bucket=input_bucket,
70
+ output_bucket=output_bucket,
71
+ **hyperparams,
72
+ )
73
+
74
+ inference_pipeline = InferencePipeline(
75
+ model=model_name,
76
+ preprocessor=preprocessor,
77
+ inference_function=inference_function,
78
+ postprocessor=postprocessor,
79
+ )
80
+
81
+ # Step 5: Run the batch evaluation
82
+ print(f"Running Vertex AI Batch API evaluation with model: {model_name}")
83
+ print(f"Project ID: {project_id}")
84
+ print(f"Input bucket: {input_bucket}")
85
+ print(f"Output bucket: {output_bucket}")
86
+ print(f"Processing {len(dataset)} items using batch inference...")
87
+ print("Note: Batch processing may take several minutes to complete.")
88
+
89
+ # For demonstration, limit to 25 items
90
+ results = evaluate(inference_pipeline, dataset, item_limit=25, score_type="all")
91
+ print("\nBatch evaluation completed!")
92
+ print(results)
93
+
94
+ # Step 6: Save results to file
95
+ output_file = output_dir / "vertex_batch_output.json"
96
+ with open(output_file, "w") as f:
97
+ json.dump(results, f, indent=4)
98
+ print(f"Results saved in {output_file}")
99
+
100
+
101
+ # ============================================================================
102
+ # Utility Functions
103
+ # ============================================================================
104
+
105
+
106
+ def setup_arguments() -> tuple[Path, str, str, str, str]:
107
+ """Parse command line arguments."""
108
+ import argparse
109
+
110
+ parser = argparse.ArgumentParser(
111
+ description="Run Vertex AI Batch API evaluation and save results."
112
+ )
113
+ parser.add_argument(
114
+ "--output-dir",
115
+ type=str,
116
+ default=str(Path.cwd() / "results"),
117
+ help="Directory to save evaluation outputs (JSON).",
118
+ )
119
+ parser.add_argument(
120
+ "--model",
121
+ type=str,
122
+ required=True,
123
+ help="Gemini model to use for batch inference (e.g., gemini-2.0-flash-001)",
124
+ )
125
+ parser.add_argument(
126
+ "--input-bucket",
127
+ type=str,
128
+ required=True,
129
+ help="GCS bucket name for input data (without gs:// prefix)",
130
+ )
131
+ parser.add_argument(
132
+ "--output-bucket",
133
+ type=str,
134
+ required=True,
135
+ help="GCS bucket name for output data (without gs:// prefix)",
136
+ )
137
+ parser.add_argument(
138
+ "--project-id",
139
+ type=str,
140
+ help="Google Cloud Project ID (defaults to GOOGLE_CLOUD_PROJECT env var)",
141
+ )
142
+
143
+ args = parser.parse_args()
144
+
145
+ output_dir = Path(args.output_dir)
146
+ output_dir.mkdir(parents=True, exist_ok=True)
147
+
148
+ # Handle project ID fallback
149
+ project_id = args.project_id or os.getenv("GOOGLE_CLOUD_PROJECT")
150
+ if not project_id:
151
+ raise ValueError(
152
+ "Project ID must be provided via --project-id or "
153
+ "GOOGLE_CLOUD_PROJECT environment variable"
154
+ )
155
+
156
+ return (
157
+ output_dir,
158
+ str(args.model),
159
+ str(args.input_bucket),
160
+ str(args.output_bucket),
161
+ str(project_id),
162
+ )
163
+
164
+
165
+ if __name__ == "__main__":
166
+ main()
@@ -0,0 +1,142 @@
1
+ """
2
+ Google Cloud Vertex AI Model Inference Example.
3
+
4
+ This example demonstrates how to evaluate language models using Google Cloud
5
+ Vertex AI's Gemini models with Scorebook for real-time API calls.
6
+
7
+ Prerequisites: Google Cloud SDK (gcloud) authenticated and GOOGLE_CLOUD_PROJECT
8
+ environment variable set, or pass project ID as command line argument.
9
+ """
10
+
11
+ import json
12
+ from pathlib import Path
13
+ from typing import Any, Dict
14
+
15
+ from dotenv import load_dotenv
16
+
17
+ from scorebook import EvalDataset, InferencePipeline, evaluate
18
+ from scorebook.inference.clients.vertex import responses
19
+ from scorebook.metrics.accuracy import Accuracy
20
+
21
+
22
+ def main() -> None:
23
+ """Run the Vertex AI inference example."""
24
+ # Load environment variables from .env file for configuration
25
+ load_dotenv()
26
+
27
+ output_dir, model_name, project_id = setup_arguments()
28
+
29
+ # Step 1: Load the evaluation dataset
30
+ # Create an EvalDataset from local JSON file
31
+ # - Uses 'answer' field as ground truth labels
32
+ # - Configures Accuracy metric for evaluation
33
+ # - Loads from examples/example_datasets/dataset.json
34
+ dataset = EvalDataset.from_json(
35
+ "examples/example_datasets/dataset.json", label="answer", metrics=[Accuracy]
36
+ )
37
+
38
+ # Step 2: Define the preprocessing function
39
+ # Convert raw dataset items into Vertex AI API-compatible format
40
+ # This function formats the question for the Gemini model
41
+ def preprocessor(eval_item: Dict[str, str]) -> str:
42
+ """Pre-process dataset items into Vertex AI string format."""
43
+ return eval_item["question"]
44
+
45
+ # Step 3: Define the postprocessing function
46
+ # Extract the final answer from Vertex AI API response
47
+ # Handles response parsing and returns the response text
48
+ def postprocessor(response: Any) -> str:
49
+ """Post-process Vertex AI response to extract the answer."""
50
+ return str(response.text.strip())
51
+
52
+ # Step 4: Create the inference pipeline for cloud-based evaluation
53
+ # Combine preprocessing, Vertex AI inference, and postprocessing
54
+ # Uses scorebook's built-in Vertex AI responses function for API calls
55
+
56
+ # Create a system message with instructions for direct answers
57
+ system_prompt = """
58
+ Answer the question directly and concisely.
59
+ Do not provide lengthy explanations unless specifically asked.
60
+ """.strip()
61
+
62
+ async def inference_function(items: list, **hyperparams: Any) -> Any:
63
+ return await responses(
64
+ items,
65
+ model=model_name,
66
+ project_id=project_id,
67
+ system_instruction=system_prompt,
68
+ **hyperparams,
69
+ )
70
+
71
+ inference_pipeline = InferencePipeline(
72
+ model=model_name,
73
+ preprocessor=preprocessor,
74
+ inference_function=inference_function,
75
+ postprocessor=postprocessor,
76
+ )
77
+
78
+ # Step 5: Run the cloud-based evaluation
79
+ # Execute evaluation using Vertex AI with the inference pipeline
80
+ # - Uses score_type="all" to get both aggregate and per-item results
81
+ # - Limits to 10 items for quick demonstration and cost control
82
+ print(f"Running Vertex AI evaluation with model: {model_name}")
83
+ print(f"Project ID: {project_id}")
84
+ print("Evaluating 10 items from local dataset...")
85
+
86
+ results = evaluate(inference_pipeline, dataset, item_limit=10, score_type="all")
87
+ print(results)
88
+
89
+ # Step 6: Save results to file
90
+ # Export evaluation results as JSON for later analysis
91
+ output_file = output_dir / "vertex_messages_output.json"
92
+ with open(output_file, "w") as f:
93
+ json.dump(results, f, indent=4)
94
+ print(f"Results saved in {output_file}")
95
+
96
+
97
+ # ============================================================================
98
+ # Utility Functions
99
+ # ============================================================================
100
+
101
+
102
+ def setup_arguments() -> tuple[Path, str, str]:
103
+ """Parse command line arguments."""
104
+ import argparse
105
+ import os
106
+
107
+ parser = argparse.ArgumentParser(description="Run Vertex AI evaluation and save results.")
108
+ parser.add_argument(
109
+ "--output-dir",
110
+ type=str,
111
+ default=str(Path.cwd() / "results"),
112
+ help="Directory to save evaluation outputs (JSON).",
113
+ )
114
+ parser.add_argument(
115
+ "--model",
116
+ type=str,
117
+ required=True,
118
+ help="Gemini model to use for inference (e.g., gemini-2.0-flash-001)",
119
+ )
120
+ parser.add_argument(
121
+ "--project-id",
122
+ type=str,
123
+ help="Google Cloud Project ID (defaults to GOOGLE_CLOUD_PROJECT env var)",
124
+ )
125
+ args = parser.parse_args()
126
+
127
+ output_dir = Path(args.output_dir)
128
+ output_dir.mkdir(parents=True, exist_ok=True)
129
+
130
+ # Handle project ID fallback
131
+ project_id = args.project_id or os.getenv("GOOGLE_CLOUD_PROJECT")
132
+ if not project_id:
133
+ raise ValueError(
134
+ "Project ID must be provided via --project-id or "
135
+ "GOOGLE_CLOUD_PROJECT environment variable"
136
+ )
137
+
138
+ return output_dir, str(args.model), str(project_id)
139
+
140
+
141
+ if __name__ == "__main__":
142
+ main()
File without changes
@@ -0,0 +1,162 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "metadata": {},
5
+ "cell_type": "markdown",
6
+ "source": "# Scoring Model Outputs with Scorebook\n\nThis notebook demonstrates how to use Scorebook's `score()` function to evaluate pre-generated model predictions.\n\n## When to use `score()`\n\n- You already have model predictions and want to compute metrics\n- You want to re-score existing results with different metrics\n- You're importing evaluation results from another framework\n"
7
+ },
8
+ {
9
+ "metadata": {},
10
+ "cell_type": "markdown",
11
+ "source": [
12
+ "## Setup\n",
13
+ "\n",
14
+ "First, let's import the necessary modules:"
15
+ ]
16
+ },
17
+ {
18
+ "metadata": {},
19
+ "cell_type": "code",
20
+ "source": [
21
+ "from pprint import pprint\n",
22
+ "from scorebook import score\n",
23
+ "from scorebook.metrics.accuracy import Accuracy"
24
+ ],
25
+ "outputs": [],
26
+ "execution_count": null
27
+ },
28
+ {
29
+ "cell_type": "markdown",
30
+ "metadata": {},
31
+ "source": [
32
+ "## Prepare Your Data\n",
33
+ "\n",
34
+ "The `score()` function expects a list of items, where each item is a dictionary with:\n",
35
+ "- `input`: The input to the model (optional, for reference)\n",
36
+ "- `output`: The model's prediction\n",
37
+ "- `label`: The ground truth answer"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "code",
42
+ "metadata": {},
43
+ "source": [
44
+ "# Example: Pre-generated model outputs\n",
45
+ "items = [\n",
46
+ " {\n",
47
+ " \"input\": \"What is 2 + 2?\",\n",
48
+ " \"output\": \"4\",\n",
49
+ " \"label\": \"4\"\n",
50
+ " },\n",
51
+ " {\n",
52
+ " \"input\": \"What is the capital of France?\",\n",
53
+ " \"output\": \"Paris\",\n",
54
+ " \"label\": \"Paris\"\n",
55
+ " },\n",
56
+ " {\n",
57
+ " \"input\": \"Who wrote Romeo and Juliet?\",\n",
58
+ " \"output\": \"William Shakespeare\",\n",
59
+ " \"label\": \"William Shakespeare\"\n",
60
+ " },\n",
61
+ " {\n",
62
+ " \"input\": \"What is 5 * 6?\",\n",
63
+ " \"output\": \"30\",\n",
64
+ " \"label\": \"30\"\n",
65
+ " },\n",
66
+ " {\n",
67
+ " \"input\": \"What is the largest planet in our solar system?\",\n",
68
+ " \"output\": \"Jupiter\",\n",
69
+ " \"label\": \"Jupiter\"\n",
70
+ " },\n",
71
+ "]\n",
72
+ "\n",
73
+ "print(f\"Prepared {len(items)} items for scoring\")"
74
+ ],
75
+ "outputs": [],
76
+ "execution_count": null
77
+ },
78
+ {
79
+ "cell_type": "markdown",
80
+ "metadata": {},
81
+ "source": [
82
+ "## Score the Results\n",
83
+ "\n",
84
+ "Now we'll use the `score()` function to compute accuracy metrics:"
85
+ ]
86
+ },
87
+ {
88
+ "cell_type": "code",
89
+ "metadata": {},
90
+ "source": [
91
+ "results = score(\n",
92
+ " items=items,\n",
93
+ " metrics=Accuracy,\n",
94
+ " dataset_name=\"basic_questions\",\n",
95
+ " model_name=\"example-model\",\n",
96
+ " upload_results=False, # Set to True to upload to Trismik\n",
97
+ ")\n",
98
+ "\n",
99
+ "pprint(results)"
100
+ ],
101
+ "outputs": [],
102
+ "execution_count": null
103
+ },
104
+ {
105
+ "cell_type": "markdown",
106
+ "metadata": {},
107
+ "source": [
108
+ "## Understanding the Results\n",
109
+ "\n",
110
+ "The results dictionary contains:\n",
111
+ "- `aggregates`: Overall metrics (e.g., accuracy across all items)\n",
112
+ "- `items`: Per-item scores and predictions\n",
113
+ "- `metadata`: Information about the dataset and model"
114
+ ]
115
+ },
116
+ {
117
+ "cell_type": "code",
118
+ "metadata": {},
119
+ "source": [
120
+ "# View aggregate metrics\n",
121
+ "print(\"\\nAggregate Metrics:\")\n",
122
+ "print(f\"\\nAccuracy: {results['aggregate_results'][0]['accuracy']}\")\n",
123
+ "\n",
124
+ "# View per-item scores\n",
125
+ "print(\"\\nPer-Item Scores:\")\n",
126
+ "for i, item in enumerate(results['item_results'][:3], 1):\n",
127
+ " print(f\"\\nItem {i}:\")\n",
128
+ " print(f\" Output: {item['output']}\")\n",
129
+ " print(f\" Label: {item['label']}\")\n",
130
+ " print(f\" Accuracy: {item['accuracy']}\")"
131
+ ],
132
+ "outputs": [],
133
+ "execution_count": null
134
+ },
135
+ {
136
+ "cell_type": "markdown",
137
+ "source": "## Next Steps\n\n- Try the **Evaluate** notebook to learn how to run inference and scoring together\n- See the **Upload Results** notebook to upload your scores to Trismik's dashboard\n- Explore custom metrics in the Scorebook documentation",
138
+ "metadata": {}
139
+ }
140
+ ],
141
+ "metadata": {
142
+ "kernelspec": {
143
+ "display_name": "Python 3",
144
+ "language": "python",
145
+ "name": "python3"
146
+ },
147
+ "language_info": {
148
+ "codemirror_mode": {
149
+ "name": "ipython",
150
+ "version": 3
151
+ },
152
+ "file_extension": ".py",
153
+ "mimetype": "text/x-python",
154
+ "name": "python",
155
+ "nbconvert_exporter": "python",
156
+ "pygments_lexer": "ipython3",
157
+ "version": "3.11.0"
158
+ }
159
+ },
160
+ "nbformat": 4,
161
+ "nbformat_minor": 4
162
+ }