langfun 0.1.2.dev202509290805__py3-none-any.whl → 0.1.2.dev202510010805__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.

@@ -752,6 +752,11 @@ class Gemini(rest.REST):
752
752
  prompt.as_format('gemini', chunk_preprocessor=modality_conversion)
753
753
  )
754
754
  request['contents'] = contents
755
+ # Users could use `metadata_gemini_tools` to pass Gemini tools. For example,
756
+ # for enabling Search Grounding, users could pass:
757
+ # metadata_gemini_tools=[{'google_search': {}}]
758
+ if tools := prompt.metadata.get('gemini_tools'):
759
+ request['tools'] = tools
755
760
  return request
756
761
 
757
762
  def _generation_config(
langfun/core/llms/rest.py CHANGED
@@ -98,7 +98,9 @@ class REST(lf.LanguageModel):
98
98
  raise lf.TemporaryLMError(str(e)) from e
99
99
  except (
100
100
  requests.exceptions.ConnectionError,
101
+ requests.exceptions.ChunkedEncodingError,
101
102
  ConnectionError,
103
+ ConnectionResetError,
102
104
  ) as e:
103
105
  error_message = str(e)
104
106
  if 'REJECTED_CLIENT_THROTTLED' in error_message:
@@ -107,6 +109,8 @@ class REST(lf.LanguageModel):
107
109
  raise lf.TemporaryLMError(error_message) from e
108
110
  if 'UNREACHABLE_ERROR' in error_message:
109
111
  raise lf.TemporaryLMError(error_message) from e
112
+ if 'Connection reset by peer' in error_message:
113
+ raise lf.TemporaryLMError(error_message) from e
110
114
  raise lf.LMError(error_message) from e
111
115
 
112
116
  def _error(self, status_code: int, content: str) -> lf.LMError:
@@ -221,7 +221,7 @@ class BaseEnvironment(interface.Environment):
221
221
  self._sandbox_pool = [None] * self.min_pool_size
222
222
  for i, sandbox, _ in lf.concurrent_map(
223
223
  lambda i: self._bring_up_sandbox_with_retry(
224
- sandbox_id=str(i), shutdown_env_upon_outage=False
224
+ sandbox_id=f'{i}:0', shutdown_env_upon_outage=False
225
225
  ),
226
226
  range(self.min_pool_size),
227
227
  silence_on_errors=None,
@@ -335,6 +335,7 @@ class BaseEnvironment(interface.Environment):
335
335
  f'it is in {self._status.value!r} status.'
336
336
  )
337
337
 
338
+ self.on_starting()
338
339
  starting_time = time.time()
339
340
  try:
340
341
  self._start()
@@ -358,12 +359,14 @@ class BaseEnvironment(interface.Environment):
358
359
  return
359
360
 
360
361
  self._set_status(self.Status.SHUTTING_DOWN)
362
+ self.on_shutting_down()
361
363
 
364
+ shutting_down_time = time.time()
362
365
  try:
363
366
  self._shutdown()
364
- self.on_shutdown()
367
+ self.on_shutdown(duration=time.time() - shutting_down_time)
365
368
  except BaseException as e: # pylint: disable=broad-except
366
- self.on_shutdown(error=e)
369
+ self.on_shutdown(duration=time.time() - shutting_down_time, error=e)
367
370
  raise e
368
371
 
369
372
  #
@@ -412,11 +415,11 @@ class BaseEnvironment(interface.Environment):
412
415
  else:
413
416
  try:
414
417
  sandbox = self._bring_up_sandbox(
415
- sandbox_id=str(self._increment_sandbox_id()), set_acquired=True,
418
+ sandbox_id=f'{self._increment_sandbox_id()}:0',
419
+ set_acquired=True,
416
420
  )
417
421
  # Append is atomic and does not require locking.
418
422
  self._sandbox_pool.append(sandbox)
419
- self._offline_start_time = None
420
423
  return sandbox
421
424
  except (
422
425
  interface.EnvironmentError, interface.SandboxStateError
@@ -429,18 +432,28 @@ class BaseEnvironment(interface.Environment):
429
432
  set_acquired: bool = False,
430
433
  ) -> base_sandbox.BaseSandbox:
431
434
  """Brings up a new sandbox."""
432
- sandbox = self._create_sandbox(
433
- sandbox_id=sandbox_id,
434
- reusable=self.enable_pooling,
435
- proactive_session_setup=self.proactive_session_setup,
436
- keepalive_interval=self.sandbox_keepalive_interval,
437
- )
438
- for handler in self.event_handlers:
439
- sandbox.add_event_handler(handler)
440
- sandbox.start()
441
- if set_acquired:
442
- sandbox.set_acquired()
443
- return sandbox
435
+ env_error = None
436
+ try:
437
+ sandbox = self._create_sandbox(
438
+ sandbox_id=sandbox_id,
439
+ reusable=self.enable_pooling,
440
+ proactive_session_setup=self.proactive_session_setup,
441
+ keepalive_interval=self.sandbox_keepalive_interval,
442
+ )
443
+ for handler in self.event_handlers:
444
+ sandbox.add_event_handler(handler)
445
+ sandbox.start()
446
+ if set_acquired:
447
+ sandbox.set_acquired()
448
+ return sandbox
449
+ except (interface.EnvironmentError, interface.SandboxStateError) as e:
450
+ env_error = e
451
+ raise e
452
+ finally:
453
+ if env_error is None:
454
+ self._offline_start_time = None
455
+ elif self._offline_start_time is None:
456
+ self._offline_start_time = time.time()
444
457
 
445
458
  def _bring_up_sandbox_with_retry(
446
459
  self,
@@ -483,8 +496,6 @@ class BaseEnvironment(interface.Environment):
483
496
  shutdown_env_upon_outage: bool = True
484
497
  ):
485
498
  """Raises error if the grace period has passed or wait for retry."""
486
- if self._offline_start_time is None:
487
- self._offline_start_time = time.time()
488
499
  if self.offline_duration > self.outage_grace_period:
489
500
  if shutdown_env_upon_outage:
490
501
  self.shutdown()
@@ -502,39 +513,47 @@ class BaseEnvironment(interface.Environment):
502
513
  """Housekeeping loop for the environment."""
503
514
  while self._status not in (self.Status.SHUTTING_DOWN, self.Status.OFFLINE):
504
515
  housekeep_start_time = time.time()
516
+
517
+ is_online = True
505
518
  dead_pool_indices = [
506
519
  i for i, s in enumerate(self._sandbox_pool)
507
520
  if s.status == interface.Sandbox.Status.OFFLINE
508
521
  ]
509
- if dead_pool_indices and not self._replace_dead_sandboxes(
510
- dead_pool_indices
511
- ):
522
+ replaced_indices = []
523
+
524
+ if dead_pool_indices:
525
+ replaced_indices = self._replace_dead_sandboxes(dead_pool_indices)
526
+ if not replaced_indices:
527
+ is_online = self.offline_duration < self.outage_grace_period
528
+
529
+ self._housekeep_counter += 1
530
+ duration = time.time() - housekeep_start_time
531
+ kwargs = dict(
532
+ dead_pool_indices=dead_pool_indices,
533
+ replaced_indices=replaced_indices,
534
+ offline_duration=self.offline_duration,
535
+ )
536
+ if is_online:
537
+ self.on_housekeep(duration, **kwargs)
538
+ time.sleep(self.housekeep_interval)
539
+ else:
512
540
  self.shutdown()
513
- self._housekeep_counter += 1
514
541
  self.on_housekeep(
515
- time.time() - housekeep_start_time,
542
+ duration,
516
543
  interface.EnvironmentOutageError(
517
- environment=self,
518
- offline_duration=self.offline_duration
519
- )
544
+ environment=self, offline_duration=self.offline_duration
545
+ ),
546
+ **kwargs
520
547
  )
521
- break
522
- self._housekeep_counter += 1
523
- self.on_housekeep(time.time() - housekeep_start_time)
524
- time.sleep(self.housekeep_interval)
525
548
 
526
- def _replace_dead_sandboxes(self, dead_pool_indices: list[int]) -> bool:
549
+ def _replace_dead_sandboxes(self, dead_pool_indices: list[int]) -> list[int]:
527
550
  """Replaces a dead sandbox with a new one.
528
551
 
529
552
  Args:
530
553
  dead_pool_indices: The indices of the dead sandboxes to replace.
531
554
 
532
555
  Returns:
533
- Whether all of the dead sandboxes are replaced successfully.
534
-
535
- Raises:
536
- interface.EnvironmentOutageError: If the XBox sandboxes cannot be created
537
- within the wait time specified by `xbox_outage_grace_period`.
556
+ Successfully replaced indices.
538
557
  """
539
558
  pg.logging.warning(
540
559
  '[%s]: %s maintenance: '
@@ -544,26 +563,42 @@ class BaseEnvironment(interface.Environment):
544
563
  len(dead_pool_indices),
545
564
  )
546
565
  def _replace(i: int):
547
- self._sandbox_pool[i] = self._bring_up_sandbox_with_retry(
548
- str(i), shutdown_env_upon_outage=False
549
- )
566
+ generation = int(self._sandbox_pool[i].id.sandbox_id.split(':')[1])
567
+ self._sandbox_pool[i] = self._bring_up_sandbox(f'{i}:{generation + 1}')
550
568
 
551
569
  # TODO(daiyip): Consider to loose the condition to allow some dead
552
570
  # sandboxes to be replaced successfully.
553
- return not any([
554
- error for _, _, error in lf.concurrent_map(
555
- _replace, dead_pool_indices,
556
- max_workers=min(
557
- self.pool_operation_max_parallelism,
558
- len(dead_pool_indices)
559
- ),
560
- )
561
- ])
571
+ replaced_indices = []
572
+ for index, _, error in lf.concurrent_map(
573
+ _replace, dead_pool_indices,
574
+ max_workers=min(
575
+ self.pool_operation_max_parallelism,
576
+ len(dead_pool_indices)
577
+ ),
578
+ ):
579
+ if error is None:
580
+ replaced_indices.append(index)
581
+
582
+ pg.logging.warning(
583
+ '[%s]: %s maintenance: '
584
+ '%d/%d dead sandbox(es) have been replaced with new ones. (slots=%s)',
585
+ self.id,
586
+ self.__class__.__name__,
587
+ len(replaced_indices),
588
+ len(dead_pool_indices),
589
+ replaced_indices
590
+ )
591
+ return replaced_indices
562
592
 
563
593
  #
564
594
  # Event handlers subclasses can override.
565
595
  #
566
596
 
597
+ def on_starting(self) -> None:
598
+ """Called when the environment is getting started."""
599
+ for handler in self.event_handlers:
600
+ handler.on_environment_starting(self)
601
+
567
602
  def on_start(
568
603
  self,
569
604
  duration: float, error: BaseException | None = None
@@ -575,15 +610,26 @@ class BaseEnvironment(interface.Environment):
575
610
  def on_housekeep(
576
611
  self,
577
612
  duration: float,
578
- error: BaseException | None = None
613
+ error: BaseException | None = None,
614
+ **kwargs
579
615
  ) -> None:
580
616
  """Called when the environment finishes a round of housekeeping."""
581
617
  housekeep_counter = self.housekeep_counter
582
618
  for handler in self.event_handlers:
583
- handler.on_environment_housekeep(self, housekeep_counter, duration, error)
619
+ handler.on_environment_housekeep(
620
+ self, housekeep_counter, duration, error, **kwargs
621
+ )
584
622
 
585
- def on_shutdown(self, error: BaseException | None = None) -> None:
623
+ def on_shutting_down(self) -> None:
624
+ """Called when the environment is shutting down."""
625
+ for handler in self.event_handlers:
626
+ handler.on_environment_shutting_down(self, self.offline_duration)
627
+
628
+ def on_shutdown(
629
+ self,
630
+ duration: float,
631
+ error: BaseException | None = None) -> None:
586
632
  """Called when the environment is shutdown."""
587
633
  lifetime = (time.time() - self.start_time) if self.start_time else 0.0
588
634
  for handler in self.event_handlers:
589
- handler.on_environment_shutdown(self, lifetime, error)
635
+ handler.on_environment_shutdown(self, duration, lifetime, error)
@@ -186,11 +186,12 @@ class BaseFeature(interface.Feature):
186
186
  def on_housekeep(
187
187
  self,
188
188
  duration: float,
189
- error: BaseException | None = None
189
+ error: BaseException | None = None,
190
+ **kwargs
190
191
  ) -> None:
191
192
  """Called when the feature has done housekeeping."""
192
193
  self.sandbox.on_feature_housekeep(
193
- self, self._housekeep_counter, duration, error
194
+ self, self._housekeep_counter, duration, error, **kwargs
194
195
  )
195
196
 
196
197
  def on_setup_session(
@@ -418,6 +418,7 @@ class BaseSandbox(interface.Sandbox):
418
418
  self.end_session(shutdown_sandbox=True)
419
419
  return
420
420
 
421
+ shutting_down_time = time.time()
421
422
  self._set_status(interface.Sandbox.Status.SHUTTING_DOWN)
422
423
 
423
424
  if (self._housekeep_thread is not None
@@ -429,7 +430,10 @@ class BaseSandbox(interface.Sandbox):
429
430
  try:
430
431
  self._shutdown()
431
432
  self._set_status(interface.Sandbox.Status.OFFLINE)
432
- self.on_shutdown(teardown_error)
433
+ self.on_shutdown(
434
+ duration=time.time() - shutting_down_time,
435
+ error=teardown_error
436
+ )
433
437
  shutdown_error = None
434
438
  except BaseException as e: # pylint: disable=broad-except
435
439
  shutdown_error = e
@@ -439,7 +443,10 @@ class BaseSandbox(interface.Sandbox):
439
443
  '[%s]: Sandbox shutdown with error: %s',
440
444
  self.id, e
441
445
  )
442
- self.on_shutdown(teardown_error or shutdown_error)
446
+ self.on_shutdown(
447
+ duration=time.time() - shutting_down_time,
448
+ error=teardown_error or shutdown_error
449
+ )
443
450
 
444
451
  # We raise non-state errors to the user following timely order, so the user
445
452
  # code could be surfaced and handled properly.
@@ -591,6 +598,7 @@ class BaseSandbox(interface.Sandbox):
591
598
  # Set sandbox status to EXITING_SESSION to avoid re-entry.
592
599
  self._set_status(self.Status.EXITING_SESSION)
593
600
  shutdown_sandbox = shutdown_sandbox or not self.reusable
601
+ ending_time = time.time()
594
602
 
595
603
  # Teardown features for the current session.
596
604
  end_session_error = self._end_session()
@@ -617,7 +625,9 @@ class BaseSandbox(interface.Sandbox):
617
625
  self.shutdown()
618
626
 
619
627
  # End session before setting up the next session.
620
- self.on_session_end(previous_session_id)
628
+ self.on_session_end(
629
+ previous_session_id, duration=time.time() - ending_time
630
+ )
621
631
 
622
632
  # Mark the sandbox as setting up to prevent it from being acquired by
623
633
  # other threads.
@@ -628,7 +638,9 @@ class BaseSandbox(interface.Sandbox):
628
638
  threading.Thread(target=_setup_next_session).start()
629
639
  else:
630
640
  # End session before reporting sandbox status change.
631
- self.on_session_end(previous_session_id)
641
+ self.on_session_end(
642
+ previous_session_id, duration=time.time() - ending_time
643
+ )
632
644
 
633
645
  # If shutdown is requested, mark the sandbox as acquired to prevent it
634
646
  # from being acquired by other threads.
@@ -639,7 +651,11 @@ class BaseSandbox(interface.Sandbox):
639
651
 
640
652
  # Otherwise, shutdown the sandbox.
641
653
  else:
642
- self.on_session_end(previous_session_id, self.state_errors[0])
654
+ self.on_session_end(
655
+ previous_session_id,
656
+ duration=time.time() - ending_time,
657
+ error=self.state_errors[0]
658
+ )
643
659
  self._set_status(interface.Sandbox.Status.ACQUIRED)
644
660
  shutdown_sandbox = True
645
661
 
@@ -690,7 +706,6 @@ class BaseSandbox(interface.Sandbox):
690
706
  while self._status not in (self.Status.SHUTTING_DOWN, self.Status.OFFLINE):
691
707
  housekeep_start = time.time()
692
708
  if self.keepalive_interval is not None:
693
-
694
709
  if time.time() - last_ping > self.keepalive_interval:
695
710
  try:
696
711
  self.ping()
@@ -762,25 +777,32 @@ class BaseSandbox(interface.Sandbox):
762
777
  time.time() - self._status_start_time
763
778
  )
764
779
 
765
- def on_shutdown(self, error: BaseException | None = None) -> None:
780
+ def on_shutdown(
781
+ self,
782
+ duration: float,
783
+ error: BaseException | None = None
784
+ ) -> None:
766
785
  """Called when the sandbox is shutdown."""
767
786
  if self._start_time is None:
768
787
  lifetime = 0.0
769
788
  else:
770
789
  lifetime = time.time() - self._start_time
771
790
  for handler in self._event_handlers:
772
- handler.on_sandbox_shutdown(self.environment, self, lifetime, error)
791
+ handler.on_sandbox_shutdown(
792
+ self.environment, self, duration, lifetime, error
793
+ )
773
794
 
774
795
  def on_housekeep(
775
796
  self,
776
797
  duration: float,
777
- error: BaseException | None = None
798
+ error: BaseException | None = None,
799
+ **kwargs
778
800
  ) -> None:
779
801
  """Called when the sandbox finishes a round of housekeeping."""
780
802
  counter = self._housekeep_counter
781
803
  for handler in self._event_handlers:
782
804
  handler.on_sandbox_housekeep(
783
- self.environment, self, counter, duration, error
805
+ self.environment, self, counter, duration, error, **kwargs
784
806
  )
785
807
 
786
808
  def on_feature_setup(
@@ -880,13 +902,14 @@ class BaseSandbox(interface.Sandbox):
880
902
  def on_session_end(
881
903
  self,
882
904
  session_id: str,
905
+ duration: float,
883
906
  error: BaseException | None = None
884
907
  ) -> None:
885
908
  """Called when the user session ends."""
886
909
  lifetime = time.time() - self._session_start_time
887
910
  for handler in self._event_handlers:
888
911
  handler.on_session_end(
889
- self.environment, self, session_id, lifetime, error
912
+ self.environment, self, session_id, duration, lifetime, error
890
913
  )
891
914
 
892
915