langfun 0.1.2.dev202510280805__py3-none-any.whl → 0.1.2.dev202510300805__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.

langfun/env/interface.py CHANGED
@@ -17,8 +17,9 @@ import abc
17
17
  import contextlib
18
18
  import dataclasses
19
19
  import enum
20
+ import functools
20
21
  import os
21
- from typing import Annotated, Any, ContextManager, ClassVar, Iterator, Optional
22
+ from typing import Annotated, Any, Callable, ContextManager, ClassVar, Iterator, Optional, Sequence, Type
22
23
 
23
24
  import pyglove as pg
24
25
 
@@ -170,6 +171,9 @@ class SessionTeardownError(SandboxError):
170
171
  class Environment(pg.Object):
171
172
  """Base class for an environment."""
172
173
 
174
+ # Disable symbolic comparison and hashing for environment objects.
175
+ use_symbolic_comparison = False
176
+
173
177
  @dataclasses.dataclass(frozen=True)
174
178
  class Id:
175
179
  """Identifier for an environment."""
@@ -220,6 +224,20 @@ class Environment(pg.Object):
220
224
  def id(self) -> Id:
221
225
  """Returns the identifier for the environment."""
222
226
 
227
+ @property
228
+ @abc.abstractmethod
229
+ def image_ids(self) -> list[str]:
230
+ """Returns the non-dynamic image IDs served by the environment."""
231
+
232
+ def image_id_for(self, feature: 'Feature') -> str:
233
+ """Returns the default image ID for the environment."""
234
+ for image_id in self.image_ids:
235
+ if feature.is_applicable(image_id):
236
+ return image_id
237
+ raise ValueError(
238
+ f'No image ID found for feature {feature.name} in {self.image_ids}.'
239
+ )
240
+
223
241
  @property
224
242
  @abc.abstractmethod
225
243
  def status(self) -> Status:
@@ -245,9 +263,16 @@ class Environment(pg.Object):
245
263
  """
246
264
 
247
265
  @abc.abstractmethod
248
- def acquire(self) -> 'Sandbox':
266
+ def acquire(
267
+ self,
268
+ image_id: str | None = None,
269
+ ) -> 'Sandbox':
249
270
  """Acquires a free sandbox from the environment.
250
271
 
272
+ Args:
273
+ image_id: The image ID to use for the sandbox. If None, it will be
274
+ automatically determined by the environment.
275
+
251
276
  Returns:
252
277
  A free sandbox from the environment.
253
278
 
@@ -257,7 +282,7 @@ class Environment(pg.Object):
257
282
  """
258
283
 
259
284
  @abc.abstractmethod
260
- def new_session_id(self) -> str:
285
+ def new_session_id(self, feature_hint: str | None = None) -> str:
261
286
  """Generates a new session ID."""
262
287
 
263
288
  #
@@ -298,33 +323,63 @@ class Environment(pg.Object):
298
323
 
299
324
  def sandbox(
300
325
  self,
326
+ image_id: str | None = None,
301
327
  session_id: str | None = None,
302
328
  ) -> ContextManager['Sandbox']:
303
329
  """Gets a sandbox from the environment and starts a new user session."""
304
- return self.acquire().new_session(session_id)
330
+ return self.acquire(image_id=image_id).new_session(session_id)
305
331
 
306
332
  def __getattr__(self, name: str) -> Any:
307
- """Gets a feature from a free sandbox from the environment.
333
+ """Gets a feature session from a free sandbox from the environment.
308
334
 
309
335
  Example:
310
336
  ```
311
337
  with XboxEnvironment(
312
338
  features={'selenium': SeleniumFeature()}
313
339
  ) as env:
314
- driver = env.selenium.get_driver()
340
+ with env.selenium() as selenium:
341
+ driver = selenium.get_driver()
315
342
  ```
316
343
 
317
344
  Args:
318
345
  name: The name of the feature.
319
346
 
320
347
  Returns:
321
- A feature from a free sandbox from the environment.
348
+ A callable `(image_id, *, session_id) -> ContextManager[Feature]` that
349
+ creates a context manager for the requested feature under a new client
350
+ session.
322
351
  """
323
352
  if name in self.features:
324
- return self.acquire().features[name]
353
+ return _feature_session_creator(self, self.features[name])
325
354
  raise AttributeError(name)
326
355
 
327
356
 
357
+ @contextlib.contextmanager
358
+ def _session_for_feature(
359
+ environment: Environment,
360
+ feature: 'Feature',
361
+ image_id: str | None = None,
362
+ session_id: str | None = None,
363
+ ) -> Iterator['Feature']:
364
+ """Returns a context manager for a session for a feature."""
365
+ if image_id is None:
366
+ image_id = environment.image_id_for(feature)
367
+ elif not feature.is_applicable(image_id):
368
+ raise ValueError(
369
+ f'Feature {feature.name!r} is not applicable to image {image_id!r}.'
370
+ )
371
+ sandbox = environment.acquire(image_id=image_id)
372
+ with sandbox.new_session(session_id=session_id, feature_hint=feature.name):
373
+ yield sandbox.features[feature.name]
374
+
375
+
376
+ def _feature_session_creator(environment: Environment, feature: 'Feature'):
377
+ """Returns a callable that returns a context manager for a feature session."""
378
+ def fn(image_id: str | None = None, session_id: str | None = None):
379
+ return _session_for_feature(environment, feature, image_id, session_id)
380
+ return fn
381
+
382
+
328
383
  # Enable automatic conversion from str to Environment.Id.
329
384
  pg.typing.register_converter(str, Environment.Id, Environment.Id)
330
385
 
@@ -332,14 +387,18 @@ pg.typing.register_converter(str, Environment.Id, Environment.Id)
332
387
  class Sandbox(pg.Object):
333
388
  """Interface for sandboxes."""
334
389
 
390
+ # Disable symbolic comparison and hashing for sandbox objects.
391
+ use_symbolic_comparison = False
392
+
335
393
  @dataclasses.dataclass(frozen=True, slots=True)
336
394
  class Id:
337
395
  """Identifier for a sandbox."""
338
396
  environment_id: Environment.Id
397
+ image_id: str
339
398
  sandbox_id: str
340
399
 
341
400
  def __str__(self) -> str:
342
- return f'{self.environment_id}/{self.sandbox_id}'
401
+ return f'{self.environment_id}/{self.image_id}:{self.sandbox_id}'
343
402
 
344
403
  def working_dir(self, root_dir: str | None) -> str | None:
345
404
  """Returns the download directory for the sandbox."""
@@ -347,6 +406,7 @@ class Sandbox(pg.Object):
347
406
  return None
348
407
  return os.path.join(
349
408
  self.environment_id.working_dir(root_dir),
409
+ _make_path_compatible(self.image_id),
350
410
  _make_path_compatible(self.sandbox_id)
351
411
  )
352
412
 
@@ -438,6 +498,11 @@ class Sandbox(pg.Object):
438
498
  def id(self) -> Id:
439
499
  """Returns the identifier for the sandbox."""
440
500
 
501
+ @property
502
+ @abc.abstractmethod
503
+ def image_id(self) -> str:
504
+ """Returns the image ID used for bootstrapping the sandbox."""
505
+
441
506
  @property
442
507
  @abc.abstractmethod
443
508
  def environment(self) -> Environment:
@@ -458,10 +523,17 @@ class Sandbox(pg.Object):
458
523
  """Returns True if the sandbox is online."""
459
524
  return self.status.is_online
460
525
 
461
- @property
462
526
  @abc.abstractmethod
463
- def state_errors(self) -> list[SandboxStateError]:
464
- """Returns state errors encountered during sandbox's lifecycle."""
527
+ def report_state_error(self, error: SandboxStateError) -> None:
528
+ """Reports state error the sandbox.
529
+
530
+ If state errors are reported, the sandbox will be forcefully shutdown when
531
+ `Sandbox.end_session()` is called, even if the sandbox is set to be
532
+ reusable.
533
+
534
+ Args:
535
+ error: SandboxStateError to report.
536
+ """
465
537
 
466
538
  @abc.abstractmethod
467
539
  def start(self) -> None:
@@ -592,6 +664,9 @@ class Sandbox(pg.Object):
592
664
  `end_session` should always be called for each `start_session` call, even
593
665
  when the session fails to start, to ensure proper cleanup.
594
666
 
667
+ When `end_session` is called with state errors reported, the sandbox will be
668
+ forcefully shutdown even if the sandbox is set to be reusable.
669
+
595
670
  `end_session` may fail with two sources of errors:
596
671
 
597
672
  1. SandboxStateError: If the sandbox is in a bad state or session teardown
@@ -615,6 +690,24 @@ class Sandbox(pg.Object):
615
690
  def session_id(self) -> str | None:
616
691
  """Returns the current user session identifier."""
617
692
 
693
+ @abc.abstractmethod
694
+ def track_activity(
695
+ self,
696
+ name: str,
697
+ feature: Optional['Feature'] = None,
698
+ **kwargs: Any
699
+ ) -> ContextManager[None]:
700
+ """Context manager that tracks a sandbox activity.
701
+
702
+ Args:
703
+ name: The name of the activity.
704
+ feature: The feature that the activity is associated with.
705
+ **kwargs: Additional keyword arguments to pass to the activity handler.
706
+
707
+ Returns:
708
+ A context manager that tracks the activity, including duration and error.
709
+ """
710
+
618
711
  #
619
712
  # API related to a user session.
620
713
  # A sandbox could be reused across different user sessions.
@@ -626,6 +719,8 @@ class Sandbox(pg.Object):
626
719
  def new_session(
627
720
  self,
628
721
  session_id: str | None = None,
722
+ *,
723
+ feature_hint: str | None = None,
629
724
  ) -> Iterator['Sandbox']:
630
725
  """Context manager for obtaining a sandbox for a user session.
631
726
 
@@ -636,6 +731,9 @@ class Sandbox(pg.Object):
636
731
  Args:
637
732
  session_id: The identifier for the user session. If not provided, a random
638
733
  ID will be generated.
734
+ feature_hint: A hint of which feature is the main user intent when
735
+ starting the session. This is used for generating the session id if
736
+ `session_id` is not provided.
639
737
 
640
738
  Yields:
641
739
  The sandbox for the user session.
@@ -646,10 +744,13 @@ class Sandbox(pg.Object):
646
744
  errors.
647
745
  """
648
746
  if session_id is None:
649
- session_id = self.environment.new_session_id()
747
+ session_id = self.environment.new_session_id(feature_hint)
650
748
  self.start_session(session_id)
651
749
  try:
652
750
  yield self
751
+ except SandboxStateError as e:
752
+ self.report_state_error(e)
753
+ raise
653
754
  finally:
654
755
  self.end_session()
655
756
 
@@ -683,6 +784,9 @@ class Sandbox(pg.Object):
683
784
  class Feature(pg.Object):
684
785
  """Interface for sandbox features."""
685
786
 
787
+ # Disable symbolic comparison and hashing for sandbox objects.
788
+ allow_symbolic_comparison = False
789
+
686
790
  @property
687
791
  @abc.abstractmethod
688
792
  def name(self) -> str:
@@ -700,6 +804,10 @@ class Feature(pg.Object):
700
804
  AssertError: If the feature is not set up with a sandbox yet.
701
805
  """
702
806
 
807
+ @abc.abstractmethod
808
+ def is_applicable(self, image_id: str) -> bool:
809
+ """Returns True if the feature is applicable to the given image."""
810
+
703
811
  @abc.abstractmethod
704
812
  def setup(self, sandbox: Sandbox) -> None:
705
813
  """Sets up the feature, which is called once when the sandbox is up.
@@ -811,6 +919,12 @@ class Feature(pg.Object):
811
919
  assert self.sandbox is not None
812
920
  return self.sandbox.session_id
813
921
 
922
+ def track_activity(self, name: str, **kwargs: Any) -> ContextManager[None]:
923
+ """Context manager that tracks a feature activity."""
924
+ return self.sandbox.track_activity(
925
+ f'{self.name}.{name}', feature=self, **kwargs
926
+ )
927
+
814
928
 
815
929
  def _make_path_compatible(id_str: str) -> str:
816
930
  """Makes a path compatible with CNS."""
@@ -824,3 +938,73 @@ def _make_path_compatible(id_str: str) -> str:
824
938
  '>': '',
825
939
  })
826
940
  )
941
+
942
+
943
+ def treat_as_sandbox_state_error(
944
+ errors: Sequence[
945
+ Type[BaseException] | tuple[Type[BaseException], str]
946
+ ] | None = None
947
+ ) -> Callable[..., Any]:
948
+ """Decorator for Sandbox/Feature methods to convert errors to SandboxStateError.
949
+
950
+ Args:
951
+ errors: A sequence of exception types or tuples of (error_type, msg_regex).
952
+ when matched, treat the error as SandboxStateError, which will lead to
953
+ a sandbox shutdown when caught by `Sandbox.new_session()` context manager.
954
+
955
+ Returns:
956
+ The decorator function.
957
+ """
958
+
959
+ def decorator(func):
960
+ @functools.wraps(func)
961
+ def method_wrapper(self, *args, **kwargs) -> Any:
962
+ """Helper function to safely execute logics in the sandbox."""
963
+
964
+ assert isinstance(self, (Sandbox, Feature)), self
965
+ sandbox = self.sandbox if isinstance(self, Feature) else self
966
+
967
+ try:
968
+ # Execute the service function.
969
+ return func(self, *args, **kwargs)
970
+ except BaseException as e:
971
+ if pg.match_error(e, errors):
972
+ state_error = SandboxStateError(
973
+ 'Sandbox encountered an unexpected error executing '
974
+ f'`{func.__name__}` (args={args!r}, kwargs={kwargs!r}): {e}',
975
+ sandbox=sandbox
976
+ )
977
+ raise state_error from e
978
+ raise
979
+ return method_wrapper
980
+ return decorator
981
+
982
+
983
+ def log_sandbox_activity(name: str | None = None):
984
+ """Decorator for Sandbox/Feature methods to log sandbox activity."""
985
+
986
+ def decorator(func):
987
+ signature = pg.typing.get_signature(func)
988
+ def to_kwargs(*args, **kwargs):
989
+ num_non_self_args = len(signature.arg_names) - 1
990
+ if len(args) > num_non_self_args:
991
+ assert signature.varargs is not None, (signature, args)
992
+ kwargs[signature.varargs.name] = tuple(args[num_non_self_args:])
993
+ args = args[:num_non_self_args]
994
+ for i in range(len(args)):
995
+ # The first argument is `self`.
996
+ kwargs[signature.arg_names[i + 1]] = args[i]
997
+ return kwargs
998
+
999
+ @functools.wraps(func)
1000
+ def method_wrapper(self, *args, **kwargs) -> Any:
1001
+ """Helper function to safely execute logics in the sandbox."""
1002
+
1003
+ assert isinstance(self, (Sandbox, Feature)), self
1004
+ with self.track_activity(
1005
+ name or func.__name__,
1006
+ **to_kwargs(*args, **kwargs)
1007
+ ):
1008
+ return func(self, *args, **kwargs)
1009
+ return method_wrapper
1010
+ return decorator
@@ -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,92 @@ 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
 
41
44
 
45
+ class TestingSandbox(interface.Sandbox):
46
+
47
+ id: interface.Sandbox.Id = interface.Sandbox.Id(
48
+ environment_id=interface.Environment.Id('env'),
49
+ image_id='test_image',
50
+ sandbox_id='0:0'
51
+ )
52
+ image_id: str = 'test_image'
53
+ features: dict[str, interface.Feature] = {}
54
+ status: interface.Sandbox.Status = interface.Sandbox.Status.READY
55
+ session_id: str | None = None
56
+
57
+ def environment(self) -> interface.Environment:
58
+ pass
59
+
60
+ def _on_bound(self) -> None:
61
+ self.activities = []
62
+
63
+ def report_state_error(self, error: interface.SandboxStateError) -> None:
64
+ pass
65
+
66
+ def start(self) -> None:
67
+ pass
68
+
69
+ def shutdown(self) -> None:
70
+ pass
71
+
72
+ def start_session(self, session_id: str) -> None:
73
+ pass
74
+
75
+ def end_session(self, shutdown_sandbox: bool = False) -> None:
76
+ pass
77
+
78
+ @contextlib.contextmanager
79
+ def track_activity(
80
+ self,
81
+ name: str,
82
+ feature: interface.Feature | None = None,
83
+ **kwargs
84
+ ) -> Iterator[None]:
85
+ error = None
86
+ try:
87
+ yield
88
+ except BaseException as e:
89
+ error = e
90
+ raise
91
+ finally:
92
+ self.activities.append((name, error, kwargs))
93
+
94
+
95
+ class DecoratorTest(unittest.TestCase):
96
+
97
+ def test_treat_as_sandbox_state_error(self):
98
+
99
+ class SandboxA(TestingSandbox):
100
+
101
+ @interface.treat_as_sandbox_state_error(errors=(ValueError,))
102
+ def foo(self, bar: str) -> None:
103
+ raise ValueError(bar)
104
+
105
+ with self.assertRaises(interface.SandboxStateError):
106
+ SandboxA().foo('foo')
107
+
108
+ def test_log_sandbox_activity(self):
109
+
110
+ class SandboxB(TestingSandbox):
111
+
112
+ @interface.log_sandbox_activity()
113
+ def bar(self, x: str) -> None:
114
+ pass
115
+
116
+ sb = SandboxB()
117
+ sb.bar('foo')
118
+ self.assertEqual(sb.activities, [('bar', None, {'x': 'foo'})])
119
+
120
+
42
121
  if __name__ == '__main__':
43
122
  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,7 @@ 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'
25
28
 
26
29
  def _on_bound(self) -> None:
27
30
  super()._on_bound()
@@ -31,6 +34,7 @@ class TestingSandbox(interface.Sandbox):
31
34
  def id(self) -> interface.Sandbox.Id:
32
35
  return interface.Sandbox.Id(
33
36
  environment_id=interface.Environment.Id('testing-env'),
37
+ image_id=self.image_id,
34
38
  sandbox_id=self.sandbox_id
35
39
  )
36
40
 
@@ -46,6 +50,9 @@ class TestingSandbox(interface.Sandbox):
46
50
  def state_errors(self) -> list[interface.SandboxStateError]:
47
51
  return []
48
52
 
53
+ def report_state_error(self, error: interface.SandboxStateError) -> None:
54
+ pass
55
+
49
56
  def set_status(self, status: interface.Sandbox.Status) -> None:
50
57
  self.rebind(status=status, skip_notification=True)
51
58
 
@@ -68,6 +75,18 @@ class TestingSandbox(interface.Sandbox):
68
75
  def session_id(self) -> str | None:
69
76
  return self._session_id
70
77
 
78
+ @contextlib.contextmanager
79
+ def track_activity(
80
+ self,
81
+ name: str,
82
+ feature: interface.Feature | None = None,
83
+ **kwargs: Any
84
+ ) -> Iterator[None]:
85
+ try:
86
+ yield
87
+ finally:
88
+ pass
89
+
71
90
 
72
91
  class RoundRobinTest(unittest.TestCase):
73
92
 
langfun/env/test_utils.py CHANGED
@@ -27,7 +27,7 @@ import pyglove as pg
27
27
 
28
28
  class TestingEnvironment(base_environment.BaseEnvironment):
29
29
  """Testing environment for unit tests."""
30
-
30
+ image_ids: list[str] = ['test_image']
31
31
  housekeep_interval: float = 0.0
32
32
  simulate_start_error: Type[BaseException] | None = None
33
33
  simulate_shutdown_error: Type[BaseException] | None = None
@@ -45,6 +45,7 @@ class TestingEnvironment(base_environment.BaseEnvironment):
45
45
 
46
46
  def _create_sandbox(
47
47
  self,
48
+ image_id: str,
48
49
  sandbox_id: str,
49
50
  reusable: bool,
50
51
  proactive_session_setup: bool,
@@ -54,8 +55,10 @@ class TestingEnvironment(base_environment.BaseEnvironment):
54
55
  environment=self,
55
56
  id=interface.Sandbox.Id(
56
57
  environment_id=self.id,
58
+ image_id=image_id,
57
59
  sandbox_id=sandbox_id
58
60
  ),
61
+ image_id=image_id,
59
62
  reusable=reusable,
60
63
  proactive_session_setup=proactive_session_setup,
61
64
  keepalive_interval=keepalive_interval,
@@ -109,7 +112,8 @@ class TestingSandbox(base_sandbox.BaseSandbox):
109
112
  self._raise_error('Sandbox shutdown error', self.simulate_shutdown_error)
110
113
  super()._shutdown()
111
114
 
112
- @base_sandbox.sandbox_service(critical_errors=(RuntimeError,))
115
+ @interface.treat_as_sandbox_state_error(errors=(RuntimeError,))
116
+ @interface.log_sandbox_activity()
113
117
  def shell(
114
118
  self,
115
119
  code: str,
@@ -181,19 +185,19 @@ class TestingFeature(base_feature.BaseFeature):
181
185
  if self.call_end_session_on_teardown_session:
182
186
  self.sandbox.end_session()
183
187
 
184
- @base_sandbox.sandbox_service()
188
+ @interface.log_sandbox_activity()
185
189
  def num_shell_calls(self) -> int:
186
190
  return len(self.sandbox._shell_history) # pylint: disable=protected-access
187
191
 
188
- @base_sandbox.sandbox_service()
192
+ @interface.log_sandbox_activity()
189
193
  def bad_shell_call(self) -> None:
190
194
  self.sandbox.shell('bad command', raise_error=RuntimeError)
191
195
 
192
- @base_sandbox.sandbox_service()
196
+ @interface.log_sandbox_activity()
193
197
  def show_session_id(self):
194
198
  return self.session_id
195
199
 
196
- @base_sandbox.sandbox_service()
200
+ @interface.log_sandbox_activity()
197
201
  def call_with_varargs(self, code: str, *args, **kwargs):
198
202
  del code, args, kwargs
199
203
  return 0
@@ -202,7 +206,6 @@ class TestingFeature(base_feature.BaseFeature):
202
206
  super()._on_bound()
203
207
  self._service = None
204
208
 
205
- @base_sandbox.sandbox_service()
206
209
  @contextlib.contextmanager
207
210
  def my_service(self) -> Iterator[Service]:
208
211
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510280805
3
+ Version: 0.1.2.dev202510300805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -35,10 +35,10 @@ langfun/core/subscription_test.py,sha256=Y4ZdbZEwm83YNZBxHff0QR4QUa4rdaNXA3_jfIc
35
35
  langfun/core/template.py,sha256=CQOEA1Lq0gU_uk43K1FJjpBSwL8o5fglFr2tGCbOxpI,26008
36
36
  langfun/core/template_test.py,sha256=vQZvFVS4VHk-6GUdOEQ-UA_4tlVBbpRPqB1Bw7DFJJg,19052
37
37
  langfun/core/agentic/__init__.py,sha256=vsnuvjaz9-nysBjdihGf43JC8AyLPhPJwIOevyONyAQ,1517
38
- langfun/core/agentic/action.py,sha256=ojwaPIV_a_khKPR6x1Fk5i2dsUTSe3VjKaxnZ92b0nE,58243
38
+ langfun/core/agentic/action.py,sha256=Zz-17gegJxzCZ9hcOhv-atJX1EfIN-vHonS6z99Bllc,59523
39
39
  langfun/core/agentic/action_eval.py,sha256=YTilyUEkJl_8FVMgdfO17PurWWaEJ6oA15CuefJJRLk,4887
40
40
  langfun/core/agentic/action_eval_test.py,sha256=7AkOwNbUX-ZgR1R0a7bvUZ5abNTUV7blf_8Mnrwb-II,2811
41
- langfun/core/agentic/action_test.py,sha256=a2D4FOuob7MviuPZR2Wy6xNKnjlTLxhK8HUy8WIyt08,19076
41
+ langfun/core/agentic/action_test.py,sha256=4X11bNffNZG8WfjB1Z-Sm33Z2hs0YPzz-6ct77V8M7Q,19824
42
42
  langfun/core/coding/__init__.py,sha256=5utju_fwEsImaiftx4oXKl9FAM8p281k8-Esdh_-m1w,835
43
43
  langfun/core/coding/python/__init__.py,sha256=yTXm92oLpQb4A-fZ2qy-bzfhPYND7B-oidtbv1PNaX0,1678
44
44
  langfun/core/coding/python/correction.py,sha256=4PD76Xfv36Xrm8Ji3-GgGDNImtcDqWfMw3z6ircJMlM,7285
@@ -101,7 +101,7 @@ langfun/core/llms/deepseek.py,sha256=Pyv2Pmviu91HfNGR994Mh7AKNvWHAAlue7Xfb9MZaPo
101
101
  langfun/core/llms/deepseek_test.py,sha256=DvROWPlDuow5E1lfoSkhyGt_ELA19JoQoDsTnRgDtTg,1847
102
102
  langfun/core/llms/fake.py,sha256=bDk_4u7V2LmYUotyOaicwzi0-lnWOIIBbR3-Bil1P3o,3481
103
103
  langfun/core/llms/fake_test.py,sha256=lC-C2TpEsnf2kmZpa3OiH2H944I4hMWTAaHEXzRj1DU,7855
104
- langfun/core/llms/gemini.py,sha256=-DL5PebzaTjz7rTFw_1RC5O1aE4EYSv3oNsM65YKCoo,30143
104
+ langfun/core/llms/gemini.py,sha256=Bsd9UmI-Z6j_ZJposExuh7ChCt1ZF2QE1nWPWHaE39k,30204
105
105
  langfun/core/llms/gemini_test.py,sha256=y1s0W65SrdepbZxzgIeoTB2MI7sXnfBDf1NsGn57LbM,7617
106
106
  langfun/core/llms/google_genai.py,sha256=ogyoOUK4s1OcSFKun0YK5xBRDVyxmvz9WsYNKAwuB0g,5918
107
107
  langfun/core/llms/google_genai_test.py,sha256=NKNtpebArQ9ZR7Qsnhd2prFIpMjleojy6o6VMXkJ1zY,1502
@@ -174,24 +174,24 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
174
174
  langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
175
175
  langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
176
176
  langfun/core/templates/selfplay_test.py,sha256=Ot__1P1M8oJfoTp-M9-PQ6HUXqZKyMwvZ5f7yQ3yfyM,2326
177
- langfun/env/__init__.py,sha256=m6Y8y16ms9lytvO_r-Br8HmTp2rNDhb3R6JJaH5dEEk,1491
178
- langfun/env/base_environment.py,sha256=m7IJNHqt4dlgBRwv0E8BxKtARvcNJis9FrrALguzX1E,19472
179
- langfun/env/base_feature.py,sha256=gGUCWLcSaQZ7MoXNKogXEdcpfoqJxcfnoXhWQjqi8jk,6462
180
- langfun/env/base_sandbox.py,sha256=yG_4qG5MVIEP5uyXO2KssZFSFDewwNvF7Hr_qE7yGfQ,37301
181
- langfun/env/base_test.py,sha256=DrBtyweO4v8Fz3Oz-gnpJ4W_9hRhuVXA1CTLvXJDa9s,61099
182
- langfun/env/interface.py,sha256=2Amf-_op7dGRF8c4-wYxcFxs1UCBYz1AB20Lk7__V4E,25724
183
- langfun/env/interface_test.py,sha256=hfQn4RRTEo1YfVHXTPzH1puzD14BTo8R_5v1IpXVZ90,1398
177
+ langfun/env/__init__.py,sha256=VTjLmS_SkxtkBCmr6hBb4iACeLLPmcbWJkK7MEXsATA,1652
178
+ langfun/env/base_environment.py,sha256=8Cpwb4D37zHj5AK4qgmGIPyxIMEDxTIPHXqxTSNkE_M,25030
179
+ langfun/env/base_feature.py,sha256=JDEhL9LkbBHB0c603guchry7cy_zaIReg5vqExyQQgg,6902
180
+ langfun/env/base_sandbox.py,sha256=0dduNozBtPqawvSX-pv6sEweDNTi89lrT_5qKk5FWak,30656
181
+ langfun/env/base_test.py,sha256=6yHtExP3Soa-l0ug73ZSV9ZfS0B4vu-uaS_K_xvsDwk,74261
182
+ langfun/env/interface.py,sha256=2X-gGD41cnejde5DIQ7QxgW3r85oKP4Gp0iWP-ABmLM,32126
183
+ langfun/env/interface_test.py,sha256=d8vdXL1PkrNQGwzfEI2r6esd6SBnTMgc-3GNArsnuj4,3295
184
184
  langfun/env/load_balancers.py,sha256=qRhCthqzjZIQBwta8qC1C0s0J-VQAVomJQqI7Nqv-r4,1948
185
- langfun/env/load_balancers_test.py,sha256=73QQG-Lufy5DBhQuzWNUaaH24YiZFdZEHe67TkJn7a0,3708
186
- langfun/env/test_utils.py,sha256=rnIQZ2ZpiFuB7yfl5ckGtacBoySAwguzBzXzqhyo8jw,14042
185
+ langfun/env/load_balancers_test.py,sha256=Bg0h-AL7LpWb_aixFXs_FpgQp4dWLvodcsQj-mys6zs,4125
186
+ langfun/env/test_utils.py,sha256=9fqhxxKERiyAte9bbtwqXqZ1ihNGyuOdCiPPY8G0I8w,14171
187
187
  langfun/env/event_handlers/__init__.py,sha256=H34n-TbKSgtxqBhE-yAti8fY6weF2_v3yw59M9_zmGM,443
188
188
  langfun/env/event_handlers/base.py,sha256=eGdJ6N5em9kX-c9wzm1TdnRP5_5IAltX5JTHILdjzLM,10124
189
189
  langfun/env/event_handlers/event_logger.py,sha256=3dbPjBe53dBgntYHlyLlj_77hVecPSXkmKeiGXMxlO0,12699
190
- langfun/env/event_handlers/event_logger_test.py,sha256=PGof3rPllNnyzs3Yp8kaOHLeTkVrzUgCJwlODTrVRKI,9111
191
- langfun/env/event_handlers/metric_writer.py,sha256=NgJKsd6xWOtEd0IjYi7coGEaqGYkkPcDjXN9CQ3vxPU,18043
192
- langfun/env/event_handlers/metric_writer_test.py,sha256=deqCIdo-Q89rRCF2lerU3GpWmFG_SvDx162UQIZxPgE,5338
193
- langfun-0.1.2.dev202510280805.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
194
- langfun-0.1.2.dev202510280805.dist-info/METADATA,sha256=MGrbvXO1FXOqziKIPFJanHl1OImCc7gDu2_222FTCLY,7522
195
- langfun-0.1.2.dev202510280805.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
196
- langfun-0.1.2.dev202510280805.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
197
- langfun-0.1.2.dev202510280805.dist-info/RECORD,,
190
+ langfun/env/event_handlers/event_logger_test.py,sha256=ryupxaEP9D8wdtSsSwZRSZwqFaHCaSD-bFSea_T7QJo,9133
191
+ langfun/env/event_handlers/metric_writer.py,sha256=F_Gk1lpJX5SZ6-Hyrf_-utf4gvSKvMmcov8VkKogZCU,19618
192
+ langfun/env/event_handlers/metric_writer_test.py,sha256=sntUifTPHGixUshIgVBX4Q9vJL-xbeS0Cpd5X5hSQyQ,5955
193
+ langfun-0.1.2.dev202510300805.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
194
+ langfun-0.1.2.dev202510300805.dist-info/METADATA,sha256=mKbGN2kywExiipSgV10M59iPfOncuioqSZ-9BDa2zRo,7522
195
+ langfun-0.1.2.dev202510300805.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
196
+ langfun-0.1.2.dev202510300805.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
197
+ langfun-0.1.2.dev202510300805.dist-info/RECORD,,