langfun 0.1.2.dev202509250804__py3-none-any.whl → 0.1.2.dev202509260805__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/__init__.py CHANGED
@@ -13,16 +13,12 @@
13
13
  # limitations under the License.
14
14
  """Environment for LLM agents."""
15
15
 
16
- # pylint: disable=g-importing-member, g-bad-import-order
17
- from langfun.env.interface import EnvironmentId
18
- from langfun.env.interface import SandboxId
19
-
16
+ # pylint: disable=g-importing-member, g-bad-import-order, g-import-not-at-top
20
17
  from langfun.env.interface import EnvironmentError # pylint: disable=redefined-builtin
21
18
  from langfun.env.interface import EnvironmentOutageError
22
19
  from langfun.env.interface import EnvironmentOverloadError
23
20
  from langfun.env.interface import SandboxError
24
21
  from langfun.env.interface import SandboxStateError
25
- from langfun.env.interface import EnvironmentEventHandler
26
22
 
27
23
  from langfun.env.interface import Environment
28
24
  from langfun.env.interface import Sandbox
@@ -35,4 +31,7 @@ from langfun.env.base_feature import BaseFeature
35
31
  from langfun.env import load_balancers
36
32
  from langfun.env.load_balancers import LoadBalancer
37
33
 
34
+ from langfun.env import event_handlers
35
+ EventHandler = event_handlers.EventHandler
36
+
38
37
  # Google-internal imports.
@@ -34,6 +34,7 @@ import langfun.core as lf
34
34
  from langfun.env import base_sandbox
35
35
  from langfun.env import interface
36
36
  from langfun.env import load_balancers
37
+ from langfun.env.event_handlers import base as event_handler_base
37
38
  import pyglove as pg
38
39
 
39
40
 
@@ -65,10 +66,20 @@ class BaseEnvironment(interface.Environment):
65
66
  load_balancer: Annotated[
66
67
  load_balancers.LoadBalancer,
67
68
  (
68
- 'The load balancer for the environment.'
69
+ 'The load balancer for the environment to acquire sandboxes.'
69
70
  )
70
71
  ] = load_balancers.RoundRobin()
71
72
 
73
+ sandbox_keepalive_interval: Annotated[
74
+ float | None,
75
+ (
76
+ 'The interval in seconds to send keepalive pings to sandboxes. '
77
+ 'If None, sandbox keepalive is disabled. Please note that sandbox '
78
+ 'keepalive is different from feature housekeeping. Usually sandbox '
79
+ 'keepalive and feature housekeeping are different operations.'
80
+ )
81
+ ] = None
82
+
72
83
  proactive_session_setup: Annotated[
73
84
  bool,
74
85
  (
@@ -79,6 +90,13 @@ class BaseEnvironment(interface.Environment):
79
90
  )
80
91
  ] = True
81
92
 
93
+ event_handlers: Annotated[
94
+ list[event_handler_base.EventHandler],
95
+ (
96
+ 'User handler for the environment events.'
97
+ )
98
+ ] = []
99
+
82
100
  outage_grace_period: Annotated[
83
101
  float,
84
102
  (
@@ -97,13 +115,15 @@ class BaseEnvironment(interface.Environment):
97
115
  )
98
116
  ] = 10.0
99
117
 
100
- stats_report_interval: Annotated[
118
+ housekeep_interval: Annotated[
101
119
  float,
102
120
  (
103
- 'The interval in seconds for reporting the environment stats. '
104
- 'If 0, stats will not be reported.'
121
+ 'The interval in seconds for environment housekeeping. It recycles '
122
+ 'the dead sandboxes in the pool. This interval is the minimal time '
123
+ 'to detect outage while there is no request to obtain new sandboxes.'
124
+ 'This is applicable only when the environment enables pooling.'
105
125
  )
106
- ] = 60.0
126
+ ] = 10.0
107
127
 
108
128
  pool_operation_max_parallelism: Annotated[
109
129
  int,
@@ -145,6 +165,7 @@ class BaseEnvironment(interface.Environment):
145
165
  sandbox_id: str,
146
166
  reusable: bool,
147
167
  proactive_session_setup: bool,
168
+ keepalive_interval: float | None,
148
169
  ) -> base_sandbox.BaseSandbox:
149
170
  """Creates a sandbox with the given identifier.
150
171
 
@@ -153,6 +174,8 @@ class BaseEnvironment(interface.Environment):
153
174
  reusable: Whether the sandbox is reusable across user sessions.
154
175
  proactive_session_setup: Whether the sandbox performs session setup work
155
176
  before a user session is started.
177
+ keepalive_interval: Interval to ping the sandbox for keeping it alive.
178
+ If None, the sandbox will not be pinged.
156
179
 
157
180
  Returns:
158
181
  The created sandbox.
@@ -194,6 +217,7 @@ class BaseEnvironment(interface.Environment):
194
217
  def _start(self) -> None:
195
218
  """Implementation of starting the environment."""
196
219
  if self.min_pool_size > 0:
220
+ # Pre-allocate the sandbox pool before usage.
197
221
  self._sandbox_pool = [None] * self.min_pool_size
198
222
  for i, sandbox, _ in lf.concurrent_map(
199
223
  lambda i: self._bring_up_sandbox_with_retry(
@@ -207,12 +231,15 @@ class BaseEnvironment(interface.Environment):
207
231
  ),
208
232
  ):
209
233
  self._sandbox_pool[i] = sandbox
234
+
210
235
  self._next_sandbox_id = len(self._sandbox_pool)
211
- self._housekeep_thread = threading.Thread(
212
- target=self._housekeep_loop, daemon=True
213
- )
214
- self._housekeep_counter = 0
215
- self._housekeep_thread.start()
236
+
237
+ if self.enable_pooling:
238
+ self._housekeep_thread = threading.Thread(
239
+ target=self._housekeep_loop, daemon=True
240
+ )
241
+ self._housekeep_counter = 0
242
+ self._housekeep_thread.start()
216
243
 
217
244
  def _shutdown(self) -> None:
218
245
  """Implementation of shutting down the environment."""
@@ -256,7 +283,7 @@ class BaseEnvironment(interface.Environment):
256
283
  @property
257
284
  def enable_pooling(self) -> bool:
258
285
  """Returns whether the environment enables pooling."""
259
- return self.min_pool_size > 0
286
+ return self.max_pool_size > 0
260
287
 
261
288
  @property
262
289
  def status(self) -> interface.Environment.Status:
@@ -314,11 +341,6 @@ class BaseEnvironment(interface.Environment):
314
341
  self._start_time = time.time()
315
342
  self._set_status(self.Status.ONLINE)
316
343
  self.on_start(duration=time.time() - starting_time)
317
-
318
- pg.logging.info(
319
- '[%s]: %s started in %.2f seconds.',
320
- self.id, self.__class__.__name__, time.time() - starting_time
321
- )
322
344
  except BaseException as e:
323
345
  self.on_start(duration=time.time() - starting_time, error=e)
324
346
  self.shutdown()
@@ -338,13 +360,8 @@ class BaseEnvironment(interface.Environment):
338
360
  self._set_status(self.Status.SHUTTING_DOWN)
339
361
 
340
362
  try:
341
- with pg.timeit('env.shutdown') as t:
342
- self._shutdown()
363
+ self._shutdown()
343
364
  self.on_shutdown()
344
- pg.logging.info(
345
- '[%s]: %s shutdown in %.2f seconds.',
346
- self.id, self.__class__.__name__, t.elapse
347
- )
348
365
  except BaseException as e: # pylint: disable=broad-except
349
366
  self.on_shutdown(error=e)
350
367
  raise e
@@ -416,6 +433,7 @@ class BaseEnvironment(interface.Environment):
416
433
  sandbox_id=sandbox_id,
417
434
  reusable=self.enable_pooling,
418
435
  proactive_session_setup=self.proactive_session_setup,
436
+ keepalive_interval=self.sandbox_keepalive_interval,
419
437
  )
420
438
  for handler in self.event_handlers:
421
439
  sandbox.add_event_handler(handler)
@@ -482,19 +500,8 @@ class BaseEnvironment(interface.Environment):
482
500
 
483
501
  def _housekeep_loop(self) -> None:
484
502
  """Housekeeping loop for the environment."""
485
- pg.logging.info(
486
- '[%s]: %s maintenance thread started.', self.id, self.__class__.__name__
487
- )
488
- stats_report_time = time.time()
489
503
  while self._status not in (self.Status.SHUTTING_DOWN, self.Status.OFFLINE):
490
504
  housekeep_start_time = time.time()
491
- if time.time() - stats_report_time > self.stats_report_interval:
492
- pg.logging.info(
493
- '[%s] %s stats: %s.',
494
- self.id, self.__class__.__name__, self.stats()
495
- )
496
- stats_report_time = time.time()
497
-
498
505
  dead_pool_indices = [
499
506
  i for i, s in enumerate(self._sandbox_pool)
500
507
  if s.status == interface.Sandbox.Status.OFFLINE
@@ -514,7 +521,7 @@ class BaseEnvironment(interface.Environment):
514
521
  break
515
522
  self._housekeep_counter += 1
516
523
  self.on_housekeep(time.time() - housekeep_start_time)
517
- time.sleep(1)
524
+ time.sleep(self.housekeep_interval)
518
525
 
519
526
  def _replace_dead_sandboxes(self, dead_pool_indices: list[int]) -> bool:
520
527
  """Replaces a dead sandbox with a new one.
@@ -23,6 +23,7 @@ the `Environment` and `Sandbox` interfaces directly.
23
23
  """
24
24
 
25
25
  import functools
26
+ import os
26
27
  import time
27
28
  from typing import Annotated, Callable
28
29
 
@@ -106,6 +107,14 @@ class BaseFeature(interface.Feature):
106
107
  assert self._sandbox is not None, 'Feature has not been set up yet.'
107
108
  return self._sandbox
108
109
 
110
+ @property
111
+ def working_dir(self) -> str | None:
112
+ """Returns the working directory of the feature."""
113
+ sandbox_workdir = self.sandbox.working_dir
114
+ if sandbox_workdir is None:
115
+ return None
116
+ return os.path.join(sandbox_workdir, self.name)
117
+
109
118
  #
110
119
  # Setup and teardown of the feature.
111
120
  #
@@ -204,7 +213,7 @@ class BaseFeature(interface.Feature):
204
213
  self,
205
214
  name: str,
206
215
  duration: float,
207
- error: BaseException | None,
216
+ error: BaseException | None = None,
208
217
  **kwargs
209
218
  ) -> None:
210
219
  """Called when a sandbox activity is performed."""
@@ -29,6 +29,7 @@ import time
29
29
  from typing import Annotated, Any, Callable, Iterator, Sequence, Type
30
30
 
31
31
  from langfun.env import interface
32
+ from langfun.env.event_handlers import base as event_handler_base
32
33
  import pyglove as pg
33
34
 
34
35
 
@@ -36,7 +37,7 @@ class BaseSandbox(interface.Sandbox):
36
37
  """Base class for a sandbox."""
37
38
 
38
39
  id: Annotated[
39
- interface.SandboxId,
40
+ interface.Sandbox.Id,
40
41
  'The identifier for the sandbox.'
41
42
  ]
42
43
 
@@ -242,14 +243,14 @@ class BaseSandbox(interface.Sandbox):
242
243
 
243
244
  def add_event_handler(
244
245
  self,
245
- event_handler: interface.EnvironmentEventHandler | None
246
+ event_handler: event_handler_base.EventHandler | None
246
247
  ) -> None:
247
248
  """Sets the event handler for the sandbox."""
248
249
  self._event_handlers.append(event_handler)
249
250
 
250
251
  def remove_event_handler(
251
252
  self,
252
- event_handler: interface.EnvironmentEventHandler | None
253
+ event_handler: event_handler_base.EventHandler | None
253
254
  ) -> None:
254
255
  """Removes the event handler for the sandbox."""
255
256
  self._event_handlers.remove(event_handler)
@@ -357,10 +358,6 @@ class BaseSandbox(interface.Sandbox):
357
358
 
358
359
  duration = time.time() - starting_time
359
360
  self.on_start(duration)
360
- pg.logging.info(
361
- '[%s]: Sandbox started in %.2f seconds.',
362
- self.id, duration
363
- )
364
361
  except BaseException as e: # pylint: disable=broad-except
365
362
  duration = time.time() - starting_time
366
363
  pg.logging.error(
@@ -422,7 +419,6 @@ class BaseSandbox(interface.Sandbox):
422
419
  return
423
420
 
424
421
  self._set_status(interface.Sandbox.Status.SHUTTING_DOWN)
425
- shutdown_start_time = time.time()
426
422
 
427
423
  if (self._housekeep_thread is not None
428
424
  and threading.current_thread() is not self._housekeep_thread):
@@ -433,15 +429,6 @@ class BaseSandbox(interface.Sandbox):
433
429
  try:
434
430
  self._shutdown()
435
431
  self._set_status(interface.Sandbox.Status.OFFLINE)
436
-
437
- pg.logging.info(
438
- '[%s]: Sandbox shutdown in %.2f seconds. '
439
- '(lifetime: %.2f seconds, teardown errors: %s)',
440
- self.id,
441
- time.time() - shutdown_start_time,
442
- time.time() - self._start_time if self._start_time else 0,
443
- teardown_error
444
- )
445
432
  self.on_shutdown(teardown_error)
446
433
  shutdown_error = None
447
434
  except BaseException as e: # pylint: disable=broad-except
@@ -656,14 +643,6 @@ class BaseSandbox(interface.Sandbox):
656
643
  self._set_status(interface.Sandbox.Status.ACQUIRED)
657
644
  shutdown_sandbox = True
658
645
 
659
- pg.logging.info(
660
- '[%s]: User session %s ended. '
661
- '(lifetime: %.2f seconds, teardown errors: %s).',
662
- self.id,
663
- self._session_id,
664
- time.time() - self._session_start_time,
665
- end_session_error
666
- )
667
646
  self._session_start_time = None
668
647
  self._session_event_handler = None
669
648
 
@@ -687,9 +666,31 @@ class BaseSandbox(interface.Sandbox):
687
666
  last_ping = now
688
667
  last_housekeep_time = {name: now for name in self._features.keys()}
689
668
 
669
+ def _next_housekeep_wait_time() -> float:
670
+ # Decide how long to sleep for the next housekeeping.
671
+ next_housekeep_time = None
672
+ if self.keepalive_interval is not None:
673
+ next_housekeep_time = last_ping + self.keepalive_interval
674
+
675
+ for name, feature in self._features.items():
676
+ if feature.housekeep_interval is None:
677
+ continue
678
+ next_feature_housekeep_time = (
679
+ last_housekeep_time[name] + feature.housekeep_interval
680
+ )
681
+ if (next_housekeep_time is None
682
+ or next_housekeep_time > next_feature_housekeep_time):
683
+ next_housekeep_time = next_feature_housekeep_time
684
+
685
+ # Housekeep loop is installed when at least one feature requires
686
+ # housekeeping or the sandbox has a keepalive interval.
687
+ assert next_housekeep_time is not None
688
+ return max(0, next_housekeep_time - time.time())
689
+
690
690
  while self._status not in (self.Status.SHUTTING_DOWN, self.Status.OFFLINE):
691
691
  housekeep_start = time.time()
692
692
  if self.keepalive_interval is not None:
693
+
693
694
  if time.time() - last_ping > self.keepalive_interval:
694
695
  try:
695
696
  self.ping()
@@ -731,7 +732,7 @@ class BaseSandbox(interface.Sandbox):
731
732
 
732
733
  self._housekeep_counter += 1
733
734
  self.on_housekeep(time.time() - housekeep_start)
734
- time.sleep(1)
735
+ time.sleep(_next_housekeep_wait_time())
735
736
 
736
737
  #
737
738
  # Event handlers subclasses can override.
langfun/env/base_test.py CHANGED
@@ -18,12 +18,13 @@ import unittest
18
18
  from langfun.env import base_sandbox
19
19
  from langfun.env import interface
20
20
  from langfun.env import test_utils
21
+ from langfun.env.event_handlers import base as event_handler_base
21
22
 
22
23
 
23
24
  TestingEnvironment = test_utils.TestingEnvironment
24
25
  TestingSandbox = test_utils.TestingSandbox
25
26
  TestingFeature = test_utils.TestingFeature
26
- TestingEnvironmentEventHandler = test_utils.TestingEnvironmentEventHandler
27
+ TestingEventHandler = test_utils.TestingEventHandler
27
28
 
28
29
 
29
30
  class EnvironmentTests(unittest.TestCase):
@@ -42,9 +43,8 @@ class EnvironmentTests(unittest.TestCase):
42
43
  self.assertEqual(env.min_pool_size, 0)
43
44
  self.assertEqual(env.max_pool_size, 0)
44
45
  self.assertEqual(env.sandbox_pool, [])
45
- self.assertEqual(env.id, interface.EnvironmentId('testing-env'))
46
+ self.assertEqual(env.id, interface.Environment.Id('testing-env'))
46
47
  self.assertEqual(env.outage_grace_period, 1)
47
- self.assertEqual(env.stats_report_interval, 60)
48
48
  self.assertEqual(env.features['test_feature'].name, 'test_feature')
49
49
 
50
50
  self.assertIsNone(env.start_time)
@@ -60,12 +60,16 @@ class EnvironmentTests(unittest.TestCase):
60
60
 
61
61
  with env.sandbox('session1') as sb:
62
62
  self.assertEqual(
63
- sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
63
+ sb.id, interface.Sandbox.Id(environment_id=env.id, sandbox_id='0')
64
64
  )
65
65
  self.assertEqual(sb.session_id, 'session1')
66
66
  self.assertEqual(sb.working_dir, '/tmp/testing-env/0')
67
67
  self.assertTrue(sb.is_online)
68
68
  self.assertIs(sb.test_feature, sb.features['test_feature'])
69
+ self.assertEqual(
70
+ sb.test_feature.working_dir,
71
+ '/tmp/testing-env/0/test_feature'
72
+ )
69
73
  with self.assertRaises(AttributeError):
70
74
  _ = sb.test_feature2
71
75
  self.assertFalse(sb.is_online)
@@ -74,14 +78,26 @@ class EnvironmentTests(unittest.TestCase):
74
78
  with self.assertRaises(AttributeError):
75
79
  _ = env.test_feature2
76
80
 
81
+ def test_del(self):
82
+ env = TestingEnvironment(
83
+ features={'test_feature': TestingFeature()},
84
+ pool_size=0,
85
+ outage_grace_period=1,
86
+ outage_retry_interval=0,
87
+ sandbox_keepalive_interval=0,
88
+ )
89
+ env.start()
90
+ sb = env.acquire()
91
+ del sb
92
+ del env
93
+
77
94
  def test_acquire_env_offline(self):
78
95
  env = TestingEnvironment(
79
96
  features={'test_feature': TestingFeature()},
80
97
  pool_size=0,
81
98
  outage_grace_period=1,
82
99
  outage_retry_interval=0,
83
- keepalive_interval=0,
84
- stats_report_interval=1,
100
+ sandbox_keepalive_interval=0,
85
101
  )
86
102
  with self.assertRaises(interface.EnvironmentOutageError):
87
103
  env.acquire()
@@ -92,12 +108,14 @@ class EnvironmentTests(unittest.TestCase):
92
108
  pool_size=0,
93
109
  outage_grace_period=1,
94
110
  outage_retry_interval=0,
95
- keepalive_interval=0,
96
- stats_report_interval=1,
111
+ sandbox_keepalive_interval=0,
97
112
  )
98
113
  with env:
99
114
  sb = env.acquire()
100
115
  self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
116
+ self.assertIsNone(env.working_dir)
117
+ self.assertIsNone(sb.working_dir)
118
+ self.assertIsNone(sb.test_feature.working_dir)
101
119
 
102
120
  def test_acquire_no_pooling_with_error(self):
103
121
  env = TestingEnvironment(
@@ -109,8 +127,7 @@ class EnvironmentTests(unittest.TestCase):
109
127
  pool_size=0,
110
128
  outage_grace_period=1,
111
129
  outage_retry_interval=0,
112
- keepalive_interval=0,
113
- stats_report_interval=1,
130
+ sandbox_keepalive_interval=0,
114
131
  )
115
132
  with env:
116
133
  with self.assertRaises(interface.EnvironmentOutageError):
@@ -122,8 +139,7 @@ class EnvironmentTests(unittest.TestCase):
122
139
  pool_size=1,
123
140
  outage_grace_period=1,
124
141
  outage_retry_interval=0,
125
- keepalive_interval=0,
126
- stats_report_interval=1,
142
+ sandbox_keepalive_interval=0,
127
143
  )
128
144
  with env:
129
145
  sb = env.acquire()
@@ -135,8 +151,7 @@ class EnvironmentTests(unittest.TestCase):
135
151
  pool_size=1,
136
152
  outage_grace_period=1,
137
153
  outage_retry_interval=0,
138
- keepalive_interval=0,
139
- stats_report_interval=1,
154
+ sandbox_keepalive_interval=0,
140
155
  )
141
156
  with env:
142
157
  sb = env.acquire()
@@ -150,17 +165,76 @@ class EnvironmentTests(unittest.TestCase):
150
165
  pool_size=(1, 3),
151
166
  outage_grace_period=1,
152
167
  outage_retry_interval=0,
153
- keepalive_interval=0,
154
- stats_report_interval=1,
168
+ sandbox_keepalive_interval=0,
155
169
  )
156
170
  with env:
157
171
  self.assertEqual(len(env.sandbox_pool), 1)
172
+ self.assertEqual(
173
+ env.stats(),
174
+ {
175
+ 'sandbox': {
176
+ 'created': 0,
177
+ 'setting_up': 0,
178
+ 'ready': 1,
179
+ 'acquired': 0,
180
+ 'in_session': 0,
181
+ 'exiting_session': 0,
182
+ 'shutting_down': 0,
183
+ 'offline': 0,
184
+ }
185
+ }
186
+ )
158
187
  sb = env.acquire()
159
188
  self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
189
+ self.assertEqual(
190
+ env.stats(),
191
+ {
192
+ 'sandbox': {
193
+ 'created': 0,
194
+ 'setting_up': 0,
195
+ 'ready': 0,
196
+ 'acquired': 1,
197
+ 'in_session': 0,
198
+ 'exiting_session': 0,
199
+ 'shutting_down': 0,
200
+ 'offline': 0,
201
+ }
202
+ }
203
+ )
160
204
  self.assertEqual(len(env.sandbox_pool), 1)
161
205
  sb2 = env.acquire()
162
206
  self.assertEqual(sb2.status, interface.Sandbox.Status.ACQUIRED)
163
207
  self.assertEqual(len(env.sandbox_pool), 2)
208
+ self.assertEqual(
209
+ env.stats(),
210
+ {
211
+ 'sandbox': {
212
+ 'created': 0,
213
+ 'setting_up': 0,
214
+ 'ready': 0,
215
+ 'acquired': 2,
216
+ 'in_session': 0,
217
+ 'exiting_session': 0,
218
+ 'shutting_down': 0,
219
+ 'offline': 0,
220
+ }
221
+ }
222
+ )
223
+ self.assertEqual(
224
+ env.stats(),
225
+ {
226
+ 'sandbox': {
227
+ 'created': 0,
228
+ 'setting_up': 0,
229
+ 'ready': 0,
230
+ 'acquired': 0,
231
+ 'in_session': 0,
232
+ 'exiting_session': 0,
233
+ 'shutting_down': 0,
234
+ 'offline': 0,
235
+ }
236
+ }
237
+ )
164
238
 
165
239
  def test_acquire_with_growing_pool_failure(self):
166
240
  env = TestingEnvironment(
@@ -168,8 +242,7 @@ class EnvironmentTests(unittest.TestCase):
168
242
  pool_size=(1, 3),
169
243
  outage_grace_period=1,
170
244
  outage_retry_interval=0,
171
- keepalive_interval=0,
172
- stats_report_interval=1,
245
+ sandbox_keepalive_interval=0,
173
246
  )
174
247
  with env:
175
248
  self.assertEqual(len(env.sandbox_pool), 1)
@@ -184,15 +257,14 @@ class EnvironmentTests(unittest.TestCase):
184
257
  with self.assertRaises(interface.EnvironmentOutageError):
185
258
  env.acquire()
186
259
 
187
- def test_maintenance_error(self):
260
+ def test_housekeep_error(self):
188
261
  env = TestingEnvironment(
189
262
  features={'test_feature': TestingFeature()},
190
263
  pool_size=1,
191
264
  proactive_session_setup=True,
192
265
  outage_grace_period=1,
193
266
  outage_retry_interval=0,
194
- keepalive_interval=0,
195
- stats_report_interval=1,
267
+ sandbox_keepalive_interval=0,
196
268
  )
197
269
  with env:
198
270
  self.assertEqual(len(env.sandbox_pool), 1)
@@ -216,7 +288,7 @@ class SandboxStatusTests(unittest.TestCase):
216
288
 
217
289
  def setUp(self):
218
290
  super().setUp()
219
- self.event_handler = TestingEnvironmentEventHandler(
291
+ self.event_handler = TestingEventHandler(
220
292
  log_sandbox_status=True,
221
293
  log_feature_setup=True,
222
294
  log_session_setup=True,
@@ -246,6 +318,7 @@ class SandboxStatusTests(unittest.TestCase):
246
318
  'feature2': TestingFeature(),
247
319
  },
248
320
  )
321
+ self.assertFalse(env.enable_pooling)
249
322
  with env:
250
323
  with env.sandbox('session1') as sb:
251
324
  sb.shell('echo "hello"')
@@ -294,6 +367,7 @@ class SandboxStatusTests(unittest.TestCase):
294
367
  pool_size=1,
295
368
  proactive_session_setup=True,
296
369
  )
370
+ self.assertTrue(env.enable_pooling)
297
371
  with env:
298
372
  with env.sandbox('session1') as sb:
299
373
  sb.shell('echo "hello"')
@@ -343,7 +417,7 @@ class SandboxStatusTests(unittest.TestCase):
343
417
  features={'test_feature': TestingFeature(setup_session_delay=0.5)},
344
418
  pool_size=1,
345
419
  )
346
- event_handler = TestingEnvironmentEventHandler(
420
+ event_handler = TestingEventHandler(
347
421
  log_sandbox_status=True,
348
422
  log_feature_setup=True,
349
423
  log_session_setup=True,
@@ -919,6 +993,7 @@ class SandboxStatusTests(unittest.TestCase):
919
993
  self.assertEqual(len(sb.state_errors), 0)
920
994
  sb.shell('echo bar')
921
995
  self.assertEqual(sb.status, interface.Sandbox.Status.IN_SESSION)
996
+ sb.wait_until_not(interface.Sandbox.Status.SETTING_UP)
922
997
  self.assertEqual(sb.status, interface.Sandbox.Status.READY)
923
998
  self.assertEqual(
924
999
  self.event_handler.logs,
@@ -1020,7 +1095,7 @@ class SandboxActivityTests(unittest.TestCase):
1020
1095
  env = TestingEnvironment(
1021
1096
  features={'test_feature': TestingFeature(housekeep_interval=0)},
1022
1097
  pool_size=1,
1023
- keepalive_interval=0,
1098
+ sandbox_keepalive_interval=0,
1024
1099
  )
1025
1100
  with env:
1026
1101
  with env.sandbox('session1') as sb:
@@ -1037,7 +1112,7 @@ class SandboxActivityTests(unittest.TestCase):
1037
1112
  pool_size=1,
1038
1113
  outage_grace_period=0,
1039
1114
  outage_retry_interval=0,
1040
- keepalive_interval=0,
1115
+ sandbox_keepalive_interval=0,
1041
1116
  )
1042
1117
  with env:
1043
1118
  with env.sandbox('session1') as sb:
@@ -1052,7 +1127,7 @@ class SandboxActivityTests(unittest.TestCase):
1052
1127
  while sb.housekeep_counter == housekeep_count or (
1053
1128
  sb.status == interface.Sandbox.Status.IN_SESSION
1054
1129
  ):
1055
- time.sleep(0.1)
1130
+ time.sleep(0.01)
1056
1131
  self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
1057
1132
 
1058
1133
  def test_remove_event_handler(self):
@@ -1061,10 +1136,9 @@ class SandboxActivityTests(unittest.TestCase):
1061
1136
  pool_size=1,
1062
1137
  outage_grace_period=0,
1063
1138
  outage_retry_interval=0,
1064
- keepalive_interval=0,
1065
- stats_report_interval=1,
1139
+ sandbox_keepalive_interval=0,
1066
1140
  )
1067
- event_handler = TestingEnvironmentEventHandler()
1141
+ event_handler = TestingEventHandler()
1068
1142
  with env:
1069
1143
  with env.sandbox('session1') as sb:
1070
1144
  sb.add_event_handler(event_handler)
@@ -1082,21 +1156,20 @@ class SandboxServiceTests(unittest.TestCase):
1082
1156
  def setUp(self):
1083
1157
  super().setUp()
1084
1158
  self.maxDiff = None
1085
- self.event_handler = TestingEnvironmentEventHandler()
1159
+ self.event_handler = TestingEventHandler()
1086
1160
  self.env = TestingEnvironment(
1087
1161
  features={'test_feature': TestingFeature()},
1088
1162
  pool_size=0,
1089
1163
  outage_grace_period=0,
1090
1164
  outage_retry_interval=0,
1091
- keepalive_interval=0,
1165
+ sandbox_keepalive_interval=0,
1092
1166
  event_handlers=[self.event_handler],
1093
- stats_report_interval=1,
1094
1167
  random_seed=1,
1095
1168
  )
1096
1169
 
1097
1170
  def test_service_call_activity_log(self):
1098
1171
 
1099
- class CustomEventHandler(interface.EnvironmentEventHandler):
1172
+ class CustomEventHandler(event_handler_base.EventHandler):
1100
1173
 
1101
1174
  def __init__(self):
1102
1175
  self.calls = []
@@ -0,0 +1,12 @@
1
+ """Environment event handlers."""
2
+
3
+ # pylint: disable=g-importing-member
4
+ # pylint: disable=g-bad-import-order
5
+
6
+ from langfun.env.event_handlers.base import EventHandler
7
+
8
+ from langfun.env.event_handlers.event_logger import EventLogger
9
+ from langfun.env.event_handlers.event_logger import ConsoleEventLogger
10
+
11
+ # pylint: enable=g-importing-member
12
+ # pylint: enable=g-bad-import-order