langfun 0.1.2.dev202510230805__py3-none-any.whl → 0.1.2.dev202511270805__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 (155) hide show
  1. langfun/core/__init__.py +2 -0
  2. langfun/core/agentic/__init__.py +4 -1
  3. langfun/core/agentic/action.py +447 -29
  4. langfun/core/agentic/action_eval.py +9 -2
  5. langfun/core/agentic/action_test.py +149 -21
  6. langfun/core/async_support.py +32 -3
  7. langfun/core/coding/python/correction.py +19 -9
  8. langfun/core/coding/python/execution.py +14 -12
  9. langfun/core/coding/python/generation.py +21 -16
  10. langfun/core/coding/python/sandboxing.py +23 -3
  11. langfun/core/component.py +42 -3
  12. langfun/core/concurrent.py +70 -6
  13. langfun/core/concurrent_test.py +1 -0
  14. langfun/core/console.py +1 -1
  15. langfun/core/data/conversion/anthropic.py +12 -3
  16. langfun/core/data/conversion/anthropic_test.py +8 -6
  17. langfun/core/data/conversion/gemini.py +9 -2
  18. langfun/core/data/conversion/gemini_test.py +12 -9
  19. langfun/core/data/conversion/openai.py +145 -31
  20. langfun/core/data/conversion/openai_test.py +161 -17
  21. langfun/core/eval/base.py +47 -43
  22. langfun/core/eval/base_test.py +5 -5
  23. langfun/core/eval/matching.py +5 -2
  24. langfun/core/eval/patching.py +3 -3
  25. langfun/core/eval/scoring.py +4 -3
  26. langfun/core/eval/v2/__init__.py +1 -0
  27. langfun/core/eval/v2/checkpointing.py +64 -6
  28. langfun/core/eval/v2/checkpointing_test.py +9 -2
  29. langfun/core/eval/v2/eval_test_helper.py +103 -2
  30. langfun/core/eval/v2/evaluation.py +91 -16
  31. langfun/core/eval/v2/evaluation_test.py +9 -3
  32. langfun/core/eval/v2/example.py +50 -40
  33. langfun/core/eval/v2/example_test.py +16 -8
  34. langfun/core/eval/v2/experiment.py +74 -8
  35. langfun/core/eval/v2/experiment_test.py +19 -0
  36. langfun/core/eval/v2/metric_values.py +31 -3
  37. langfun/core/eval/v2/metric_values_test.py +32 -0
  38. langfun/core/eval/v2/metrics.py +157 -44
  39. langfun/core/eval/v2/metrics_test.py +39 -18
  40. langfun/core/eval/v2/progress.py +30 -1
  41. langfun/core/eval/v2/progress_test.py +27 -0
  42. langfun/core/eval/v2/progress_tracking.py +12 -3
  43. langfun/core/eval/v2/progress_tracking_test.py +6 -1
  44. langfun/core/eval/v2/reporting.py +90 -71
  45. langfun/core/eval/v2/reporting_test.py +24 -6
  46. langfun/core/eval/v2/runners/__init__.py +30 -0
  47. langfun/core/eval/v2/{runners.py → runners/base.py} +59 -142
  48. langfun/core/eval/v2/runners/beam.py +341 -0
  49. langfun/core/eval/v2/runners/beam_test.py +131 -0
  50. langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
  51. langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -0
  52. langfun/core/eval/v2/runners/debug.py +40 -0
  53. langfun/core/eval/v2/runners/debug_test.py +76 -0
  54. langfun/core/eval/v2/runners/parallel.py +100 -0
  55. langfun/core/eval/v2/runners/parallel_test.py +95 -0
  56. langfun/core/eval/v2/runners/sequential.py +47 -0
  57. langfun/core/eval/v2/runners/sequential_test.py +172 -0
  58. langfun/core/langfunc.py +45 -130
  59. langfun/core/langfunc_test.py +7 -5
  60. langfun/core/language_model.py +141 -21
  61. langfun/core/language_model_test.py +54 -3
  62. langfun/core/llms/__init__.py +9 -1
  63. langfun/core/llms/anthropic.py +157 -2
  64. langfun/core/llms/azure_openai.py +29 -17
  65. langfun/core/llms/cache/base.py +25 -3
  66. langfun/core/llms/cache/in_memory.py +48 -7
  67. langfun/core/llms/cache/in_memory_test.py +14 -4
  68. langfun/core/llms/compositional.py +25 -1
  69. langfun/core/llms/deepseek.py +30 -2
  70. langfun/core/llms/fake.py +32 -1
  71. langfun/core/llms/gemini.py +55 -17
  72. langfun/core/llms/gemini_test.py +84 -0
  73. langfun/core/llms/google_genai.py +34 -1
  74. langfun/core/llms/groq.py +28 -3
  75. langfun/core/llms/llama_cpp.py +23 -4
  76. langfun/core/llms/openai.py +36 -3
  77. langfun/core/llms/openai_compatible.py +148 -27
  78. langfun/core/llms/openai_compatible_test.py +207 -20
  79. langfun/core/llms/openai_test.py +0 -2
  80. langfun/core/llms/rest.py +12 -1
  81. langfun/core/llms/vertexai.py +58 -8
  82. langfun/core/logging.py +1 -1
  83. langfun/core/mcp/client.py +77 -22
  84. langfun/core/mcp/client_test.py +8 -35
  85. langfun/core/mcp/session.py +94 -29
  86. langfun/core/mcp/session_test.py +54 -0
  87. langfun/core/mcp/tool.py +151 -22
  88. langfun/core/mcp/tool_test.py +197 -0
  89. langfun/core/memory.py +1 -0
  90. langfun/core/message.py +160 -55
  91. langfun/core/message_test.py +65 -81
  92. langfun/core/modalities/__init__.py +8 -0
  93. langfun/core/modalities/audio.py +21 -1
  94. langfun/core/modalities/image.py +19 -1
  95. langfun/core/modalities/mime.py +64 -3
  96. langfun/core/modalities/mime_test.py +11 -0
  97. langfun/core/modalities/pdf.py +19 -1
  98. langfun/core/modalities/video.py +21 -1
  99. langfun/core/modality.py +167 -29
  100. langfun/core/modality_test.py +42 -12
  101. langfun/core/natural_language.py +1 -1
  102. langfun/core/sampling.py +4 -4
  103. langfun/core/sampling_test.py +20 -4
  104. langfun/core/structured/__init__.py +2 -24
  105. langfun/core/structured/completion.py +34 -44
  106. langfun/core/structured/completion_test.py +23 -43
  107. langfun/core/structured/description.py +54 -50
  108. langfun/core/structured/function_generation.py +29 -12
  109. langfun/core/structured/mapping.py +81 -37
  110. langfun/core/structured/parsing.py +95 -79
  111. langfun/core/structured/parsing_test.py +0 -3
  112. langfun/core/structured/querying.py +215 -142
  113. langfun/core/structured/querying_test.py +65 -29
  114. langfun/core/structured/schema/__init__.py +49 -0
  115. langfun/core/structured/schema/base.py +664 -0
  116. langfun/core/structured/schema/base_test.py +531 -0
  117. langfun/core/structured/schema/json.py +174 -0
  118. langfun/core/structured/schema/json_test.py +121 -0
  119. langfun/core/structured/schema/python.py +316 -0
  120. langfun/core/structured/schema/python_test.py +410 -0
  121. langfun/core/structured/schema_generation.py +33 -14
  122. langfun/core/structured/scoring.py +47 -36
  123. langfun/core/structured/tokenization.py +26 -11
  124. langfun/core/subscription.py +2 -2
  125. langfun/core/template.py +174 -49
  126. langfun/core/template_test.py +123 -17
  127. langfun/env/__init__.py +8 -2
  128. langfun/env/base_environment.py +320 -128
  129. langfun/env/base_environment_test.py +473 -0
  130. langfun/env/base_feature.py +92 -15
  131. langfun/env/base_feature_test.py +228 -0
  132. langfun/env/base_sandbox.py +84 -361
  133. langfun/env/base_sandbox_test.py +1235 -0
  134. langfun/env/event_handlers/__init__.py +1 -1
  135. langfun/env/event_handlers/chain.py +233 -0
  136. langfun/env/event_handlers/chain_test.py +253 -0
  137. langfun/env/event_handlers/event_logger.py +95 -98
  138. langfun/env/event_handlers/event_logger_test.py +21 -21
  139. langfun/env/event_handlers/metric_writer.py +225 -140
  140. langfun/env/event_handlers/metric_writer_test.py +23 -6
  141. langfun/env/interface.py +854 -40
  142. langfun/env/interface_test.py +112 -2
  143. langfun/env/load_balancers_test.py +23 -2
  144. langfun/env/test_utils.py +126 -84
  145. {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/METADATA +1 -1
  146. langfun-0.1.2.dev202511270805.dist-info/RECORD +215 -0
  147. langfun/core/eval/v2/runners_test.py +0 -343
  148. langfun/core/structured/schema.py +0 -987
  149. langfun/core/structured/schema_test.py +0 -982
  150. langfun/env/base_test.py +0 -1481
  151. langfun/env/event_handlers/base.py +0 -350
  152. langfun-0.1.2.dev202510230805.dist-info/RECORD +0 -195
  153. {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/WHEEL +0 -0
  154. {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/licenses/LICENSE +0 -0
  155. {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,8 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import contextlib
15
+ from typing import Iterator
14
16
  import unittest
15
17
  from langfun.env import interface
16
18
 
@@ -29,15 +31,123 @@ class IdTest(unittest.TestCase):
29
31
  def test_sandbox_id(self):
30
32
  sandbox_id = interface.Sandbox.Id(
31
33
  environment_id=interface.Environment.Id('env'),
34
+ image_id='image:2025_01_01_00_00_00',
32
35
  sandbox_id='sandbox'
33
36
  )
34
- self.assertEqual(str(sandbox_id), 'env/sandbox')
37
+ self.assertEqual(str(sandbox_id), 'env/image:2025_01_01_00_00_00:sandbox')
35
38
  self.assertEqual(
36
39
  sandbox_id.working_dir(root_dir='/tmp'),
37
- '/tmp/env/sandbox'
40
+ '/tmp/env/image_2025_01_01_00_00_00/sandbox'
38
41
  )
39
42
  self.assertIsNone(sandbox_id.working_dir(root_dir=None))
40
43
 
44
+ def test_feature_id(self):
45
+ # For non-sandboxed feature.
46
+ feature_id = interface.Feature.Id(
47
+ container_id=interface.Environment.Id('env'),
48
+ feature_name='feature'
49
+ )
50
+ self.assertEqual(str(feature_id), 'env/feature')
51
+ self.assertEqual(
52
+ feature_id.working_dir(root_dir='/tmp'),
53
+ '/tmp/env/feature'
54
+ )
55
+ self.assertIsNone(feature_id.working_dir(root_dir=None))
56
+
57
+ # For sandboxed feature.
58
+ feature_id = interface.Feature.Id(
59
+ container_id=interface.Sandbox.Id(
60
+ environment_id=interface.Environment.Id('env'),
61
+ image_id='image1',
62
+ sandbox_id='0'
63
+ ),
64
+ feature_name='feature'
65
+ )
66
+ self.assertEqual(str(feature_id), 'env/image1:0/feature')
67
+ self.assertEqual(
68
+ feature_id.working_dir(root_dir='/tmp'),
69
+ '/tmp/env/image1/0/feature'
70
+ )
71
+ self.assertIsNone(feature_id.working_dir(root_dir=None))
72
+
73
+
74
+ class TestingSandbox(interface.Sandbox):
75
+
76
+ id: interface.Sandbox.Id = interface.Sandbox.Id(
77
+ environment_id=interface.Environment.Id('env'),
78
+ image_id='test_image',
79
+ sandbox_id='0:0'
80
+ )
81
+ image_id: str = 'test_image'
82
+ features: dict[str, interface.Feature] = {}
83
+ status: interface.Sandbox.Status = interface.Sandbox.Status.READY
84
+ session_id: str | None = None
85
+
86
+ __test__ = False
87
+
88
+ def environment(self) -> interface.Environment:
89
+ pass
90
+
91
+ def _on_bound(self) -> None:
92
+ self.activities = []
93
+
94
+ def report_state_error(self, error: interface.SandboxStateError) -> None:
95
+ pass
96
+
97
+ def start(self) -> None:
98
+ pass
99
+
100
+ def shutdown(self) -> None:
101
+ pass
102
+
103
+ def start_session(self, session_id: str) -> None:
104
+ pass
105
+
106
+ def end_session(self, shutdown_sandbox: bool = False) -> None:
107
+ pass
108
+
109
+ @contextlib.contextmanager
110
+ def track_activity(
111
+ self,
112
+ name: str,
113
+ feature: interface.Feature | None = None,
114
+ **kwargs
115
+ ) -> Iterator[None]:
116
+ error = None
117
+ try:
118
+ yield
119
+ except BaseException as e:
120
+ error = e
121
+ raise
122
+ finally:
123
+ self.activities.append((name, error, kwargs))
124
+
125
+
126
+ class DecoratorTest(unittest.TestCase):
127
+
128
+ def test_treat_as_sandbox_state_error(self):
129
+
130
+ class SandboxA(TestingSandbox):
131
+
132
+ @interface.treat_as_sandbox_state_error(errors=(ValueError,))
133
+ def foo(self, bar: str) -> None:
134
+ raise ValueError(bar)
135
+
136
+ with self.assertRaises(interface.SandboxStateError):
137
+ SandboxA().foo('foo')
138
+
139
+ def test_log_sandbox_activity(self):
140
+
141
+ class SandboxB(TestingSandbox):
142
+
143
+ @interface.log_activity()
144
+ def bar(self, x: str) -> None:
145
+ pass
146
+
147
+ sb = SandboxB()
148
+ sb.bar('foo')
149
+ self.assertEqual(sb.activities, [('bar', None, {'x': 'foo'})])
150
+
41
151
 
42
152
  if __name__ == '__main__':
43
153
  unittest.main()
@@ -12,7 +12,9 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import concurrent.futures
15
+ import contextlib
15
16
  import time
17
+ from typing import Any, Iterator
16
18
  import unittest
17
19
 
18
20
  from langfun.env import interface
@@ -22,6 +24,9 @@ from langfun.env import load_balancers
22
24
  class TestingSandbox(interface.Sandbox):
23
25
  sandbox_id: str
24
26
  status: interface.Sandbox.Status = interface.Sandbox.Status.READY
27
+ image_id: str = 'test_image'
28
+
29
+ __test__ = False
25
30
 
26
31
  def _on_bound(self) -> None:
27
32
  super()._on_bound()
@@ -31,6 +36,7 @@ class TestingSandbox(interface.Sandbox):
31
36
  def id(self) -> interface.Sandbox.Id:
32
37
  return interface.Sandbox.Id(
33
38
  environment_id=interface.Environment.Id('testing-env'),
39
+ image_id=self.image_id,
34
40
  sandbox_id=self.sandbox_id
35
41
  )
36
42
 
@@ -46,6 +52,9 @@ class TestingSandbox(interface.Sandbox):
46
52
  def state_errors(self) -> list[interface.SandboxStateError]:
47
53
  return []
48
54
 
55
+ def report_state_error(self, error: interface.SandboxStateError) -> None:
56
+ pass
57
+
49
58
  def set_status(self, status: interface.Sandbox.Status) -> None:
50
59
  self.rebind(status=status, skip_notification=True)
51
60
 
@@ -53,10 +62,10 @@ class TestingSandbox(interface.Sandbox):
53
62
  self.set_status(self.Status.ACQUIRED)
54
63
 
55
64
  def start(self) -> None:
56
- self.set_alive()
65
+ pass
57
66
 
58
67
  def shutdown(self) -> None:
59
- self.set_alive(False)
68
+ pass
60
69
 
61
70
  def start_session(self, session_id: str) -> None:
62
71
  self._session_id = session_id
@@ -68,6 +77,18 @@ class TestingSandbox(interface.Sandbox):
68
77
  def session_id(self) -> str | None:
69
78
  return self._session_id
70
79
 
80
+ @contextlib.contextmanager
81
+ def track_activity(
82
+ self,
83
+ name: str,
84
+ feature: interface.Feature | None = None,
85
+ **kwargs: Any
86
+ ) -> Iterator[None]:
87
+ try:
88
+ yield
89
+ finally:
90
+ pass
91
+
71
92
 
72
93
  class RoundRobinTest(unittest.TestCase):
73
94
 
langfun/env/test_utils.py CHANGED
@@ -21,19 +21,20 @@ from langfun.env import base_environment
21
21
  from langfun.env import base_feature
22
22
  from langfun.env import base_sandbox
23
23
  from langfun.env import interface
24
- from langfun.env.event_handlers import base as event_handler_base
25
24
  import pyglove as pg
26
25
 
27
26
 
28
27
  class TestingEnvironment(base_environment.BaseEnvironment):
29
28
  """Testing environment for unit tests."""
30
-
29
+ image_ids: list[str] = ['test_image']
31
30
  housekeep_interval: float = 0.0
32
31
  simulate_start_error: Type[BaseException] | None = None
33
32
  simulate_shutdown_error: Type[BaseException] | None = None
34
33
  simulate_ping_error: Type[BaseException] | None = None
35
34
  offline: bool = False
36
35
 
36
+ __test__ = False
37
+
37
38
  @property
38
39
  def id(self) -> interface.Environment.Id:
39
40
  return interface.Environment.Id('testing-env')
@@ -45,6 +46,7 @@ class TestingEnvironment(base_environment.BaseEnvironment):
45
46
 
46
47
  def _create_sandbox(
47
48
  self,
49
+ image_id: str,
48
50
  sandbox_id: str,
49
51
  reusable: bool,
50
52
  proactive_session_setup: bool,
@@ -54,8 +56,10 @@ class TestingEnvironment(base_environment.BaseEnvironment):
54
56
  environment=self,
55
57
  id=interface.Sandbox.Id(
56
58
  environment_id=self.id,
59
+ image_id=image_id,
57
60
  sandbox_id=sandbox_id
58
61
  ),
62
+ image_id=image_id,
59
63
  reusable=reusable,
60
64
  proactive_session_setup=proactive_session_setup,
61
65
  keepalive_interval=keepalive_interval,
@@ -72,6 +76,8 @@ class TestingSandbox(base_sandbox.BaseSandbox):
72
76
  simulate_shutdown_error: Type[BaseException] | None = None
73
77
  simulate_ping_error: Type[BaseException] | None = None
74
78
 
79
+ __test__ = False
80
+
75
81
  def _on_bound(self) -> None:
76
82
  super()._on_bound()
77
83
  self._shell_history = []
@@ -109,7 +115,8 @@ class TestingSandbox(base_sandbox.BaseSandbox):
109
115
  self._raise_error('Sandbox shutdown error', self.simulate_shutdown_error)
110
116
  super()._shutdown()
111
117
 
112
- @base_sandbox.sandbox_service(critical_errors=(RuntimeError,))
118
+ @interface.treat_as_sandbox_state_error(errors=(RuntimeError,))
119
+ @interface.log_activity()
113
120
  def shell(
114
121
  self,
115
122
  code: str,
@@ -138,6 +145,8 @@ class TestingFeature(base_feature.BaseFeature):
138
145
  simulate_teardown_session_error: Type[BaseException] | None = None
139
146
  call_end_session_on_teardown_session: bool = False
140
147
 
148
+ __test__ = False
149
+
141
150
  class Service:
142
151
  """Sandbox."""
143
152
 
@@ -181,19 +190,19 @@ class TestingFeature(base_feature.BaseFeature):
181
190
  if self.call_end_session_on_teardown_session:
182
191
  self.sandbox.end_session()
183
192
 
184
- @base_sandbox.sandbox_service()
193
+ @interface.log_activity()
185
194
  def num_shell_calls(self) -> int:
186
195
  return len(self.sandbox._shell_history) # pylint: disable=protected-access
187
196
 
188
- @base_sandbox.sandbox_service()
197
+ @interface.log_activity()
189
198
  def bad_shell_call(self) -> None:
190
199
  self.sandbox.shell('bad command', raise_error=RuntimeError)
191
200
 
192
- @base_sandbox.sandbox_service()
201
+ @interface.log_activity()
193
202
  def show_session_id(self):
194
203
  return self.session_id
195
204
 
196
- @base_sandbox.sandbox_service()
205
+ @interface.log_activity()
197
206
  def call_with_varargs(self, code: str, *args, **kwargs):
198
207
  del code, args, kwargs
199
208
  return 0
@@ -202,7 +211,6 @@ class TestingFeature(base_feature.BaseFeature):
202
211
  super()._on_bound()
203
212
  self._service = None
204
213
 
205
- @base_sandbox.sandbox_service()
206
214
  @contextlib.contextmanager
207
215
  def my_service(self) -> Iterator[Service]:
208
216
  try:
@@ -218,9 +226,46 @@ class TestingFeature(base_feature.BaseFeature):
218
226
  )
219
227
 
220
228
 
221
- class TestingEventHandler(
222
- pg.Object, event_handler_base.EventHandler
223
- ):
229
+ class TestingNonSandboxBasedFeature(base_feature.BaseFeature):
230
+ """Testing non-sandbox based feature for unit tests."""
231
+ is_sandbox_based: bool = False
232
+ simulate_setup_error: Type[BaseException] | None = None
233
+ simulate_teardown_error: Type[BaseException] | None = None
234
+ simulate_setup_session_error: Type[BaseException] | None = None
235
+ simulate_teardown_session_error: Type[BaseException] | None = None
236
+ simulate_housekeep_error: Type[BaseException] | None = None
237
+
238
+ __test__ = False
239
+
240
+ def _setup(self) -> None:
241
+ if self.simulate_setup_error:
242
+ raise self.simulate_setup_error('Feature setup error')
243
+
244
+ def _teardown(self) -> None:
245
+ if self.simulate_teardown_error:
246
+ raise self.simulate_teardown_error('Feature teardown error')
247
+
248
+ def _setup_session(self) -> None:
249
+ if self.simulate_setup_session_error:
250
+ raise self.simulate_setup_session_error('Feature session setup error')
251
+
252
+ def _teardown_session(self) -> None:
253
+ if self.simulate_teardown_session_error:
254
+ raise self.simulate_teardown_session_error(
255
+ 'Feature session teardown error'
256
+ )
257
+
258
+ def _housekeep(self) -> None:
259
+ if self.simulate_housekeep_error:
260
+ raise self.simulate_housekeep_error('Feature housekeeping error')
261
+ _ = self.foo(1)
262
+
263
+ @interface.log_activity()
264
+ def foo(self, x: int) -> int:
265
+ return x + 1
266
+
267
+
268
+ class TestingEventHandler(pg.Object, interface.EventHandler):
224
269
  """Testing environment event handler for unit tests."""
225
270
 
226
271
  log_sandbox_status: bool = False
@@ -228,6 +273,8 @@ class TestingEventHandler(
228
273
  log_session_setup: bool = False
229
274
  log_housekeep: bool = False
230
275
 
276
+ __test__ = False
277
+
231
278
  def _on_bound(self) -> None:
232
279
  super()._on_bound()
233
280
  self._logs = []
@@ -281,7 +328,6 @@ class TestingEventHandler(
281
328
 
282
329
  def on_sandbox_start(
283
330
  self,
284
- environment: interface.Environment,
285
331
  sandbox: interface.Sandbox,
286
332
  duration: float,
287
333
  error: BaseException | None
@@ -291,7 +337,6 @@ class TestingEventHandler(
291
337
 
292
338
  def on_sandbox_status_change(
293
339
  self,
294
- environment: interface.Environment,
295
340
  sandbox: interface.Sandbox,
296
341
  old_status: interface.Sandbox.Status,
297
342
  new_status: interface.Sandbox.Status,
@@ -306,7 +351,6 @@ class TestingEventHandler(
306
351
 
307
352
  def on_sandbox_shutdown(
308
353
  self,
309
- environment: interface.Environment,
310
354
  sandbox: interface.Sandbox,
311
355
  duration: float,
312
356
  lifetime: float,
@@ -315,9 +359,53 @@ class TestingEventHandler(
315
359
  assert duration > 0 and lifetime is not None
316
360
  self._add_message(f'[{sandbox.id}] sandbox shutdown', error)
317
361
 
362
+ def on_sandbox_session_start(
363
+ self,
364
+ sandbox: interface.Sandbox,
365
+ session_id: str,
366
+ duration: float,
367
+ error: BaseException | None
368
+ ) -> None:
369
+ """Called when a sandbox session starts."""
370
+ assert duration > 0
371
+ self._add_message(
372
+ f'[{sandbox.id}] session {session_id!r} started', error
373
+ )
374
+
375
+ def on_sandbox_session_end(
376
+ self,
377
+ sandbox: interface.Sandbox,
378
+ session_id: str,
379
+ duration: float,
380
+ lifetime: float,
381
+ error: BaseException | None
382
+ ) -> None:
383
+ """Called when a sandbox session ends."""
384
+ assert duration > 0 and lifetime > 0
385
+ self._add_message(
386
+ f'[{sandbox.id}] session {session_id!r} ended', error
387
+ )
388
+
389
+ def on_sandbox_activity(
390
+ self,
391
+ name: str,
392
+ sandbox: interface.Sandbox,
393
+ session_id: str | None,
394
+ duration: float,
395
+ error: BaseException | None,
396
+ *,
397
+ code: str | None = None,
398
+ **kwargs
399
+ ) -> None:
400
+ """Called when a sandbox activity is performed."""
401
+ del kwargs
402
+ log_id = f'{sandbox.id}@{session_id or "<idle>"}'
403
+ self._add_message(
404
+ f'[{log_id}] {name}: {code}', error
405
+ )
406
+
318
407
  def on_sandbox_housekeep(
319
408
  self,
320
- environment: interface.Environment,
321
409
  sandbox: interface.Sandbox,
322
410
  counter: int,
323
411
  duration: float,
@@ -332,8 +420,6 @@ class TestingEventHandler(
332
420
 
333
421
  def on_feature_setup(
334
422
  self,
335
- environment: interface.Environment,
336
- sandbox: interface.Sandbox,
337
423
  feature: interface.Feature,
338
424
  duration: float,
339
425
  error: BaseException | None
@@ -342,13 +428,11 @@ class TestingEventHandler(
342
428
  assert duration > 0
343
429
  if self.log_feature_setup:
344
430
  self._add_message(
345
- f'[{sandbox.id}/{feature.name}] feature setup', error
431
+ f'[{feature.id}] feature setup', error
346
432
  )
347
433
 
348
434
  def on_feature_teardown(
349
435
  self,
350
- environment: interface.Environment,
351
- sandbox: interface.Sandbox,
352
436
  feature: interface.Feature,
353
437
  duration: float,
354
438
  error: BaseException | None
@@ -357,13 +441,11 @@ class TestingEventHandler(
357
441
  assert duration > 0
358
442
  if self.log_feature_setup:
359
443
  self._add_message(
360
- f'[{sandbox.id}/{feature.name}] feature teardown', error
444
+ f'[{feature.id}] feature teardown', error
361
445
  )
362
446
 
363
447
  def on_feature_setup_session(
364
448
  self,
365
- environment: interface.Environment,
366
- sandbox: interface.Sandbox,
367
449
  feature: interface.Feature,
368
450
  session_id: str | None,
369
451
  duration: float,
@@ -373,13 +455,12 @@ class TestingEventHandler(
373
455
  assert duration > 0
374
456
  if self.log_session_setup:
375
457
  self._add_message(
376
- f'[{sandbox.id}/{feature.name}] feature setup session', error
458
+ f'[{feature.id}@{session_id or "<idle>"}] feature setup session',
459
+ error
377
460
  )
378
461
 
379
462
  def on_feature_teardown_session(
380
463
  self,
381
- environment: interface.Environment,
382
- sandbox: interface.Sandbox,
383
464
  feature: interface.Feature,
384
465
  session_id: str,
385
466
  duration: float,
@@ -389,77 +470,38 @@ class TestingEventHandler(
389
470
  assert duration > 0
390
471
  if self.log_session_setup:
391
472
  self._add_message(
392
- f'[{sandbox.id}/{feature.name}] feature teardown session', error
473
+ f'[{feature.id}@{session_id}] feature teardown session', error
393
474
  )
394
475
 
395
- def on_feature_housekeep(
476
+ def on_feature_activity(
396
477
  self,
397
- environment: interface.Environment,
398
- sandbox: interface.Sandbox,
478
+ name: str,
399
479
  feature: interface.Feature,
400
- counter: int,
480
+ session_id: str | None,
401
481
  duration: float,
402
482
  error: BaseException | None,
483
+ *,
484
+ code: str | None = None,
403
485
  **kwargs
404
486
  ) -> None:
405
- """Called when a sandbox feature is housekeeping."""
406
- assert duration > 0
407
- if self.log_housekeep:
408
- self._add_message(
409
- f'[{sandbox.id}/{feature.name}] feature housekeeping {counter}', error
410
- )
411
-
412
- def on_session_start(
413
- self,
414
- environment: interface.Environment,
415
- sandbox: interface.Sandbox,
416
- session_id: str,
417
- duration: float,
418
- error: BaseException | None
419
- ) -> None:
420
- """Called when a sandbox session starts."""
421
- assert duration > 0
422
- self._add_message(
423
- f'[{sandbox.id}] session {session_id!r} started', error
424
- )
425
-
426
- def on_session_end(
427
- self,
428
- environment: interface.Environment,
429
- sandbox: interface.Sandbox,
430
- session_id: str,
431
- duration: float,
432
- lifetime: float,
433
- error: BaseException | None
434
- ) -> None:
435
- """Called when a sandbox session ends."""
436
- assert duration > 0 and lifetime > 0
487
+ """Called when a sandbox activity is performed."""
488
+ del kwargs
489
+ log_id = f'{feature.id}@{session_id or "<idle>"}'
437
490
  self._add_message(
438
- f'[{sandbox.id}] session {session_id!r} ended', error
491
+ f'[{log_id}] {name}: {code}', error
439
492
  )
440
493
 
441
- def on_sandbox_activity(
494
+ def on_feature_housekeep(
442
495
  self,
443
- name: str,
444
- environment: interface.Environment,
445
- sandbox: interface.Sandbox,
446
- feature: interface.Feature | None,
447
- session_id: str | None,
496
+ feature: interface.Feature,
497
+ counter: int,
448
498
  duration: float,
449
499
  error: BaseException | None,
450
- *,
451
- code: str | None = None,
452
500
  **kwargs
453
501
  ) -> None:
454
- """Called when a sandbox activity is performed."""
455
- del environment, kwargs
456
- if session_id is None:
457
- log_id = sandbox.id
458
- else:
459
- log_id = f'{sandbox.id}/{session_id}'
460
-
461
- if feature is not None:
462
- log_id = f'{log_id}/{feature.name}'
463
- self._add_message(
464
- f'[{log_id}] {name}: {code}', error
465
- )
502
+ """Called when a sandbox feature is housekeeping."""
503
+ assert duration > 0
504
+ if self.log_housekeep:
505
+ self._add_message(
506
+ f'[{feature.id}] feature housekeeping {counter}', error
507
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510230805
3
+ Version: 0.1.2.dev202511270805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors