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.
- scorebook/__init__.py +12 -5
- scorebook/cli/auth.py +1 -1
- scorebook/dashboard/__init__.py +1 -0
- scorebook/dashboard/create_project.py +91 -0
- scorebook/{trismik → dashboard}/credentials.py +57 -12
- scorebook/{trismik → dashboard}/upload_results.py +1 -1
- scorebook/eval_datasets/__init__.py +0 -4
- scorebook/eval_datasets/eval_dataset.py +4 -2
- scorebook/evaluate/__init__.py +1 -15
- scorebook/evaluate/_async/evaluate_async.py +36 -19
- scorebook/evaluate/_sync/evaluate.py +36 -19
- scorebook/evaluate/evaluate_helpers.py +4 -3
- scorebook/inference/__init__.py +1 -11
- scorebook/inference/clients/__init__.py +1 -8
- scorebook/inference/inference_pipeline.py +1 -1
- scorebook/metrics/README.md +121 -0
- scorebook/metrics/__init__.py +7 -16
- scorebook/metrics/accuracy.py +2 -6
- scorebook/metrics/bertscore.py +50 -0
- scorebook/metrics/bleu.py +82 -0
- scorebook/metrics/core/__init__.py +1 -0
- scorebook/metrics/{metric_base.py → core/metric_base.py} +1 -2
- scorebook/metrics/core/metric_registry.py +195 -0
- scorebook/metrics/exactmatch.py +95 -0
- scorebook/metrics/f1.py +96 -0
- scorebook/metrics/precision.py +84 -9
- scorebook/metrics/recall.py +94 -0
- scorebook/metrics/rouge.py +85 -0
- scorebook/score/__init__.py +0 -5
- scorebook/score/_async/score_async.py +3 -2
- scorebook/score/_sync/score.py +3 -2
- scorebook/score/score_helpers.py +29 -12
- scorebook/types.py +3 -3
- scorebook/utils/__init__.py +0 -22
- scorebook/utils/common_helpers.py +1 -1
- scorebook/utils/mock_llm/__init__.py +41 -0
- scorebook/utils/mock_llm/data/mock_llm_data.json +21970 -0
- scorebook/utils/progress_bars.py +58 -786
- scorebook-0.0.15.dist-info/METADATA +300 -0
- scorebook-0.0.15.dist-info/RECORD +110 -0
- {scorebook-0.0.13.dist-info → scorebook-0.0.15.dist-info}/WHEEL +1 -1
- tutorials/README.md +147 -0
- tutorials/__init__.py +5 -0
- tutorials/examples/1-score/1-scoring_model_accuracy.py +47 -0
- tutorials/examples/1-score/2-scoring_model_bleu.py +46 -0
- tutorials/examples/1-score/3-scoring_model_f1.py +64 -0
- tutorials/examples/1-score/4-scoring_model_rouge.py +64 -0
- tutorials/examples/1-score/5-scoring_model_exact_match.py +84 -0
- tutorials/examples/1-score/6-scoring_with_bertscore.py +57 -0
- tutorials/examples/1-score/__init__.py +0 -0
- tutorials/examples/2-evaluate/1-evaluating_local_models.py +106 -0
- tutorials/examples/2-evaluate/2-evaluating_local_models_with_batching.py +108 -0
- tutorials/examples/2-evaluate/3-evaluating_cloud_models.py +109 -0
- tutorials/examples/2-evaluate/4-evaluating_cloud_models_with_batching.py +170 -0
- tutorials/examples/2-evaluate/5-hyperparameter_sweeps.py +122 -0
- tutorials/examples/2-evaluate/6-inference_pipelines.py +141 -0
- tutorials/examples/3-evaluation_datasets/1-evaluation_datasets_from_files.py +110 -0
- tutorials/examples/3-evaluation_datasets/2-evaluation_datasets_from_huggingface.py +101 -0
- tutorials/examples/3-evaluation_datasets/3-evaluation_datasets_from_huggingface_with_yaml_configs.py +110 -0
- tutorials/examples/3-evaluation_datasets/example_datasets/basic_questions.csv +11 -0
- tutorials/examples/3-evaluation_datasets/example_datasets/basic_questions.json +42 -0
- tutorials/examples/3-evaluation_datasets/example_yaml_configs/Cais-MMLU.yaml +19 -0
- tutorials/examples/3-evaluation_datasets/example_yaml_configs/TIGER-Lab-MMLU-Pro.yaml +18 -0
- tutorials/examples/4-adaptive_evaluations/1-adaptive_evaluation.py +114 -0
- tutorials/examples/4-adaptive_evaluations/2-adaptive_dataset_splits.py +106 -0
- tutorials/examples/5-upload_results/1-uploading_score_results.py +92 -0
- tutorials/examples/5-upload_results/2-uploading_evaluate_results.py +117 -0
- tutorials/examples/5-upload_results/3-uploading_your_results.py +153 -0
- tutorials/examples/6-providers/aws/__init__.py +1 -0
- tutorials/examples/6-providers/aws/batch_example.py +219 -0
- tutorials/examples/6-providers/portkey/__init__.py +1 -0
- tutorials/examples/6-providers/portkey/batch_example.py +120 -0
- tutorials/examples/6-providers/portkey/messages_example.py +121 -0
- tutorials/examples/6-providers/vertex/__init__.py +1 -0
- tutorials/examples/6-providers/vertex/batch_example.py +166 -0
- tutorials/examples/6-providers/vertex/messages_example.py +142 -0
- tutorials/examples/__init__.py +0 -0
- tutorials/notebooks/1-scoring.ipynb +162 -0
- tutorials/notebooks/2-evaluating.ipynb +316 -0
- tutorials/notebooks/3.1-adaptive_evaluation_phi.ipynb +354 -0
- tutorials/notebooks/3.2-adaptive_evaluation_gpt.ipynb +243 -0
- tutorials/notebooks/4-uploading_results.ipynb +175 -0
- tutorials/quickstarts/adaptive_evaluations/adaptive_evaluation_openai_demo.ipynb +229 -0
- tutorials/quickstarts/adaptive_evaluations/adaptive_evaluation_qwen_demo.ipynb +256 -0
- tutorials/quickstarts/classical_evaluations/classical_evaluation_demo.ipynb +277 -0
- tutorials/quickstarts/getting_started.ipynb +197 -0
- tutorials/utils/__init__.py +35 -0
- tutorials/utils/args_parser.py +132 -0
- tutorials/utils/output.py +23 -0
- tutorials/utils/setup.py +98 -0
- scorebook/metrics/metric_registry.py +0 -105
- scorebook/trismik/__init__.py +0 -10
- scorebook-0.0.13.dist-info/METADATA +0 -389
- scorebook-0.0.13.dist-info/RECORD +0 -50
- {scorebook-0.0.13.dist-info → scorebook-0.0.15.dist-info}/entry_points.txt +0 -0
- {scorebook-0.0.13.dist-info → scorebook-0.0.15.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# Adaptive Evaluations with Scorebook (GPT)\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"This notebook demonstrates Trismik's adaptive evaluation feature using **OpenAI's GPT models** for high-accuracy results.\n",
|
|
10
|
+
"\n",
|
|
11
|
+
"> **Looking for a version without API costs?** See `3-adaptive_evaluation_local.ipynb` for a version using local open-source models (Phi-3) that runs on your machine without API keys.\n",
|
|
12
|
+
"\n",
|
|
13
|
+
"## What are Adaptive Evaluations?\n",
|
|
14
|
+
"\n",
|
|
15
|
+
"Adaptive evaluations dynamically select questions based on a model's previous responses, similar to adaptive testing in education (like the GRE or GMAT).\n",
|
|
16
|
+
"\n",
|
|
17
|
+
"### Benefits:\n",
|
|
18
|
+
"- **More efficient**: Fewer questions needed to assess capability\n",
|
|
19
|
+
"- **Precise measurement**: Better statistical confidence intervals\n",
|
|
20
|
+
"- **Optimal difficulty**: Questions adapt to the model's skill level\n",
|
|
21
|
+
"\n",
|
|
22
|
+
"## Prerequisites\n",
|
|
23
|
+
"\n",
|
|
24
|
+
"- **Trismik API key**: Get yours at https://app.trismik.com/settings\n",
|
|
25
|
+
"- **Trismik Project**: Create a project at https://app.trismik.com and copy its Project ID\n",
|
|
26
|
+
"- **OpenAI API key**: For high-accuracy results on complex reasoning tasks"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"cell_type": "markdown",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"source": "## Setup Credentials\n\nSet your API credentials here:"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"cell_type": "code",
|
|
36
|
+
"metadata": {},
|
|
37
|
+
"source": "# STEP 1: Get your Trismik API key from https://app.trismik.com/settings\n# STEP 2: Create a project at https://app.trismik.com and copy the Project ID\n# STEP 3: Get your OpenAI API key from https://platform.openai.com/api-keys\n\n# Set your credentials here\nTRISMIK_API_KEY = \"your-trismik-api-key\"\nTRISMIK_PROJECT_ID = \"your-project-id\"\nOPENAI_API_KEY = \"your-openai-api-key\"",
|
|
38
|
+
"outputs": [],
|
|
39
|
+
"execution_count": null
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"cell_type": "markdown",
|
|
43
|
+
"metadata": {},
|
|
44
|
+
"source": [
|
|
45
|
+
"## Import Dependencies"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"cell_type": "code",
|
|
50
|
+
"metadata": {},
|
|
51
|
+
"source": [
|
|
52
|
+
"import asyncio\n",
|
|
53
|
+
"import string\n",
|
|
54
|
+
"from pprint import pprint\n",
|
|
55
|
+
"from typing import Any, List\n",
|
|
56
|
+
"\n",
|
|
57
|
+
"from openai import AsyncOpenAI\n",
|
|
58
|
+
"from scorebook import evaluate_async, login"
|
|
59
|
+
],
|
|
60
|
+
"outputs": [],
|
|
61
|
+
"execution_count": null
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"cell_type": "markdown",
|
|
65
|
+
"metadata": {},
|
|
66
|
+
"source": [
|
|
67
|
+
"## Login to Trismik\n",
|
|
68
|
+
"\n",
|
|
69
|
+
"Authenticate with your Trismik account:"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"cell_type": "code",
|
|
74
|
+
"metadata": {},
|
|
75
|
+
"source": "if not TRISMIK_API_KEY or TRISMIK_API_KEY == \"your-trismik-api-key\":\n raise ValueError(\"Please set TRISMIK_API_KEY. Get your API key from https://app.trismik.com/settings\")\n\nlogin(TRISMIK_API_KEY)\nprint(\"✓ Logged in to Trismik\")\n\nif not TRISMIK_PROJECT_ID or TRISMIK_PROJECT_ID == \"your-project-id\":\n raise ValueError(\"Please set TRISMIK_PROJECT_ID. Create a project at https://app.trismik.com\")\n\nprint(f\"✓ Using project: {TRISMIK_PROJECT_ID}\")",
|
|
76
|
+
"outputs": [],
|
|
77
|
+
"execution_count": null
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"cell_type": "markdown",
|
|
81
|
+
"metadata": {},
|
|
82
|
+
"source": [
|
|
83
|
+
"## Initialize OpenAI Client"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"cell_type": "code",
|
|
88
|
+
"metadata": {},
|
|
89
|
+
"source": "if not OPENAI_API_KEY or OPENAI_API_KEY == \"your-openai-api-key\":\n raise ValueError(\"Please set OPENAI_API_KEY. Get your API key from https://platform.openai.com/api-keys\")\n\nclient = AsyncOpenAI(api_key=OPENAI_API_KEY) # pragma: allowlist secret\nmodel_name = \"gpt-4o-mini\"\n\nprint(f\"✓ Using model: {model_name}\")",
|
|
90
|
+
"outputs": [],
|
|
91
|
+
"execution_count": null
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"cell_type": "markdown",
|
|
95
|
+
"metadata": {},
|
|
96
|
+
"source": [
|
|
97
|
+
"## Define Async Inference Function\n",
|
|
98
|
+
"\n",
|
|
99
|
+
"Create an async function to process inputs through the OpenAI API:"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"cell_type": "code",
|
|
104
|
+
"metadata": {},
|
|
105
|
+
"source": [
|
|
106
|
+
"async def inference(inputs: List[Any], **hyperparameters: Any) -> List[Any]:\n",
|
|
107
|
+
" \"\"\"Process inputs through OpenAI's API.\n",
|
|
108
|
+
" \n",
|
|
109
|
+
" Args:\n",
|
|
110
|
+
" inputs: Input values from an EvalDataset. For adaptive MMLU-Pro,\n",
|
|
111
|
+
" each input is a dict with 'question' and 'options' keys.\n",
|
|
112
|
+
" hyperparameters: Model hyperparameters.\n",
|
|
113
|
+
" \n",
|
|
114
|
+
" Returns:\n",
|
|
115
|
+
" List of model outputs for all inputs.\n",
|
|
116
|
+
" \"\"\"\n",
|
|
117
|
+
" outputs = []\n",
|
|
118
|
+
" \n",
|
|
119
|
+
" for input_val in inputs:\n",
|
|
120
|
+
" # Handle dict input from adaptive dataset\n",
|
|
121
|
+
" if isinstance(input_val, dict):\n",
|
|
122
|
+
" prompt = input_val.get(\"question\", \"\")\n",
|
|
123
|
+
" if \"options\" in input_val:\n",
|
|
124
|
+
" prompt += \"\\nOptions:\\n\" + \"\\n\".join(\n",
|
|
125
|
+
" f\"{letter}: {choice}\"\n",
|
|
126
|
+
" for letter, choice in zip(string.ascii_uppercase, input_val[\"options\"])\n",
|
|
127
|
+
" )\n",
|
|
128
|
+
" else:\n",
|
|
129
|
+
" prompt = str(input_val)\n",
|
|
130
|
+
" \n",
|
|
131
|
+
" # Build messages for OpenAI API\n",
|
|
132
|
+
" messages = [\n",
|
|
133
|
+
" {\n",
|
|
134
|
+
" \"role\": \"system\",\n",
|
|
135
|
+
" \"content\": \"Answer the question with a single letter representing the correct answer from the list of choices. Do not provide any additional explanation or output beyond the single letter.\",\n",
|
|
136
|
+
" },\n",
|
|
137
|
+
" {\"role\": \"user\", \"content\": prompt},\n",
|
|
138
|
+
" ]\n",
|
|
139
|
+
" \n",
|
|
140
|
+
" # Call OpenAI API\n",
|
|
141
|
+
" try:\n",
|
|
142
|
+
" response = await client.chat.completions.create(\n",
|
|
143
|
+
" model=model_name,\n",
|
|
144
|
+
" messages=messages,\n",
|
|
145
|
+
" temperature=0.7,\n",
|
|
146
|
+
" )\n",
|
|
147
|
+
" output = response.choices[0].message.content.strip()\n",
|
|
148
|
+
" except Exception as e:\n",
|
|
149
|
+
" output = f\"Error: {str(e)}\"\n",
|
|
150
|
+
" \n",
|
|
151
|
+
" outputs.append(output)\n",
|
|
152
|
+
" \n",
|
|
153
|
+
" return outputs"
|
|
154
|
+
],
|
|
155
|
+
"outputs": [],
|
|
156
|
+
"execution_count": null
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"cell_type": "markdown",
|
|
160
|
+
"metadata": {},
|
|
161
|
+
"source": [
|
|
162
|
+
"## Run Adaptive Evaluation\n",
|
|
163
|
+
"\n",
|
|
164
|
+
"Use `evaluate_async()` with an adaptive dataset (indicated by the `:adaptive` suffix):"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"cell_type": "code",
|
|
169
|
+
"metadata": {},
|
|
170
|
+
"source": [
|
|
171
|
+
"print(f\"Running adaptive evaluation on Common Sense QA with model: {model_name}\")\n",
|
|
172
|
+
"print(\"Note: Adaptive evaluation selects questions dynamically based on responses.\\n\")\n",
|
|
173
|
+
"\n",
|
|
174
|
+
"# Run adaptive evaluation\n",
|
|
175
|
+
"results = await evaluate_async(\n",
|
|
176
|
+
" inference,\n",
|
|
177
|
+
" datasets=\"trismik/CommonSenseQA:adaptive\", # Adaptive datasets have the \":adaptive\" suffix\n",
|
|
178
|
+
" experiment_id=\"Adaptive-Common-Sense-QA-Notebook\",\n",
|
|
179
|
+
" project_id=TRISMIK_PROJECT_ID,\n",
|
|
180
|
+
" return_dict=True,\n",
|
|
181
|
+
" return_aggregates=True,\n",
|
|
182
|
+
" return_items=True,\n",
|
|
183
|
+
" return_output=True,\n",
|
|
184
|
+
")\n",
|
|
185
|
+
"\n",
|
|
186
|
+
"print(\"\\n✓ Adaptive evaluation complete!\")"
|
|
187
|
+
],
|
|
188
|
+
"outputs": [],
|
|
189
|
+
"execution_count": null
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"cell_type": "markdown",
|
|
193
|
+
"metadata": {},
|
|
194
|
+
"source": [
|
|
195
|
+
"## View Results"
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"cell_type": "code",
|
|
200
|
+
"metadata": {},
|
|
201
|
+
"source": [
|
|
202
|
+
"pprint(results)"
|
|
203
|
+
],
|
|
204
|
+
"outputs": [],
|
|
205
|
+
"execution_count": null
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"cell_type": "markdown",
|
|
209
|
+
"metadata": {},
|
|
210
|
+
"source": [
|
|
211
|
+
"## View Results on Dashboard\n",
|
|
212
|
+
"\n",
|
|
213
|
+
"Your results have been uploaded to Trismik's dashboard for visualization and tracking:"
|
|
214
|
+
]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"cell_type": "markdown",
|
|
218
|
+
"metadata": {},
|
|
219
|
+
"source": "## Understanding Adaptive Testing\n\n### How it works:\n1. **Initial Questions**: Start with medium-difficulty questions\n2. **Adaptation**: If the model answers correctly, harder questions follow; if incorrect, easier questions are selected\n3. **Convergence**: The test converges to the model's true ability level\n4. **Stopping Criteria**: Stops when sufficient confidence is reached\n\n### Benefits vs. Traditional Testing:\n- **Efficiency**: Typically requires 50-70% fewer questions for the same precision\n- **Precision**: Better estimates of model capability\n- **Engagement**: Questions are appropriately challenging\n\n## Next Steps\n\n- Try adaptive evaluation with different models to compare\n- **Don't have an OpenAI API key?** See `3-adaptive_evaluation_local.ipynb` to run adaptive evaluations with local open-source models (Phi-3, Llama, etc.)\n- Explore other adaptive datasets available on Trismik\n- See the **Upload Results** notebook for non-adaptive result tracking"
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
"metadata": {
|
|
223
|
+
"kernelspec": {
|
|
224
|
+
"display_name": "Python 3",
|
|
225
|
+
"language": "python",
|
|
226
|
+
"name": "python3"
|
|
227
|
+
},
|
|
228
|
+
"language_info": {
|
|
229
|
+
"codemirror_mode": {
|
|
230
|
+
"name": "ipython",
|
|
231
|
+
"version": 3
|
|
232
|
+
},
|
|
233
|
+
"file_extension": ".py",
|
|
234
|
+
"mimetype": "text/x-python",
|
|
235
|
+
"name": "python",
|
|
236
|
+
"nbconvert_exporter": "python",
|
|
237
|
+
"pygments_lexer": "ipython3",
|
|
238
|
+
"version": "3.11.0"
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"nbformat": 4,
|
|
242
|
+
"nbformat_minor": 4
|
|
243
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": "# Uploading Results to Trismik Dashboard\n\nThis notebook demonstrates three ways to upload evaluation results to Trismik's dashboard for tracking and visualization.\n\n## Why Upload Results?\n\n- **Track Progress**: Monitor model performance over time\n- **Compare Models**: Visualize performance across different models and experiments\n- **Share Results**: Collaborate with your team on evaluation insights\n- **Historical Analysis**: Maintain a record of all evaluations\n\n## Prerequisites\n\n- **Trismik API key**: Get yours at https://app.trismik.com/settings\n- **Trismik Project**: Create a project at https://app.trismik.com and copy its Project ID"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"cell_type": "markdown",
|
|
10
|
+
"metadata": {},
|
|
11
|
+
"source": "## Setup Credentials\n\nSet your Trismik credentials here:"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"cell_type": "code",
|
|
15
|
+
"metadata": {},
|
|
16
|
+
"source": "# STEP 1: Get your Trismik API key from https://app.trismik.com/settings\n# STEP 2: Create a project at https://app.trismik.com and copy the Project ID\n\n# Set your credentials here\nTRISMIK_API_KEY = \"your-trismik-api-key\"\nTRISMIK_PROJECT_ID = \"your-project-id\"",
|
|
17
|
+
"outputs": [],
|
|
18
|
+
"execution_count": null
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"cell_type": "code",
|
|
22
|
+
"metadata": {},
|
|
23
|
+
"source": [
|
|
24
|
+
"from pprint import pprint\n",
|
|
25
|
+
"from scorebook import score, login\n",
|
|
26
|
+
"from scorebook.metrics.accuracy import Accuracy"
|
|
27
|
+
],
|
|
28
|
+
"outputs": [],
|
|
29
|
+
"execution_count": null
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"cell_type": "markdown",
|
|
33
|
+
"metadata": {},
|
|
34
|
+
"source": [
|
|
35
|
+
"## Login to Trismik"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"cell_type": "code",
|
|
40
|
+
"metadata": {},
|
|
41
|
+
"source": "if not TRISMIK_API_KEY or TRISMIK_API_KEY == \"your-trismik-api-key\":\n raise ValueError(\"Please set TRISMIK_API_KEY. Get your API key from https://app.trismik.com/settings\")\n\nlogin(TRISMIK_API_KEY)\nprint(\"✓ Logged in to Trismik\")\n\nif not TRISMIK_PROJECT_ID or TRISMIK_PROJECT_ID == \"your-project-id\":\n raise ValueError(\"Please set TRISMIK_PROJECT_ID. Create a project at https://app.trismik.com\")\n\nprint(f\"✓ Using project: {TRISMIK_PROJECT_ID}\")",
|
|
42
|
+
"outputs": [],
|
|
43
|
+
"execution_count": null
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"cell_type": "markdown",
|
|
47
|
+
"metadata": {},
|
|
48
|
+
"source": [
|
|
49
|
+
"## Method 1: Upload score() Results\n",
|
|
50
|
+
"\n",
|
|
51
|
+
"Score pre-computed outputs and upload to Trismik:"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"cell_type": "code",
|
|
56
|
+
"metadata": {},
|
|
57
|
+
"source": "# Prepare items with pre-computed outputs\nitems = [\n {\"input\": \"What is 2 + 2?\", \"output\": \"4\", \"label\": \"4\"},\n {\"input\": \"What is the capital of France?\", \"output\": \"Paris\", \"label\": \"Paris\"},\n {\"input\": \"Who wrote Romeo and Juliet?\", \"output\": \"William Shakespeare\", \"label\": \"William Shakespeare\"},\n {\"input\": \"What is 5 * 6?\", \"output\": \"30\", \"label\": \"30\"},\n {\"input\": \"What is the largest planet?\", \"output\": \"Jupiter\", \"label\": \"Jupiter\"},\n]\n\n# Score and upload\nresults = score(\n items=items,\n metrics=Accuracy,\n dataset_name=\"basic_questions\",\n model_name=\"example-model-v1\",\n experiment_id=\"Score-Upload-Notebook\",\n project_id=TRISMIK_PROJECT_ID,\n metadata={\n \"description\": \"Example from Jupyter notebook\",\n \"note\": \"Pre-computed outputs uploaded via score()\",\n },\n upload_results=True, # Enable uploading\n)\n\nprint(f\"\\n✓ Results uploaded successfully!\")\nprint(f\"Accuracy: {results['aggregate_results'][0]['accuracy']:.2%}\")",
|
|
58
|
+
"outputs": [],
|
|
59
|
+
"execution_count": null
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"cell_type": "markdown",
|
|
63
|
+
"metadata": {},
|
|
64
|
+
"source": [
|
|
65
|
+
"## Method 2: Upload evaluate() Results\n",
|
|
66
|
+
"\n",
|
|
67
|
+
"Run inference and automatically upload results:"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"cell_type": "code",
|
|
72
|
+
"metadata": {},
|
|
73
|
+
"source": "from typing import Any, List\nfrom scorebook import EvalDataset, evaluate\n\n# Create a simple dataset\nimport json\nfrom pathlib import Path\n\nsample_data = [\n {\"question\": \"What is 10 + 5?\", \"answer\": \"15\"},\n {\"question\": \"What is the capital of Spain?\", \"answer\": \"Madrid\"},\n]\n\ntemp_file = Path(\"temp_eval_dataset.json\")\nwith open(temp_file, \"w\") as f:\n json.dump(sample_data, f)\n\ndataset = EvalDataset.from_json(\n path=str(temp_file),\n metrics=\"accuracy\",\n input=\"question\",\n label=\"answer\",\n)\n\n# Define a simple inference function (mock)\ndef mock_inference(inputs: List[Any], **hyperparameters: Any) -> List[Any]:\n \"\"\"Mock inference that returns the expected answers.\"\"\"\n # In practice, this would call your model\n return [\"15\", \"Madrid\"] # Mock perfect answers\n\n# Run evaluation with upload\neval_results = evaluate(\n mock_inference,\n dataset,\n hyperparameters={\"temperature\": 0.7},\n experiment_id=\"Evaluate-Upload-Notebook\",\n project_id=TRISMIK_PROJECT_ID,\n metadata={\n \"model\": \"mock-model\",\n \"description\": \"Evaluation results from notebook\",\n },\n return_aggregates=True,\n return_items=True,\n return_output=True,\n)\n\nprint(f\"\\n✓ Evaluation results uploaded!\")\nprint(f\"Accuracy: {eval_results['aggregate_results'][0]['accuracy']:.2%}\")\n\n# Cleanup\ntemp_file.unlink()",
|
|
74
|
+
"outputs": [],
|
|
75
|
+
"execution_count": null
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"cell_type": "markdown",
|
|
79
|
+
"metadata": {},
|
|
80
|
+
"source": [
|
|
81
|
+
"## Method 3: Upload External Results\n",
|
|
82
|
+
"\n",
|
|
83
|
+
"Import results from external evaluation frameworks or historical data:"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"cell_type": "code",
|
|
88
|
+
"metadata": {},
|
|
89
|
+
"source": "# Example: Import results from another evaluation framework\nexternal_results = [\n {\"input\": \"Translate 'hello' to Spanish\", \"output\": \"hola\", \"label\": \"hola\"},\n {\"input\": \"Translate 'goodbye' to Spanish\", \"output\": \"adiós\", \"label\": \"adiós\"},\n {\"input\": \"Translate 'thank you' to Spanish\", \"output\": \"gracias\", \"label\": \"gracias\"},\n {\"input\": \"Translate 'please' to Spanish\", \"output\": \"por favor\", \"label\": \"por favor\"},\n]\n\n# Upload external results\nexternal_upload = score(\n items=external_results,\n metrics=\"accuracy\",\n dataset_name=\"spanish_translation\",\n model_name=\"external-translator-v2\",\n experiment_id=\"External-Results-Upload\",\n project_id=TRISMIK_PROJECT_ID,\n metadata={\n \"description\": \"Historical results imported from external framework\",\n \"source\": \"Custom evaluation pipeline\",\n \"date\": \"2025-01-15\",\n },\n upload_results=True,\n)\n\nprint(f\"\\n✓ External results uploaded!\")\nprint(f\"Accuracy: {external_upload['aggregate_results'][0]['accuracy']:.2%}\")",
|
|
90
|
+
"outputs": [],
|
|
91
|
+
"execution_count": null
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"cell_type": "markdown",
|
|
95
|
+
"metadata": {},
|
|
96
|
+
"source": [
|
|
97
|
+
"## View Results on Dashboard\n",
|
|
98
|
+
"\n",
|
|
99
|
+
"All uploaded results are now visible on your Trismik dashboard:"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"cell_type": "code",
|
|
104
|
+
"metadata": {},
|
|
105
|
+
"source": "from IPython.display import display, Markdown\n\ndashboard_url = f\"https://app.trismik.com/projects/{TRISMIK_PROJECT_ID}\"\ndisplay(Markdown(f\"### 📊 [View All Results on Dashboard]({dashboard_url})\"))\nprint(f\"\\nDirect link: {dashboard_url}\")\nprint(\"\\nYou should see three experiments:\")\nprint(\" 1. Score-Upload-Notebook\")\nprint(\" 2. Evaluate-Upload-Notebook\")\nprint(\" 3. External-Results-Upload\")",
|
|
106
|
+
"outputs": [],
|
|
107
|
+
"execution_count": null
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"cell_type": "markdown",
|
|
111
|
+
"metadata": {},
|
|
112
|
+
"source": [
|
|
113
|
+
"## Organizing Results with Metadata\n",
|
|
114
|
+
"\n",
|
|
115
|
+
"Use metadata to add context and organization to your results:"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"cell_type": "code",
|
|
120
|
+
"metadata": {},
|
|
121
|
+
"source": "# Example: Organizing a model comparison experiment\nmodels_to_test = [\n {\"name\": \"model-a\", \"version\": \"1.0\"},\n {\"name\": \"model-b\", \"version\": \"2.0\"},\n]\n\ntest_items = [\n {\"output\": \"positive\", \"label\": \"positive\"},\n {\"output\": \"negative\", \"label\": \"negative\"},\n]\n\nfor model_info in models_to_test:\n result = score(\n items=test_items,\n metrics=Accuracy,\n dataset_name=\"sentiment_test\",\n model_name=model_info[\"name\"],\n experiment_id=\"Model-Comparison-Notebook\",\n project_id=TRISMIK_PROJECT_ID,\n metadata={\n \"model_version\": model_info[\"version\"],\n \"comparison_group\": \"sentiment_analysis\",\n \"date\": \"2025-01-26\",\n \"notes\": f\"Testing {model_info['name']} v{model_info['version']}\",\n },\n upload_results=True,\n )\n print(f\"✓ Uploaded results for {model_info['name']} v{model_info['version']}\")",
|
|
122
|
+
"outputs": [],
|
|
123
|
+
"execution_count": null
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"cell_type": "markdown",
|
|
127
|
+
"metadata": {},
|
|
128
|
+
"source": [
|
|
129
|
+
"## Best Practices\n",
|
|
130
|
+
"\n",
|
|
131
|
+
"### Experiment Naming\n",
|
|
132
|
+
"- Use descriptive `experiment_id` values (e.g., \"GPT4-MMLU-Baseline\")\n",
|
|
133
|
+
"- Group related runs under the same experiment ID\n",
|
|
134
|
+
"- Use different experiment IDs for different types of tests\n",
|
|
135
|
+
"\n",
|
|
136
|
+
"### Metadata\n",
|
|
137
|
+
"- Include model version, hyperparameters, and configuration\n",
|
|
138
|
+
"- Add timestamps and descriptions for historical tracking\n",
|
|
139
|
+
"- Use consistent keys across experiments for easy comparison\n",
|
|
140
|
+
"\n",
|
|
141
|
+
"### Organization\n",
|
|
142
|
+
"- Create separate projects for different use cases\n",
|
|
143
|
+
"- Use tags or metadata fields to categorize experiments\n",
|
|
144
|
+
"- Document your evaluation methodology in metadata\n",
|
|
145
|
+
"\n",
|
|
146
|
+
"## Next Steps\n",
|
|
147
|
+
"\n",
|
|
148
|
+
"- Explore the Trismik dashboard to visualize trends and comparisons\n",
|
|
149
|
+
"- Set up automated evaluation pipelines with result uploading\n",
|
|
150
|
+
"- Try the **Adaptive Evaluations** notebook for efficient testing with automatic uploads"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"metadata": {
|
|
155
|
+
"kernelspec": {
|
|
156
|
+
"display_name": "Python 3",
|
|
157
|
+
"language": "python",
|
|
158
|
+
"name": "python3"
|
|
159
|
+
},
|
|
160
|
+
"language_info": {
|
|
161
|
+
"codemirror_mode": {
|
|
162
|
+
"name": "ipython",
|
|
163
|
+
"version": 3
|
|
164
|
+
},
|
|
165
|
+
"file_extension": ".py",
|
|
166
|
+
"mimetype": "text/x-python",
|
|
167
|
+
"name": "python",
|
|
168
|
+
"nbconvert_exporter": "python",
|
|
169
|
+
"pygments_lexer": "ipython3",
|
|
170
|
+
"version": "3.11.0"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"nbformat": 4,
|
|
174
|
+
"nbformat_minor": 4
|
|
175
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "bc3ba3cd77800bb4",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Adaptive Evaluations with Scorebook - Evaluating an OpenAI GPT Model\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"This quick-start guide showcases an adaptive evaluation of OpenAI's GPT-4o Mini model.\n",
|
|
11
|
+
"\n",
|
|
12
|
+
"We recommend that you first see our [getting started quick-start guide](https://colab.research.google.com/github/trismik/scorebook/blob/main/tutorials/quickstarts/getting_started.ipynb) if you have not done so already, for more of a detailed overview on adaptive testing and setting up Trismik credentials.\n",
|
|
13
|
+
"\n",
|
|
14
|
+
"## Prerequisites\n",
|
|
15
|
+
"\n",
|
|
16
|
+
"- **Trismik API key**: Generate a Trismik API key from the [Trismik dashboard's settings page](https://app.trismik.com/settings).\n",
|
|
17
|
+
"- **Trismik Project Id**: We recommend you use the project id generated in the [Getting Started Quick-Start Guide](https://colab.research.google.com/github/trismik/scorebook/blob/main/tutorials/quickstarts/getting_started.ipynb).\n",
|
|
18
|
+
"- **OpenAI API key**: Generate an OpenAI API key from [OpenAI's API Platform](https://openai.com/api/).\n",
|
|
19
|
+
"\n",
|
|
20
|
+
"## Install Scorebook"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"metadata": {},
|
|
25
|
+
"cell_type": "code",
|
|
26
|
+
"source": [
|
|
27
|
+
"!pip install scorebook\n",
|
|
28
|
+
"# if you're running this locally, please run !pip install scorebook\"[examples, providers]\""
|
|
29
|
+
],
|
|
30
|
+
"id": "f454e876551a4a0c",
|
|
31
|
+
"outputs": [],
|
|
32
|
+
"execution_count": null
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"metadata": {},
|
|
36
|
+
"cell_type": "markdown",
|
|
37
|
+
"source": [
|
|
38
|
+
"\n",
|
|
39
|
+
"## Setup Credentials\n",
|
|
40
|
+
"\n",
|
|
41
|
+
"Enter your Trismik API key, project id and OpenAI API Key below."
|
|
42
|
+
],
|
|
43
|
+
"id": "cad992b287d4d0ac"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"cell_type": "code",
|
|
47
|
+
"id": "14e576282749edb7",
|
|
48
|
+
"metadata": {},
|
|
49
|
+
"source": [
|
|
50
|
+
"# Set your credentials here\n",
|
|
51
|
+
"TRISMIK_API_KEY = \"your-trismik-api-key-here\"\n",
|
|
52
|
+
"TRISMIK_PROJECT_ID = \"your-trismik-project-id-here\"\n",
|
|
53
|
+
"OPENAI_API_KEY = \"your-openai-api-key-here\""
|
|
54
|
+
],
|
|
55
|
+
"outputs": [],
|
|
56
|
+
"execution_count": null
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"cell_type": "markdown",
|
|
60
|
+
"id": "700950d039e4c0f6",
|
|
61
|
+
"metadata": {},
|
|
62
|
+
"source": [
|
|
63
|
+
"## Login with Trismik API Key"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"cell_type": "code",
|
|
68
|
+
"id": "initial_id",
|
|
69
|
+
"metadata": {},
|
|
70
|
+
"source": [
|
|
71
|
+
"from scorebook import login\n",
|
|
72
|
+
"\n",
|
|
73
|
+
"# Login to Trismik\n",
|
|
74
|
+
"login(TRISMIK_API_KEY)\n",
|
|
75
|
+
"print(\"✓ Logged in to Trismik\")"
|
|
76
|
+
],
|
|
77
|
+
"outputs": [],
|
|
78
|
+
"execution_count": null
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"cell_type": "markdown",
|
|
82
|
+
"id": "13084db21e549ccf",
|
|
83
|
+
"metadata": {},
|
|
84
|
+
"source": [
|
|
85
|
+
"## Define an Inference Function\n",
|
|
86
|
+
"\n",
|
|
87
|
+
"To evaluate a model with Scorebook, it must be encapsulated within an inference function. An inference function must accept a list of model inputs, pass these to the model for inference, collect and return outputs generated.\n",
|
|
88
|
+
"\n",
|
|
89
|
+
"An inference function can be defined to encapsulate any model, local or cloud-hosted. There is flexibility in how an inference function can be defined, the only requirements are the function signature. An inference function must,\n",
|
|
90
|
+
"\n",
|
|
91
|
+
"Accept:\n",
|
|
92
|
+
"\n",
|
|
93
|
+
"- A list of model inputs.\n",
|
|
94
|
+
"- Hyperparameters which can be optionally accessed via kwargs.\n",
|
|
95
|
+
"\n",
|
|
96
|
+
"Return\n",
|
|
97
|
+
"\n",
|
|
98
|
+
"- A list of parsed model outputs for scoring."
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"cell_type": "code",
|
|
103
|
+
"id": "8aa99f513db6241a",
|
|
104
|
+
"metadata": {},
|
|
105
|
+
"source": [
|
|
106
|
+
"from openai import OpenAI\n",
|
|
107
|
+
"from typing import Any, List\n",
|
|
108
|
+
"import string\n",
|
|
109
|
+
"\n",
|
|
110
|
+
"client = OpenAI(api_key=OPENAI_API_KEY)\n",
|
|
111
|
+
"\n",
|
|
112
|
+
"# define an inference function for GPT-4o Mini.\n",
|
|
113
|
+
"def gpt4o_mini(inputs: List[Any], **hyperparameters: Any) -> List[Any]:\n",
|
|
114
|
+
" \"\"\"Process inputs through OpenAI's API\"\"\"\n",
|
|
115
|
+
"\n",
|
|
116
|
+
" outputs = []\n",
|
|
117
|
+
" for idx, input_item in enumerate(inputs):\n",
|
|
118
|
+
"\n",
|
|
119
|
+
" # Format prompt\n",
|
|
120
|
+
" choices = input_item.get(\"options\", [])\n",
|
|
121
|
+
" prompt = (\n",
|
|
122
|
+
" str(input_item.get(\"question\", \"\"))\n",
|
|
123
|
+
" + \"\\nOptions:\\n\"\n",
|
|
124
|
+
" + \"\\n\".join(\n",
|
|
125
|
+
" f\"{letter}: {choice['text'] if isinstance(choice, dict) else choice}\"\n",
|
|
126
|
+
" for letter, choice in zip(string.ascii_uppercase, choices)\n",
|
|
127
|
+
" )\n",
|
|
128
|
+
" )\n",
|
|
129
|
+
"\n",
|
|
130
|
+
" # Build messages for OpenAI API\n",
|
|
131
|
+
" messages = [\n",
|
|
132
|
+
" {\n",
|
|
133
|
+
" \"role\": \"system\",\n",
|
|
134
|
+
" \"content\": hyperparameters[\"system_message\"]\n",
|
|
135
|
+
" },\n",
|
|
136
|
+
" {\"role\": \"user\", \"content\": prompt},\n",
|
|
137
|
+
" ]\n",
|
|
138
|
+
"\n",
|
|
139
|
+
" # Call OpenAI API and extract output from the response\n",
|
|
140
|
+
" try:\n",
|
|
141
|
+
" response = client.chat.completions.create(\n",
|
|
142
|
+
" model=\"gpt-4o-mini\",\n",
|
|
143
|
+
" messages=messages,\n",
|
|
144
|
+
" temperature=0.7,\n",
|
|
145
|
+
" )\n",
|
|
146
|
+
" output = response.choices[0].message.content.strip()\n",
|
|
147
|
+
"\n",
|
|
148
|
+
" except Exception as e:\n",
|
|
149
|
+
" output = f\"Error: {str(e)}\"\n",
|
|
150
|
+
"\n",
|
|
151
|
+
" outputs.append(output)\n",
|
|
152
|
+
"\n",
|
|
153
|
+
" return outputs"
|
|
154
|
+
],
|
|
155
|
+
"outputs": [],
|
|
156
|
+
"execution_count": null
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"cell_type": "markdown",
|
|
160
|
+
"id": "efa5c3ea791bbcd1",
|
|
161
|
+
"metadata": {},
|
|
162
|
+
"source": [
|
|
163
|
+
"## Run an Adaptive Evaluation\n",
|
|
164
|
+
"\n",
|
|
165
|
+
"When running an adaptive evaluation, we can use any single or multiple adaptive datasets and specify a split to be evaluated."
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"cell_type": "code",
|
|
170
|
+
"id": "3cbf1b2f13d5553e",
|
|
171
|
+
"metadata": {},
|
|
172
|
+
"source": [
|
|
173
|
+
"from scorebook import evaluate\n",
|
|
174
|
+
"\n",
|
|
175
|
+
"# Run adaptive evaluation\n",
|
|
176
|
+
"results = evaluate(\n",
|
|
177
|
+
" inference = gpt4o_mini,\n",
|
|
178
|
+
" datasets = \"trismik/figQA:adaptive\",\n",
|
|
179
|
+
" hyperparameters = {\"system_message\": \"Answer the question with only the letter of the correct option. No additional text or context\"},\n",
|
|
180
|
+
" split = \"validation\",\n",
|
|
181
|
+
" experiment_id = \"GPT-4o-Mini-Adaptive-Evaluation\",\n",
|
|
182
|
+
" project_id = TRISMIK_PROJECT_ID,\n",
|
|
183
|
+
")\n",
|
|
184
|
+
"\n",
|
|
185
|
+
"# Print the adaptive evaluation results\n",
|
|
186
|
+
"print(\"✓ Adaptive evaluation complete!\")\n",
|
|
187
|
+
"print(\"Results: \", results[0][\"score\"])"
|
|
188
|
+
],
|
|
189
|
+
"outputs": [],
|
|
190
|
+
"execution_count": null
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"cell_type": "markdown",
|
|
194
|
+
"id": "d37cb5e87cc297fe",
|
|
195
|
+
"metadata": {},
|
|
196
|
+
"source": [
|
|
197
|
+
"---\n",
|
|
198
|
+
"\n",
|
|
199
|
+
"## Next Steps\n",
|
|
200
|
+
"\n",
|
|
201
|
+
"- [Adaptive Testing White Paper](https://docs.trismik.com/adaptiveTesting/adaptive-testing-introduction/): An in depth overview of the science behind the adaptive testing methodology.\n",
|
|
202
|
+
"- [Dataset Page](https://dashboard.trismik.com/datasets): Trismik's full set of currently adaptive datasets from the Trismik dashboard.\n",
|
|
203
|
+
"- [Scorebook Docs](https://docs.trismik.com/scorebook/introduction-to-scorebook/): Scorebook's full documentation.\n",
|
|
204
|
+
"- [Scorebook Repository](https://github.com/trismik/scorebook): Scorebook is an open-source library, view the code and more examples."
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"metadata": {
|
|
209
|
+
"kernelspec": {
|
|
210
|
+
"display_name": "Python 3 (ipykernel)",
|
|
211
|
+
"language": "python",
|
|
212
|
+
"name": "python3"
|
|
213
|
+
},
|
|
214
|
+
"language_info": {
|
|
215
|
+
"codemirror_mode": {
|
|
216
|
+
"name": "ipython",
|
|
217
|
+
"version": 3
|
|
218
|
+
},
|
|
219
|
+
"file_extension": ".py",
|
|
220
|
+
"mimetype": "text/x-python",
|
|
221
|
+
"name": "python",
|
|
222
|
+
"nbconvert_exporter": "python",
|
|
223
|
+
"pygments_lexer": "ipython3",
|
|
224
|
+
"version": "3.13.5"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"nbformat": 4,
|
|
228
|
+
"nbformat_minor": 5
|
|
229
|
+
}
|