langfun 0.1.2.dev202511070805__tar.gz → 0.1.2.dev202512050805__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 (225) hide show
  1. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/PKG-INFO +1 -1
  2. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/__init__.py +1 -0
  3. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/agentic/__init__.py +4 -1
  4. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/agentic/action.py +340 -17
  5. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/agentic/action_test.py +124 -21
  6. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/gemini.py +2 -0
  7. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/gemini_test.py +36 -0
  8. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/base.py +1 -1
  9. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/base_test.py +5 -5
  10. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/__init__.py +2 -0
  11. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/checkpointing.py +47 -4
  12. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/checkpointing_test.py +9 -2
  13. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/config_saver.py +37 -0
  14. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/config_saver_test.py +36 -0
  15. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/eval_test_helper.py +103 -2
  16. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/evaluation.py +39 -3
  17. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/evaluation_test.py +9 -3
  18. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/example.py +29 -30
  19. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/example_test.py +16 -8
  20. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/experiment.py +44 -12
  21. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/experiment_test.py +19 -0
  22. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/metric_values.py +8 -0
  23. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/metric_values_test.py +32 -0
  24. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/metrics.py +124 -40
  25. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/metrics_test.py +39 -18
  26. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/progress.py +22 -0
  27. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/progress_test.py +27 -0
  28. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/progress_tracking.py +13 -5
  29. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/progress_tracking_test.py +3 -1
  30. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/reporting.py +78 -73
  31. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/v2/reporting_test.py +24 -6
  32. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/__init__.py +30 -0
  33. langfun-0.1.2.dev202511070805/langfun/core/eval/v2/runners.py → langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/base.py +64 -192
  34. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/beam.py +354 -0
  35. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/beam_test.py +153 -0
  36. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/ckpt_monitor.py +350 -0
  37. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/ckpt_monitor_test.py +213 -0
  38. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/debug.py +40 -0
  39. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/debug_test.py +76 -0
  40. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/parallel.py +243 -0
  41. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/parallel_test.py +182 -0
  42. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/sequential.py +47 -0
  43. langfun-0.1.2.dev202512050805/langfun/core/eval/v2/runners/sequential_test.py +169 -0
  44. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/langfunc_test.py +3 -3
  45. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/language_model.py +38 -5
  46. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/language_model_test.py +45 -0
  47. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/__init__.py +3 -0
  48. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/gemini.py +50 -8
  49. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/gemini_test.py +110 -0
  50. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/google_genai.py +5 -0
  51. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/openai.py +27 -0
  52. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/vertexai.py +7 -0
  53. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/tool.py +1 -3
  54. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/image.py +54 -2
  55. langfun-0.1.2.dev202512050805/langfun/core/modalities/image_test.py +224 -0
  56. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/mime.py +2 -0
  57. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/mime_test.py +11 -0
  58. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/__init__.py +2 -24
  59. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/mapping.py +7 -9
  60. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/parsing.py +5 -5
  61. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/querying.py +1 -1
  62. langfun-0.1.2.dev202512050805/langfun/core/structured/schema/__init__.py +49 -0
  63. langfun-0.1.2.dev202511070805/langfun/core/structured/schema.py → langfun-0.1.2.dev202512050805/langfun/core/structured/schema/base.py +112 -495
  64. langfun-0.1.2.dev202512050805/langfun/core/structured/schema/base_test.py +531 -0
  65. langfun-0.1.2.dev202512050805/langfun/core/structured/schema/json.py +174 -0
  66. langfun-0.1.2.dev202512050805/langfun/core/structured/schema/json_test.py +121 -0
  67. langfun-0.1.2.dev202512050805/langfun/core/structured/schema/python.py +316 -0
  68. langfun-0.1.2.dev202512050805/langfun/core/structured/schema/python_test.py +410 -0
  69. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/scoring.py +2 -2
  70. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/tokenization.py +2 -2
  71. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/base_environment.py +1 -1
  72. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/interface_test.py +2 -0
  73. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/load_balancers_test.py +2 -0
  74. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/test_utils.py +10 -0
  75. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun.egg-info/PKG-INFO +1 -1
  76. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun.egg-info/SOURCES.txt +21 -4
  77. langfun-0.1.2.dev202511070805/langfun/core/eval/v2/runners_test.py +0 -346
  78. langfun-0.1.2.dev202511070805/langfun/core/modalities/image_test.py +0 -108
  79. langfun-0.1.2.dev202511070805/langfun/core/structured/schema_test.py +0 -982
  80. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/LICENSE +0 -0
  81. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/README.md +0 -0
  82. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/__init__.py +0 -0
  83. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/__init__.py +0 -0
  84. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
  85. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
  86. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/drawing.py +0 -0
  87. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
  88. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/location.py +0 -0
  89. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/assistant/capabilities/gui/location_test.py +0 -0
  90. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/agentic/action_eval.py +0 -0
  91. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/agentic/action_eval_test.py +0 -0
  92. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/async_support.py +0 -0
  93. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/async_support_test.py +0 -0
  94. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/__init__.py +0 -0
  95. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/__init__.py +0 -0
  96. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/correction.py +0 -0
  97. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/correction_test.py +0 -0
  98. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/execution.py +0 -0
  99. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/execution_test.py +0 -0
  100. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/generation.py +0 -0
  101. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/generation_test.py +0 -0
  102. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/parsing.py +0 -0
  103. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/parsing_test.py +0 -0
  104. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/sandboxing.py +0 -0
  105. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/coding/python/sandboxing_test.py +0 -0
  106. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/component.py +0 -0
  107. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/component_test.py +0 -0
  108. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/concurrent.py +0 -0
  109. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/concurrent_test.py +0 -0
  110. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/console.py +0 -0
  111. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/console_test.py +0 -0
  112. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/__init__.py +0 -0
  113. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/__init__.py +0 -0
  114. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/anthropic.py +0 -0
  115. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/anthropic_test.py +0 -0
  116. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/openai.py +0 -0
  117. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/data/conversion/openai_test.py +0 -0
  118. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/__init__.py +0 -0
  119. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/matching.py +0 -0
  120. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/matching_test.py +0 -0
  121. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/patching.py +0 -0
  122. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/patching_test.py +0 -0
  123. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/scoring.py +0 -0
  124. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/eval/scoring_test.py +0 -0
  125. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/langfunc.py +0 -0
  126. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/anthropic.py +0 -0
  127. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/anthropic_test.py +0 -0
  128. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/azure_openai.py +0 -0
  129. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/azure_openai_test.py +0 -0
  130. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/cache/__init__.py +0 -0
  131. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/cache/base.py +0 -0
  132. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/cache/in_memory.py +0 -0
  133. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/cache/in_memory_test.py +0 -0
  134. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/compositional.py +0 -0
  135. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/compositional_test.py +0 -0
  136. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/deepseek.py +0 -0
  137. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/deepseek_test.py +0 -0
  138. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/fake.py +0 -0
  139. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/fake_test.py +0 -0
  140. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/google_genai_test.py +0 -0
  141. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/groq.py +0 -0
  142. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/groq_test.py +0 -0
  143. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/llama_cpp.py +0 -0
  144. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/llama_cpp_test.py +0 -0
  145. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/openai_compatible.py +0 -0
  146. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/openai_compatible_test.py +0 -0
  147. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/openai_test.py +0 -0
  148. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/rest.py +0 -0
  149. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/rest_test.py +0 -0
  150. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/llms/vertexai_test.py +0 -0
  151. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/logging.py +0 -0
  152. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/logging_test.py +0 -0
  153. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/__init__.py +0 -0
  154. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/client.py +0 -0
  155. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/client_test.py +0 -0
  156. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/session.py +0 -0
  157. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/session_test.py +0 -0
  158. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/testing/simple_mcp_client.py +0 -0
  159. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/testing/simple_mcp_server.py +0 -0
  160. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/mcp/tool_test.py +0 -0
  161. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/memories/__init__.py +0 -0
  162. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/memories/conversation_history.py +0 -0
  163. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/memories/conversation_history_test.py +0 -0
  164. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/memory.py +0 -0
  165. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/message.py +0 -0
  166. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/message_test.py +0 -0
  167. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/__init__.py +0 -0
  168. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/audio.py +0 -0
  169. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/audio_test.py +0 -0
  170. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/pdf.py +0 -0
  171. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/pdf_test.py +0 -0
  172. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/video.py +0 -0
  173. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modalities/video_test.py +0 -0
  174. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modality.py +0 -0
  175. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/modality_test.py +0 -0
  176. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/natural_language.py +0 -0
  177. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/natural_language_test.py +0 -0
  178. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/sampling.py +0 -0
  179. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/sampling_test.py +0 -0
  180. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/completion.py +0 -0
  181. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/completion_test.py +0 -0
  182. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/description.py +0 -0
  183. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/description_test.py +0 -0
  184. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/function_generation.py +0 -0
  185. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/function_generation_test.py +0 -0
  186. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/mapping_test.py +0 -0
  187. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/parsing_test.py +0 -0
  188. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/querying_test.py +0 -0
  189. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/schema_generation.py +0 -0
  190. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/schema_generation_test.py +0 -0
  191. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/scoring_test.py +0 -0
  192. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/structured/tokenization_test.py +0 -0
  193. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/subscription.py +0 -0
  194. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/subscription_test.py +0 -0
  195. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/template.py +0 -0
  196. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/template_test.py +0 -0
  197. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/__init__.py +0 -0
  198. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/completion.py +0 -0
  199. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/completion_test.py +0 -0
  200. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/conversation.py +0 -0
  201. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/conversation_test.py +0 -0
  202. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/demonstration.py +0 -0
  203. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/demonstration_test.py +0 -0
  204. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/selfplay.py +0 -0
  205. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/core/templates/selfplay_test.py +0 -0
  206. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/__init__.py +0 -0
  207. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/base_environment_test.py +0 -0
  208. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/base_feature.py +0 -0
  209. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/base_feature_test.py +0 -0
  210. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/base_sandbox.py +0 -0
  211. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/base_sandbox_test.py +0 -0
  212. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/__init__.py +0 -0
  213. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/chain.py +0 -0
  214. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/chain_test.py +0 -0
  215. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/event_logger.py +0 -0
  216. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/event_logger_test.py +0 -0
  217. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/metric_writer.py +0 -0
  218. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/event_handlers/metric_writer_test.py +0 -0
  219. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/interface.py +0 -0
  220. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun/env/load_balancers.py +0 -0
  221. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun.egg-info/dependency_links.txt +0 -0
  222. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun.egg-info/requires.txt +0 -0
  223. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/langfun.egg-info/top_level.txt +0 -0
  224. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/setup.cfg +0 -0
  225. {langfun-0.1.2.dev202511070805 → langfun-0.1.2.dev202512050805}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202511070805
3
+ Version: 0.1.2.dev202512050805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -121,6 +121,7 @@ from langfun.core.language_model import LMDebugMode
121
121
  from langfun.core.language_model import LMError
122
122
  from langfun.core.language_model import LMInputError
123
123
  from langfun.core.language_model import ContextLimitError
124
+ from langfun.core.language_model import EmptyGenerationError
124
125
  from langfun.core.language_model import RetryableLMError
125
126
  from langfun.core.language_model import RateLimitError
126
127
  from langfun.core.language_model import TemporaryLMError
@@ -21,9 +21,12 @@ from langfun.core.agentic.action import ActionError
21
21
  from langfun.core.agentic.action import ActionTimeoutError
22
22
 
23
23
  from langfun.core.agentic.action import Action
24
+
25
+ from langfun.core.agentic.action import ExecutionUnit
24
26
  from langfun.core.agentic.action import ActionInvocation
25
- from langfun.core.agentic.action import ExecutionTrace
26
27
  from langfun.core.agentic.action import ParallelExecutions
28
+
29
+ from langfun.core.agentic.action import ExecutionTrace
27
30
  from langfun.core.agentic.action import Session
28
31
 
29
32
  from langfun.core.agentic.action import SessionEventHandler
@@ -14,9 +14,11 @@
14
14
  """Base classes for agentic actions."""
15
15
 
16
16
  import abc
17
+ import collections
17
18
  import contextlib
18
19
  import dataclasses
19
20
  import functools
21
+ import itertools
20
22
  import threading
21
23
  import time
22
24
  import typing
@@ -313,12 +315,176 @@ class Action(pg.Object):
313
315
  """
314
316
 
315
317
 
318
+ #
319
+ # Execution tracking.
320
+ #
321
+
322
+
323
+ class ExecutionUnit(pg.Object):
324
+ """Base class for execution units in an agentic trajectory.
325
+
326
+ An `ExecutionUnit` represents a logical step or container in the agent's
327
+ execution flow. It serves as the common interface for top-level executable
328
+ items.
329
+
330
+ The concrete subclasses of `ExecutionUnit` are typically:
331
+ * **`ActionInvocation`**: Represents a single, specific action executed by
332
+ the agent.
333
+ * **`ParallelExecutions`**: Represents a container for a group of
334
+ `ExecutionUnits` that were executed concurrently.
335
+
336
+ Users can retrieve the immediate child execution units from an `ExecutionUnit`
337
+ object. To access the leaf nodes of the execution tree, use `all_actions`,
338
+ `all_queries`, and `all_logs` instead.
339
+
340
+ Each unit exposes a **`position`** property to reveal its specific location
341
+ within the execution hierarchy (e.g., '1.2.3').
342
+
343
+ Users could use **`parent_execution_unit`** to get the parent execution unit
344
+ of the current execution unit.
345
+ """
346
+
347
+ @dataclasses.dataclass
348
+ class Position:
349
+ """The position of an executed unit under current session."""
350
+
351
+ parent: Optional['ExecutionUnit.Position'] = None
352
+ index: int = 0
353
+
354
+ def indices(self) -> tuple[int, ...]:
355
+ """Returns the indices from root to current execution unit."""
356
+ # A deque is efficient for adding items to the front.
357
+ path = collections.deque()
358
+ current_pos = self
359
+
360
+ # Traverse up from the current position to the root.
361
+ while current_pos.parent is not None:
362
+ path.appendleft(current_pos.index)
363
+ current_pos = current_pos.parent
364
+
365
+ path.appendleft(current_pos.index)
366
+ return tuple(path)
367
+
368
+ def to_str(
369
+ self,
370
+ *,
371
+ index_base: int = 1,
372
+ separator: str = '.',
373
+ **kwargs
374
+ ) -> str:
375
+ """Returns a string description of the position."""
376
+ # For root action, we return empty string as it's position descriptor.
377
+ if self.parent is None:
378
+ return ''
379
+ parent_descriptor = self.parent.to_str(
380
+ index_base=index_base,
381
+ separator=separator,
382
+ **kwargs
383
+ )
384
+ if not parent_descriptor:
385
+ return f'{self.index + index_base}'
386
+ return parent_descriptor + separator + f'{self.index + index_base}'
387
+
388
+ def __repr__(self) -> str:
389
+ return f'Position({", ".join(str(x) for x in self.indices())})'
390
+
391
+ def __str__(self) -> str:
392
+ return self.to_str()
393
+
394
+ def __eq__(self, other: 'ExecutionUnit.Position') -> bool:
395
+ if isinstance(other, ExecutionUnit.Position):
396
+ return self.indices() == other.indices()
397
+ if isinstance(other, tuple):
398
+ return self.indices() == other
399
+ if isinstance(other, str):
400
+ return str(self) == other
401
+ return False
402
+
403
+ def __ne__(self, other: 'ExecutionUnit.Position') -> bool:
404
+ return not self == other
405
+
406
+ def __hash__(self) -> int:
407
+ return hash(self.indices())
408
+
409
+ def __lt__(self, other: 'ExecutionUnit.Position') -> bool:
410
+ return self.indices() < other.indices()
411
+
412
+ def __gt__(self, other: 'ExecutionUnit.Position') -> bool:
413
+ return self.indices() > other.indices()
414
+
415
+ def _on_parent_change(self, *args, **kwargs):
416
+ super()._on_parent_change(*args, **kwargs)
417
+ self.__dict__.pop('parent_execution_unit', None)
418
+ self.__dict__.pop('position', None)
419
+
420
+ @functools.cached_property
421
+ def parent_execution_unit(self) -> Optional['ExecutionUnit']:
422
+ """Returns the parent execution unit of the current execution unit."""
423
+ parent_trace = self.sym_ancestor(lambda x: isinstance(x, ExecutionTrace))
424
+ assert isinstance(parent_trace, ExecutionTrace), (
425
+ 'Execution unit is not associated with any `ExecutionTrace`: '
426
+ f'{self}'
427
+ )
428
+ return parent_trace.parent_execution_unit
429
+
430
+ @functools.cached_property
431
+ def position(self) -> Position:
432
+ """Returns the execution position of the action."""
433
+ parent_trace = self.sym_ancestor(lambda x: isinstance(x, ExecutionTrace))
434
+ while parent_trace is not None:
435
+ parent_position = parent_trace.position
436
+ if parent_position is not None:
437
+ return ExecutionUnit.Position(
438
+ parent_position, parent_trace.indexof(self, ExecutionUnit)
439
+ )
440
+ parent_trace = parent_trace.sym_ancestor(
441
+ lambda x: isinstance(x, ExecutionTrace)
442
+ )
443
+ return ExecutionUnit.Position(None, 0)
444
+
445
+ @property
446
+ @abc.abstractmethod
447
+ def execution_units(
448
+ self,
449
+ ) -> list['ExecutionUnit']:
450
+ """Returns immediate child execution items."""
451
+
452
+ @property
453
+ @abc.abstractmethod
454
+ def queries(self) -> list[lf_structured.QueryInvocation]:
455
+ """Returns queries issued by the execution item."""
456
+
457
+ @property
458
+ @abc.abstractmethod
459
+ def actions(self) -> list['ActionInvocation']:
460
+ """Returns immediate child action invocations."""
461
+
462
+ @property
463
+ @abc.abstractmethod
464
+ def logs(self) -> list[lf.logging.LogEntry]:
465
+ """Returns immediate logs under current execution item."""
466
+
467
+ @property
468
+ @abc.abstractmethod
469
+ def all_queries(self) -> list[lf_structured.QueryInvocation]:
470
+ """Returns all queries from the subtree."""
471
+
472
+ @property
473
+ @abc.abstractmethod
474
+ def all_actions(self) -> list['ActionInvocation']:
475
+ """Returns all action invocations from the subtree."""
476
+
477
+ @property
478
+ @abc.abstractmethod
479
+ def all_logs(self) -> list[lf.logging.LogEntry]:
480
+ """Returns all logs from the subtree."""
481
+
482
+
316
483
  # Type definition for traced item during execution.
317
484
  TracedItem = Union[
485
+ ExecutionUnit,
318
486
  lf_structured.QueryInvocation,
319
- 'ActionInvocation',
320
487
  'ExecutionTrace',
321
- 'ParallelExecutions',
322
488
  # NOTE(daiyip): Consider remove log entry once we migrate existing agents.
323
489
  lf.logging.LogEntry,
324
490
  ]
@@ -372,8 +538,14 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
372
538
  def _on_parent_change(self, *args, **kwargs):
373
539
  super()._on_parent_change(*args, **kwargs)
374
540
  self.__dict__.pop('id', None)
541
+ self.__dict__.pop('parent_execution_unit', None)
542
+ self.__dict__.pop('position', None)
375
543
 
376
- def indexof(self, item: TracedItem, count_item_cls: Type[Any]) -> int:
544
+ def indexof(
545
+ self,
546
+ item: TracedItem,
547
+ count_item_cls: Type[Any] | tuple[Type[Any], ...]
548
+ ) -> int:
377
549
  """Returns the index of the child item of given type."""
378
550
  pos = 0
379
551
  for x in self._iter_children(count_item_cls):
@@ -382,6 +554,52 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
382
554
  pos += 1
383
555
  return -1
384
556
 
557
+ @functools.cached_property
558
+ def parent_execution_unit(self) -> ExecutionUnit:
559
+ """Returns the parent execution unit of the current execution trace."""
560
+ parent = self.sym_parent
561
+ if isinstance(parent, ActionInvocation):
562
+ # Current execution trace is the body of an action.
563
+ return parent
564
+ elif isinstance(parent, pg.List):
565
+ container = parent.sym_parent
566
+ if isinstance(container, ParallelExecutions):
567
+ return container
568
+ elif isinstance(container, ExecutionTrace):
569
+ return container.parent_execution_unit
570
+ assert False, (
571
+ 'Execution trace is not associated with any `ActionInvocation` or '
572
+ f'`ParallelExecutions`: {self}'
573
+ )
574
+
575
+ @functools.cached_property
576
+ def position(self) -> ExecutionUnit.Position | None:
577
+ """Returns the execution position of the execution trace.
578
+
579
+ Returns:
580
+ The execution position of the execution trace, or None if the execution
581
+ trace is either not associated with an execution unit or the execution
582
+ trace is a phase under another execution trace.
583
+ """
584
+ parent = self.sym_parent
585
+ if isinstance(parent, ActionInvocation):
586
+ # Current execution trace is the body of an action.
587
+ return parent.position
588
+ elif isinstance(parent, pg.List):
589
+ container = parent.sym_parent
590
+ if isinstance(container, ParallelExecutions):
591
+ return ExecutionUnit.Position(
592
+ container.position, self.sym_path.key
593
+ )
594
+ elif isinstance(container, ExecutionTrace):
595
+ # When execution trace is a phase under another execution trace,
596
+ # we return None as the position.
597
+ return None
598
+ assert False, (
599
+ 'Execution trace is not associated with any `ActionInvocation` or '
600
+ f'`ParallelExecutions`: {self}'
601
+ )
602
+
385
603
  @functools.cached_property
386
604
  def id(self) -> str:
387
605
  parent = self.sym_parent
@@ -453,6 +671,11 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
453
671
  """Returns action invocations from the sequence."""
454
672
  return list(self._iter_children(ActionInvocation))
455
673
 
674
+ @property
675
+ def execution_units(self) -> list[ExecutionUnit]:
676
+ """Returns parallel executions from the sequence."""
677
+ return list(self._iter_children(ExecutionUnit))
678
+
456
679
  @property
457
680
  def logs(self) -> list[lf.logging.LogEntry]:
458
681
  """Returns logs from the sequence."""
@@ -473,7 +696,9 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
473
696
  """Returns all logs from current trace and its child execution items."""
474
697
  return list(self._iter_subtree(lf.logging.LogEntry))
475
698
 
476
- def _iter_children(self, item_cls: Type[Any]) -> Iterator[TracedItem]:
699
+ def _iter_children(
700
+ self, item_cls: Type[Any] | tuple[Type[Any], ...]
701
+ ) -> Iterator[TracedItem]:
477
702
  for item in self.items:
478
703
  if isinstance(item, item_cls):
479
704
  yield item
@@ -485,7 +710,10 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
485
710
  for x in branch._iter_children(item_cls): # pylint: disable=protected-access
486
711
  yield x
487
712
 
488
- def _iter_subtree(self, item_cls: Type[Any]) -> Iterator[TracedItem]:
713
+ def _iter_subtree(
714
+ self,
715
+ item_cls: Type[Any] | tuple[Type[Any], ...]
716
+ ) -> Iterator[TracedItem]:
489
717
  for item in self.items:
490
718
  if isinstance(item, item_cls):
491
719
  yield item
@@ -798,7 +1026,7 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
798
1026
  ]
799
1027
 
800
1028
 
801
- class ParallelExecutions(pg.Object, pg.views.html.HtmlTreeView.Extension):
1029
+ class ParallelExecutions(ExecutionUnit, pg.views.html.HtmlTreeView.Extension):
802
1030
  """A container for multiple parallel execution traces.
803
1031
 
804
1032
  When `session.concurrent_map` is used, it creates a `ParallelExecutions`
@@ -850,8 +1078,64 @@ class ParallelExecutions(pg.Object, pg.views.html.HtmlTreeView.Extension):
850
1078
  self.branches.append(branch)
851
1079
  if self._tab_control is not None:
852
1080
  self._tab_control.append(self._branch_tab(branch))
1081
+
1082
+ # Invalidate cached properties.
1083
+ self.__dict__.pop('all_queries', None)
1084
+ self.__dict__.pop('all_actions', None)
1085
+ self.__dict__.pop('all_logs', None)
853
1086
  return branch
854
1087
 
1088
+ #
1089
+ # ExecutionUnit interface.
1090
+ #
1091
+
1092
+ @property
1093
+ def execution_units(self) -> list[ExecutionUnit]:
1094
+ """Returns immediate child execution items from execution sequence."""
1095
+ return []
1096
+
1097
+ @property
1098
+ def queries(self) -> list[lf_structured.QueryInvocation]:
1099
+ """Returns immediate queries made by the parallel execution."""
1100
+ return []
1101
+
1102
+ @property
1103
+ def actions(self) -> list['ActionInvocation']:
1104
+ """Returns immediate child action invocations."""
1105
+ return []
1106
+
1107
+ @property
1108
+ def logs(self) -> list[lf.logging.LogEntry]:
1109
+ """Returns immediate child logs from execution sequence."""
1110
+ return []
1111
+
1112
+ @functools.cached_property
1113
+ def all_queries(self) -> list[lf_structured.QueryInvocation]:
1114
+ """Returns all queries made by the action and its child execution items."""
1115
+ return list(
1116
+ itertools.chain.from_iterable(
1117
+ branch.all_queries for branch in self.branches
1118
+ )
1119
+ )
1120
+
1121
+ @functools.cached_property
1122
+ def all_actions(self) -> list['ActionInvocation']:
1123
+ """Returns all actions made by the action and its child execution items."""
1124
+ return list(
1125
+ itertools.chain.from_iterable(
1126
+ branch.all_actions for branch in self.branches
1127
+ )
1128
+ )
1129
+
1130
+ @property
1131
+ def all_logs(self) -> list[lf.logging.LogEntry]:
1132
+ """Returns all logs made by the action and its child execution items."""
1133
+ return list(
1134
+ itertools.chain.from_iterable(
1135
+ branch.all_logs for branch in self.branches
1136
+ )
1137
+ )
1138
+
855
1139
  #
856
1140
  # HTML views.
857
1141
  #
@@ -892,7 +1176,7 @@ class ParallelExecutions(pg.Object, pg.views.html.HtmlTreeView.Extension):
892
1176
  )
893
1177
 
894
1178
 
895
- class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
1179
+ class ActionInvocation(ExecutionUnit, pg.views.html.HtmlTreeView.Extension):
896
1180
  """An invocation of an action, capturing its execution and result.
897
1181
 
898
1182
  `ActionInvocation` represents a single call to an `Action`. It contains
@@ -953,6 +1237,7 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
953
1237
  def _on_parent_change(self, *args, **kwargs):
954
1238
  super()._on_parent_change(*args, **kwargs)
955
1239
  self.__dict__.pop('id', None)
1240
+ self.__dict__.pop('position', None)
956
1241
 
957
1242
  @property
958
1243
  def parent_action(self) -> Optional['ActionInvocation']:
@@ -989,6 +1274,15 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
989
1274
  """Returns the state of the action."""
990
1275
  return self.action.state
991
1276
 
1277
+ #
1278
+ # Implement `ExecutionUnit` interface.
1279
+ #
1280
+
1281
+ @property
1282
+ def execution_units(self) -> list[ExecutionUnit]:
1283
+ """Returns immediate child execution items from execution sequence."""
1284
+ return self.execution.execution_units
1285
+
992
1286
  @property
993
1287
  def logs(self) -> list[lf.logging.LogEntry]:
994
1288
  """Returns immediate child logs from execution sequence."""
@@ -1040,10 +1334,12 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
1040
1334
  metadata: dict[str, Any] | None = None,
1041
1335
  ) -> None:
1042
1336
  """Ends the execution of the action with result and metadata."""
1043
- rebind_dict = dict(result=result, error=error)
1044
- if metadata is not None:
1045
- rebind_dict['metadata'] = metadata
1046
- self.rebind(**rebind_dict, skip_notification=True, raise_on_no_change=False)
1337
+ with pg.notify_on_change(False):
1338
+ self.result = result
1339
+ self.error = error
1340
+ if metadata:
1341
+ self.metadata.update(metadata)
1342
+
1047
1343
  self.execution.stop()
1048
1344
  if self._tab_control is not None:
1049
1345
  if self.metadata:
@@ -1201,6 +1497,19 @@ class RootAction(Action):
1201
1497
  class SessionEventHandler:
1202
1498
  """Interface for handling session events."""
1203
1499
 
1500
+ def get(
1501
+ self,
1502
+ session_cls: type['SessionEventHandler']
1503
+ ) -> Optional['SessionEventHandler']:
1504
+ """Returns this or a child event handler for the given session class."""
1505
+ if isinstance(self, session_cls):
1506
+ return self
1507
+ elif isinstance(self, SessionEventHandlerChain):
1508
+ for handler in self.handlers:
1509
+ if v := handler.get(session_cls):
1510
+ return v
1511
+ return None
1512
+
1204
1513
  def on_session_start(
1205
1514
  self,
1206
1515
  session: 'Session'
@@ -1485,11 +1794,6 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1485
1794
  'An optional identifier for the session, which will be used for logging.'
1486
1795
  ]
1487
1796
 
1488
- event_handler: Annotated[
1489
- SessionEventHandler,
1490
- 'Event handler for the session.'
1491
- ]
1492
-
1493
1797
  @pg.explicit_method_override
1494
1798
  def __init__(
1495
1799
  self,
@@ -1503,14 +1807,33 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1503
1807
  super().__init__(
1504
1808
  id=id,
1505
1809
  root=root or ActionInvocation(RootAction()),
1506
- event_handler=event_handler or SessionLogging(verbose=verbose),
1507
1810
  **kwargs
1508
1811
  )
1812
+ self._event_handler = event_handler or SessionLogging(verbose=verbose)
1813
+
1814
+ @property
1815
+ def event_handler(self) -> SessionEventHandler:
1816
+ """Returns the event handler for the session."""
1817
+ return self._event_handler
1818
+
1819
+ def _sym_clone(self, deep: bool, memo: Any = None) -> 'Session':
1820
+ other = super()._sym_clone(deep=deep, memo=memo)
1821
+ if deep:
1822
+ event_handler = pg.clone(self.event_handler, deep=deep, memo=memo)
1823
+ else:
1824
+ event_handler = self.event_handler
1825
+ other._event_handler = event_handler # pylint: disable=protected-access
1826
+ return other
1509
1827
 
1510
1828
  #
1511
1829
  # Shortcut methods for accessing the root action invocation.
1512
1830
  #
1513
1831
 
1832
+ @property
1833
+ def metadata(self) -> dict[str, Any]:
1834
+ """Returns metadata associated with the root of the session."""
1835
+ return self.root.metadata
1836
+
1514
1837
  @property
1515
1838
  def all_queries(self) -> list[lf_structured.QueryInvocation]:
1516
1839
  """Returns all queries made by the session."""