langfun 0.1.2.dev202510170805__tar.gz → 0.1.2.dev202510190804__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 (200) hide show
  1. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/PKG-INFO +5 -3
  2. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/__init__.py +1 -1
  3. langfun-0.1.2.dev202510190804/langfun/core/mcp/__init__.py +10 -0
  4. langfun-0.1.2.dev202510190804/langfun/core/mcp/client.py +122 -0
  5. langfun-0.1.2.dev202510190804/langfun/core/mcp/client_test.py +98 -0
  6. langfun-0.1.2.dev202510190804/langfun/core/mcp/session.py +176 -0
  7. langfun-0.1.2.dev202510190804/langfun/core/mcp/testing/simple_mcp_client.py +33 -0
  8. langfun-0.1.2.dev202510190804/langfun/core/mcp/testing/simple_mcp_server.py +33 -0
  9. langfun-0.1.2.dev202510190804/langfun/core/mcp/tool.py +125 -0
  10. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun.egg-info/PKG-INFO +5 -3
  11. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun.egg-info/SOURCES.txt +7 -0
  12. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun.egg-info/requires.txt +4 -2
  13. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/LICENSE +0 -0
  14. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/README.md +0 -0
  15. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/__init__.py +0 -0
  16. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
  17. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
  18. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/drawing.py +0 -0
  19. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
  20. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/location.py +0 -0
  21. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/assistant/capabilities/gui/location_test.py +0 -0
  22. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/__init__.py +0 -0
  23. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/agentic/__init__.py +0 -0
  24. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/agentic/action.py +0 -0
  25. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/agentic/action_eval.py +0 -0
  26. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/agentic/action_eval_test.py +0 -0
  27. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/agentic/action_test.py +0 -0
  28. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/async_support.py +0 -0
  29. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/async_support_test.py +0 -0
  30. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/__init__.py +0 -0
  31. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/__init__.py +0 -0
  32. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/correction.py +0 -0
  33. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/correction_test.py +0 -0
  34. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/execution.py +0 -0
  35. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/execution_test.py +0 -0
  36. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/generation.py +0 -0
  37. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/generation_test.py +0 -0
  38. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/parsing.py +0 -0
  39. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/parsing_test.py +0 -0
  40. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/sandboxing.py +0 -0
  41. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/coding/python/sandboxing_test.py +0 -0
  42. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/component.py +0 -0
  43. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/component_test.py +0 -0
  44. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/concurrent.py +0 -0
  45. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/concurrent_test.py +0 -0
  46. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/console.py +0 -0
  47. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/console_test.py +0 -0
  48. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/__init__.py +0 -0
  49. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/__init__.py +0 -0
  50. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/anthropic.py +0 -0
  51. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/anthropic_test.py +0 -0
  52. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/gemini.py +0 -0
  53. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/gemini_test.py +0 -0
  54. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/openai.py +0 -0
  55. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/data/conversion/openai_test.py +0 -0
  56. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/__init__.py +0 -0
  57. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/base.py +0 -0
  58. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/base_test.py +0 -0
  59. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/matching.py +0 -0
  60. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/matching_test.py +0 -0
  61. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/patching.py +0 -0
  62. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/patching_test.py +0 -0
  63. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/scoring.py +0 -0
  64. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/scoring_test.py +0 -0
  65. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/__init__.py +0 -0
  66. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/checkpointing.py +0 -0
  67. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/checkpointing_test.py +0 -0
  68. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/eval_test_helper.py +0 -0
  69. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/evaluation.py +0 -0
  70. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/evaluation_test.py +0 -0
  71. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/example.py +0 -0
  72. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/example_test.py +0 -0
  73. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/experiment.py +0 -0
  74. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/experiment_test.py +0 -0
  75. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/metric_values.py +0 -0
  76. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/metric_values_test.py +0 -0
  77. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/metrics.py +0 -0
  78. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/metrics_test.py +0 -0
  79. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/progress.py +0 -0
  80. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/progress_test.py +0 -0
  81. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/progress_tracking.py +0 -0
  82. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/progress_tracking_test.py +0 -0
  83. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/reporting.py +0 -0
  84. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/reporting_test.py +0 -0
  85. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/runners.py +0 -0
  86. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/eval/v2/runners_test.py +0 -0
  87. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/langfunc.py +0 -0
  88. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/langfunc_test.py +0 -0
  89. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/language_model.py +0 -0
  90. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/language_model_test.py +0 -0
  91. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/__init__.py +0 -0
  92. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/anthropic.py +0 -0
  93. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/anthropic_test.py +0 -0
  94. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/azure_openai.py +0 -0
  95. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/azure_openai_test.py +0 -0
  96. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/cache/__init__.py +0 -0
  97. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/cache/base.py +0 -0
  98. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/cache/in_memory.py +0 -0
  99. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/cache/in_memory_test.py +0 -0
  100. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/compositional.py +0 -0
  101. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/compositional_test.py +0 -0
  102. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/deepseek.py +0 -0
  103. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/deepseek_test.py +0 -0
  104. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/fake.py +0 -0
  105. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/fake_test.py +0 -0
  106. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/gemini.py +0 -0
  107. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/gemini_test.py +0 -0
  108. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/google_genai.py +0 -0
  109. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/google_genai_test.py +0 -0
  110. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/groq.py +0 -0
  111. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/groq_test.py +0 -0
  112. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/llama_cpp.py +0 -0
  113. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/llama_cpp_test.py +0 -0
  114. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/openai.py +0 -0
  115. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/openai_compatible.py +0 -0
  116. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/openai_compatible_test.py +0 -0
  117. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/openai_test.py +0 -0
  118. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/rest.py +0 -0
  119. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/rest_test.py +0 -0
  120. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/vertexai.py +0 -0
  121. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/llms/vertexai_test.py +0 -0
  122. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/logging.py +0 -0
  123. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/logging_test.py +0 -0
  124. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/memories/__init__.py +0 -0
  125. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/memories/conversation_history.py +0 -0
  126. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/memories/conversation_history_test.py +0 -0
  127. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/memory.py +0 -0
  128. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/message.py +0 -0
  129. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/message_test.py +0 -0
  130. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/__init__.py +0 -0
  131. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/audio.py +0 -0
  132. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/audio_test.py +0 -0
  133. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/image.py +0 -0
  134. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/image_test.py +0 -0
  135. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/mime.py +0 -0
  136. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/mime_test.py +0 -0
  137. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/pdf.py +0 -0
  138. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/pdf_test.py +0 -0
  139. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/video.py +0 -0
  140. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modalities/video_test.py +0 -0
  141. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modality.py +0 -0
  142. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/modality_test.py +0 -0
  143. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/natural_language.py +0 -0
  144. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/natural_language_test.py +0 -0
  145. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/sampling.py +0 -0
  146. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/sampling_test.py +0 -0
  147. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/__init__.py +0 -0
  148. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/completion.py +0 -0
  149. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/completion_test.py +0 -0
  150. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/description.py +0 -0
  151. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/description_test.py +0 -0
  152. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/function_generation.py +0 -0
  153. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/function_generation_test.py +0 -0
  154. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/mapping.py +0 -0
  155. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/mapping_test.py +0 -0
  156. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/parsing.py +0 -0
  157. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/parsing_test.py +0 -0
  158. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/querying.py +0 -0
  159. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/querying_test.py +0 -0
  160. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/schema.py +0 -0
  161. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/schema_generation.py +0 -0
  162. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/schema_generation_test.py +0 -0
  163. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/schema_test.py +0 -0
  164. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/scoring.py +0 -0
  165. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/scoring_test.py +0 -0
  166. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/tokenization.py +0 -0
  167. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/structured/tokenization_test.py +0 -0
  168. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/subscription.py +0 -0
  169. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/subscription_test.py +0 -0
  170. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/template.py +0 -0
  171. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/template_test.py +0 -0
  172. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/__init__.py +0 -0
  173. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/completion.py +0 -0
  174. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/completion_test.py +0 -0
  175. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/conversation.py +0 -0
  176. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/conversation_test.py +0 -0
  177. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/demonstration.py +0 -0
  178. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/demonstration_test.py +0 -0
  179. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/selfplay.py +0 -0
  180. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/core/templates/selfplay_test.py +0 -0
  181. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/__init__.py +0 -0
  182. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/base_environment.py +0 -0
  183. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/base_feature.py +0 -0
  184. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/base_sandbox.py +0 -0
  185. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/base_test.py +0 -0
  186. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/event_handlers/__init__.py +0 -0
  187. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/event_handlers/base.py +0 -0
  188. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/event_handlers/event_logger.py +0 -0
  189. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/event_handlers/event_logger_test.py +0 -0
  190. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/event_handlers/metric_writer.py +0 -0
  191. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/event_handlers/metric_writer_test.py +0 -0
  192. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/interface.py +0 -0
  193. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/interface_test.py +0 -0
  194. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/load_balancers.py +0 -0
  195. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/load_balancers_test.py +0 -0
  196. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun/env/test_utils.py +0 -0
  197. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun.egg-info/dependency_links.txt +0 -0
  198. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/langfun.egg-info/top_level.txt +0 -0
  199. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/setup.cfg +0 -0
  200. {langfun-0.1.2.dev202510170805 → langfun-0.1.2.dev202510190804}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510170805
3
+ Version: 0.1.2.dev202510190804
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -23,14 +23,16 @@ Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: anyio>=4.7.0
25
25
  Requires-Dist: jinja2>=3.1.2
26
+ Requires-Dist: mcp>=1.17.0
26
27
  Requires-Dist: puremagic>=1.20
27
- Requires-Dist: pyglove>=0.4.5.dev202507140812
28
+ Requires-Dist: pyglove>=0.5.0.dev202510170226
28
29
  Requires-Dist: requests>=2.31.0
29
30
  Provides-Extra: all
30
31
  Requires-Dist: anyio>=4.7.0; extra == "all"
31
32
  Requires-Dist: jinja2>=3.1.2; extra == "all"
33
+ Requires-Dist: mcp>=1.17.0; extra == "all"
32
34
  Requires-Dist: puremagic>=1.20; extra == "all"
33
- Requires-Dist: pyglove>=0.4.5.dev202507140812; extra == "all"
35
+ Requires-Dist: pyglove>=0.5.0.dev202510170226; extra == "all"
34
36
  Requires-Dist: requests>=2.31.0; extra == "all"
35
37
  Requires-Dist: google-auth>=2.16.0; extra == "all"
36
38
  Requires-Dist: pillow>=10.0.0; extra == "all"
@@ -66,8 +66,8 @@ from langfun.core import agentic
66
66
  Action = agentic.Action
67
67
  Session = agentic.Session
68
68
 
69
+ from langfun.core import mcp
69
70
  from langfun.core import memories
70
-
71
71
  from langfun.core import modalities
72
72
 
73
73
  Mime = modalities.Mime
@@ -0,0 +1,10 @@
1
+ """Langfun MCP support."""
2
+
3
+ # pylint: disable=g-importing-member
4
+
5
+ from langfun.core.mcp.client import McpClient
6
+ from langfun.core.mcp.session import McpSession
7
+ from langfun.core.mcp.tool import McpTool
8
+ from langfun.core.mcp.tool import McpToolInput
9
+
10
+ # pylint: enable=g-importing-member
@@ -0,0 +1,122 @@
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
+ """MCP client."""
15
+
16
+ import abc
17
+ from typing import Annotated, Type
18
+
19
+ from langfun.core.mcp import session as mcp_session
20
+ from langfun.core.mcp import tool as mcp_tool
21
+ from mcp.server import fastmcp as fastmcp_lib
22
+ import pyglove as pg
23
+
24
+
25
+ class McpClient(pg.Object):
26
+ """Base class for MCP client.
27
+
28
+ Usage:
29
+
30
+ ```python
31
+
32
+ def tool_use():
33
+ client = lf.mcp.McpClient.from_command('<MCP_CMD>', ['<ARG1>', 'ARG2'])
34
+ tools = client.list_tools()
35
+ tool_cls = tools['<TOOL_NAME>']
36
+
37
+ # Print the python definition of the tool.
38
+ print(tool_cls.python_definition())
39
+
40
+ with client.session() as session:
41
+ return tool_cls(x=1, y=2)(session)
42
+
43
+ async def tool_use_async_version():
44
+ client = lf.mcp.McpClient.from_url('http://localhost:8000/mcp')
45
+ tools = client.list_tools()
46
+ tool_cls = tools['<TOOL_NAME>']
47
+
48
+ # Print the python definition of the tool.
49
+ print(tool_cls.python_definition())
50
+
51
+ async with client.session() as session:
52
+ return await tool_cls(x=1, y=2).acall(session)
53
+ ```
54
+ """
55
+
56
+ def _on_bound(self):
57
+ super()._on_bound()
58
+ self._tools = None
59
+
60
+ def list_tools(
61
+ self, refresh: bool = False
62
+ ) -> dict[str, Type[mcp_tool.McpTool]]:
63
+ """Lists all MCP tools."""
64
+ if self._tools is None or refresh:
65
+ with self.session() as session:
66
+ self._tools = session.list_tools()
67
+ return self._tools
68
+
69
+ @abc.abstractmethod
70
+ def session(self) -> mcp_session.McpSession:
71
+ """Creates a MCP session."""
72
+
73
+ @classmethod
74
+ def from_command(cls, command: str, args: list[str]) -> 'McpClient':
75
+ """Creates a MCP client from a tool."""
76
+ return _StdioMcpClient(command=command, args=args)
77
+
78
+ @classmethod
79
+ def from_url(
80
+ cls,
81
+ url: str,
82
+ headers: dict[str, str] | None = None
83
+ ) -> 'McpClient':
84
+ """Creates a MCP client from a URL."""
85
+ return _HttpMcpClient(url=url, headers=headers or {})
86
+
87
+ @classmethod
88
+ def from_fastmcp(cls, fastmcp: fastmcp_lib.FastMCP) -> 'McpClient':
89
+ """Creates a MCP client from a MCP server."""
90
+ return _InMemoryFastMcpClient(fastmcp=fastmcp)
91
+
92
+
93
+ class _StdioMcpClient(McpClient):
94
+ """Stdio-based MCP client."""
95
+
96
+ command: Annotated[str, 'Command to execute.']
97
+ args: Annotated[list[str], 'Arguments to pass to the command.']
98
+
99
+ def session(self) -> mcp_session.McpSession:
100
+ """Creates a MCP session."""
101
+ return mcp_session.McpSession.from_command(self.command, self.args)
102
+
103
+
104
+ class _HttpMcpClient(McpClient):
105
+ """Server-Sent Events (SSE)/Streamable HTTP-based MCP client."""
106
+
107
+ url: Annotated[str, 'URL to connect to.']
108
+ headers: Annotated[dict[str, str], 'Headers to send with the request.'] = {}
109
+
110
+ def session(self) -> mcp_session.McpSession:
111
+ """Creates a MCP session."""
112
+ return mcp_session.McpSession.from_url(self.url, self.headers)
113
+
114
+
115
+ class _InMemoryFastMcpClient(McpClient):
116
+ """In-memory MCP client."""
117
+
118
+ fastmcp: Annotated[fastmcp_lib.FastMCP, 'MCP server to connect to.']
119
+
120
+ def session(self) -> mcp_session.McpSession:
121
+ """Creates a MCP session."""
122
+ return mcp_session.McpSession.from_fastmcp(self.fastmcp)
@@ -0,0 +1,98 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Tests for MCP client."""
15
+
16
+ import inspect
17
+ import unittest
18
+ from langfun.core import async_support
19
+ from langfun.core import mcp as lf_mcp
20
+ from mcp.server import fastmcp as fastmcp_lib
21
+
22
+ mcp = fastmcp_lib.FastMCP(host='0.0.0.0', port=1234)
23
+
24
+
25
+ @mcp.tool()
26
+ async def add(a: int, b: int) -> int:
27
+ """Adds two integers and returns their sum.
28
+
29
+ Args:
30
+ a: The first integer.
31
+ b: The second integer.
32
+
33
+ Returns:
34
+ The sum of the two integers.
35
+ """
36
+ return a + b
37
+
38
+
39
+ class McpTest(unittest.TestCase):
40
+
41
+ def test_sync_usages(self):
42
+ client = lf_mcp.McpClient.from_fastmcp(mcp)
43
+ tools = client.list_tools()
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
+ with client.session() as session:
75
+ self.assertEqual(
76
+ tool_cls(a=1, b=2)(session), 3
77
+ )
78
+
79
+ def test_async_usages(self):
80
+ async def _test():
81
+ client = lf_mcp.McpClient.from_fastmcp(mcp)
82
+ tools = client.list_tools()
83
+ self.assertEqual(len(tools), 1)
84
+ tool_cls = tools['add']
85
+ self.assertEqual(tool_cls.__name__, 'Add')
86
+ self.assertEqual(tool_cls.TOOL_NAME, 'add')
87
+ async with client.session() as session:
88
+ self.assertEqual(
89
+ (await tool_cls(a=1, b=2).acall(
90
+ session, returns_call_result=True))
91
+ .structuredContent['result'],
92
+ 3
93
+ )
94
+ async_support.invoke_sync(_test)
95
+
96
+
97
+ if __name__ == '__main__':
98
+ unittest.main()
@@ -0,0 +1,176 @@
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
+ """MCP session."""
15
+
16
+ import contextlib
17
+ from typing import Any, Type
18
+ import anyio
19
+ from langfun.core import async_support
20
+ from langfun.core.mcp import tool as mcp_tool
21
+ import mcp
22
+ from mcp.client import sse
23
+ from mcp.client import streamable_http
24
+ from mcp.server import fastmcp as fastmcp_lib
25
+ from mcp.shared import memory
26
+
27
+
28
+ class McpSession:
29
+ """Langfun's MCP session.
30
+
31
+ Compared to the standard mcp.ClientSession, Langfun's MCP session could be
32
+ used both synchronously and asynchronously.
33
+ """
34
+
35
+ def __init__(self, stream) -> None:
36
+ self._stream = stream
37
+ self._session = None
38
+ self._session_exit_stack = None
39
+ self._in_session = False
40
+
41
+ # For supporting sync context manager.
42
+ self._sync_context_manager_exit_stack = None
43
+
44
+ def __enter__(self) -> 'McpSession':
45
+ exit_stack = contextlib.ExitStack()
46
+ exit_stack.enter_context(async_support.sync_context_manager(self))
47
+ self._sync_context_manager_exit_stack = exit_stack
48
+ return self
49
+
50
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
51
+ assert self._sync_context_manager_exit_stack is not None
52
+ self._sync_context_manager_exit_stack.close()
53
+
54
+ async def __aenter__(self) -> 'McpSession':
55
+ assert self._session_exit_stack is None, 'Session cannot be re-entered.'
56
+
57
+ self._session_exit_stack = contextlib.AsyncExitStack()
58
+ stream_output = await self._session_exit_stack.enter_async_context(
59
+ self._stream
60
+ )
61
+ assert isinstance(stream_output, tuple) and len(stream_output) in (2, 3)
62
+ read, write = stream_output[:2]
63
+ self._session = mcp.ClientSession(read, write)
64
+ await self._session_exit_stack.enter_async_context(self._session)
65
+ await self._session.initialize()
66
+ return self
67
+
68
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
69
+ del exc_type, exc_val, exc_tb
70
+ if self._session is None:
71
+ return
72
+ assert self._session_exit_stack is not None
73
+ await self._session_exit_stack.aclose()
74
+ self._session = None
75
+
76
+ def list_tools(self) -> dict[str, Type[mcp_tool.McpTool]]:
77
+ """Lists all MCP tools synchronously."""
78
+ return async_support.invoke_sync(self.alist_tools)
79
+
80
+ async def alist_tools(self) -> dict[str, Type[mcp_tool.McpTool]]:
81
+ """Lists all MCP tools asynchronously."""
82
+ assert self._session is not None, 'MCP session is not entered.'
83
+ return {
84
+ t.name: mcp_tool.McpTool.make_class(t)
85
+ for t in (await self._session.list_tools()).tools
86
+ }
87
+
88
+ def call_tool(
89
+ self,
90
+ tool: mcp_tool.McpTool,
91
+ *,
92
+ returns_call_result: bool = False
93
+ ) -> Any:
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
+ )
100
+
101
+ async def acall_tool(
102
+ self,
103
+ tool: mcp_tool.McpTool,
104
+ *,
105
+ returns_call_result: bool = False
106
+ ) -> Any:
107
+ """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
120
+
121
+ @classmethod
122
+ def from_command(
123
+ cls,
124
+ command: str,
125
+ args: list[str] | None = None
126
+ ) -> 'McpSession':
127
+ """Creates a MCP session from a command."""
128
+ return cls(
129
+ mcp.stdio_client(
130
+ mcp.StdioServerParameters(command=command, args=args or [])
131
+ )
132
+ )
133
+
134
+ @classmethod
135
+ def from_url(
136
+ cls,
137
+ url: str,
138
+ headers: dict[str, str] | None = None
139
+ ) -> 'McpSession':
140
+ """Creates a MCP session from a URL."""
141
+ transport = url.removesuffix('/').split('/')[-1].lower()
142
+ if transport == 'mcp':
143
+ return cls(streamable_http.streamablehttp_client(url, headers or {}))
144
+ elif transport == 'sse':
145
+ return cls(sse.sse_client(url, headers or {}))
146
+ else:
147
+ raise ValueError(f'Unsupported transport: {transport}')
148
+
149
+ @classmethod
150
+ def from_fastmcp(
151
+ cls,
152
+ fastmcp: fastmcp_lib.FastMCP
153
+ ):
154
+ return cls(_client_streams_from_fastmcp(fastmcp))
155
+
156
+
157
+ @contextlib.asynccontextmanager
158
+ async def _client_streams_from_fastmcp(fastmcp: fastmcp_lib.FastMCP):
159
+ """Creates client streams from a MCP server."""
160
+ server = fastmcp._mcp_server # pylint: disable=protected-access
161
+ async with memory.create_client_server_memory_streams(
162
+ ) as (client_streams, server_streams):
163
+ client_read, client_write = client_streams
164
+ server_read, server_write = server_streams
165
+
166
+ # Create a cancel scope for the server task
167
+ async with anyio.create_task_group() as tg:
168
+ tg.start_soon(
169
+ lambda: server.run(
170
+ server_read,
171
+ server_write,
172
+ server.create_initialization_options(),
173
+ raise_exceptions=True,
174
+ )
175
+ )
176
+ yield client_read, client_write
@@ -0,0 +1,33 @@
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
+ """A simple MCP client for testing."""
15
+
16
+ from absl import app
17
+ from absl import flags
18
+ from langfun.core import mcp
19
+
20
+
21
+ _URL = flags.DEFINE_string(
22
+ 'url',
23
+ 'http://localhost:8000/mcp',
24
+ 'URL of the MCP server.',
25
+ )
26
+
27
+
28
+ def main(_):
29
+ print(mcp.McpClient.from_url(url=_URL.value).list_tools())
30
+
31
+
32
+ if __name__ == '__main__':
33
+ app.run(main)
@@ -0,0 +1,33 @@
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
+ """Simple MCP server for testing."""
15
+
16
+ from absl import app as absl_app
17
+ from mcp.server import fastmcp as fastmcp_lib
18
+
19
+ mcp = fastmcp_lib.FastMCP(host='0.0.0.0', port=8000)
20
+
21
+
22
+ @mcp.tool()
23
+ async def add(a: int, b: int) -> int:
24
+ """Adds two integers and returns their sum."""
25
+ return a + b
26
+
27
+
28
+ def main(_):
29
+ mcp.run(transport='streamable-http', mount_path='/mcp')
30
+
31
+
32
+ if __name__ == '__main__':
33
+ absl_app.run(main)
@@ -0,0 +1,125 @@
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
+ """MCP tool."""
15
+
16
+ from typing import Annotated, Any, ClassVar
17
+
18
+ from langfun.core.structured import schema as lf_schema
19
+ import mcp
20
+ import pyglove as pg
21
+
22
+
23
+ class _McpToolMeta(pg.symbolic.ObjectMeta):
24
+
25
+ def __repr__(self) -> str:
26
+ return f'<tool-class \'{self.__name__}\'>'
27
+
28
+
29
+ class McpTool(pg.Object, metaclass=_McpToolMeta):
30
+ """Base class for MCP tools."""
31
+
32
+ TOOL_NAME: Annotated[
33
+ ClassVar[str],
34
+ 'Tool name.'
35
+ ]
36
+
37
+ @classmethod
38
+ def python_definition(cls, markdown: bool = True) -> str:
39
+ """Returns the Python definition of the tool."""
40
+ return lf_schema.Schema.from_value(cls).schema_str(
41
+ protocol='python', markdown=markdown
42
+ )
43
+
44
+ def __call__(
45
+ self,
46
+ session,
47
+ *,
48
+ returns_call_result: bool = False) -> Any:
49
+ """Calls a MCP tool synchronously.
50
+
51
+ Args:
52
+ 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.
55
+
56
+ Returns:
57
+ The call result, or the result from structured content, or content.
58
+ """
59
+ return session.call_tool(
60
+ self,
61
+ returns_call_result=returns_call_result
62
+ )
63
+
64
+ async def acall(
65
+ self,
66
+ session,
67
+ *,
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)
71
+
72
+ def input_parameters(self) -> dict[str, Any]:
73
+ """Returns the input parameters of the tool."""
74
+ json = self.to_json()
75
+ def _transform(path: pg.KeyPath, x: Any) -> Any:
76
+ del path
77
+ if isinstance(x, dict):
78
+ x.pop(pg.JSONConvertible.TYPE_NAME_KEY, None)
79
+ return x
80
+ return pg.utils.transform(json, _transform)
81
+
82
+ @classmethod
83
+ def make_class(cls, tool_definition: mcp.Tool) -> type['McpTool']:
84
+ """Makes a MCP tool class from tool definition."""
85
+
86
+ class _McpTool(cls):
87
+ auto_schema = False
88
+
89
+ tool_cls = _McpTool
90
+ tool_cls.TOOL_NAME = tool_definition.name
91
+ tool_cls.__name__ = _snake_to_camel(tool_definition.name)
92
+ tool_cls.__doc__ = tool_definition.description
93
+ schema = pg.Schema.from_json_schema(
94
+ tool_definition.inputSchema, class_fn=McpToolInput.make_class
95
+ )
96
+ tool_cls.apply_schema(schema)
97
+ return tool_cls
98
+
99
+
100
+ class _McpToolInputMeta(pg.symbolic.ObjectMeta):
101
+
102
+ def __repr__(self) -> str:
103
+ return f'<input-class \'{self.__name__}\'>'
104
+
105
+
106
+ class McpToolInput(pg.Object, metaclass=_McpToolInputMeta):
107
+ """Base class for MCP tool inputs."""
108
+
109
+ @classmethod
110
+ def make_class(cls, name: str, schema: pg.Schema):
111
+ """Converts a schema to an input class."""
112
+
113
+ class _McpToolInput(cls):
114
+ pass
115
+
116
+ input_cls = _McpToolInput
117
+ input_cls.__name__ = _snake_to_camel(name)
118
+ input_cls.__doc__ = schema.description
119
+ input_cls.apply_schema(schema)
120
+ return input_cls
121
+
122
+
123
+ def _snake_to_camel(name: str) -> str:
124
+ """Converts a snake_case name to a CamelCase name."""
125
+ return ''.join(x.capitalize() for x in name.split('_'))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202510170805
3
+ Version: 0.1.2.dev202510190804
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -23,14 +23,16 @@ Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: anyio>=4.7.0
25
25
  Requires-Dist: jinja2>=3.1.2
26
+ Requires-Dist: mcp>=1.17.0
26
27
  Requires-Dist: puremagic>=1.20
27
- Requires-Dist: pyglove>=0.4.5.dev202507140812
28
+ Requires-Dist: pyglove>=0.5.0.dev202510170226
28
29
  Requires-Dist: requests>=2.31.0
29
30
  Provides-Extra: all
30
31
  Requires-Dist: anyio>=4.7.0; extra == "all"
31
32
  Requires-Dist: jinja2>=3.1.2; extra == "all"
33
+ Requires-Dist: mcp>=1.17.0; extra == "all"
32
34
  Requires-Dist: puremagic>=1.20; extra == "all"
33
- Requires-Dist: pyglove>=0.4.5.dev202507140812; extra == "all"
35
+ Requires-Dist: pyglove>=0.5.0.dev202510170226; extra == "all"
34
36
  Requires-Dist: requests>=2.31.0; extra == "all"
35
37
  Requires-Dist: google-auth>=2.16.0; extra == "all"
36
38
  Requires-Dist: pillow>=10.0.0; extra == "all"
@@ -129,6 +129,13 @@ langfun/core/llms/cache/__init__.py
129
129
  langfun/core/llms/cache/base.py
130
130
  langfun/core/llms/cache/in_memory.py
131
131
  langfun/core/llms/cache/in_memory_test.py
132
+ langfun/core/mcp/__init__.py
133
+ langfun/core/mcp/client.py
134
+ langfun/core/mcp/client_test.py
135
+ langfun/core/mcp/session.py
136
+ langfun/core/mcp/tool.py
137
+ langfun/core/mcp/testing/simple_mcp_client.py
138
+ langfun/core/mcp/testing/simple_mcp_server.py
132
139
  langfun/core/memories/__init__.py
133
140
  langfun/core/memories/conversation_history.py
134
141
  langfun/core/memories/conversation_history_test.py