langfun 0.1.2.dev202509020804__py3-none-any.whl → 0.1.2.dev202511110805__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 langfun might be problematic. Click here for more details.

Files changed (133) hide show
  1. langfun/__init__.py +1 -1
  2. langfun/core/__init__.py +6 -1
  3. langfun/core/agentic/__init__.py +4 -0
  4. langfun/core/agentic/action.py +412 -103
  5. langfun/core/agentic/action_eval.py +9 -2
  6. langfun/core/agentic/action_test.py +68 -6
  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 +9 -2
  20. langfun/core/data/conversion/gemini_test.py +12 -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 +47 -43
  24. langfun/core/eval/base_test.py +4 -4
  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 +1 -0
  29. langfun/core/eval/v2/checkpointing.py +30 -4
  30. langfun/core/eval/v2/eval_test_helper.py +1 -1
  31. langfun/core/eval/v2/evaluation.py +60 -14
  32. langfun/core/eval/v2/example.py +22 -11
  33. langfun/core/eval/v2/experiment.py +51 -8
  34. langfun/core/eval/v2/metric_values.py +31 -3
  35. langfun/core/eval/v2/metric_values_test.py +32 -0
  36. langfun/core/eval/v2/metrics.py +39 -4
  37. langfun/core/eval/v2/metrics_test.py +14 -0
  38. langfun/core/eval/v2/progress.py +30 -1
  39. langfun/core/eval/v2/progress_test.py +27 -0
  40. langfun/core/eval/v2/progress_tracking_test.py +6 -0
  41. langfun/core/eval/v2/reporting.py +90 -71
  42. langfun/core/eval/v2/reporting_test.py +20 -6
  43. langfun/core/eval/v2/runners.py +27 -7
  44. langfun/core/eval/v2/runners_test.py +3 -0
  45. langfun/core/langfunc.py +45 -130
  46. langfun/core/langfunc_test.py +6 -4
  47. langfun/core/language_model.py +151 -31
  48. langfun/core/language_model_test.py +9 -3
  49. langfun/core/llms/__init__.py +12 -1
  50. langfun/core/llms/anthropic.py +157 -2
  51. langfun/core/llms/azure_openai.py +29 -17
  52. langfun/core/llms/cache/base.py +25 -3
  53. langfun/core/llms/cache/in_memory.py +48 -7
  54. langfun/core/llms/cache/in_memory_test.py +14 -4
  55. langfun/core/llms/compositional.py +25 -1
  56. langfun/core/llms/deepseek.py +30 -2
  57. langfun/core/llms/fake.py +39 -1
  58. langfun/core/llms/fake_test.py +9 -0
  59. langfun/core/llms/gemini.py +43 -7
  60. langfun/core/llms/google_genai.py +34 -1
  61. langfun/core/llms/groq.py +28 -3
  62. langfun/core/llms/llama_cpp.py +23 -4
  63. langfun/core/llms/openai.py +93 -3
  64. langfun/core/llms/openai_compatible.py +148 -27
  65. langfun/core/llms/openai_compatible_test.py +207 -20
  66. langfun/core/llms/openai_test.py +0 -2
  67. langfun/core/llms/rest.py +16 -1
  68. langfun/core/llms/vertexai.py +59 -8
  69. langfun/core/logging.py +1 -1
  70. langfun/core/mcp/__init__.py +10 -0
  71. langfun/core/mcp/client.py +177 -0
  72. langfun/core/mcp/client_test.py +71 -0
  73. langfun/core/mcp/session.py +241 -0
  74. langfun/core/mcp/session_test.py +54 -0
  75. langfun/core/mcp/testing/simple_mcp_client.py +33 -0
  76. langfun/core/mcp/testing/simple_mcp_server.py +33 -0
  77. langfun/core/mcp/tool.py +256 -0
  78. langfun/core/mcp/tool_test.py +197 -0
  79. langfun/core/memory.py +1 -0
  80. langfun/core/message.py +160 -55
  81. langfun/core/message_test.py +65 -81
  82. langfun/core/modalities/__init__.py +8 -0
  83. langfun/core/modalities/audio.py +21 -1
  84. langfun/core/modalities/image.py +19 -1
  85. langfun/core/modalities/mime.py +62 -3
  86. langfun/core/modalities/pdf.py +19 -1
  87. langfun/core/modalities/video.py +21 -1
  88. langfun/core/modality.py +167 -29
  89. langfun/core/modality_test.py +42 -12
  90. langfun/core/natural_language.py +1 -1
  91. langfun/core/sampling.py +4 -4
  92. langfun/core/sampling_test.py +20 -4
  93. langfun/core/structured/completion.py +34 -44
  94. langfun/core/structured/completion_test.py +23 -43
  95. langfun/core/structured/description.py +54 -50
  96. langfun/core/structured/function_generation.py +29 -12
  97. langfun/core/structured/mapping.py +74 -28
  98. langfun/core/structured/parsing.py +90 -74
  99. langfun/core/structured/parsing_test.py +0 -3
  100. langfun/core/structured/querying.py +242 -156
  101. langfun/core/structured/querying_test.py +95 -64
  102. langfun/core/structured/schema.py +70 -10
  103. langfun/core/structured/schema_generation.py +33 -14
  104. langfun/core/structured/scoring.py +45 -34
  105. langfun/core/structured/tokenization.py +24 -9
  106. langfun/core/subscription.py +2 -2
  107. langfun/core/template.py +175 -50
  108. langfun/core/template_test.py +123 -17
  109. langfun/env/__init__.py +43 -0
  110. langfun/env/base_environment.py +827 -0
  111. langfun/env/base_environment_test.py +473 -0
  112. langfun/env/base_feature.py +304 -0
  113. langfun/env/base_feature_test.py +228 -0
  114. langfun/env/base_sandbox.py +842 -0
  115. langfun/env/base_sandbox_test.py +1235 -0
  116. langfun/env/event_handlers/__init__.py +14 -0
  117. langfun/env/event_handlers/chain.py +233 -0
  118. langfun/env/event_handlers/chain_test.py +253 -0
  119. langfun/env/event_handlers/event_logger.py +472 -0
  120. langfun/env/event_handlers/event_logger_test.py +304 -0
  121. langfun/env/event_handlers/metric_writer.py +726 -0
  122. langfun/env/event_handlers/metric_writer_test.py +214 -0
  123. langfun/env/interface.py +1640 -0
  124. langfun/env/interface_test.py +151 -0
  125. langfun/env/load_balancers.py +59 -0
  126. langfun/env/load_balancers_test.py +139 -0
  127. langfun/env/test_utils.py +497 -0
  128. {langfun-0.1.2.dev202509020804.dist-info → langfun-0.1.2.dev202511110805.dist-info}/METADATA +7 -3
  129. langfun-0.1.2.dev202511110805.dist-info/RECORD +200 -0
  130. langfun-0.1.2.dev202509020804.dist-info/RECORD +0 -172
  131. {langfun-0.1.2.dev202509020804.dist-info → langfun-0.1.2.dev202511110805.dist-info}/WHEEL +0 -0
  132. {langfun-0.1.2.dev202509020804.dist-info → langfun-0.1.2.dev202511110805.dist-info}/licenses/LICENSE +0 -0
  133. {langfun-0.1.2.dev202509020804.dist-info → langfun-0.1.2.dev202511110805.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,497 @@
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
+ """Test utils for base environment."""
15
+
16
+ import contextlib
17
+ import time
18
+ from typing import Iterator, Type
19
+
20
+ from langfun.env import base_environment
21
+ from langfun.env import base_feature
22
+ from langfun.env import base_sandbox
23
+ from langfun.env import interface
24
+ import pyglove as pg
25
+
26
+
27
+ class TestingEnvironment(base_environment.BaseEnvironment):
28
+ """Testing environment for unit tests."""
29
+ image_ids: list[str] = ['test_image']
30
+ housekeep_interval: float = 0.0
31
+ simulate_start_error: Type[BaseException] | None = None
32
+ simulate_shutdown_error: Type[BaseException] | None = None
33
+ simulate_ping_error: Type[BaseException] | None = None
34
+ offline: bool = False
35
+
36
+ @property
37
+ def id(self) -> interface.Environment.Id:
38
+ return interface.Environment.Id('testing-env')
39
+
40
+ def wait_for_housekeeping(self):
41
+ housekeep_counter = self.housekeep_counter
42
+ while self.housekeep_counter == housekeep_counter:
43
+ time.sleep(0.01)
44
+
45
+ def _create_sandbox(
46
+ self,
47
+ image_id: str,
48
+ sandbox_id: str,
49
+ reusable: bool,
50
+ proactive_session_setup: bool,
51
+ keepalive_interval: float | None,
52
+ ) -> base_sandbox.BaseSandbox:
53
+ return TestingSandbox(
54
+ environment=self,
55
+ id=interface.Sandbox.Id(
56
+ environment_id=self.id,
57
+ image_id=image_id,
58
+ sandbox_id=sandbox_id
59
+ ),
60
+ image_id=image_id,
61
+ reusable=reusable,
62
+ proactive_session_setup=proactive_session_setup,
63
+ keepalive_interval=keepalive_interval,
64
+ simulate_start_error=self.simulate_start_error,
65
+ simulate_shutdown_error=self.simulate_shutdown_error,
66
+ simulate_ping_error=self.simulate_ping_error,
67
+ )
68
+
69
+
70
+ class TestingSandbox(base_sandbox.BaseSandbox):
71
+ """Testing sandbox for unit tests."""
72
+
73
+ simulate_start_error: Type[BaseException] | None = None
74
+ simulate_shutdown_error: Type[BaseException] | None = None
75
+ simulate_ping_error: Type[BaseException] | None = None
76
+
77
+ def _on_bound(self) -> None:
78
+ super()._on_bound()
79
+ self._shell_history = []
80
+ self._ping_history = []
81
+
82
+ def _raise_error(self, message, error_type: Type[BaseException], **kwargs):
83
+ if (error_type is interface.SandboxStateError or
84
+ issubclass(error_type, interface.SandboxStateError)):
85
+ kwargs['sandbox'] = self
86
+ raise error_type(message, **kwargs)
87
+ else:
88
+ raise error_type(message)
89
+
90
+ def wait_until_not(
91
+ self,
92
+ status: interface.Sandbox.Status | tuple[interface.Sandbox.Status, ...]
93
+ ) -> None:
94
+ if not isinstance(status, tuple):
95
+ status = (status,)
96
+ while self.status in status:
97
+ time.sleep(0.01)
98
+
99
+ def wait_until_next_housekeep(self) -> None:
100
+ housekeep_counter = self.housekeep_counter
101
+ while self.housekeep_counter == housekeep_counter:
102
+ time.sleep(0.01)
103
+
104
+ def _start(self) -> None:
105
+ if self.simulate_start_error:
106
+ self._raise_error('Sandbox start error', self.simulate_start_error)
107
+ super()._start()
108
+
109
+ def _shutdown(self) -> None:
110
+ if self.simulate_shutdown_error:
111
+ self._raise_error('Sandbox shutdown error', self.simulate_shutdown_error)
112
+ super()._shutdown()
113
+
114
+ @interface.treat_as_sandbox_state_error(errors=(RuntimeError,))
115
+ @interface.log_activity()
116
+ def shell(
117
+ self,
118
+ code: str,
119
+ raise_error: Type[BaseException] | None = None,
120
+ ) -> str:
121
+ self._shell_history.append(code)
122
+ if raise_error is not None:
123
+ self._raise_error(f'shell "{code}" failed', raise_error)
124
+ return f'shell "{code}" succeeded'
125
+
126
+ def _ping(self) -> None:
127
+ self._ping_history.append(not self.simulate_ping_error)
128
+ if self.simulate_ping_error:
129
+ self._raise_error('Ping error', self.simulate_ping_error, code='ping')
130
+
131
+
132
+ class TestingFeature(base_feature.BaseFeature):
133
+ """Testing feature for unit tests."""
134
+
135
+ housekeep_interval = 0
136
+ setup_session_delay: float = 0.0
137
+ simulate_housekeep_error: Type[BaseException] | None = None
138
+ simulate_setup_error: Type[BaseException] | None = None
139
+ simulate_teardown_error: Type[BaseException] | None = None
140
+ simulate_setup_session_error: Type[BaseException] | None = None
141
+ simulate_teardown_session_error: Type[BaseException] | None = None
142
+ call_end_session_on_teardown_session: bool = False
143
+
144
+ class Service:
145
+ """Sandbox."""
146
+
147
+ def __init__(self, sandbox: interface.Sandbox):
148
+ self._sandbox = sandbox
149
+
150
+ def do(self, code: str, raise_error: Type[BaseException] | None = None):
151
+ self._sandbox.shell(code, raise_error=raise_error)
152
+
153
+ def _raise_error(self, message, error_type: Type[BaseException], **kwargs):
154
+ self._sandbox._raise_error(message, error_type, **kwargs) # pylint: disable=protected-access
155
+
156
+ def _setup(self) -> None:
157
+ if self.simulate_setup_error:
158
+ self._raise_error(f'{self.name} setup error', self.simulate_setup_error)
159
+ self.sandbox.shell(f'"{self.name}" setup')
160
+
161
+ def _teardown(self) -> None:
162
+ if self.simulate_teardown_error:
163
+ self._raise_error(
164
+ f'{self.name} teardown error', self.simulate_teardown_error
165
+ )
166
+ self.sandbox.shell(f'"{self.name}" teardown')
167
+
168
+ def _setup_session(self) -> None:
169
+ if self.setup_session_delay > 0:
170
+ time.sleep(self.setup_session_delay)
171
+
172
+ if self.simulate_setup_session_error:
173
+ self._raise_error(
174
+ 'Feature session setup error', self.simulate_setup_session_error
175
+ )
176
+ self.sandbox.shell(f'"{self.name}" setup session')
177
+
178
+ def _teardown_session(self) -> None:
179
+ if self.simulate_teardown_session_error:
180
+ self._raise_error(
181
+ 'Feature session teardown error', self.simulate_teardown_session_error
182
+ )
183
+ self.sandbox.shell(f'"{self.name}" teardown session')
184
+ if self.call_end_session_on_teardown_session:
185
+ self.sandbox.end_session()
186
+
187
+ @interface.log_activity()
188
+ def num_shell_calls(self) -> int:
189
+ return len(self.sandbox._shell_history) # pylint: disable=protected-access
190
+
191
+ @interface.log_activity()
192
+ def bad_shell_call(self) -> None:
193
+ self.sandbox.shell('bad command', raise_error=RuntimeError)
194
+
195
+ @interface.log_activity()
196
+ def show_session_id(self):
197
+ return self.session_id
198
+
199
+ @interface.log_activity()
200
+ def call_with_varargs(self, code: str, *args, **kwargs):
201
+ del code, args, kwargs
202
+ return 0
203
+
204
+ def _on_bound(self) -> None:
205
+ super()._on_bound()
206
+ self._service = None
207
+
208
+ @contextlib.contextmanager
209
+ def my_service(self) -> Iterator[Service]:
210
+ try:
211
+ self._service = TestingFeature.Service(sandbox=self.sandbox)
212
+ yield self._service
213
+ finally:
214
+ self._service = None
215
+
216
+ def _housekeep(self) -> None:
217
+ if self.simulate_housekeep_error:
218
+ raise interface.SandboxStateError(
219
+ 'House keeping error', sandbox=self.sandbox
220
+ )
221
+
222
+
223
+ class TestingNonSandboxBasedFeature(base_feature.BaseFeature):
224
+ """Testing non-sandbox based feature for unit tests."""
225
+ is_sandbox_based: bool = False
226
+ simulate_setup_error: Type[BaseException] | None = None
227
+ simulate_teardown_error: Type[BaseException] | None = None
228
+ simulate_setup_session_error: Type[BaseException] | None = None
229
+ simulate_teardown_session_error: Type[BaseException] | None = None
230
+ simulate_housekeep_error: Type[BaseException] | None = None
231
+
232
+ def _setup(self) -> None:
233
+ if self.simulate_setup_error:
234
+ raise self.simulate_setup_error('Feature setup error')
235
+
236
+ def _teardown(self) -> None:
237
+ if self.simulate_teardown_error:
238
+ raise self.simulate_teardown_error('Feature teardown error')
239
+
240
+ def _setup_session(self) -> None:
241
+ if self.simulate_setup_session_error:
242
+ raise self.simulate_setup_session_error('Feature session setup error')
243
+
244
+ def _teardown_session(self) -> None:
245
+ if self.simulate_teardown_session_error:
246
+ raise self.simulate_teardown_session_error(
247
+ 'Feature session teardown error'
248
+ )
249
+
250
+ def _housekeep(self) -> None:
251
+ if self.simulate_housekeep_error:
252
+ raise self.simulate_housekeep_error('Feature housekeeping error')
253
+ _ = self.foo(1)
254
+
255
+ @interface.log_activity()
256
+ def foo(self, x: int) -> int:
257
+ return x + 1
258
+
259
+
260
+ class TestingEventHandler(pg.Object, interface.EventHandler):
261
+ """Testing environment event handler for unit tests."""
262
+
263
+ log_sandbox_status: bool = False
264
+ log_feature_setup: bool = True
265
+ log_session_setup: bool = False
266
+ log_housekeep: bool = False
267
+
268
+ def _on_bound(self) -> None:
269
+ super()._on_bound()
270
+ self._logs = []
271
+
272
+ @property
273
+ def logs(self) -> list[str]:
274
+ return self._logs
275
+
276
+ def _add_message(self, message: str, error: BaseException | None) -> None:
277
+ """Adds a message to the history."""
278
+ if error is None:
279
+ self._logs.append(message)
280
+ else:
281
+ self._logs.append(f'{message} with {error.__class__.__name__}')
282
+
283
+ def on_environment_start(
284
+ self,
285
+ environment: interface.Environment,
286
+ duration: float,
287
+ error: BaseException | None
288
+ ) -> None:
289
+ """Called when the environment is started."""
290
+ assert duration > 0
291
+ self._add_message(f'[{environment.id}] environment started', error)
292
+
293
+ def on_environment_housekeep(
294
+ self,
295
+ environment: interface.Environment,
296
+ counter: int,
297
+ duration: float,
298
+ error: BaseException | None,
299
+ **kwargs
300
+ ) -> None:
301
+ """Called when the environment finishes a round of housekeeping."""
302
+ assert duration > 0
303
+ if self.log_housekeep:
304
+ self._add_message(
305
+ f'[{environment.id}] environment housekeeping {counter}', error
306
+ )
307
+
308
+ def on_environment_shutdown(
309
+ self,
310
+ environment: interface.Environment,
311
+ duration: float,
312
+ lifetime: float,
313
+ error: BaseException | None
314
+ ) -> None:
315
+ """Called when the environment is shutdown."""
316
+ assert duration > 0 and lifetime is not None
317
+ self._add_message(f'[{environment.id}] environment shutdown', error)
318
+
319
+ def on_sandbox_start(
320
+ self,
321
+ sandbox: interface.Sandbox,
322
+ duration: float,
323
+ error: BaseException | None
324
+ ) -> None:
325
+ assert duration > 0
326
+ self._add_message(f'[{sandbox.id}] sandbox started', error)
327
+
328
+ def on_sandbox_status_change(
329
+ self,
330
+ sandbox: interface.Sandbox,
331
+ old_status: interface.Sandbox.Status,
332
+ new_status: interface.Sandbox.Status,
333
+ span: float
334
+ ) -> None:
335
+ assert span > 0
336
+ if self.log_sandbox_status:
337
+ self._add_message(
338
+ f'[{sandbox.id}] {old_status.value} -> {new_status.value}',
339
+ None,
340
+ )
341
+
342
+ def on_sandbox_shutdown(
343
+ self,
344
+ sandbox: interface.Sandbox,
345
+ duration: float,
346
+ lifetime: float,
347
+ error: BaseException | None
348
+ ) -> None:
349
+ assert duration > 0 and lifetime is not None
350
+ self._add_message(f'[{sandbox.id}] sandbox shutdown', error)
351
+
352
+ def on_sandbox_session_start(
353
+ self,
354
+ sandbox: interface.Sandbox,
355
+ session_id: str,
356
+ duration: float,
357
+ error: BaseException | None
358
+ ) -> None:
359
+ """Called when a sandbox session starts."""
360
+ assert duration > 0
361
+ self._add_message(
362
+ f'[{sandbox.id}] session {session_id!r} started', error
363
+ )
364
+
365
+ def on_sandbox_session_end(
366
+ self,
367
+ sandbox: interface.Sandbox,
368
+ session_id: str,
369
+ duration: float,
370
+ lifetime: float,
371
+ error: BaseException | None
372
+ ) -> None:
373
+ """Called when a sandbox session ends."""
374
+ assert duration > 0 and lifetime > 0
375
+ self._add_message(
376
+ f'[{sandbox.id}] session {session_id!r} ended', error
377
+ )
378
+
379
+ def on_sandbox_activity(
380
+ self,
381
+ name: str,
382
+ sandbox: interface.Sandbox,
383
+ session_id: str | None,
384
+ duration: float,
385
+ error: BaseException | None,
386
+ *,
387
+ code: str | None = None,
388
+ **kwargs
389
+ ) -> None:
390
+ """Called when a sandbox activity is performed."""
391
+ del kwargs
392
+ log_id = f'{sandbox.id}@{session_id or "<idle>"}'
393
+ self._add_message(
394
+ f'[{log_id}] {name}: {code}', error
395
+ )
396
+
397
+ def on_sandbox_housekeep(
398
+ self,
399
+ sandbox: interface.Sandbox,
400
+ counter: int,
401
+ duration: float,
402
+ error: BaseException | None,
403
+ **kwargs
404
+ ) -> None:
405
+ assert duration > 0
406
+ if self.log_housekeep:
407
+ self._add_message(
408
+ f'[{sandbox.id}] sandbox housekeeping {counter}', error
409
+ )
410
+
411
+ def on_feature_setup(
412
+ self,
413
+ feature: interface.Feature,
414
+ duration: float,
415
+ error: BaseException | None
416
+ ) -> None:
417
+ """Called when a sandbox feature is setup."""
418
+ assert duration > 0
419
+ if self.log_feature_setup:
420
+ self._add_message(
421
+ f'[{feature.id}] feature setup', error
422
+ )
423
+
424
+ def on_feature_teardown(
425
+ self,
426
+ feature: interface.Feature,
427
+ duration: float,
428
+ error: BaseException | None
429
+ ) -> None:
430
+ """Called when a sandbox feature is teardown."""
431
+ assert duration > 0
432
+ if self.log_feature_setup:
433
+ self._add_message(
434
+ f'[{feature.id}] feature teardown', error
435
+ )
436
+
437
+ def on_feature_setup_session(
438
+ self,
439
+ feature: interface.Feature,
440
+ session_id: str | None,
441
+ duration: float,
442
+ error: BaseException | None
443
+ ) -> None:
444
+ """Called when a sandbox feature is setup."""
445
+ assert duration > 0
446
+ if self.log_session_setup:
447
+ self._add_message(
448
+ f'[{feature.id}@{session_id or "<idle>"}] feature setup session',
449
+ error
450
+ )
451
+
452
+ def on_feature_teardown_session(
453
+ self,
454
+ feature: interface.Feature,
455
+ session_id: str,
456
+ duration: float,
457
+ error: BaseException | None
458
+ ) -> None:
459
+ """Called when a sandbox feature is teardown."""
460
+ assert duration > 0
461
+ if self.log_session_setup:
462
+ self._add_message(
463
+ f'[{feature.id}@{session_id}] feature teardown session', error
464
+ )
465
+
466
+ def on_feature_activity(
467
+ self,
468
+ name: str,
469
+ feature: interface.Feature,
470
+ session_id: str | None,
471
+ duration: float,
472
+ error: BaseException | None,
473
+ *,
474
+ code: str | None = None,
475
+ **kwargs
476
+ ) -> None:
477
+ """Called when a sandbox activity is performed."""
478
+ del kwargs
479
+ log_id = f'{feature.id}@{session_id or "<idle>"}'
480
+ self._add_message(
481
+ f'[{log_id}] {name}: {code}', error
482
+ )
483
+
484
+ def on_feature_housekeep(
485
+ self,
486
+ feature: interface.Feature,
487
+ counter: int,
488
+ duration: float,
489
+ error: BaseException | None,
490
+ **kwargs
491
+ ) -> None:
492
+ """Called when a sandbox feature is housekeeping."""
493
+ assert duration > 0
494
+ if self.log_housekeep:
495
+ self._add_message(
496
+ f'[{feature.id}] feature housekeeping {counter}', error
497
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202509020804
3
+ Version: 0.1.2.dev202511110805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -21,14 +21,18 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Topic :: Software Development :: Libraries
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
+ Requires-Dist: anyio>=4.7.0
24
25
  Requires-Dist: jinja2>=3.1.2
26
+ Requires-Dist: mcp>=1.17.0
25
27
  Requires-Dist: puremagic>=1.20
26
- Requires-Dist: pyglove>=0.4.5.dev202507140812
28
+ Requires-Dist: pyglove>=0.5.0.dev202510170226
27
29
  Requires-Dist: requests>=2.31.0
28
30
  Provides-Extra: all
31
+ Requires-Dist: anyio>=4.7.0; extra == "all"
29
32
  Requires-Dist: jinja2>=3.1.2; extra == "all"
33
+ Requires-Dist: mcp>=1.17.0; extra == "all"
30
34
  Requires-Dist: puremagic>=1.20; extra == "all"
31
- Requires-Dist: pyglove>=0.4.5.dev202507140812; extra == "all"
35
+ Requires-Dist: pyglove>=0.5.0.dev202510170226; extra == "all"
32
36
  Requires-Dist: requests>=2.31.0; extra == "all"
33
37
  Requires-Dist: google-auth>=2.16.0; extra == "all"
34
38
  Requires-Dist: pillow>=10.0.0; extra == "all"