langfun 0.1.2.dev202510270805__tar.gz → 0.1.2.dev202510280805__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 (202) hide show
  1. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/PKG-INFO +1 -1
  2. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/__init__.py +1 -0
  3. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/anthropic.py +2 -0
  4. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/client_test.py +8 -35
  5. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/session.py +4 -19
  6. langfun-0.1.2.dev202510280805/langfun/core/mcp/session_test.py +54 -0
  7. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/tool.py +81 -10
  8. langfun-0.1.2.dev202510280805/langfun/core/mcp/tool_test.py +197 -0
  9. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/message.py +7 -0
  10. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun.egg-info/PKG-INFO +1 -1
  11. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun.egg-info/SOURCES.txt +2 -0
  12. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/LICENSE +0 -0
  13. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/README.md +0 -0
  14. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/__init__.py +0 -0
  15. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/__init__.py +0 -0
  16. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
  17. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
  18. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/drawing.py +0 -0
  19. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
  20. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/location.py +0 -0
  21. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/location_test.py +0 -0
  22. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/__init__.py +0 -0
  23. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action.py +0 -0
  24. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_eval.py +0 -0
  25. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_eval_test.py +0 -0
  26. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_test.py +0 -0
  27. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/async_support.py +0 -0
  28. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/async_support_test.py +0 -0
  29. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/__init__.py +0 -0
  30. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/__init__.py +0 -0
  31. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/correction.py +0 -0
  32. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/correction_test.py +0 -0
  33. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/execution.py +0 -0
  34. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/execution_test.py +0 -0
  35. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/generation.py +0 -0
  36. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/generation_test.py +0 -0
  37. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/parsing.py +0 -0
  38. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/parsing_test.py +0 -0
  39. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/sandboxing.py +0 -0
  40. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/sandboxing_test.py +0 -0
  41. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/component.py +0 -0
  42. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/component_test.py +0 -0
  43. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/concurrent.py +0 -0
  44. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/concurrent_test.py +0 -0
  45. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/console.py +0 -0
  46. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/console_test.py +0 -0
  47. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/__init__.py +0 -0
  48. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/__init__.py +0 -0
  49. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/anthropic_test.py +0 -0
  50. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/gemini.py +0 -0
  51. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/gemini_test.py +0 -0
  52. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/openai.py +0 -0
  53. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/openai_test.py +0 -0
  54. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/__init__.py +0 -0
  55. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/base.py +0 -0
  56. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/base_test.py +0 -0
  57. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/matching.py +0 -0
  58. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/matching_test.py +0 -0
  59. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/patching.py +0 -0
  60. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/patching_test.py +0 -0
  61. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/scoring.py +0 -0
  62. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/scoring_test.py +0 -0
  63. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/__init__.py +0 -0
  64. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/checkpointing.py +0 -0
  65. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/checkpointing_test.py +0 -0
  66. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/eval_test_helper.py +0 -0
  67. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/evaluation.py +0 -0
  68. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/evaluation_test.py +0 -0
  69. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/example.py +0 -0
  70. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/example_test.py +0 -0
  71. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/experiment.py +0 -0
  72. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/experiment_test.py +0 -0
  73. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metric_values.py +0 -0
  74. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metric_values_test.py +0 -0
  75. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metrics.py +0 -0
  76. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metrics_test.py +0 -0
  77. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress.py +0 -0
  78. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress_test.py +0 -0
  79. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress_tracking.py +0 -0
  80. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress_tracking_test.py +0 -0
  81. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/reporting.py +0 -0
  82. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/reporting_test.py +0 -0
  83. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/runners.py +0 -0
  84. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/runners_test.py +0 -0
  85. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/langfunc.py +0 -0
  86. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/langfunc_test.py +0 -0
  87. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/language_model.py +0 -0
  88. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/language_model_test.py +0 -0
  89. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/__init__.py +0 -0
  90. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/anthropic.py +0 -0
  91. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/anthropic_test.py +0 -0
  92. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/azure_openai.py +0 -0
  93. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/azure_openai_test.py +0 -0
  94. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/__init__.py +0 -0
  95. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/base.py +0 -0
  96. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/in_memory.py +0 -0
  97. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/in_memory_test.py +0 -0
  98. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/compositional.py +0 -0
  99. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/compositional_test.py +0 -0
  100. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/deepseek.py +0 -0
  101. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/deepseek_test.py +0 -0
  102. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/fake.py +0 -0
  103. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/fake_test.py +0 -0
  104. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/gemini.py +0 -0
  105. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/gemini_test.py +0 -0
  106. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/google_genai.py +0 -0
  107. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/google_genai_test.py +0 -0
  108. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/groq.py +0 -0
  109. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/groq_test.py +0 -0
  110. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/llama_cpp.py +0 -0
  111. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/llama_cpp_test.py +0 -0
  112. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai.py +0 -0
  113. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai_compatible.py +0 -0
  114. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai_compatible_test.py +0 -0
  115. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai_test.py +0 -0
  116. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/rest.py +0 -0
  117. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/rest_test.py +0 -0
  118. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/vertexai.py +0 -0
  119. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/llms/vertexai_test.py +0 -0
  120. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/logging.py +0 -0
  121. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/logging_test.py +0 -0
  122. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/__init__.py +0 -0
  123. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/client.py +0 -0
  124. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/testing/simple_mcp_client.py +0 -0
  125. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/testing/simple_mcp_server.py +0 -0
  126. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/memories/__init__.py +0 -0
  127. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/memories/conversation_history.py +0 -0
  128. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/memories/conversation_history_test.py +0 -0
  129. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/memory.py +0 -0
  130. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/message_test.py +0 -0
  131. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/__init__.py +0 -0
  132. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/audio.py +0 -0
  133. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/audio_test.py +0 -0
  134. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/image.py +0 -0
  135. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/image_test.py +0 -0
  136. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/mime.py +0 -0
  137. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/mime_test.py +0 -0
  138. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/pdf.py +0 -0
  139. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/pdf_test.py +0 -0
  140. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/video.py +0 -0
  141. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/video_test.py +0 -0
  142. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modality.py +0 -0
  143. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/modality_test.py +0 -0
  144. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/natural_language.py +0 -0
  145. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/natural_language_test.py +0 -0
  146. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/sampling.py +0 -0
  147. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/sampling_test.py +0 -0
  148. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/__init__.py +0 -0
  149. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/completion.py +0 -0
  150. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/completion_test.py +0 -0
  151. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/description.py +0 -0
  152. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/description_test.py +0 -0
  153. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/function_generation.py +0 -0
  154. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/function_generation_test.py +0 -0
  155. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/mapping.py +0 -0
  156. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/mapping_test.py +0 -0
  157. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/parsing.py +0 -0
  158. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/parsing_test.py +0 -0
  159. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/querying.py +0 -0
  160. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/querying_test.py +0 -0
  161. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema.py +0 -0
  162. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema_generation.py +0 -0
  163. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema_generation_test.py +0 -0
  164. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema_test.py +0 -0
  165. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/scoring.py +0 -0
  166. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/scoring_test.py +0 -0
  167. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/tokenization.py +0 -0
  168. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/structured/tokenization_test.py +0 -0
  169. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/subscription.py +0 -0
  170. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/subscription_test.py +0 -0
  171. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/template.py +0 -0
  172. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/template_test.py +0 -0
  173. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/__init__.py +0 -0
  174. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/completion.py +0 -0
  175. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/completion_test.py +0 -0
  176. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/conversation.py +0 -0
  177. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/conversation_test.py +0 -0
  178. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/demonstration.py +0 -0
  179. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/demonstration_test.py +0 -0
  180. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/selfplay.py +0 -0
  181. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/core/templates/selfplay_test.py +0 -0
  182. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/__init__.py +0 -0
  183. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/base_environment.py +0 -0
  184. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/base_feature.py +0 -0
  185. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/base_sandbox.py +0 -0
  186. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/base_test.py +0 -0
  187. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/__init__.py +0 -0
  188. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/base.py +0 -0
  189. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/event_logger.py +0 -0
  190. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/event_logger_test.py +0 -0
  191. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/metric_writer.py +0 -0
  192. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/metric_writer_test.py +0 -0
  193. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/interface.py +0 -0
  194. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/interface_test.py +0 -0
  195. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/load_balancers.py +0 -0
  196. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/load_balancers_test.py +0 -0
  197. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun/env/test_utils.py +0 -0
  198. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun.egg-info/dependency_links.txt +0 -0
  199. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun.egg-info/requires.txt +0 -0
  200. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/langfun.egg-info/top_level.txt +0 -0
  201. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/setup.cfg +0 -0
  202. {langfun-0.1.2.dev202510270805 → langfun-0.1.2.dev202510280805}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510270805
3
+ Version: 0.1.2.dev202510280805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -93,6 +93,7 @@ from langfun.core.message import UserMessage
93
93
  from langfun.core.message import AIMessage
94
94
  from langfun.core.message import SystemMessage
95
95
  from langfun.core.message import MemoryRecord
96
+ from langfun.core.message import ToolMessage
96
97
 
97
98
  from langfun.core.message import MessageConverter
98
99
 
@@ -97,6 +97,8 @@ class AnthropicMessageConverter(lf.MessageConverter):
97
97
  self._safe_read(source, 'media_type')
98
98
  ).from_bytes(base64.b64decode(self._safe_read(source, 'data')))
99
99
  )
100
+ elif t in ('server_tool_use', 'web_search_tool_result'):
101
+ continue
100
102
  else:
101
103
  raise ValueError(f'Unsupported content part: {part!r}.')
102
104
  message = message_cls.from_chunks(chunks)
@@ -13,10 +13,10 @@
13
13
  # limitations under the License.
14
14
  """Tests for MCP client."""
15
15
 
16
- import inspect
17
16
  import unittest
18
17
  from langfun.core import async_support
19
18
  from langfun.core import mcp as lf_mcp
19
+ from langfun.core import message as lf_message
20
20
  from mcp.server import fastmcp as fastmcp_lib
21
21
 
22
22
  mcp = fastmcp_lib.FastMCP(host='0.0.0.0', port=1234)
@@ -42,38 +42,11 @@ class McpTest(unittest.TestCase):
42
42
  client = lf_mcp.McpClient.from_fastmcp(mcp)
43
43
  tools = client.list_tools()
44
44
  self.assertEqual(len(tools), 1)
45
- tool_cls = tools['add']
46
- print(tool_cls.python_definition())
47
- self.assertEqual(
48
- tool_cls.python_definition(),
49
- inspect.cleandoc(
50
- '''
51
- Add
52
-
53
- ```python
54
- class Add:
55
- """Adds two integers and returns their sum.
56
-
57
- Args:
58
- a: The first integer.
59
- b: The second integer.
60
-
61
- Returns:
62
- The sum of the two integers.
63
- """
64
- a: int
65
- b: int
66
- ```
67
- '''
68
- )
69
- )
70
- self.assertEqual(repr(tool_cls), '<tool-class \'Add\'>')
71
- self.assertEqual(tool_cls.__name__, 'Add')
72
- self.assertEqual(tool_cls.TOOL_NAME, 'add')
73
- self.assertEqual(tool_cls(a=1, b=2).input_parameters(), {'a': 1, 'b': 2})
74
45
  with client.session() as session:
75
46
  self.assertEqual(
76
- tool_cls(a=1, b=2)(session), 3
47
+ # Test `session.call_tool` method as `tool.__call__` is already tested
48
+ # in `tool_test.py`.
49
+ session.call_tool(tools['add'](a=1, b=2)), 3
77
50
  )
78
51
 
79
52
  def test_async_usages(self):
@@ -86,10 +59,10 @@ class McpTest(unittest.TestCase):
86
59
  self.assertEqual(tool_cls.TOOL_NAME, 'add')
87
60
  async with client.session() as session:
88
61
  self.assertEqual(
89
- (await tool_cls(a=1, b=2).acall(
90
- session, returns_call_result=True))
91
- .structuredContent['result'],
92
- 3
62
+ # Test `session.acall_tool` method as `tool.acall` is already
63
+ # tested in `tool_test.py`.
64
+ await session.acall_tool(tool_cls(a=1, b=2), returns_message=True),
65
+ lf_message.ToolMessage(text='3', result=3)
93
66
  )
94
67
  async_support.invoke_sync(_test)
95
68
 
@@ -89,34 +89,19 @@ class McpSession:
89
89
  self,
90
90
  tool: mcp_tool.McpTool,
91
91
  *,
92
- returns_call_result: bool = False
92
+ returns_message: bool = False
93
93
  ) -> Any:
94
94
  """Calls a MCP tool synchronously."""
95
- return async_support.invoke_sync(
96
- self.acall_tool,
97
- tool,
98
- returns_call_result=returns_call_result
99
- )
95
+ return tool(self, returns_message=returns_message)
100
96
 
101
97
  async def acall_tool(
102
98
  self,
103
99
  tool: mcp_tool.McpTool,
104
100
  *,
105
- returns_call_result: bool = False
101
+ returns_message: bool = False
106
102
  ) -> Any:
107
103
  """Calls a MCP tool asynchronously."""
108
- assert self._session is not None, 'MCP session is not entered.'
109
- tool_call_result = await self._session.call_tool(
110
- tool.TOOL_NAME, tool.input_parameters()
111
- )
112
- if returns_call_result:
113
- return tool_call_result
114
- if (
115
- tool_call_result.structuredContent
116
- and 'result' in tool_call_result.structuredContent
117
- ):
118
- return tool_call_result.structuredContent['result']
119
- return tool_call_result.content
104
+ return await tool.acall(self, returns_message=returns_message)
120
105
 
121
106
  @classmethod
122
107
  def from_command(
@@ -0,0 +1,54 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Tests for MCP session."""
15
+
16
+ import unittest
17
+ from unittest import mock
18
+
19
+ from langfun.core.mcp import session as mcp_session
20
+ import mcp
21
+ from mcp.client import sse
22
+ from mcp.client import streamable_http
23
+
24
+
25
+ class McpSessionTest(unittest.TestCase):
26
+
27
+ @mock.patch.object(mcp, 'stdio_client', autospec=True)
28
+ def test_from_command(self, mock_stdio_client):
29
+ mcp_session.McpSession.from_command('my-command', ['--foo'])
30
+ mock_stdio_client.assert_called_once_with(
31
+ mcp.StdioServerParameters(command='my-command', args=['--foo'])
32
+ )
33
+
34
+ @mock.patch.object(streamable_http, 'streamablehttp_client', autospec=True)
35
+ def test_from_url_mcp(self, mock_streamablehttp_client):
36
+ mcp_session.McpSession.from_url(
37
+ 'http://localhost/mcp', headers={'k': 'v'}
38
+ )
39
+ mock_streamablehttp_client.assert_called_once_with(
40
+ 'http://localhost/mcp', {'k': 'v'}
41
+ )
42
+
43
+ @mock.patch.object(sse, 'sse_client', autospec=True)
44
+ def test_from_url_sse(self, mock_sse_client):
45
+ mcp_session.McpSession.from_url('http://localhost/sse', headers={'k': 'v'})
46
+ mock_sse_client.assert_called_once_with('http://localhost/sse', {'k': 'v'})
47
+
48
+ def test_from_url_unsupported(self):
49
+ with self.assertRaisesRegex(ValueError, 'Unsupported transport: foo'):
50
+ mcp_session.McpSession.from_url('http://localhost/foo')
51
+
52
+
53
+ if __name__ == '__main__':
54
+ unittest.main()
@@ -13,8 +13,12 @@
13
13
  # limitations under the License.
14
14
  """MCP tool."""
15
15
 
16
+ import base64
16
17
  from typing import Annotated, Any, ClassVar
17
18
 
19
+ from langfun.core import async_support
20
+ from langfun.core import message as lf_message
21
+ from langfun.core import modalities as lf_modalities
18
22
  from langfun.core.structured import schema as lf_schema
19
23
  import mcp
20
24
  import pyglove as pg
@@ -41,37 +45,99 @@ class McpTool(pg.Object, metaclass=_McpToolMeta):
41
45
  protocol='python', markdown=markdown
42
46
  )
43
47
 
48
+ @classmethod
49
+ def result_to_message(
50
+ cls, result: mcp.types.CallToolResult
51
+ ) -> lf_message.ToolMessage:
52
+ """Converts a tool call result to a message.
53
+
54
+ This method allows users to convert an existing mcp.CallToolResult to a
55
+ Langfun ToolMessage.
56
+
57
+ Args:
58
+ result: The MCP tool call result.
59
+
60
+ Returns:
61
+ A ToolMessage object.
62
+ """
63
+ chunks = []
64
+ for item in result.content:
65
+ if isinstance(item, mcp.types.TextContent):
66
+ chunk = item.text
67
+ elif isinstance(item, mcp.types.ImageContent):
68
+ chunk = lf_modalities.Image.from_bytes(_base64_decode(item.data))
69
+ elif isinstance(item, mcp.types.AudioContent):
70
+ chunk = lf_modalities.Audio.from_bytes(_base64_decode(item.data))
71
+ else:
72
+ raise ValueError(f'Unsupported item type: {type(item)}')
73
+ chunks.append(chunk)
74
+ message = lf_message.ToolMessage.from_chunks(chunks)
75
+ if result.structuredContent:
76
+ message.metadata.update(result.structuredContent)
77
+ return message
78
+
44
79
  def __call__(
45
80
  self,
46
81
  session,
47
82
  *,
48
- returns_call_result: bool = False) -> Any:
83
+ returns_message: bool = False) -> Any:
49
84
  """Calls a MCP tool synchronously.
50
85
 
51
86
  Args:
52
87
  session: A MCP session.
53
- returns_call_result: If True, returns the call result. Otherwise returns
54
- the result from structured content, or return content.
88
+ returns_message: If True, always returns a ToolMessage. Otherwise,
89
+ return structured content (result) if available, otherwise return
90
+ ToolMessage if there is multi-modal content, otherwise return text.
55
91
 
56
92
  Returns:
57
93
  The call result, or the result from structured content, or content.
58
94
  """
59
- return session.call_tool(
60
- self,
61
- returns_call_result=returns_call_result
95
+ return async_support.invoke_sync(
96
+ self.acall,
97
+ session,
98
+ returns_message=returns_message
62
99
  )
63
100
 
64
101
  async def acall(
65
102
  self,
66
103
  session,
67
104
  *,
68
- returns_call_result: bool = False) -> Any:
69
- """Calls a MCP tool asynchronously."""
70
- return await session.acall_tool(self, returns_call_result=returns_call_result)
105
+ returns_message: bool = False
106
+ ) -> Any:
107
+ """Calls a MCP tool asynchronously.
108
+
109
+ Args:
110
+ session: McpSession or mcp.ClientSession.
111
+ returns_message: If True, always returns a ToolMessage. Otherwise,
112
+ return structured content (result) if available, otherwise return
113
+ ToolMessage if there is multi-modal content, otherwise return text.
114
+
115
+ Returns:
116
+ The call result, or the result from structured content, or content.
117
+ """
118
+ if not isinstance(session, mcp.ClientSession):
119
+ session = getattr(session, '_session', None)
120
+ assert session is not None, 'MCP session is not entered.'
121
+ tool_call_result = await session.call_tool(
122
+ self.TOOL_NAME, self.input_parameters()
123
+ )
124
+ message = self.result_to_message(tool_call_result)
125
+ if returns_message:
126
+ return message
127
+ if message.result:
128
+ return message.result
129
+ if message.referred_modalities:
130
+ return message
131
+ return message.text
71
132
 
72
133
  def input_parameters(self) -> dict[str, Any]:
73
134
  """Returns the input parameters of the tool."""
74
- json = self.to_json()
135
+ # Optional fields are represented as fields with default values. Therefore,
136
+ # we need to remove the default values from the JSON representation of the
137
+ # tool.
138
+ json = self.to_json(hide_default_values=True)
139
+
140
+ # Remove the type name key from the JSON representation of the tool.
75
141
  def _transform(path: pg.KeyPath, x: Any) -> Any:
76
142
  del path
77
143
  if isinstance(x, dict):
@@ -123,3 +189,8 @@ class McpToolInput(pg.Object, metaclass=_McpToolInputMeta):
123
189
  def _snake_to_camel(name: str) -> str:
124
190
  """Converts a snake_case name to a CamelCase name."""
125
191
  return ''.join(x.capitalize() for x in name.split('_'))
192
+
193
+
194
+ def _base64_decode(data: str) -> bytes:
195
+ """Decodes a base64 string."""
196
+ return base64.b64decode(data.encode('utf-8'))
@@ -0,0 +1,197 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Tests for MCP tool."""
15
+
16
+ import base64
17
+ import inspect
18
+ import unittest
19
+
20
+ from langfun.core import async_support
21
+ from langfun.core import message as lf_message
22
+ from langfun.core import modalities as lf_modalities
23
+ from langfun.core.mcp import client as mcp_client
24
+ from langfun.core.mcp import tool as mcp_tool
25
+ import mcp
26
+ from mcp.server import fastmcp as fastmcp_lib
27
+ import pyglove as pg
28
+
29
+
30
+ # MCP server setup for testing.
31
+ _mcp_server = fastmcp_lib.FastMCP(host='0.0.0.0', port=1235)
32
+
33
+
34
+ @_mcp_server.tool()
35
+ async def add(a: int, b: int) -> int:
36
+ """Adds two integers."""
37
+ return a + b
38
+
39
+
40
+ class McpToolTest(unittest.TestCase):
41
+
42
+ def setUp(self):
43
+ super().setUp()
44
+ self.client = mcp_client.McpClient.from_fastmcp(_mcp_server)
45
+ self.tools = self.client.list_tools()
46
+
47
+ def test_snake_to_camel(self):
48
+ self.assertEqual(mcp_tool._snake_to_camel('foo_bar'), 'FooBar')
49
+ self.assertEqual(mcp_tool._snake_to_camel('foo'), 'Foo')
50
+
51
+ def test_base64_decode(self):
52
+ self.assertEqual(
53
+ mcp_tool._base64_decode(base64.b64encode(b'foo').decode('utf-8')),
54
+ b'foo'
55
+ )
56
+
57
+ def test_make_input_class(self):
58
+ schema = pg.Schema(
59
+ description='Foo input.',
60
+ fields=[
61
+ pg.typing.Field('x', pg.typing.Int(), 'Integer x.'),
62
+ pg.typing.Field('y', pg.typing.Str(), 'String y.'),
63
+ ],
64
+ )
65
+ input_cls = mcp_tool.McpToolInput.make_class('foo_input', schema)
66
+ self.assertTrue(issubclass(input_cls, mcp_tool.McpToolInput))
67
+ self.assertEqual(input_cls.__name__, 'FooInput')
68
+ self.assertEqual(input_cls.__doc__, 'Foo input.')
69
+ s = input_cls.__schema__
70
+ self.assertEqual(list(s.fields.keys()), ['x', 'y'])
71
+ self.assertEqual(repr(input_cls), "<input-class 'FooInput'>")
72
+ self.assertEqual(
73
+ repr(input_cls(x=1, y='abc')),
74
+ "FooInput(x=1, y='abc')",
75
+ )
76
+
77
+ def test_make_tool_class(self):
78
+ tool_def = mcp.Tool(
79
+ name='my_tool',
80
+ inputSchema={
81
+ 'type': 'object',
82
+ 'properties': {
83
+ 'a': {'type': 'integer', 'description': 'Integer a.'},
84
+ 'b': {'type': 'string', 'description': 'String b.'},
85
+ },
86
+ 'required': ['a'],
87
+ },
88
+ description='My tool.',
89
+ )
90
+ tool_cls = mcp_tool.McpTool.make_class(tool_def)
91
+ self.assertTrue(issubclass(tool_cls, mcp_tool.McpTool))
92
+ self.assertEqual(tool_cls.__name__, 'MyTool')
93
+ self.assertEqual(tool_cls.TOOL_NAME, 'my_tool')
94
+ self.assertEqual(tool_cls.__doc__, 'My tool.')
95
+ s = tool_cls.__schema__
96
+ self.assertEqual(list(s.fields.keys()), ['a', 'b'])
97
+ self.assertEqual(repr(tool_cls), "<tool-class 'MyTool'>")
98
+ self.assertEqual(s.fields['a'].description, 'Integer a.')
99
+ self.assertEqual(s.fields['b'].description, 'String b.')
100
+
101
+ self.assertEqual(
102
+ tool_cls.python_definition(markdown=True),
103
+ inspect.cleandoc(
104
+ """
105
+ MyTool
106
+
107
+ ```python
108
+ class MyTool:
109
+ \"\"\"My tool.\"\"\"
110
+ # Integer a.
111
+ a: int
112
+ # String b.
113
+ b: str | None
114
+ ```
115
+ """
116
+ ),
117
+ )
118
+ self.assertEqual(
119
+ tool_cls.python_definition(markdown=False),
120
+ inspect.cleandoc(
121
+ """
122
+ MyTool
123
+
124
+ class MyTool:
125
+ \"\"\"My tool.\"\"\"
126
+ # Integer a.
127
+ a: int
128
+ # String b.
129
+ b: str | None
130
+ """
131
+ ),
132
+ )
133
+
134
+ def test_input_parameters(self):
135
+ tool_cls = self.tools['add']
136
+ self.assertEqual(tool_cls(a=1, b=2).input_parameters(), {'a': 1, 'b': 2})
137
+
138
+ def test_result_to_message(self):
139
+ img_data = base64.b64encode(b'image-data').decode('utf-8')
140
+ audio_data = base64.b64encode(b'audio-data').decode('utf-8')
141
+
142
+ tool_def = self.tools['add']
143
+ result = mcp.types.CallToolResult(
144
+ content=[
145
+ mcp.types.TextContent(type='text', text='hello'),
146
+ mcp.types.ImageContent(
147
+ type='image', data=img_data, mimeType='image/png'
148
+ ),
149
+ mcp.types.AudioContent(
150
+ type='audio', data=audio_data, mimeType='audio/wav'
151
+ ),
152
+ ],
153
+ structuredContent={'x': 1},
154
+ )
155
+ message = tool_def.result_to_message(result)
156
+ self.assertIsInstance(message, lf_message.ToolMessage)
157
+ self.assertIn('hello', message.text)
158
+ self.assertIn('<<[[image', message.text)
159
+ self.assertIn('<<[[audio', message.text)
160
+ self.assertEqual(message.metadata, {'x': 1})
161
+ modalities = message.modalities()
162
+ self.assertEqual(len(modalities), 2)
163
+ self.assertIsInstance(modalities[0], lf_modalities.Image)
164
+ self.assertEqual(modalities[0].to_bytes(), b'image-data')
165
+ self.assertIsInstance(modalities[1], lf_modalities.Audio)
166
+ self.assertEqual(modalities[1].to_bytes(), b'audio-data')
167
+
168
+ def test_sync_call(self):
169
+ add_tool_cls = self.tools['add']
170
+ with self.client.session() as session:
171
+ # Test returning structured content.
172
+ self.assertEqual(add_tool_cls(a=1, b=2)(session), 3)
173
+
174
+ # Test returning message.
175
+ self.assertEqual(
176
+ add_tool_cls(a=1, b=2)(session, returns_message=True),
177
+ lf_message.ToolMessage(text='3', result=3),
178
+ )
179
+
180
+ def test_async_call(self):
181
+ async def _test():
182
+ add_tool_cls = self.tools['add']
183
+ async with self.client.session() as session:
184
+ # Test returning structured content.
185
+ self.assertEqual(await add_tool_cls(a=1, b=2).acall(session), 3)
186
+
187
+ # Test returning message.
188
+ self.assertEqual(
189
+ await add_tool_cls(a=1, b=2).acall(session, returns_message=True),
190
+ lf_message.ToolMessage(text='3', result=3),
191
+ )
192
+
193
+ async_support.invoke_sync(_test)
194
+
195
+
196
+ if __name__ == '__main__':
197
+ unittest.main()
@@ -1070,3 +1070,10 @@ class MemoryRecord(Message):
1070
1070
  """Message used as a memory record."""
1071
1071
 
1072
1072
  sender = 'Memory'
1073
+
1074
+
1075
+ @pg.use_init_args(['text', 'sender', 'metadata'])
1076
+ class ToolMessage(Message):
1077
+ """Message used as a tool call."""
1078
+
1079
+ sender = 'Tool'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510270805
3
+ Version: 0.1.2.dev202510280805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -133,7 +133,9 @@ langfun/core/mcp/__init__.py
133
133
  langfun/core/mcp/client.py
134
134
  langfun/core/mcp/client_test.py
135
135
  langfun/core/mcp/session.py
136
+ langfun/core/mcp/session_test.py
136
137
  langfun/core/mcp/tool.py
138
+ langfun/core/mcp/tool_test.py
137
139
  langfun/core/mcp/testing/simple_mcp_client.py
138
140
  langfun/core/mcp/testing/simple_mcp_server.py
139
141
  langfun/core/memories/__init__.py