langfun 0.1.2.dev202509120804__py3-none-any.whl → 0.1.2.dev202512040805__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 (162) hide show
  1. langfun/__init__.py +1 -1
  2. langfun/core/__init__.py +7 -1
  3. langfun/core/agentic/__init__.py +8 -1
  4. langfun/core/agentic/action.py +740 -112
  5. langfun/core/agentic/action_eval.py +9 -2
  6. langfun/core/agentic/action_test.py +189 -24
  7. langfun/core/async_support.py +104 -5
  8. langfun/core/async_support_test.py +23 -0
  9. langfun/core/coding/python/correction.py +19 -9
  10. langfun/core/coding/python/execution.py +14 -12
  11. langfun/core/coding/python/generation.py +21 -16
  12. langfun/core/coding/python/sandboxing.py +23 -3
  13. langfun/core/component.py +42 -3
  14. langfun/core/concurrent.py +70 -6
  15. langfun/core/concurrent_test.py +9 -2
  16. langfun/core/console.py +1 -1
  17. langfun/core/data/conversion/anthropic.py +12 -3
  18. langfun/core/data/conversion/anthropic_test.py +8 -6
  19. langfun/core/data/conversion/gemini.py +11 -2
  20. langfun/core/data/conversion/gemini_test.py +48 -9
  21. langfun/core/data/conversion/openai.py +145 -31
  22. langfun/core/data/conversion/openai_test.py +161 -17
  23. langfun/core/eval/base.py +48 -44
  24. langfun/core/eval/base_test.py +5 -5
  25. langfun/core/eval/matching.py +5 -2
  26. langfun/core/eval/patching.py +3 -3
  27. langfun/core/eval/scoring.py +4 -3
  28. langfun/core/eval/v2/__init__.py +2 -0
  29. langfun/core/eval/v2/checkpointing.py +76 -7
  30. langfun/core/eval/v2/checkpointing_test.py +9 -2
  31. langfun/core/eval/v2/config_saver.py +37 -0
  32. langfun/core/eval/v2/config_saver_test.py +36 -0
  33. langfun/core/eval/v2/eval_test_helper.py +104 -3
  34. langfun/core/eval/v2/evaluation.py +92 -17
  35. langfun/core/eval/v2/evaluation_test.py +9 -3
  36. langfun/core/eval/v2/example.py +50 -40
  37. langfun/core/eval/v2/example_test.py +16 -8
  38. langfun/core/eval/v2/experiment.py +84 -15
  39. langfun/core/eval/v2/experiment_test.py +19 -0
  40. langfun/core/eval/v2/metric_values.py +31 -3
  41. langfun/core/eval/v2/metric_values_test.py +32 -0
  42. langfun/core/eval/v2/metrics.py +157 -44
  43. langfun/core/eval/v2/metrics_test.py +39 -18
  44. langfun/core/eval/v2/progress.py +31 -1
  45. langfun/core/eval/v2/progress_test.py +27 -0
  46. langfun/core/eval/v2/progress_tracking.py +13 -5
  47. langfun/core/eval/v2/progress_tracking_test.py +9 -1
  48. langfun/core/eval/v2/reporting.py +90 -71
  49. langfun/core/eval/v2/reporting_test.py +24 -6
  50. langfun/core/eval/v2/runners/__init__.py +30 -0
  51. langfun/core/eval/v2/{runners.py → runners/base.py} +72 -180
  52. langfun/core/eval/v2/runners/beam.py +354 -0
  53. langfun/core/eval/v2/runners/beam_test.py +153 -0
  54. langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
  55. langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -0
  56. langfun/core/eval/v2/runners/debug.py +40 -0
  57. langfun/core/eval/v2/runners/debug_test.py +76 -0
  58. langfun/core/eval/v2/runners/parallel.py +243 -0
  59. langfun/core/eval/v2/runners/parallel_test.py +182 -0
  60. langfun/core/eval/v2/runners/sequential.py +47 -0
  61. langfun/core/eval/v2/runners/sequential_test.py +169 -0
  62. langfun/core/langfunc.py +45 -130
  63. langfun/core/langfunc_test.py +7 -5
  64. langfun/core/language_model.py +189 -36
  65. langfun/core/language_model_test.py +54 -3
  66. langfun/core/llms/__init__.py +12 -1
  67. langfun/core/llms/anthropic.py +157 -2
  68. langfun/core/llms/azure_openai.py +29 -17
  69. langfun/core/llms/cache/base.py +25 -3
  70. langfun/core/llms/cache/in_memory.py +48 -7
  71. langfun/core/llms/cache/in_memory_test.py +14 -4
  72. langfun/core/llms/compositional.py +25 -1
  73. langfun/core/llms/deepseek.py +30 -2
  74. langfun/core/llms/fake.py +32 -1
  75. langfun/core/llms/gemini.py +64 -12
  76. langfun/core/llms/gemini_test.py +110 -0
  77. langfun/core/llms/google_genai.py +34 -1
  78. langfun/core/llms/groq.py +28 -3
  79. langfun/core/llms/llama_cpp.py +23 -4
  80. langfun/core/llms/openai.py +120 -3
  81. langfun/core/llms/openai_compatible.py +148 -27
  82. langfun/core/llms/openai_compatible_test.py +207 -20
  83. langfun/core/llms/openai_test.py +0 -2
  84. langfun/core/llms/rest.py +16 -1
  85. langfun/core/llms/vertexai.py +58 -8
  86. langfun/core/logging.py +1 -1
  87. langfun/core/mcp/__init__.py +10 -0
  88. langfun/core/mcp/client.py +177 -0
  89. langfun/core/mcp/client_test.py +71 -0
  90. langfun/core/mcp/session.py +241 -0
  91. langfun/core/mcp/session_test.py +54 -0
  92. langfun/core/mcp/testing/simple_mcp_client.py +33 -0
  93. langfun/core/mcp/testing/simple_mcp_server.py +33 -0
  94. langfun/core/mcp/tool.py +254 -0
  95. langfun/core/mcp/tool_test.py +197 -0
  96. langfun/core/memory.py +1 -0
  97. langfun/core/message.py +160 -55
  98. langfun/core/message_test.py +65 -81
  99. langfun/core/modalities/__init__.py +8 -0
  100. langfun/core/modalities/audio.py +21 -1
  101. langfun/core/modalities/image.py +73 -3
  102. langfun/core/modalities/image_test.py +116 -0
  103. langfun/core/modalities/mime.py +64 -3
  104. langfun/core/modalities/mime_test.py +11 -0
  105. langfun/core/modalities/pdf.py +19 -1
  106. langfun/core/modalities/video.py +21 -1
  107. langfun/core/modality.py +167 -29
  108. langfun/core/modality_test.py +42 -12
  109. langfun/core/natural_language.py +1 -1
  110. langfun/core/sampling.py +4 -4
  111. langfun/core/sampling_test.py +20 -4
  112. langfun/core/structured/__init__.py +2 -24
  113. langfun/core/structured/completion.py +34 -44
  114. langfun/core/structured/completion_test.py +23 -43
  115. langfun/core/structured/description.py +54 -50
  116. langfun/core/structured/function_generation.py +29 -12
  117. langfun/core/structured/mapping.py +81 -37
  118. langfun/core/structured/parsing.py +95 -79
  119. langfun/core/structured/parsing_test.py +0 -3
  120. langfun/core/structured/querying.py +230 -154
  121. langfun/core/structured/querying_test.py +69 -33
  122. langfun/core/structured/schema/__init__.py +49 -0
  123. langfun/core/structured/schema/base.py +664 -0
  124. langfun/core/structured/schema/base_test.py +531 -0
  125. langfun/core/structured/schema/json.py +174 -0
  126. langfun/core/structured/schema/json_test.py +121 -0
  127. langfun/core/structured/schema/python.py +316 -0
  128. langfun/core/structured/schema/python_test.py +410 -0
  129. langfun/core/structured/schema_generation.py +33 -14
  130. langfun/core/structured/scoring.py +47 -36
  131. langfun/core/structured/tokenization.py +26 -11
  132. langfun/core/subscription.py +2 -2
  133. langfun/core/template.py +175 -50
  134. langfun/core/template_test.py +123 -17
  135. langfun/env/__init__.py +43 -0
  136. langfun/env/base_environment.py +827 -0
  137. langfun/env/base_environment_test.py +473 -0
  138. langfun/env/base_feature.py +304 -0
  139. langfun/env/base_feature_test.py +228 -0
  140. langfun/env/base_sandbox.py +842 -0
  141. langfun/env/base_sandbox_test.py +1235 -0
  142. langfun/env/event_handlers/__init__.py +14 -0
  143. langfun/env/event_handlers/chain.py +233 -0
  144. langfun/env/event_handlers/chain_test.py +253 -0
  145. langfun/env/event_handlers/event_logger.py +472 -0
  146. langfun/env/event_handlers/event_logger_test.py +304 -0
  147. langfun/env/event_handlers/metric_writer.py +726 -0
  148. langfun/env/event_handlers/metric_writer_test.py +214 -0
  149. langfun/env/interface.py +1640 -0
  150. langfun/env/interface_test.py +153 -0
  151. langfun/env/load_balancers.py +59 -0
  152. langfun/env/load_balancers_test.py +141 -0
  153. langfun/env/test_utils.py +507 -0
  154. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/METADATA +7 -3
  155. langfun-0.1.2.dev202512040805.dist-info/RECORD +217 -0
  156. langfun/core/eval/v2/runners_test.py +0 -343
  157. langfun/core/structured/schema.py +0 -987
  158. langfun/core/structured/schema_test.py +0 -982
  159. langfun-0.1.2.dev202509120804.dist-info/RECORD +0 -172
  160. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/WHEEL +0 -0
  161. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/licenses/LICENSE +0 -0
  162. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,726 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Environment event logger."""
15
+
16
+ from typing import Annotated
17
+ from langfun.env import interface
18
+ import pyglove as pg
19
+
20
+
21
+ _METRIC_NAMESPACE = '/langfun/env'
22
+
23
+
24
+ class MetricWriter(pg.Object, interface.EventHandler):
25
+ """Event handler for streamz metrics."""
26
+
27
+ app: Annotated[
28
+ str,
29
+ 'Application name that will be used as a metric parameter.'
30
+ ] = ''
31
+
32
+ def _get_counter(
33
+ self,
34
+ name: str,
35
+ description: str,
36
+ parameters: dict[str, type[str]] | None = None,
37
+ ) -> pg.monitoring.Counter:
38
+ return self._metric_collection.get_counter(
39
+ name=name,
40
+ description=description,
41
+ parameters=parameters,
42
+ )
43
+
44
+ def _get_scalar(
45
+ self,
46
+ name: str,
47
+ description: str,
48
+ parameters: dict[str, type[str]] | None = None
49
+ ) -> pg.monitoring.Metric:
50
+ return self._metric_collection.get_scalar(
51
+ name=name,
52
+ description=description,
53
+ parameters=parameters
54
+ )
55
+
56
+ def _get_distribution(
57
+ self,
58
+ name: str,
59
+ description: str,
60
+ parameters: dict[str, type[str]] | None = None
61
+ ) -> pg.monitoring.Metric:
62
+ return self._metric_collection.get_distribution(
63
+ name=name,
64
+ description=description,
65
+ parameters=parameters
66
+ )
67
+
68
+ def _error_tag(self, error: BaseException | None) -> str:
69
+ if error is None:
70
+ return 'Success'
71
+ return pg.utils.ErrorInfo.from_exception(error).tag
72
+
73
+ def _initialize_metrics(self) -> None:
74
+ """Initializes metrics."""
75
+
76
+ self._metric_collection = pg.monitoring.metric_collection(_METRIC_NAMESPACE)
77
+
78
+ #
79
+ # Environment metrics.
80
+ #
81
+
82
+ self._environment_housekeep_duration_ms = self._get_distribution(
83
+ 'environment_housekeep_duration_ms',
84
+ description='Environment housekeeping duration in milliseconds',
85
+ parameters={
86
+ 'app': str,
87
+ 'environment_id': str,
88
+ 'error': str,
89
+ }
90
+ )
91
+
92
+ #
93
+ # Sandbox metrics.
94
+ #
95
+
96
+ # Sandbox counters.
97
+ self._sandbox_start = self._get_counter(
98
+ 'sandbox_start',
99
+ description='Sandbox start counter',
100
+ parameters={
101
+ 'app': str,
102
+ 'environment_id': str,
103
+ 'image_id': str,
104
+ 'error': str,
105
+ }
106
+ )
107
+ self._sandbox_shutdown = self._get_counter(
108
+ 'sandbox_shutdown',
109
+ description='Sandbox shutdown counter',
110
+ parameters={
111
+ 'app': str,
112
+ 'environment_id': str,
113
+ 'image_id': str,
114
+ 'error': str
115
+ }
116
+ )
117
+ self._sandbox_count = self._get_scalar(
118
+ 'sandbox_count',
119
+ description='Sandbox count',
120
+ parameters={
121
+ 'app': str,
122
+ 'environment_id': str,
123
+ 'image_id': str,
124
+ 'status': str,
125
+ },
126
+ )
127
+ self._sandbox_housekeep = self._get_counter(
128
+ 'sandbox_housekeep',
129
+ description='Sandbox housekeeping counter',
130
+ parameters={
131
+ 'app': str,
132
+ 'environment_id': str,
133
+ 'image_id': str,
134
+ 'error': str,
135
+ }
136
+ )
137
+ self._sandbox_activity = self._get_counter(
138
+ 'sandbox_activity',
139
+ description='Sandbox activity counter',
140
+ parameters={
141
+ 'app': str,
142
+ 'environment_id': str,
143
+ 'image_id': str,
144
+ 'activity': str,
145
+ 'error': str,
146
+ }
147
+ )
148
+
149
+ # Sandbox scalars.
150
+ self._sandbox_lifetime_ms = self._get_distribution(
151
+ 'sandbox_lifetime_ms',
152
+ description='Sandbox life time in milliseconds',
153
+ parameters={
154
+ 'app': str,
155
+ 'environment_id': str,
156
+ 'image_id': str,
157
+ 'error': str,
158
+ }
159
+ )
160
+ self._sandbox_start_duration_ms = self._get_distribution(
161
+ 'sandbox_start_duration_ms',
162
+ description='Sandbox start duration in milliseconds',
163
+ parameters={
164
+ 'app': str,
165
+ 'environment_id': str,
166
+ 'image_id': str,
167
+ 'error': str,
168
+ }
169
+ )
170
+ self._sandbox_shutdown_duration_ms = self._get_distribution(
171
+ 'sandbox_shutdown_duration_ms',
172
+ description='Sandbox shutdown duration in milliseconds',
173
+ parameters={
174
+ 'app': str,
175
+ 'environment_id': str,
176
+ 'image_id': str,
177
+ 'error': str,
178
+ }
179
+ )
180
+ self._sandbox_housekeep_duration_ms = self._get_distribution(
181
+ 'sandbox_housekeep_duration_ms',
182
+ description='Sandbox housekeeping duration in milliseconds',
183
+ parameters={
184
+ 'app': str,
185
+ 'environment_id': str,
186
+ 'image_id': str,
187
+ 'error': str,
188
+ }
189
+ )
190
+ self._sandbox_status_duration_ms = self._get_distribution(
191
+ 'sandbox_status_duration_ms',
192
+ description='Sandbox duration of specific status in milliseconds',
193
+ parameters={
194
+ 'app': str,
195
+ 'environment_id': str,
196
+ 'image_id': str,
197
+ 'status': str,
198
+ }
199
+ )
200
+ self._sandbox_activity_duration_ms = self._get_distribution(
201
+ 'sandbox_activity_duration_ms',
202
+ description='Sandbox activity duration in milliseconds',
203
+ parameters={
204
+ 'app': str,
205
+ 'environment_id': str,
206
+ 'image_id': str,
207
+ 'activity': str,
208
+ 'error': str,
209
+ }
210
+ )
211
+
212
+ #
213
+ # Sandbox session metrics.
214
+ #
215
+
216
+ self._sandbox_session_start_duration_ms = self._get_distribution(
217
+ 'sandbox_session_start_duration_ms',
218
+ description='Sandbox session start duration in milliseconds',
219
+ parameters={
220
+ 'app': str,
221
+ 'environment_id': str,
222
+ 'image_id': str,
223
+ 'error': str,
224
+ }
225
+ )
226
+ self._sandbox_session_end_duration_ms = self._get_distribution(
227
+ 'sandbox_session_end_duration_ms',
228
+ description='Sandbox session end duration in milliseconds',
229
+ parameters={
230
+ 'app': str,
231
+ 'environment_id': str,
232
+ 'image_id': str,
233
+ 'error': str,
234
+ }
235
+ )
236
+ self._sandbox_session_lifetime_ms = self._get_distribution(
237
+ 'sandbox_session_lifetime_ms',
238
+ description='Sandbox session lifetime in milliseconds',
239
+ parameters={
240
+ 'app': str,
241
+ 'environment_id': str,
242
+ 'image_id': str,
243
+ 'error': str,
244
+ }
245
+ )
246
+
247
+ #
248
+ # Feature metrics.
249
+ #
250
+
251
+ # Feature counters.
252
+ self._feature_setup = self._get_counter(
253
+ 'feature_setup',
254
+ description='Feature setup counter',
255
+ parameters={
256
+ 'app': str,
257
+ 'environment_id': str,
258
+ 'image_id': str,
259
+ 'feature_name': str,
260
+ 'error': str,
261
+ }
262
+ )
263
+ self._feature_teardown = self._get_counter(
264
+ 'feature_teardown',
265
+ description='Feature teardown counter',
266
+ parameters={
267
+ 'app': str,
268
+ 'environment_id': str,
269
+ 'image_id': str,
270
+ 'feature_name': str,
271
+ 'error': str,
272
+ }
273
+ )
274
+ self._feature_setup_session = self._get_counter(
275
+ 'feature_setup_session',
276
+ description='Feature setup session counter',
277
+ parameters={
278
+ 'app': str,
279
+ 'environment_id': str,
280
+ 'image_id': str,
281
+ 'feature_name': str,
282
+ 'error': str,
283
+ }
284
+ )
285
+ self._feature_teardown_session = self._get_counter(
286
+ 'feature_teardown_session',
287
+ description='Feature teardown session counter',
288
+ parameters={
289
+ 'app': str,
290
+ 'environment_id': str,
291
+ 'image_id': str,
292
+ 'feature_name': str,
293
+ 'error': str,
294
+ }
295
+ )
296
+ self._feature_activity = self._get_counter(
297
+ 'feature_activity',
298
+ description='Feature activity counter',
299
+ parameters={
300
+ 'app': str,
301
+ 'environment_id': str,
302
+ 'image_id': str,
303
+ 'activity': str,
304
+ 'error': str,
305
+ }
306
+ )
307
+ self._feature_housekeep = self._get_counter(
308
+ 'feature_housekeep',
309
+ description='Feature housekeeping counter',
310
+ parameters={
311
+ 'app': str,
312
+ 'environment_id': str,
313
+ 'image_id': str,
314
+ 'feature_name': str,
315
+ 'error': str,
316
+ }
317
+ )
318
+
319
+ # Feature scalars.
320
+ self._feature_setup_duration_ms = self._get_distribution(
321
+ 'feature_setup_duration_ms',
322
+ description='Feature setup duration in milliseconds',
323
+ parameters={
324
+ 'app': str,
325
+ 'environment_id': str,
326
+ 'image_id': str,
327
+ 'feature_name': str,
328
+ 'error': str,
329
+ }
330
+ )
331
+ self._feature_teardown_duration_ms = self._get_distribution(
332
+ 'feature_teardown_duration_ms',
333
+ description='Feature teardown duration in milliseconds',
334
+ parameters={
335
+ 'app': str,
336
+ 'environment_id': str,
337
+ 'image_id': str,
338
+ 'feature_name': str,
339
+ 'error': str,
340
+ }
341
+ )
342
+ self._feature_setup_session_duration_ms = self._get_distribution(
343
+ 'feature_setup_session_duration_ms',
344
+ description='Feature setup session duration in milliseconds',
345
+ parameters={
346
+ 'app': str,
347
+ 'environment_id': str,
348
+ 'image_id': str,
349
+ 'feature_name': str,
350
+ 'error': str,
351
+ }
352
+ )
353
+ self._feature_teardown_session_duration_ms = self._get_distribution(
354
+ 'feature_teardown_session_duration_ms',
355
+ description='Feature teardown session duration in milliseconds',
356
+ parameters={
357
+ 'app': str,
358
+ 'environment_id': str,
359
+ 'image_id': str,
360
+ 'feature_name': str,
361
+ 'error': str,
362
+ }
363
+ )
364
+ self._feature_activity_duration_ms = self._get_distribution(
365
+ 'feature_activity_duration_ms',
366
+ description='Feature activity duration in milliseconds',
367
+ parameters={
368
+ 'app': str,
369
+ 'environment_id': str,
370
+ 'image_id': str,
371
+ 'activity': str,
372
+ 'error': str,
373
+ }
374
+ )
375
+ self._feature_housekeep_duration_ms = self._get_distribution(
376
+ 'feature_housekeep_duration_ms',
377
+ description='Feature housekeeping duration in milliseconds',
378
+ parameters={
379
+ 'app': str,
380
+ 'environment_id': str,
381
+ 'image_id': str,
382
+ 'feature_name': str,
383
+ 'error': str,
384
+ }
385
+ )
386
+
387
+ def on_environment_starting(
388
+ self,
389
+ environment: interface.Environment
390
+ ) -> None:
391
+ """Called when the environment is starting."""
392
+ self._initialize_metrics()
393
+
394
+ def on_environment_housekeep(
395
+ self,
396
+ environment: interface.Environment,
397
+ counter: int,
398
+ duration: float,
399
+ error: BaseException | None,
400
+ **kwargs
401
+ ) -> None:
402
+ """Called when the environment is housekeeping."""
403
+ self._environment_housekeep_duration_ms.record(
404
+ int(duration * 1000),
405
+ app=self.app,
406
+ environment_id=str(environment.id),
407
+ error=self._error_tag(error)
408
+ )
409
+
410
+ def on_sandbox_start(
411
+ self,
412
+ sandbox: interface.Sandbox,
413
+ duration: float,
414
+ error: BaseException | None
415
+ ) -> None:
416
+ self._sandbox_start.increment(
417
+ app=self.app,
418
+ environment_id=str(sandbox.environment.id),
419
+ image_id=sandbox.image_id,
420
+ error=self._error_tag(error)
421
+ )
422
+ self._sandbox_start_duration_ms.record(
423
+ int(duration * 1000),
424
+ app=self.app,
425
+ environment_id=str(sandbox.environment.id),
426
+ image_id=sandbox.image_id,
427
+ error=self._error_tag(error)
428
+ )
429
+
430
+ def on_sandbox_status_change(
431
+ self,
432
+ sandbox: interface.Sandbox,
433
+ old_status: interface.Sandbox.Status,
434
+ new_status: interface.Sandbox.Status,
435
+ span: float
436
+ ) -> None:
437
+ self._sandbox_status_duration_ms.record(
438
+ int(span * 1000),
439
+ app=self.app,
440
+ environment_id=str(sandbox.environment.id),
441
+ image_id=sandbox.image_id,
442
+ status=old_status.value
443
+ )
444
+ if old_status != interface.Sandbox.Status.CREATED:
445
+ self._sandbox_count.increment(
446
+ delta=-1,
447
+ app=self.app,
448
+ environment_id=str(sandbox.environment.id),
449
+ image_id=sandbox.image_id,
450
+ status=old_status.value
451
+ )
452
+ if new_status != interface.Sandbox.Status.OFFLINE:
453
+ self._sandbox_count.increment(
454
+ app=self.app,
455
+ environment_id=str(sandbox.environment.id),
456
+ image_id=sandbox.image_id,
457
+ status=new_status.value
458
+ )
459
+
460
+ def on_sandbox_shutdown(
461
+ self,
462
+ sandbox: interface.Sandbox,
463
+ duration: float,
464
+ lifetime: float,
465
+ error: BaseException | None
466
+ ) -> None:
467
+ self._sandbox_shutdown.increment(
468
+ app=self.app,
469
+ environment_id=str(sandbox.environment.id),
470
+ image_id=sandbox.image_id,
471
+ error=self._error_tag(error)
472
+ )
473
+ self._sandbox_shutdown_duration_ms.record(
474
+ int(duration * 1000),
475
+ app=self.app,
476
+ environment_id=str(sandbox.environment.id),
477
+ image_id=sandbox.image_id,
478
+ error=self._error_tag(error)
479
+ )
480
+ self._sandbox_lifetime_ms.record(
481
+ int(lifetime * 1000),
482
+ app=self.app,
483
+ environment_id=str(sandbox.environment.id),
484
+ image_id=sandbox.image_id,
485
+ error=self._error_tag(error)
486
+ )
487
+
488
+ def on_sandbox_session_start(
489
+ self,
490
+ sandbox: interface.Sandbox,
491
+ session_id: str,
492
+ duration: float,
493
+ error: BaseException | None
494
+ ) -> None:
495
+ """Called when a sandbox session starts."""
496
+ self._sandbox_session_start_duration_ms.record(
497
+ int(duration * 1000),
498
+ app=self.app,
499
+ environment_id=str(sandbox.environment.id),
500
+ image_id=sandbox.image_id,
501
+ error=self._error_tag(error)
502
+ )
503
+
504
+ def on_sandbox_session_end(
505
+ self,
506
+ sandbox: interface.Sandbox,
507
+ session_id: str,
508
+ duration: float,
509
+ lifetime: float,
510
+ error: BaseException | None
511
+ ) -> None:
512
+ """Called when a sandbox session ends."""
513
+ self._sandbox_session_end_duration_ms.record(
514
+ int(duration * 1000),
515
+ app=self.app,
516
+ environment_id=str(sandbox.environment.id),
517
+ image_id=sandbox.image_id,
518
+ error=self._error_tag(error)
519
+ )
520
+ self._sandbox_session_lifetime_ms.record(
521
+ int(lifetime * 1000),
522
+ app=self.app,
523
+ environment_id=str(sandbox.environment.id),
524
+ image_id=sandbox.image_id,
525
+ error=self._error_tag(error)
526
+ )
527
+
528
+ def on_sandbox_activity(
529
+ self,
530
+ name: str,
531
+ sandbox: interface.Sandbox,
532
+ session_id: str | None,
533
+ duration: float,
534
+ error: BaseException | None,
535
+ **kwargs
536
+ ) -> None:
537
+ """Called when a sandbox activity is performed."""
538
+ self._sandbox_activity.increment(
539
+ app=self.app,
540
+ environment_id=str(sandbox.environment.id),
541
+ image_id=sandbox.image_id,
542
+ activity=name,
543
+ error=self._error_tag(error)
544
+ )
545
+ self._sandbox_activity_duration_ms.record(
546
+ int(duration * 1000),
547
+ app=self.app,
548
+ environment_id=str(sandbox.environment.id),
549
+ image_id=sandbox.image_id,
550
+ activity=name,
551
+ error=self._error_tag(error)
552
+ )
553
+
554
+ def on_sandbox_housekeep(
555
+ self,
556
+ sandbox: interface.Sandbox,
557
+ counter: int,
558
+ duration: float,
559
+ error: BaseException | None,
560
+ **kwargs
561
+ ) -> None:
562
+ """Called when a sandbox feature is housekeeping."""
563
+ self._sandbox_housekeep.increment(
564
+ app=self.app,
565
+ environment_id=str(sandbox.environment.id),
566
+ image_id=sandbox.image_id,
567
+ error=self._error_tag(error)
568
+ )
569
+ self._sandbox_housekeep_duration_ms.record(
570
+ int(duration * 1000),
571
+ app=self.app,
572
+ environment_id=str(sandbox.environment.id),
573
+ image_id=sandbox.image_id,
574
+ error=self._error_tag(error)
575
+ )
576
+
577
+ def on_feature_setup(
578
+ self,
579
+ feature: interface.Feature,
580
+ duration: float,
581
+ error: BaseException | None
582
+ ) -> None:
583
+ """Called when a sandbox feature is setup."""
584
+ image_id = feature.sandbox.image_id if feature.sandbox else ''
585
+ self._feature_setup.increment(
586
+ app=self.app,
587
+ environment_id=str(feature.environment.id),
588
+ image_id=image_id,
589
+ feature_name=feature.name,
590
+ error=self._error_tag(error)
591
+ )
592
+ self._feature_setup_duration_ms.record(
593
+ int(duration * 1000),
594
+ app=self.app,
595
+ environment_id=str(feature.environment.id),
596
+ image_id=image_id,
597
+ feature_name=feature.name,
598
+ error=self._error_tag(error)
599
+ )
600
+
601
+ def on_feature_teardown(
602
+ self,
603
+ feature: interface.Feature,
604
+ duration: float,
605
+ error: BaseException | None
606
+ ) -> None:
607
+ """Called when a sandbox feature is teardown."""
608
+ image_id = feature.sandbox.image_id if feature.sandbox else ''
609
+ self._feature_teardown.increment(
610
+ app=self.app,
611
+ environment_id=str(feature.environment.id),
612
+ image_id=image_id,
613
+ feature_name=feature.name,
614
+ error=self._error_tag(error)
615
+ )
616
+ self._feature_teardown_duration_ms.record(
617
+ int(duration * 1000),
618
+ app=self.app,
619
+ environment_id=str(feature.environment.id),
620
+ image_id=image_id,
621
+ feature_name=feature.name,
622
+ error=self._error_tag(error)
623
+ )
624
+
625
+ def on_feature_setup_session(
626
+ self,
627
+ feature: interface.Feature,
628
+ session_id: str | None,
629
+ duration: float,
630
+ error: BaseException | None
631
+ ) -> None:
632
+ """Called when a sandbox feature is setup."""
633
+ image_id = feature.sandbox.image_id if feature.sandbox else ''
634
+ self._feature_setup_session.increment(
635
+ app=self.app,
636
+ environment_id=str(feature.environment.id),
637
+ image_id=image_id,
638
+ feature_name=feature.name,
639
+ error=self._error_tag(error)
640
+ )
641
+ self._feature_setup_session_duration_ms.record(
642
+ int(duration * 1000),
643
+ app=self.app,
644
+ environment_id=str(feature.environment.id),
645
+ image_id=image_id,
646
+ feature_name=feature.name,
647
+ error=self._error_tag(error)
648
+ )
649
+
650
+ def on_feature_teardown_session(
651
+ self,
652
+ feature: interface.Feature,
653
+ session_id: str,
654
+ duration: float,
655
+ error: BaseException | None
656
+ ) -> None:
657
+ """Called when a sandbox feature is teardown."""
658
+ image_id = feature.sandbox.image_id if feature.sandbox else ''
659
+ self._feature_teardown_session.increment(
660
+ app=self.app,
661
+ environment_id=str(feature.environment.id),
662
+ image_id=image_id,
663
+ feature_name=feature.name,
664
+ error=self._error_tag(error)
665
+ )
666
+ self._feature_teardown_session_duration_ms.record(
667
+ int(duration * 1000),
668
+ app=self.app,
669
+ environment_id=str(feature.environment.id),
670
+ image_id=image_id,
671
+ feature_name=feature.name,
672
+ error=self._error_tag(error)
673
+ )
674
+
675
+ def on_feature_activity(
676
+ self,
677
+ name: str,
678
+ feature: interface.Feature,
679
+ session_id: str | None,
680
+ duration: float,
681
+ error: BaseException | None,
682
+ **kwargs
683
+ ) -> None:
684
+ """Called when a feature activity is performed."""
685
+ image_id = feature.sandbox.image_id if feature.sandbox else ''
686
+ self._feature_activity.increment(
687
+ app=self.app,
688
+ environment_id=str(feature.environment.id),
689
+ image_id=image_id,
690
+ activity=name,
691
+ error=self._error_tag(error)
692
+ )
693
+ self._feature_activity_duration_ms.record(
694
+ int(duration * 1000),
695
+ app=self.app,
696
+ environment_id=str(feature.environment.id),
697
+ image_id=image_id,
698
+ activity=name,
699
+ error=self._error_tag(error)
700
+ )
701
+
702
+ def on_feature_housekeep(
703
+ self,
704
+ feature: interface.Feature,
705
+ counter: int,
706
+ duration: float,
707
+ error: BaseException | None,
708
+ **kwargs
709
+ ) -> None:
710
+ """Called when a sandbox feature is housekeeping."""
711
+ image_id = feature.sandbox.image_id if feature.sandbox else ''
712
+ self._feature_housekeep.increment(
713
+ app=self.app,
714
+ environment_id=str(feature.environment.id),
715
+ image_id=image_id,
716
+ feature_name=feature.name,
717
+ error=self._error_tag(error)
718
+ )
719
+ self._feature_housekeep_duration_ms.record(
720
+ int(duration * 1000),
721
+ app=self.app,
722
+ environment_id=str(feature.environment.id),
723
+ image_id=image_id,
724
+ feature_name=feature.name,
725
+ error=self._error_tag(error)
726
+ )