langfun 0.1.2.dev202510150805__tar.gz → 0.1.2.dev202510170805__tar.gz

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 (194) hide show
  1. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/PKG-INFO +3 -1
  2. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/__init__.py +5 -1
  3. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/agentic/__init__.py +4 -0
  4. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/agentic/action.py +278 -89
  5. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/agentic/action_test.py +33 -5
  6. langfun-0.1.2.dev202510170805/langfun/core/async_support.py +98 -0
  7. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/async_support_test.py +23 -0
  8. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/concurrent_test.py +8 -2
  9. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/progress_tracking_test.py +3 -0
  10. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/querying.py +12 -12
  11. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/querying_test.py +4 -4
  12. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun.egg-info/PKG-INFO +3 -1
  13. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun.egg-info/requires.txt +2 -0
  14. langfun-0.1.2.dev202510150805/langfun/core/async_support.py +0 -28
  15. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/LICENSE +0 -0
  16. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/README.md +0 -0
  17. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/__init__.py +0 -0
  18. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/__init__.py +0 -0
  19. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
  20. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
  21. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/drawing.py +0 -0
  22. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
  23. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/location.py +0 -0
  24. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/assistant/capabilities/gui/location_test.py +0 -0
  25. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/agentic/action_eval.py +0 -0
  26. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/agentic/action_eval_test.py +0 -0
  27. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/__init__.py +0 -0
  28. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/__init__.py +0 -0
  29. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/correction.py +0 -0
  30. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/correction_test.py +0 -0
  31. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/execution.py +0 -0
  32. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/execution_test.py +0 -0
  33. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/generation.py +0 -0
  34. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/generation_test.py +0 -0
  35. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/parsing.py +0 -0
  36. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/parsing_test.py +0 -0
  37. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/sandboxing.py +0 -0
  38. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/coding/python/sandboxing_test.py +0 -0
  39. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/component.py +0 -0
  40. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/component_test.py +0 -0
  41. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/concurrent.py +0 -0
  42. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/console.py +0 -0
  43. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/console_test.py +0 -0
  44. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/__init__.py +0 -0
  45. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/__init__.py +0 -0
  46. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/anthropic.py +0 -0
  47. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/anthropic_test.py +0 -0
  48. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/gemini.py +0 -0
  49. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/gemini_test.py +0 -0
  50. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/openai.py +0 -0
  51. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/data/conversion/openai_test.py +0 -0
  52. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/__init__.py +0 -0
  53. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/base.py +0 -0
  54. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/base_test.py +0 -0
  55. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/matching.py +0 -0
  56. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/matching_test.py +0 -0
  57. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/patching.py +0 -0
  58. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/patching_test.py +0 -0
  59. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/scoring.py +0 -0
  60. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/scoring_test.py +0 -0
  61. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/__init__.py +0 -0
  62. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/checkpointing.py +0 -0
  63. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/checkpointing_test.py +0 -0
  64. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/eval_test_helper.py +0 -0
  65. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/evaluation.py +0 -0
  66. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/evaluation_test.py +0 -0
  67. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/example.py +0 -0
  68. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/example_test.py +0 -0
  69. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/experiment.py +0 -0
  70. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/experiment_test.py +0 -0
  71. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/metric_values.py +0 -0
  72. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/metric_values_test.py +0 -0
  73. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/metrics.py +0 -0
  74. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/metrics_test.py +0 -0
  75. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/progress.py +0 -0
  76. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/progress_test.py +0 -0
  77. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/progress_tracking.py +0 -0
  78. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/reporting.py +0 -0
  79. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/reporting_test.py +0 -0
  80. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/runners.py +0 -0
  81. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/eval/v2/runners_test.py +0 -0
  82. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/langfunc.py +0 -0
  83. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/langfunc_test.py +0 -0
  84. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/language_model.py +0 -0
  85. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/language_model_test.py +0 -0
  86. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/__init__.py +0 -0
  87. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/anthropic.py +0 -0
  88. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/anthropic_test.py +0 -0
  89. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/azure_openai.py +0 -0
  90. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/azure_openai_test.py +0 -0
  91. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/cache/__init__.py +0 -0
  92. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/cache/base.py +0 -0
  93. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/cache/in_memory.py +0 -0
  94. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/cache/in_memory_test.py +0 -0
  95. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/compositional.py +0 -0
  96. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/compositional_test.py +0 -0
  97. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/deepseek.py +0 -0
  98. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/deepseek_test.py +0 -0
  99. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/fake.py +0 -0
  100. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/fake_test.py +0 -0
  101. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/gemini.py +0 -0
  102. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/gemini_test.py +0 -0
  103. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/google_genai.py +0 -0
  104. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/google_genai_test.py +0 -0
  105. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/groq.py +0 -0
  106. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/groq_test.py +0 -0
  107. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/llama_cpp.py +0 -0
  108. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/llama_cpp_test.py +0 -0
  109. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/openai.py +0 -0
  110. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/openai_compatible.py +0 -0
  111. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/openai_compatible_test.py +0 -0
  112. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/openai_test.py +0 -0
  113. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/rest.py +0 -0
  114. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/rest_test.py +0 -0
  115. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/vertexai.py +0 -0
  116. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/llms/vertexai_test.py +0 -0
  117. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/logging.py +0 -0
  118. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/logging_test.py +0 -0
  119. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/memories/__init__.py +0 -0
  120. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/memories/conversation_history.py +0 -0
  121. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/memories/conversation_history_test.py +0 -0
  122. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/memory.py +0 -0
  123. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/message.py +0 -0
  124. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/message_test.py +0 -0
  125. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/__init__.py +0 -0
  126. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/audio.py +0 -0
  127. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/audio_test.py +0 -0
  128. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/image.py +0 -0
  129. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/image_test.py +0 -0
  130. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/mime.py +0 -0
  131. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/mime_test.py +0 -0
  132. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/pdf.py +0 -0
  133. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/pdf_test.py +0 -0
  134. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/video.py +0 -0
  135. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modalities/video_test.py +0 -0
  136. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modality.py +0 -0
  137. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/modality_test.py +0 -0
  138. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/natural_language.py +0 -0
  139. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/natural_language_test.py +0 -0
  140. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/sampling.py +0 -0
  141. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/sampling_test.py +0 -0
  142. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/__init__.py +0 -0
  143. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/completion.py +0 -0
  144. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/completion_test.py +0 -0
  145. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/description.py +0 -0
  146. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/description_test.py +0 -0
  147. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/function_generation.py +0 -0
  148. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/function_generation_test.py +0 -0
  149. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/mapping.py +0 -0
  150. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/mapping_test.py +0 -0
  151. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/parsing.py +0 -0
  152. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/parsing_test.py +0 -0
  153. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/schema.py +0 -0
  154. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/schema_generation.py +0 -0
  155. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/schema_generation_test.py +0 -0
  156. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/schema_test.py +0 -0
  157. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/scoring.py +0 -0
  158. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/scoring_test.py +0 -0
  159. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/tokenization.py +0 -0
  160. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/structured/tokenization_test.py +0 -0
  161. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/subscription.py +0 -0
  162. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/subscription_test.py +0 -0
  163. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/template.py +0 -0
  164. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/template_test.py +0 -0
  165. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/__init__.py +0 -0
  166. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/completion.py +0 -0
  167. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/completion_test.py +0 -0
  168. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/conversation.py +0 -0
  169. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/conversation_test.py +0 -0
  170. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/demonstration.py +0 -0
  171. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/demonstration_test.py +0 -0
  172. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/selfplay.py +0 -0
  173. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/core/templates/selfplay_test.py +0 -0
  174. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/__init__.py +0 -0
  175. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/base_environment.py +0 -0
  176. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/base_feature.py +0 -0
  177. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/base_sandbox.py +0 -0
  178. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/base_test.py +0 -0
  179. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/event_handlers/__init__.py +0 -0
  180. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/event_handlers/base.py +0 -0
  181. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/event_handlers/event_logger.py +0 -0
  182. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/event_handlers/event_logger_test.py +0 -0
  183. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/event_handlers/metric_writer.py +0 -0
  184. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/event_handlers/metric_writer_test.py +0 -0
  185. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/interface.py +0 -0
  186. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/interface_test.py +0 -0
  187. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/load_balancers.py +0 -0
  188. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/load_balancers_test.py +0 -0
  189. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun/env/test_utils.py +0 -0
  190. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun.egg-info/SOURCES.txt +0 -0
  191. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun.egg-info/dependency_links.txt +0 -0
  192. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/langfun.egg-info/top_level.txt +0 -0
  193. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/setup.cfg +0 -0
  194. {langfun-0.1.2.dev202510150805 → langfun-0.1.2.dev202510170805}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510150805
3
+ Version: 0.1.2.dev202510170805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -21,11 +21,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Topic :: Software Development :: Libraries
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
+ Requires-Dist: anyio>=4.7.0
24
25
  Requires-Dist: jinja2>=3.1.2
25
26
  Requires-Dist: puremagic>=1.20
26
27
  Requires-Dist: pyglove>=0.4.5.dev202507140812
27
28
  Requires-Dist: requests>=2.31.0
28
29
  Provides-Extra: all
30
+ Requires-Dist: anyio>=4.7.0; extra == "all"
29
31
  Requires-Dist: jinja2>=3.1.2; extra == "all"
30
32
  Requires-Dist: puremagic>=1.20; extra == "all"
31
33
  Requires-Dist: pyglove>=0.4.5.dev202507140812; extra == "all"
@@ -40,9 +40,13 @@ from langfun.core.component import context
40
40
  as_context = context
41
41
  use_context = context
42
42
 
43
- # Invoke a callable object asynchronously.
43
+ # Support for async IO.
44
44
  from langfun.core.async_support import invoke_async
45
45
 
46
+ # Adaptors for async function/context manager to sync versions.
47
+ from langfun.core.async_support import invoke_sync
48
+ from langfun.core.async_support import sync_context_manager
49
+
46
50
  # Shortcut function for overriding components attributes, usually for
47
51
  # override settings.
48
52
  from langfun.core.component import use_settings
@@ -26,6 +26,10 @@ from langfun.core.agentic.action import ExecutionTrace
26
26
  from langfun.core.agentic.action import ParallelExecutions
27
27
  from langfun.core.agentic.action import Session
28
28
 
29
+ from langfun.core.agentic.action import SessionEventHandler
30
+ from langfun.core.agentic.action import SessionEventHandlerChain
31
+ from langfun.core.agentic.action import SessionLogging
32
+
29
33
  from langfun.core.agentic.action_eval import ActionEval
30
34
  from langfun.core.agentic.action_eval import ActionEvalV1
31
35
 
@@ -15,6 +15,7 @@
15
15
 
16
16
  import abc
17
17
  import contextlib
18
+ import dataclasses
18
19
  import functools
19
20
  import threading
20
21
  import time
@@ -1161,26 +1162,271 @@ class RootAction(Action):
1161
1162
  raise NotImplementedError('Shall not be called.')
1162
1163
 
1163
1164
 
1165
+ class SessionEventHandler:
1166
+ """Interface for handling session events."""
1167
+
1168
+ def on_session_start(
1169
+ self,
1170
+ session: 'Session'
1171
+ ) -> None:
1172
+ """Called when a session starts."""
1173
+
1174
+ def on_session_end(
1175
+ self,
1176
+ session: 'Session'
1177
+ ) -> None:
1178
+ """Called when a session ends."""
1179
+
1180
+ def on_action_start(
1181
+ self,
1182
+ session: 'Session',
1183
+ action: ActionInvocation
1184
+ ) -> None:
1185
+ """Called when an action starts."""
1186
+
1187
+ def on_action_end(
1188
+ self,
1189
+ session: 'Session',
1190
+ action: ActionInvocation
1191
+ ) -> None:
1192
+ """Called when an action ends."""
1193
+
1194
+ def on_action_progress(
1195
+ self,
1196
+ session: 'Session',
1197
+ action: ActionInvocation,
1198
+ title: str,
1199
+ **kwargs
1200
+ ) -> None:
1201
+ """Called when an action progress is updated."""
1202
+
1203
+ def on_query_start(
1204
+ self,
1205
+ session: 'Session',
1206
+ action: ActionInvocation,
1207
+ query: lf_structured.QueryInvocation,
1208
+ ) -> None:
1209
+ """Called when a query starts."""
1210
+
1211
+ def on_query_end(
1212
+ self,
1213
+ session: 'Session',
1214
+ action: ActionInvocation,
1215
+ query: lf_structured.QueryInvocation,
1216
+ ) -> None:
1217
+ """Called when a query ends."""
1218
+
1219
+
1220
+ @dataclasses.dataclass
1221
+ class SessionEventHandlerChain(SessionEventHandler):
1222
+ """A session event handler that chains multiple event handlers."""
1223
+
1224
+ handlers: list[SessionEventHandler]
1225
+
1226
+ def on_session_start(self, session: 'Session') -> None:
1227
+ """Called when a session starts."""
1228
+ for handler in self.handlers:
1229
+ handler.on_session_start(session)
1230
+
1231
+ def on_session_end(self, session: 'Session') -> None:
1232
+ """Called when a session ends."""
1233
+ for handler in self.handlers:
1234
+ handler.on_session_end(session)
1235
+
1236
+ def on_action_start(
1237
+ self,
1238
+ session: 'Session',
1239
+ action: ActionInvocation) -> None:
1240
+ """Called when an action starts."""
1241
+ for handler in self.handlers:
1242
+ handler.on_action_start(session, action)
1243
+
1244
+ def on_action_end(
1245
+ self,
1246
+ session: 'Session',
1247
+ action: ActionInvocation) -> None:
1248
+ """Called when an action ends."""
1249
+ for handler in self.handlers:
1250
+ handler.on_action_end(session, action)
1251
+
1252
+ def on_action_progress(
1253
+ self,
1254
+ session: 'Session',
1255
+ action: ActionInvocation,
1256
+ title: str,
1257
+ **kwargs
1258
+ ) -> None:
1259
+ """Called when an action progress is updated."""
1260
+ for handler in self.handlers:
1261
+ handler.on_action_progress(session, action, title, **kwargs)
1262
+
1263
+ def on_query_start(
1264
+ self,
1265
+ session: 'Session',
1266
+ action: ActionInvocation,
1267
+ query: lf_structured.QueryInvocation,
1268
+ ) -> None:
1269
+ """Called when a query starts."""
1270
+ for handler in self.handlers:
1271
+ handler.on_query_start(session, action, query)
1272
+
1273
+ def on_query_end(
1274
+ self,
1275
+ session: 'Session',
1276
+ action: ActionInvocation,
1277
+ query: lf_structured.QueryInvocation,
1278
+ ) -> None:
1279
+ """Called when a query ends."""
1280
+ for handler in self.handlers:
1281
+ handler.on_query_end(session, action, query)
1282
+
1283
+
1284
+ @dataclasses.dataclass
1285
+ class SessionLogging(SessionEventHandler):
1286
+ """An event handler that logs Session events."""
1287
+
1288
+ verbose: bool = False
1289
+
1290
+ def on_session_end(self, session: 'Session'):
1291
+ if session.has_error:
1292
+ session.error(
1293
+ f'Trajectory failed in {session.elapse:.2f} seconds.',
1294
+ error=session.final_error,
1295
+ metadata=session.root.metadata,
1296
+ keep=True,
1297
+ )
1298
+ elif self.verbose:
1299
+ session.info(
1300
+ f'Trajectory succeeded in {session.elapse:.2f} seconds.',
1301
+ result=session.final_result,
1302
+ metadata=session.root.metadata,
1303
+ keep=False,
1304
+ )
1305
+
1306
+ def on_action_start(
1307
+ self,
1308
+ session: 'Session',
1309
+ action: ActionInvocation
1310
+ ) -> None:
1311
+ if self.verbose:
1312
+ session.info(
1313
+ 'Action execution started.',
1314
+ action=action.action,
1315
+ keep=False,
1316
+ )
1317
+
1318
+ def on_action_end(
1319
+ self,
1320
+ session: 'Session',
1321
+ action: ActionInvocation
1322
+ ) -> None:
1323
+ if action.has_error:
1324
+ session.warning(
1325
+ (
1326
+ f'Action execution failed in '
1327
+ f'{action.execution.elapse:.2f} seconds.'
1328
+ ),
1329
+ action=action.action,
1330
+ error=action.error,
1331
+ keep=True,
1332
+ )
1333
+ elif self.verbose:
1334
+ session.info(
1335
+ (
1336
+ f'Action execution succeeded in '
1337
+ f'{action.execution.elapse:.2f} seconds.'
1338
+ ),
1339
+ action=action.action,
1340
+ result=action.result,
1341
+ keep=False,
1342
+ )
1343
+
1344
+ def on_query_start(
1345
+ self,
1346
+ session: 'Session',
1347
+ action: ActionInvocation,
1348
+ query: lf_structured.QueryInvocation,
1349
+ ) -> None:
1350
+ if self.verbose:
1351
+ session.info(
1352
+ 'Querying LLM started.',
1353
+ lm=query.lm.model_id,
1354
+ output_type=(
1355
+ lf_structured.annotation(query.schema.spec)
1356
+ if query.schema is not None else None
1357
+ ),
1358
+ keep=False,
1359
+ )
1360
+
1361
+ def on_query_end(
1362
+ self,
1363
+ session: 'Session',
1364
+ action: ActionInvocation,
1365
+ query: lf_structured.QueryInvocation,
1366
+ ) -> None:
1367
+ if query.has_error:
1368
+ session.warning(
1369
+ (
1370
+ f'Querying LLM failed in '
1371
+ f'{time.time() - query.start_time:.2f} seconds.'
1372
+ ),
1373
+ lm=query.lm.model_id,
1374
+ output_type=(
1375
+ lf_structured.annotation(query.schema.spec)
1376
+ if query.schema is not None else None
1377
+ ),
1378
+ error=query.error,
1379
+ keep=True,
1380
+ )
1381
+ elif self.verbose:
1382
+ session.info(
1383
+ (
1384
+ f'Querying LLM succeeded in '
1385
+ f'{time.time() - query.start_time:.2f} seconds.'
1386
+ ),
1387
+ lm=query.lm.model_id,
1388
+ output_type=(
1389
+ lf_structured.annotation(query.schema.spec)
1390
+ if query.schema is not None else None
1391
+ ),
1392
+ keep=False,
1393
+ )
1394
+
1395
+
1164
1396
  class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1165
1397
  """Session for performing an agentic task."""
1166
1398
 
1167
1399
  root: Annotated[
1168
1400
  ActionInvocation,
1169
1401
  'The root action invocation of the session.'
1170
- ] = ActionInvocation(RootAction())
1402
+ ]
1171
1403
 
1172
1404
  id: Annotated[
1173
1405
  str | None,
1174
- 'An optional identifier for the sessin, which will be used for logging.'
1175
- ] = None
1406
+ 'An optional identifier for the session, which will be used for logging.'
1407
+ ]
1176
1408
 
1177
- verbose: Annotated[
1178
- bool,
1179
- (
1180
- 'If True, the session will be logged with verbose action and query '
1181
- 'activities.'
1182
- )
1183
- ] = False
1409
+ event_handler: Annotated[
1410
+ SessionEventHandler,
1411
+ 'Event handler for the session.'
1412
+ ]
1413
+
1414
+ @pg.explicit_method_override
1415
+ def __init__(
1416
+ self,
1417
+ id: str | None = None, # pylint: disable=redefined-builtin
1418
+ *,
1419
+ verbose: bool = False,
1420
+ event_handler: SessionEventHandler | None = None,
1421
+ root: ActionInvocation | None = None,
1422
+ **kwargs
1423
+ ):
1424
+ super().__init__(
1425
+ id=id,
1426
+ root=root or ActionInvocation(RootAction()),
1427
+ event_handler=event_handler or SessionLogging(verbose=verbose),
1428
+ **kwargs
1429
+ )
1184
1430
 
1185
1431
  #
1186
1432
  # Shortcut methods for accessing the root action invocation.
@@ -1271,6 +1517,7 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1271
1517
  def start(self) -> None:
1272
1518
  """Starts the session."""
1273
1519
  self.root.execution.start()
1520
+ self.event_handler.on_session_start(self)
1274
1521
 
1275
1522
  def end(
1276
1523
  self,
@@ -1279,21 +1526,8 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1279
1526
  metadata: dict[str, Any] | None = None,
1280
1527
  ) -> None:
1281
1528
  """Ends the session."""
1282
- if error is not None:
1283
- self.error(
1284
- f'Trajectory failed in {self.elapse:.2f} seconds.',
1285
- error=error,
1286
- metadata=metadata,
1287
- keep=True,
1288
- )
1289
- elif self.verbose:
1290
- self.info(
1291
- f'Trajectory succeeded in {self.elapse:.2f} seconds.',
1292
- result=result,
1293
- metadata=metadata,
1294
- keep=False,
1295
- )
1296
1529
  self.root.end(result, error, metadata)
1530
+ self.event_handler.on_session_end(self)
1297
1531
 
1298
1532
  def check_execution_time(self) -> None:
1299
1533
  """Checks the execution time of the current action."""
@@ -1312,6 +1546,20 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1312
1546
  'seconds.'
1313
1547
  )
1314
1548
 
1549
+ def update_progress(self, title: str, **kwargs: Any) -> None:
1550
+ """Update the progress of current action's execution.
1551
+
1552
+ Args:
1553
+ title: The title of the progress update.
1554
+ **kwargs: Additional keyword arguments to pass to the event handler.
1555
+ """
1556
+ self.event_handler.on_action_progress(
1557
+ self,
1558
+ self._current_action,
1559
+ title,
1560
+ **kwargs
1561
+ )
1562
+
1315
1563
  def __enter__(self):
1316
1564
  """Enters the session."""
1317
1565
  self.start()
@@ -1371,34 +1619,10 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1371
1619
  self._current_execution = invocation.execution
1372
1620
  # Start the execution of the current action.
1373
1621
  self._current_action.start()
1374
- if self.verbose:
1375
- self.info(
1376
- 'Action execution started.',
1377
- action=invocation.action,
1378
- keep=False,
1379
- )
1622
+ self.event_handler.on_action_start(self, self._current_action)
1380
1623
  yield invocation
1381
1624
  finally:
1382
- if invocation.has_error:
1383
- self.warning(
1384
- (
1385
- f'Action execution failed in '
1386
- f'{invocation.execution.elapse:.2f} seconds.'
1387
- ),
1388
- action=invocation.action,
1389
- error=invocation.error,
1390
- keep=True,
1391
- )
1392
- elif self.verbose:
1393
- self.info(
1394
- (
1395
- f'Action execution succeeded in '
1396
- f'{invocation.execution.elapse:.2f} seconds.'
1397
- ),
1398
- action=invocation.action,
1399
- result=invocation.result,
1400
- keep=False,
1401
- )
1625
+ self.event_handler.on_action_end(self, self._current_action)
1402
1626
  self._current_execution = parent_execution
1403
1627
  self._current_action = parent_action
1404
1628
 
@@ -1446,51 +1670,16 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1446
1670
  skip_notification=False, raise_on_no_change=False
1447
1671
  )
1448
1672
  execution.append(invocation)
1449
- if self.verbose:
1450
- self.info(
1451
- 'Querying LLM started.',
1452
- lm=invocation.lm.model_id,
1453
- output_type=(
1454
- lf_structured.annotation(invocation.schema.spec)
1455
- if invocation.schema is not None else None
1456
- ),
1457
- keep=False,
1458
- )
1673
+ self.event_handler.on_query_start(self, self._current_action, invocation)
1459
1674
 
1460
1675
  def _query_end(invocation: lf_structured.QueryInvocation):
1461
1676
  self._current_execution.merge_usage_summary(invocation.usage_summary)
1462
- if invocation.has_error:
1463
- self.warning(
1464
- (
1465
- f'Querying LLM failed in '
1466
- f'{time.time() - invocation.start_time:.2f} seconds.'
1467
- ),
1468
- lm=invocation.lm.model_id,
1469
- output_type=(
1470
- lf_structured.annotation(invocation.schema.spec)
1471
- if invocation.schema is not None else None
1472
- ),
1473
- error=invocation.error,
1474
- keep=True,
1475
- )
1476
- elif self.verbose:
1477
- self.info(
1478
- (
1479
- f'Querying LLM succeeded in '
1480
- f'{time.time() - invocation.start_time:.2f} seconds.'
1481
- ),
1482
- lm=invocation.lm.model_id,
1483
- output_type=(
1484
- lf_structured.annotation(invocation.schema.spec)
1485
- if invocation.schema is not None else None
1486
- ),
1487
- keep=False,
1488
- )
1677
+ self.event_handler.on_query_end(self, self._current_action, invocation)
1489
1678
 
1490
1679
  with self.track_phase(phase), lf_structured.track_queries(
1491
1680
  include_child_scopes=False,
1492
- start_callabck=_query_start,
1493
- end_callabck=_query_end,
1681
+ start_callback=_query_start,
1682
+ end_callback=_query_end,
1494
1683
  ) as queries:
1495
1684
  try:
1496
1685
  yield queries
@@ -34,6 +34,7 @@ class Bar(action_lib.Action):
34
34
  time.sleep(self.simulate_execution_time)
35
35
  session.query('bar', lm=lm)
36
36
  session.add_metadata(note='bar')
37
+ session.update_progress('Query completed')
37
38
  if self.simulate_action_error:
38
39
  raise ValueError('Bar error')
39
40
  return 2 + pg.contextual_value('baz', 0)
@@ -128,7 +129,7 @@ class SessionTest(unittest.TestCase):
128
129
  self.assertIsNone(foo.result)
129
130
  self.assertIsNone(foo.metadata)
130
131
 
131
- session = action_lib.Session(id='agent@1')
132
+ session = action_lib.Session(id='agent@1', verbose=True)
132
133
  self.assertEqual(session.id, 'agent@1')
133
134
  self.assertFalse(session.has_started)
134
135
  self.assertFalse(session.has_stopped)
@@ -137,7 +138,7 @@ class SessionTest(unittest.TestCase):
137
138
  _ = session.to_html()
138
139
 
139
140
  with session:
140
- result = foo(session, lm=lm, verbose=True)
141
+ result = foo(session, lm=lm)
141
142
 
142
143
  self.assertTrue(session.has_started)
143
144
  self.assertTrue(session.has_stopped)
@@ -375,7 +376,7 @@ class SessionTest(unittest.TestCase):
375
376
  self.assertFalse(session.has_stopped)
376
377
 
377
378
  session.start()
378
- result = foo(session, lm=lm, verbose=True)
379
+ result = foo(session, lm=lm)
379
380
  session.end(result)
380
381
 
381
382
  self.assertTrue(session.has_started)
@@ -395,7 +396,7 @@ class SessionTest(unittest.TestCase):
395
396
  session = action_lib.Session(id='agent@1')
396
397
  with self.assertRaisesRegex(ValueError, 'Bar error'):
397
398
  with session:
398
- foo(session, lm=lm, verbose=True)
399
+ foo(session, lm=lm)
399
400
  self.assertTrue(session.has_started)
400
401
  self.assertTrue(session.has_stopped)
401
402
  self.assertTrue(session.has_error)
@@ -408,7 +409,7 @@ class SessionTest(unittest.TestCase):
408
409
  foo = Foo(1, simulate_action_error=True)
409
410
  session = action_lib.Session(id='agent@1')
410
411
  with self.assertRaisesRegex(ValueError, 'Please call `Session.start'):
411
- foo(session, lm=lm, verbose=True)
412
+ foo(session, lm=lm)
412
413
 
413
414
  def test_succeed_with_multiple_actions(self):
414
415
  lm = fake.StaticResponse('lm response')
@@ -489,6 +490,33 @@ class SessionTest(unittest.TestCase):
489
490
  ):
490
491
  foo(lm=lm, max_execution_time=1.0)
491
492
 
493
+ def test_event_handler(self):
494
+
495
+ class MyActionHandler(pg.Object, action_lib.SessionEventHandler):
496
+ def _on_bound(self):
497
+ super()._on_bound()
498
+ self.progresses = []
499
+
500
+ def on_action_progress(self, session, action, title, **kwargs):
501
+ self.progresses.append((action.id, title))
502
+
503
+ handler = MyActionHandler()
504
+ session = action_lib.Session(
505
+ id='agent@1',
506
+ event_handler=action_lib.SessionEventHandlerChain(
507
+ handlers=[handler, action_lib.SessionLogging()]
508
+ )
509
+ )
510
+ bar = Bar()
511
+ with session:
512
+ bar(session, lm=fake.StaticResponse('lm response'))
513
+ session.update_progress('Trajectory completed')
514
+
515
+ self.assertEqual(handler.progresses, [
516
+ ('agent@1:/a1', 'Query completed'),
517
+ ('agent@1:', 'Trajectory completed'),
518
+ ])
519
+
492
520
  def test_log(self):
493
521
  session = action_lib.Session()
494
522
  session.debug('hi', x=1, y=2)
@@ -0,0 +1,98 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Utility for async IO in Langfun."""
15
+
16
+ import asyncio
17
+ import contextlib
18
+ from typing import Any, Awaitable, Callable, Iterator
19
+ import anyio
20
+ import pyglove as pg
21
+
22
+
23
+ async def invoke_async(
24
+ sync_callable: Callable[..., Any], *args, **kwargs
25
+ ) -> Any:
26
+ """Invokes a callable asynchronously with `lf.context` manager enabled."""
27
+ return await asyncio.to_thread(
28
+ # Enable `lf.context` manager for async calls.
29
+ pg.with_contextual_override(sync_callable), *args, **kwargs
30
+ )
31
+
32
+
33
+ def invoke_sync(
34
+ async_callable: Callable[..., Awaitable[Any]],
35
+ *args,
36
+ **kwargs
37
+ ) -> Any:
38
+ """Invokes a async callable synchronously."""
39
+ async def _invoke():
40
+ return await async_callable(*args, **kwargs)
41
+ invoke_fn = pg.with_contextual_override(_invoke)
42
+ blocking_portal = pg.utils.thread_local_get('__blocking_portal__', None)
43
+ if blocking_portal is None:
44
+ return anyio.run(invoke_fn)
45
+ return blocking_portal.call(invoke_fn)
46
+
47
+
48
+ @contextlib.contextmanager
49
+ def sync_context_manager(
50
+ async_context_manager: contextlib.AbstractAsyncContextManager[Any]
51
+ ) -> Iterator[Any]:
52
+ """Adapts an async context manager to a sync context manager.
53
+
54
+ sync_context_manager installs a blocking portal in current thread to run the
55
+ async context manager in a blocking way. It's useful for running async code in
56
+ sync context managers, e.g. `sync_context_manager` can be nested and share the
57
+ same event loop.
58
+
59
+ Example:
60
+
61
+ ```python
62
+ @contextlib.asynccontextmanager
63
+ async def foo(x):
64
+ try:
65
+ yield x
66
+ finally:
67
+ pass
68
+
69
+ with lf.sync_context_manager(foo(x)) as x
70
+ with lf.sync_context_manager(foo(y)) as y:
71
+ ...
72
+ ```
73
+
74
+ Args:
75
+ async_context_manager: The async context manager to adapt.
76
+
77
+ Yields:
78
+ The value yielded by the async context manager.
79
+ """
80
+ blocking_portal = pg.utils.thread_local_get('__blocking_portal__', None)
81
+ portal_exit_stack = None
82
+
83
+ try:
84
+ if blocking_portal is None:
85
+ portal_exit_stack = contextlib.ExitStack()
86
+ blocking_portal = portal_exit_stack.enter_context(
87
+ anyio.from_thread.start_blocking_portal()
88
+ )
89
+ pg.utils.thread_local_set('__blocking_portal__', blocking_portal)
90
+ context_manager = blocking_portal.wrap_async_context_manager(
91
+ async_context_manager
92
+ )
93
+ with context_manager as value:
94
+ yield value
95
+ finally:
96
+ if portal_exit_stack is not None:
97
+ portal_exit_stack.close()
98
+ pg.utils.thread_local_del('__blocking_portal__')
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import asyncio
16
+ import contextlib
16
17
  import time
17
18
  import unittest
18
19
 
@@ -34,6 +35,28 @@ class AsyncSupportTest(unittest.TestCase):
34
35
  with pg.contextual_override(z=3):
35
36
  self.assertEqual(asyncio.run(r), 6)
36
37
 
38
+ def test_invoke_sync(self):
39
+ @contextlib.asynccontextmanager
40
+ async def bar(x):
41
+ try:
42
+ yield x
43
+ finally:
44
+ pass
45
+
46
+ async def foo(x, *, y):
47
+ time.sleep(2)
48
+ return x + y + pg.contextual_value('z', 0)
49
+
50
+ with pg.contextual_override(z=3):
51
+ with async_support.sync_context_manager(bar(1)) as x:
52
+ self.assertEqual(x, 1)
53
+ with async_support.sync_context_manager(bar(2)) as y:
54
+ self.assertEqual(y, 2)
55
+ self.assertEqual(async_support.invoke_sync(foo, 1, y=2), 6)
56
+
57
+ with pg.contextual_override(z=2):
58
+ self.assertEqual(async_support.invoke_sync(foo, 1, y=2), 5)
59
+
37
60
 
38
61
  if __name__ == '__main__':
39
62
  unittest.main()