langfun 0.1.2.dev202511200805__tar.gz → 0.1.2.dev202512260805__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 (224) hide show
  1. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/PKG-INFO +1 -1
  2. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/agentic/action.py +2 -4
  3. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/gemini.py +2 -0
  4. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/gemini_test.py +36 -0
  5. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/__init__.py +2 -0
  6. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/checkpointing.py +86 -43
  7. langfun-0.1.2.dev202512260805/langfun/core/eval/v2/config_saver.py +37 -0
  8. langfun-0.1.2.dev202512260805/langfun/core/eval/v2/config_saver_test.py +36 -0
  9. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/evaluation.py +10 -2
  10. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/experiment.py +37 -16
  11. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/progress.py +17 -11
  12. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/progress_tracking.py +1 -2
  13. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/reporting.py +3 -4
  14. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/base.py +15 -38
  15. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/beam.py +13 -0
  16. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/beam_test.py +25 -3
  17. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/ckpt_monitor.py +62 -6
  18. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/ckpt_monitor_test.py +51 -0
  19. langfun-0.1.2.dev202512260805/langfun/core/eval/v2/runners/parallel.py +243 -0
  20. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/parallel_test.py +91 -4
  21. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/sequential_test.py +1 -4
  22. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/language_model.py +30 -12
  23. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/language_model_test.py +33 -2
  24. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/__init__.py +7 -0
  25. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/anthropic.py +59 -0
  26. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/gemini.py +67 -3
  27. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/gemini_test.py +26 -0
  28. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/google_genai.py +24 -0
  29. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/openai.py +27 -0
  30. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/vertexai.py +33 -0
  31. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/image.py +54 -2
  32. langfun-0.1.2.dev202512260805/langfun/core/modalities/image_test.py +224 -0
  33. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/mime.py +14 -1
  34. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/mime_test.py +48 -0
  35. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun.egg-info/PKG-INFO +1 -1
  36. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun.egg-info/SOURCES.txt +2 -0
  37. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/parallel.py +0 -100
  38. langfun-0.1.2.dev202511200805/langfun/core/modalities/image_test.py +0 -108
  39. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/LICENSE +0 -0
  40. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/README.md +0 -0
  41. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/__init__.py +0 -0
  42. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/__init__.py +0 -0
  43. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
  44. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
  45. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/drawing.py +0 -0
  46. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
  47. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/location.py +0 -0
  48. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/assistant/capabilities/gui/location_test.py +0 -0
  49. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/__init__.py +0 -0
  50. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/agentic/__init__.py +0 -0
  51. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/agentic/action_eval.py +0 -0
  52. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/agentic/action_eval_test.py +0 -0
  53. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/agentic/action_test.py +0 -0
  54. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/async_support.py +0 -0
  55. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/async_support_test.py +0 -0
  56. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/__init__.py +0 -0
  57. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/__init__.py +0 -0
  58. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/correction.py +0 -0
  59. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/correction_test.py +0 -0
  60. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/execution.py +0 -0
  61. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/execution_test.py +0 -0
  62. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/generation.py +0 -0
  63. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/generation_test.py +0 -0
  64. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/parsing.py +0 -0
  65. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/parsing_test.py +0 -0
  66. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/sandboxing.py +0 -0
  67. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/coding/python/sandboxing_test.py +0 -0
  68. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/component.py +0 -0
  69. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/component_test.py +0 -0
  70. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/concurrent.py +0 -0
  71. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/concurrent_test.py +0 -0
  72. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/console.py +0 -0
  73. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/console_test.py +0 -0
  74. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/__init__.py +0 -0
  75. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/__init__.py +0 -0
  76. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/anthropic.py +0 -0
  77. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/anthropic_test.py +0 -0
  78. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/openai.py +0 -0
  79. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/data/conversion/openai_test.py +0 -0
  80. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/__init__.py +0 -0
  81. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/base.py +0 -0
  82. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/base_test.py +0 -0
  83. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/matching.py +0 -0
  84. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/matching_test.py +0 -0
  85. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/patching.py +0 -0
  86. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/patching_test.py +0 -0
  87. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/scoring.py +0 -0
  88. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/scoring_test.py +0 -0
  89. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/checkpointing_test.py +0 -0
  90. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/eval_test_helper.py +0 -0
  91. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/evaluation_test.py +0 -0
  92. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/example.py +0 -0
  93. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/example_test.py +0 -0
  94. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/experiment_test.py +0 -0
  95. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/metric_values.py +0 -0
  96. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/metric_values_test.py +0 -0
  97. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/metrics.py +0 -0
  98. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/metrics_test.py +0 -0
  99. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/progress_test.py +0 -0
  100. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/progress_tracking_test.py +0 -0
  101. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/reporting_test.py +0 -0
  102. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/__init__.py +0 -0
  103. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/debug.py +0 -0
  104. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/debug_test.py +0 -0
  105. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/eval/v2/runners/sequential.py +0 -0
  106. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/langfunc.py +0 -0
  107. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/langfunc_test.py +0 -0
  108. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/anthropic_test.py +0 -0
  109. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/azure_openai.py +0 -0
  110. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/azure_openai_test.py +0 -0
  111. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/cache/__init__.py +0 -0
  112. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/cache/base.py +0 -0
  113. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/cache/in_memory.py +0 -0
  114. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/cache/in_memory_test.py +0 -0
  115. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/compositional.py +0 -0
  116. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/compositional_test.py +0 -0
  117. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/deepseek.py +0 -0
  118. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/deepseek_test.py +0 -0
  119. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/fake.py +0 -0
  120. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/fake_test.py +0 -0
  121. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/google_genai_test.py +0 -0
  122. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/groq.py +0 -0
  123. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/groq_test.py +0 -0
  124. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/llama_cpp.py +0 -0
  125. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/llama_cpp_test.py +0 -0
  126. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/openai_compatible.py +0 -0
  127. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/openai_compatible_test.py +0 -0
  128. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/openai_test.py +0 -0
  129. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/rest.py +0 -0
  130. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/rest_test.py +0 -0
  131. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/llms/vertexai_test.py +0 -0
  132. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/logging.py +0 -0
  133. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/logging_test.py +0 -0
  134. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/__init__.py +0 -0
  135. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/client.py +0 -0
  136. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/client_test.py +0 -0
  137. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/session.py +0 -0
  138. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/session_test.py +0 -0
  139. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/testing/simple_mcp_client.py +0 -0
  140. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/testing/simple_mcp_server.py +0 -0
  141. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/tool.py +0 -0
  142. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/mcp/tool_test.py +0 -0
  143. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/memories/__init__.py +0 -0
  144. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/memories/conversation_history.py +0 -0
  145. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/memories/conversation_history_test.py +0 -0
  146. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/memory.py +0 -0
  147. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/message.py +0 -0
  148. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/message_test.py +0 -0
  149. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/__init__.py +0 -0
  150. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/audio.py +0 -0
  151. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/audio_test.py +0 -0
  152. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/pdf.py +0 -0
  153. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/pdf_test.py +0 -0
  154. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/video.py +0 -0
  155. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modalities/video_test.py +0 -0
  156. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modality.py +0 -0
  157. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/modality_test.py +0 -0
  158. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/natural_language.py +0 -0
  159. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/natural_language_test.py +0 -0
  160. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/sampling.py +0 -0
  161. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/sampling_test.py +0 -0
  162. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/__init__.py +0 -0
  163. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/completion.py +0 -0
  164. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/completion_test.py +0 -0
  165. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/description.py +0 -0
  166. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/description_test.py +0 -0
  167. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/function_generation.py +0 -0
  168. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/function_generation_test.py +0 -0
  169. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/mapping.py +0 -0
  170. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/mapping_test.py +0 -0
  171. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/parsing.py +0 -0
  172. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/parsing_test.py +0 -0
  173. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/querying.py +0 -0
  174. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/querying_test.py +0 -0
  175. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/__init__.py +0 -0
  176. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/base.py +0 -0
  177. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/base_test.py +0 -0
  178. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/json.py +0 -0
  179. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/json_test.py +0 -0
  180. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/python.py +0 -0
  181. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema/python_test.py +0 -0
  182. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema_generation.py +0 -0
  183. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/schema_generation_test.py +0 -0
  184. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/scoring.py +0 -0
  185. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/scoring_test.py +0 -0
  186. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/tokenization.py +0 -0
  187. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/structured/tokenization_test.py +0 -0
  188. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/subscription.py +0 -0
  189. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/subscription_test.py +0 -0
  190. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/template.py +0 -0
  191. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/template_test.py +0 -0
  192. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/__init__.py +0 -0
  193. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/completion.py +0 -0
  194. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/completion_test.py +0 -0
  195. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/conversation.py +0 -0
  196. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/conversation_test.py +0 -0
  197. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/demonstration.py +0 -0
  198. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/demonstration_test.py +0 -0
  199. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/selfplay.py +0 -0
  200. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/core/templates/selfplay_test.py +0 -0
  201. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/__init__.py +0 -0
  202. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/base_environment.py +0 -0
  203. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/base_environment_test.py +0 -0
  204. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/base_feature.py +0 -0
  205. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/base_feature_test.py +0 -0
  206. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/base_sandbox.py +0 -0
  207. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/base_sandbox_test.py +0 -0
  208. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/__init__.py +0 -0
  209. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/chain.py +0 -0
  210. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/chain_test.py +0 -0
  211. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/event_logger.py +0 -0
  212. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/event_logger_test.py +0 -0
  213. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/metric_writer.py +0 -0
  214. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/event_handlers/metric_writer_test.py +0 -0
  215. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/interface.py +0 -0
  216. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/interface_test.py +0 -0
  217. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/load_balancers.py +0 -0
  218. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/load_balancers_test.py +0 -0
  219. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun/env/test_utils.py +0 -0
  220. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun.egg-info/dependency_links.txt +0 -0
  221. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun.egg-info/requires.txt +0 -0
  222. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/langfun.egg-info/top_level.txt +0 -0
  223. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/setup.cfg +0 -0
  224. {langfun-0.1.2.dev202511200805 → langfun-0.1.2.dev202512260805}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202511200805
3
+ Version: 0.1.2.dev202512260805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -288,6 +288,8 @@ class Action(pg.Object):
288
288
 
289
289
  with session.track_action(self, max_execution_time=max_execution_time):
290
290
  try:
291
+ # Early terminate the action if the execution time is exceeded.
292
+ session.check_execution_time()
291
293
  result = self.call(session=session, **kwargs)
292
294
  self._invocation.end(result)
293
295
  except BaseException as e:
@@ -1907,7 +1909,6 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1907
1909
 
1908
1910
  def _on_bound(self):
1909
1911
  super()._on_bound()
1910
- self._event_handler = None
1911
1912
  self._tls = threading.local()
1912
1913
  self._current_action = self.root
1913
1914
  self._current_execution = self.root.execution
@@ -2006,9 +2007,6 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
2006
2007
  'signal the start and end of the session.'
2007
2008
  )
2008
2009
 
2009
- # Early terminate the action if the execution time is exceeded.
2010
- self.check_execution_time()
2011
-
2012
2010
  invocation = ActionInvocation(
2013
2011
  pg.maybe_ref(action),
2014
2012
  max_execution_time=self._child_max_execution_time(max_execution_time)
@@ -138,6 +138,8 @@ class GeminiMessageConverter(lf.MessageConverter):
138
138
  self._safe_read(data, 'mimeType')
139
139
  ).from_uri(self._safe_read(data, 'fileUri'))
140
140
  )
141
+ elif 'functionCall' in part or 'functionResponse' in part:
142
+ pass
141
143
  else:
142
144
  raise ValueError(f'Unsupported content part: {part!r}.')
143
145
  message = message_cls.from_chunks(chunks)
@@ -242,6 +242,42 @@ class GeminiConversionTest(unittest.TestCase):
242
242
  'https://www.youtube.com/watch?v=abcd'
243
243
  )
244
244
 
245
+ def test_from_value_with_function_call(self):
246
+ message = lf.Message.from_value(
247
+ {
248
+ 'role': 'model',
249
+ 'parts': [
250
+ {'text': 'Let me search for that.'},
251
+ {
252
+ 'functionCall': {
253
+ 'name': 'search',
254
+ 'args': {'query': 'test'},
255
+ }
256
+ },
257
+ ],
258
+ },
259
+ format='gemini',
260
+ )
261
+ self.assertEqual(message.text, 'Let me search for that.')
262
+
263
+ def test_from_value_with_function_response(self):
264
+ message = lf.Message.from_value(
265
+ {
266
+ 'role': 'user',
267
+ 'parts': [
268
+ {
269
+ 'functionResponse': {
270
+ 'name': 'search',
271
+ 'response': {'results': ['a', 'b']},
272
+ }
273
+ },
274
+ {'text': 'Here are the results.'},
275
+ ],
276
+ },
277
+ format='gemini',
278
+ )
279
+ self.assertEqual(message.text, 'Here are the results.')
280
+
245
281
 
246
282
  if __name__ == '__main__':
247
283
  unittest.main()
@@ -35,11 +35,13 @@ from langfun.core.eval.v2.experiment import Runner
35
35
  from langfun.core.eval.v2 import runners
36
36
 
37
37
  # Plugins
38
+ from langfun.core.eval.v2.config_saver import RunConfigSaver
38
39
  from langfun.core.eval.v2.checkpointing import BulkCheckpointer
39
40
  from langfun.core.eval.v2.checkpointing import PerExampleCheckpointer
40
41
  from langfun.core.eval.v2.reporting import HtmlReporter
41
42
  from langfun.core.eval.v2.reporting import ExampleHtmlGenerator
42
43
 
44
+ # Google-internal imports.
43
45
 
44
46
  # pylint: enable=g-bad-import-order
45
47
  # pylint: enable=g-importing-member
@@ -14,6 +14,7 @@
14
14
  """Checkpointing evaluation runs."""
15
15
  import abc
16
16
  import datetime
17
+ import os
17
18
  import re
18
19
  import threading
19
20
  import traceback
@@ -37,7 +38,7 @@ class Checkpointer(experiment_lib.Plugin):
37
38
  later. When an experiment starts, the checkpointer loads any previously saved
38
39
  examples from an earlier run (or a warm-start run) into `experiment.state`,
39
40
  so the runner can skip processing them again.
40
- Subclasses should implement `_list_checkpoint_filenames` to identify
41
+ Subclasses should implement `_list_checkpoint_files` to identify
41
42
  checkpoint files to load, and `_save_example` to save a newly processed
42
43
  example.
43
44
  """
@@ -121,7 +122,7 @@ class Checkpointer(experiment_lib.Plugin):
121
122
  example: Example,
122
123
  ) -> None:
123
124
  """Saves the example to the checkpoint file."""
124
- if example.newly_processed:
125
+ if example.newly_processed or runner.current_run.force_recompute_metrics:
125
126
  self._save_example(runner, experiment, example)
126
127
 
127
128
  def _load_experiment(
@@ -130,7 +131,7 @@ class Checkpointer(experiment_lib.Plugin):
130
131
  experiment: Experiment,
131
132
  ) -> None:
132
133
  """Creates the checkpoint file."""
133
- ckpt_files = self._list_checkpoint_filenames(runner, experiment)
134
+ ckpt_files = self._list_checkpoint_files(runner, experiment)
134
135
  experiment.info(f'Found {len(ckpt_files)} checkpoint files to load.')
135
136
 
136
137
  # Load the checkpoint files in parallel.
@@ -140,18 +141,18 @@ class Checkpointer(experiment_lib.Plugin):
140
141
  experiment
141
142
  )
142
143
  context = dict(counter=0, counter_lock=threading.Lock())
143
- copy_ckpt = current_run.input_root != current_run.output_root
144
144
 
145
145
  def _load_state(ckpt_file):
146
146
  error = None
147
147
  with pg.timeit() as t:
148
148
  try:
149
- experiment.load_state(
150
- current_run.input_path_for(experiment, ckpt_file),
149
+ loaded_examples = experiment.load_state(
150
+ ckpt_file,
151
151
  filter=lambda x: x.id in examples_to_load,
152
152
  load_example_metadata=lambda x: x.id in examples_to_load_metadata,
153
153
  )
154
154
  except BaseException as e: # pylint: disable=broad-except
155
+ loaded_examples = []
155
156
  error = e
156
157
  finally:
157
158
  with context['counter_lock']:
@@ -169,22 +170,18 @@ class Checkpointer(experiment_lib.Plugin):
169
170
  f'Skipping the file. ({progress_str})'
170
171
  )
171
172
 
172
- if not copy_ckpt:
173
- return
174
-
175
- # Copy the checkpoint records to the output directory.
176
- try:
177
- with pg.io.open_sequence(
178
- current_run.output_path_for(experiment, ckpt_file), 'w'
179
- ) as o, pg.io.open_sequence(
180
- current_run.input_path_for(experiment, ckpt_file), 'r'
181
- ) as i:
182
- for x in i:
183
- o.add(x)
184
- except BaseException as e: # pylint: disable=broad-except
185
- experiment.warning(
186
- f'Failed to copy checkpoint {ckpt_file!r}: {e}.'
187
- )
173
+ output_ckpt_file = current_run.output_path_for(
174
+ experiment, os.path.basename(ckpt_file)
175
+ )
176
+ if (not runner.current_run.force_recompute_metrics
177
+ and ckpt_file != output_ckpt_file
178
+ and any(e for e in loaded_examples if not e.has_error)):
179
+ # Write the error-free warm-start examples to the output checkpoint
180
+ # file.
181
+ with SequenceWriter(output_ckpt_file) as writer:
182
+ for example in loaded_examples:
183
+ if not example.has_error:
184
+ writer.add(example)
188
185
 
189
186
  _ = list(
190
187
  lf.concurrent_map(
@@ -196,10 +193,10 @@ class Checkpointer(experiment_lib.Plugin):
196
193
  )
197
194
 
198
195
  @abc.abstractmethod
199
- def _list_checkpoint_filenames(
196
+ def _list_checkpoint_files(
200
197
  self, runner: Runner, experiment: Experiment
201
198
  ) -> list[str]:
202
- """Lists the checkpoint filenames to restore."""
199
+ """Lists the checkpoint file paths to restore."""
203
200
 
204
201
  @abc.abstractmethod
205
202
  def _save_example(
@@ -225,22 +222,41 @@ class PerExampleCheckpointer(Checkpointer):
225
222
  self._checkpoint_file_prefix = prefix
226
223
  self._checkpoint_file_ext = ext
227
224
 
228
- def _list_checkpoint_filenames(
225
+ def _list_checkpoint_files(
229
226
  self, runner: Runner, experiment: Experiment
230
227
  ) -> list[str]:
231
- experiment_dir = runner.current_run.input_dir(experiment)
232
- filenames = []
228
+
229
+ def _list_checkpoints_from(ckpt_dir: str, examples_to_load: set[int]):
230
+ ckpt_files = []
231
+ if pg.io.path_exists(ckpt_dir):
232
+ regex = re.compile(
233
+ f'{self._checkpoint_file_prefix}_(\\d+){self._checkpoint_file_ext}'
234
+ .replace('.', '\\.')
235
+ )
236
+ for filename in pg.io.listdir(ckpt_dir):
237
+ match = regex.match(filename)
238
+ if match and int(match.group(1)) in examples_to_load:
239
+ examples_to_load.remove(int(match.group(1)))
240
+ ckpt_files.append(os.path.join(ckpt_dir, filename))
241
+ return ckpt_files
242
+
233
243
  examples_to_load = runner.current_run.examples_to_load(experiment)
234
- if pg.io.path_exists(experiment_dir):
235
- regex = re.compile(
236
- f'{self._checkpoint_file_prefix}_(\\d+){self._checkpoint_file_ext}'
237
- .replace('.', '\\.')
244
+
245
+ # Take output directory as the first priority to checkpoints processed in
246
+ # this run.
247
+ ckpt_files = _list_checkpoints_from(
248
+ runner.current_run.output_dir(experiment), examples_to_load
249
+ )
250
+ # If the input and output directories are different, also load from the
251
+ # input directory.
252
+ if (examples_to_load
253
+ and runner.current_run.input_root != runner.current_run.output_root):
254
+ ckpt_files.extend(
255
+ _list_checkpoints_from(
256
+ runner.current_run.input_dir(experiment), examples_to_load
257
+ )
238
258
  )
239
- for filename in pg.io.listdir(experiment_dir):
240
- match = regex.match(filename)
241
- if match and int(match.group(1)) in examples_to_load:
242
- filenames.append(filename)
243
- return filenames
259
+ return ckpt_files
244
260
 
245
261
  def _save_example(
246
262
  self,
@@ -340,13 +356,23 @@ class BulkCheckpointer(Checkpointer):
340
356
  if self._sequence_writer is not None:
341
357
  self._sequence_writer[experiment.id] = sequence_writer
342
358
 
343
- def _list_checkpoint_filenames(
359
+ def _list_checkpoint_files(
344
360
  self, runner: Runner, experiment: Experiment
345
361
  ) -> list[str]:
346
- if pg.io.path_exists(
347
- runner.current_run.input_path_for(experiment, self.checkpoint_filename)
348
- ):
349
- return [self.checkpoint_filename]
362
+ # Always honor the output directory if it's present, as it contains both
363
+ # the warm-started examples and newly processed examples.
364
+ output_ckpt_file = runner.current_run.output_path_for(
365
+ experiment, self.checkpoint_filename
366
+ )
367
+ if pg.io.path_exists(output_ckpt_file):
368
+ return [output_ckpt_file]
369
+
370
+ if runner.current_run.input_root != runner.current_run.output_root:
371
+ input_ckpt_file = runner.current_run.input_path_for(
372
+ experiment, self.checkpoint_filename
373
+ )
374
+ if pg.io.path_exists(input_ckpt_file):
375
+ return [input_ckpt_file]
350
376
  return []
351
377
 
352
378
  def on_experiment_complete(
@@ -394,17 +420,26 @@ class BulkCheckpointer(Checkpointer):
394
420
 
395
421
 
396
422
  class SequenceWriter:
397
- """A thread-safe writer for sequence files (e.g., Bagz).
423
+ """A thread-safe writer for sequence files (e.g., Bagz) with atomic write.
398
424
 
399
425
  `SequenceWriter` wraps a `pg.io.SequenceWriter` to provide thread-safe
400
426
  `add` and `close` operations, ensuring that examples can be written
401
427
  concurrently from multiple threads without corrupting the sequence file.
428
+ It writes to a temporary file and renames it to target path on `close` to
429
+ achieve atomic write. If the target path exists, new examples are appended
430
+ to existing content.
402
431
  """
403
432
 
404
433
  def __init__(self, path: str):
405
434
  self._lock = threading.Lock()
406
435
  self._path = path
407
- self._sequence_writer = pg.io.open_sequence(path, 'a')
436
+ basename = os.path.basename(path)
437
+ self._tmp_path = os.path.join(
438
+ os.path.dirname(path), f'tmp.{basename}'
439
+ )
440
+ if pg.io.path_exists(self._path):
441
+ pg.io.copy(self._path, self._tmp_path)
442
+ self._sequence_writer = pg.io.open_sequence(self._tmp_path, 'a')
408
443
 
409
444
  @property
410
445
  def path(self) -> str:
@@ -429,6 +464,14 @@ class SequenceWriter:
429
464
  return
430
465
  self._sequence_writer.close()
431
466
  self._sequence_writer = None
467
+ pg.io.rename(self._tmp_path, self._path)
468
+
469
+ def __enter__(self):
470
+ return self
471
+
472
+ def __exit__(self, *args, **kwargs):
473
+ del args, kwargs
474
+ self.close()
432
475
 
433
476
  def __del__(self):
434
477
  self.close()
@@ -0,0 +1,37 @@
1
+ # Copyright 2024 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
+ """Config saver plugins."""
15
+
16
+ import os
17
+ from langfun.core.eval.v2 import experiment as experiment_lib
18
+
19
+
20
+ class RunConfigSaver(experiment_lib.Plugin):
21
+ """Saves the current run."""
22
+
23
+ def on_run_start(
24
+ self,
25
+ runner: experiment_lib.Runner,
26
+ root: experiment_lib.Experiment
27
+ ) -> None:
28
+ del root # Unused.
29
+ self._save_run_config(runner)
30
+
31
+ def _save_run_config(self, runner: experiment_lib.Runner) -> None:
32
+ def _save():
33
+ runner.current_run.save(
34
+ os.path.join(runner.current_run.output_root, 'run.json'),
35
+ hide_default_values=True,
36
+ )
37
+ runner.background_run(_save)
@@ -0,0 +1,36 @@
1
+ # Copyright 2024 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
+ """Config saver test."""
15
+
16
+ import os
17
+ import tempfile
18
+ import unittest
19
+ from langfun.core.eval.v2 import config_saver
20
+ from langfun.core.eval.v2 import eval_test_helper
21
+ from langfun.core.eval.v2.runners import parallel # pylint: disable=unused-import
22
+
23
+
24
+ class RunConfigSaverTest(unittest.TestCase):
25
+
26
+ def test_save_run_config(self):
27
+ root_dir = os.path.join(tempfile.mkdtemp(), 'test_run_config_saver')
28
+ experiment = eval_test_helper.test_evaluation()
29
+ run = experiment.run(
30
+ root_dir, 'new', plugins=[config_saver.RunConfigSaver()]
31
+ )
32
+ self.assertTrue(os.path.exists(os.path.join(run.output_root, 'run.json')))
33
+
34
+
35
+ if __name__ == '__main__':
36
+ unittest.main()
@@ -114,6 +114,13 @@ class Evaluation(experiment_lib.Experiment):
114
114
  self._log_entries = []
115
115
  self._log_lock = threading.Lock()
116
116
 
117
+ def _identity(self) -> str:
118
+ """Returns the definition of the evaluation."""
119
+ return self.format(
120
+ compact=True, hide_default_values=True, use_inferred=True,
121
+ exclude_keys=('plugins', 'progress', 'usage_summary')
122
+ )
123
+
117
124
  #
118
125
  # Handling evaluation hierarchy (materialized vs. hyper evaluations).
119
126
  #
@@ -379,10 +386,10 @@ class Evaluation(experiment_lib.Experiment):
379
386
  load_example_metadata: bool = True,
380
387
  filter: Callable[[example_lib.Example], bool] | None = None, # pylint: disable=redefined-builtin
381
388
  raise_if_not_exist: bool = False
382
- ) -> None:
389
+ ) -> list[example_lib.Example]:
383
390
  """Loads saved state from a sequence IO file."""
384
391
  if pg.io.path_exists(state_file):
385
- self._state.load(
392
+ return self._state.load(
386
393
  state_file,
387
394
  example_input_by_id=self.example_input_by_id,
388
395
  load_example_metadata=load_example_metadata,
@@ -390,6 +397,7 @@ class Evaluation(experiment_lib.Experiment):
390
397
  )
391
398
  elif raise_if_not_exist:
392
399
  raise ValueError(f'State file {state_file} does not exist.')
400
+ return []
393
401
 
394
402
  def _reset(self) -> None:
395
403
  """Resets the state of the evaluation."""
@@ -268,11 +268,11 @@ class Experiment(lf.Component, pg.views.HtmlTreeView.Extension):
268
268
  @functools.cached_property
269
269
  def hash(self) -> str:
270
270
  """A 8-byte MD5 hash computed from experiment identity."""
271
- identity = self.format(
272
- compact=True, hide_default_values=True, use_inferred=True,
273
- exclude_keys=('plugins', 'progress', 'usage_summary')
274
- )
275
- return hashlib.md5(identity.encode()).hexdigest()[:8]
271
+ return hashlib.md5(self._identity().encode()).hexdigest()[:8]
272
+
273
+ @abc.abstractmethod
274
+ def _identity(self) -> str:
275
+ """Returns the identity of the experiment."""
276
276
 
277
277
  @classmethod
278
278
  def link(cls, path: str) -> str:
@@ -376,23 +376,24 @@ class Experiment(lf.Component, pg.views.HtmlTreeView.Extension):
376
376
  def run(
377
377
  self,
378
378
  root_dir: str,
379
- id: str | None = None, # pylint: disable=redefined-builtin
379
+ id: str | None = None, # pylint: disable=redefined-builtin
380
380
  *,
381
381
  runner: str = 'parallel',
382
382
  warm_start_from: str | None = None,
383
- filter: Callable[['Experiment'], bool] | None = None, # pylint: disable=redefined-builtin
383
+ filter: Callable[['Experiment'], bool] | None = None, # pylint: disable=redefined-builtin
384
384
  example_ids: list[int] | None = None,
385
385
  shuffle_inputs: bool = False,
386
386
  raise_if_has_error: bool = False,
387
387
  reevaluate_upon_previous_errors: bool = True,
388
388
  reprocess: bool | list[int] = False,
389
+ force_recompute_metrics: bool = False,
389
390
  generate_example_html: Literal['new', 'all', 'no'] | list[int] = 'new',
390
391
  process_timeout: int | None = None,
391
392
  use_cache: Literal['global', 'per_dataset', 'no'] = 'per_dataset',
392
393
  note: str | None = None,
393
394
  tags: list[str] | None = None,
394
395
  plugins: list['Plugin'] | None = None,
395
- **kwargs
396
+ **kwargs,
396
397
  ) -> 'Run':
397
398
  """Runs the experiment.
398
399
 
@@ -445,6 +446,8 @@ class Experiment(lf.Component, pg.views.HtmlTreeView.Extension):
445
446
  meaning that existing checkpoints will be ignored. If a list of
446
447
  example IDs, it indicates that only the specified examples will be
447
448
  reprocessed.
449
+ force_recompute_metrics: If True, it will recompute the metrics for all
450
+ examples, even if the previous checkpoints have metric metadata.
448
451
  generate_example_html: Among 'new', 'all', 'no' or a list of example IDs.
449
452
  If 'new', generate HTML files for all newly processed examples, and
450
453
  keep/copy existing HTML files for unchanged examples.
@@ -481,6 +484,7 @@ class Experiment(lf.Component, pg.views.HtmlTreeView.Extension):
481
484
  raise_if_has_error=raise_if_has_error,
482
485
  reevaluate_upon_previous_errors=reevaluate_upon_previous_errors,
483
486
  reprocess=reprocess,
487
+ force_recompute_metrics=force_recompute_metrics,
484
488
  generate_example_html=generate_example_html,
485
489
  use_cache=use_cache,
486
490
  process_timeout=process_timeout,
@@ -691,6 +695,12 @@ class Suite(Experiment):
691
695
  """Returns whether the task is a leaf."""
692
696
  return False
693
697
 
698
+ def _identity(self) -> str:
699
+ """Returns the definition of the evaluation."""
700
+ return '[' + ', '.join(
701
+ [child._identity() for child in self.children] # pylint: disable=protected-access
702
+ ) + ']'
703
+
694
704
 
695
705
  class RunId(pg.Object):
696
706
  """Structured repreesentation a experiment run ID."""
@@ -848,10 +858,10 @@ class Run(pg.Object, pg.views.html.HtmlTreeView.Extension):
848
858
  ] = None
849
859
 
850
860
  example_ids: Annotated[
851
- list[int] | None,
861
+ list[int] | Callable[[Experiment], list[int]] | None,
852
862
  (
853
- 'The example IDs to run. If None, it will run all examples. '
854
- 'Though '
863
+ 'The example IDs to run. Or a callable for determining the examples '
864
+ 'to run based on the experiment. If None, it will run all examples. '
855
865
  )
856
866
  ] = None
857
867
 
@@ -878,6 +888,14 @@ class Run(pg.Object, pg.views.html.HtmlTreeView.Extension):
878
888
  )
879
889
  ] = True
880
890
 
891
+ force_recompute_metrics: Annotated[
892
+ bool,
893
+ (
894
+ 'If True, force recompute the metrics even if metric metadata is '
895
+ 'already present from previous checkpoint.'
896
+ )
897
+ ] = False
898
+
881
899
  note: Annotated[
882
900
  str | None,
883
901
  'The user note for the current run.'
@@ -967,10 +985,13 @@ class Run(pg.Object, pg.views.html.HtmlTreeView.Extension):
967
985
  """Returns the example IDs to evaluate."""
968
986
  if not experiment.is_leaf:
969
987
  return set()
970
- return set(
971
- self.example_ids if self.example_ids else
972
- range(1, experiment.num_examples + 1)
973
- )
988
+ if self.example_ids is None:
989
+ return set(range(1, experiment.num_examples + 1))
990
+ elif isinstance(self.example_ids, Callable):
991
+ return set(self.example_ids(experiment))
992
+ else:
993
+ assert isinstance(self.example_ids, list), self.example_ids
994
+ return set(self.example_ids)
974
995
 
975
996
  def examples_to_reprocess(self, experiment: Experiment) -> set[int]:
976
997
  """Returns the example IDs to reprocess per request."""
@@ -994,7 +1015,7 @@ class Run(pg.Object, pg.views.html.HtmlTreeView.Extension):
994
1015
  load_metadata_ids = set()
995
1016
  if isinstance(self.generate_example_html, list):
996
1017
  load_metadata_ids = set(self.generate_example_html)
997
- elif self.generate_example_html == 'all':
1018
+ elif self.generate_example_html == 'all' or self.force_recompute_metrics:
998
1019
  load_metadata_ids = self.examples_to_evaluate(experiment)
999
1020
  load_metadata_ids -= self.examples_to_reprocess(experiment)
1000
1021
  return load_metadata_ids
@@ -92,6 +92,7 @@ class Progress(pg.Object, pg.views.HtmlTreeView.Extension):
92
92
  stop_time=None,
93
93
  execution_summary=pg.object_utils.TimeIt.StatusSummary(),
94
94
  )
95
+ self._progress_bar = None
95
96
 
96
97
  @property
97
98
  def num_completed(self) -> int:
@@ -226,7 +227,11 @@ class Progress(pg.Object, pg.views.HtmlTreeView.Extension):
226
227
 
227
228
  def merge_from(self, other: 'Progress') -> None:
228
229
  """Merges the progress from another progress."""
229
- with pg.notify_on_change(False), pg.allow_writable_accessors(True):
230
+ with (
231
+ self._lock,
232
+ pg.notify_on_change(False),
233
+ pg.allow_writable_accessors(True)
234
+ ):
230
235
  if other.start_time is not None and (
231
236
  self.start_time is None or self.start_time > other.start_time):
232
237
  self.start_time = other.start_time
@@ -267,16 +272,17 @@ class Progress(pg.Object, pg.views.HtmlTreeView.Extension):
267
272
  stop_time=self.stop_time_str,
268
273
  )
269
274
  if self.execution_summary:
270
- time_info['execution'] = pg.Dict(
271
- {
272
- k: pg.Dict(
273
- num_started=v.num_started,
274
- num_ended=v.num_ended,
275
- num_failed=v.num_failed,
276
- avg_duration=round(v.avg_duration, 2),
277
- ) for k, v in self.execution_summary.breakdown.items()
278
- }
279
- )
275
+ with self._lock:
276
+ time_info['execution'] = pg.Dict(
277
+ {
278
+ k: pg.Dict(
279
+ num_started=v.num_started,
280
+ num_ended=v.num_ended,
281
+ num_failed=v.num_failed,
282
+ avg_duration=round(v.avg_duration, 2),
283
+ ) for k, v in self.execution_summary.breakdown.items()
284
+ }
285
+ )
280
286
  return pg.format(time_info, verbose=False)
281
287
 
282
288
  def _html_tree_view(
@@ -97,8 +97,7 @@ class _TqdmProgressTracker(experiment_lib.Plugin):
97
97
  self._leaf_progresses = {
98
98
  leaf.id: lf.concurrent.ProgressBar.install(
99
99
  label=f'[#{i + 1} - {leaf.id}]',
100
- total=(len(runner.current_run.example_ids)
101
- if runner.current_run.example_ids else leaf.num_examples),
100
+ total=len(runner.current_run.examples_to_evaluate(leaf)),
102
101
  color='cyan',
103
102
  status=None
104
103
  )
@@ -86,10 +86,8 @@ class ExampleHtmlGenerator(experiment_lib.Plugin):
86
86
  return
87
87
 
88
88
  try:
89
- with pg.timeit() as t, pg.io.open(src_file, 'r') as src:
90
- content = src.read()
91
- with pg.io.open(dest_file, 'w') as dest:
92
- dest.write(content)
89
+ with pg.timeit() as t:
90
+ pg.io.copy(src_file, dest_file)
93
91
  experiment.info(
94
92
  f'\'{example.id}.html\' copied in {t.elapse:.2f} seconds.'
95
93
  )
@@ -101,6 +99,7 @@ class ExampleHtmlGenerator(experiment_lib.Plugin):
101
99
 
102
100
  generate_example_html = current_run.generate_example_html
103
101
  if (generate_example_html == 'all'
102
+ or runner.current_run.force_recompute_metrics
104
103
  or (generate_example_html == 'new' and example.newly_processed)
105
104
  or (isinstance(generate_example_html, list)
106
105
  and example.id in generate_example_html)):