langfun 0.1.2.dev202510260804__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.
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/PKG-INFO +1 -1
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/__init__.py +1 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/anthropic.py +2 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/client_test.py +8 -35
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/session.py +4 -19
- langfun-0.1.2.dev202510280805/langfun/core/mcp/session_test.py +54 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/tool.py +81 -10
- langfun-0.1.2.dev202510280805/langfun/core/mcp/tool_test.py +197 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/message.py +7 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun.egg-info/PKG-INFO +1 -1
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun.egg-info/SOURCES.txt +2 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/LICENSE +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/README.md +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/drawing.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/location.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/assistant/capabilities/gui/location_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_eval.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_eval_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/async_support.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/async_support_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/correction.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/correction_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/execution.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/execution_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/generation.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/generation_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/parsing.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/parsing_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/sandboxing.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/python/sandboxing_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/component.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/component_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/concurrent.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/concurrent_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/console.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/console_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/anthropic_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/gemini.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/gemini_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/openai.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/conversion/openai_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/base.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/base_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/matching.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/matching_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/patching.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/patching_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/scoring.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/scoring_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/checkpointing.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/checkpointing_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/eval_test_helper.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/evaluation.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/evaluation_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/example.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/example_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/experiment.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/experiment_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metric_values.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metric_values_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metrics.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/metrics_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress_tracking.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/progress_tracking_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/reporting.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/reporting_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/runners.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/eval/v2/runners_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/langfunc.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/langfunc_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/language_model.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/language_model_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/anthropic.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/anthropic_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/azure_openai.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/azure_openai_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/base.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/in_memory.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/cache/in_memory_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/compositional.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/compositional_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/deepseek.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/deepseek_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/fake.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/fake_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/gemini.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/gemini_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/google_genai.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/google_genai_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/groq.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/groq_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/llama_cpp.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/llama_cpp_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai_compatible.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai_compatible_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/openai_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/rest.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/rest_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/vertexai.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/llms/vertexai_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/logging.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/logging_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/client.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/testing/simple_mcp_client.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/testing/simple_mcp_server.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/memories/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/memories/conversation_history.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/memories/conversation_history_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/memory.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/message_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/audio.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/audio_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/image.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/image_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/mime.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/mime_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/pdf.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/pdf_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/video.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modalities/video_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modality.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/modality_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/natural_language.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/natural_language_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/sampling.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/sampling_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/completion.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/completion_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/description.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/description_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/function_generation.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/function_generation_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/mapping.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/mapping_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/parsing.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/parsing_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/querying.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/querying_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema_generation.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema_generation_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/schema_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/scoring.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/scoring_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/tokenization.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/structured/tokenization_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/subscription.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/subscription_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/template.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/template_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/completion.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/completion_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/conversation.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/conversation_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/demonstration.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/demonstration_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/selfplay.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/templates/selfplay_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/base_environment.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/base_feature.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/base_sandbox.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/base_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/__init__.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/base.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/event_logger.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/event_logger_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/metric_writer.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/event_handlers/metric_writer_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/interface.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/interface_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/load_balancers.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/load_balancers_test.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/env/test_utils.py +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun.egg-info/dependency_links.txt +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun.egg-info/requires.txt +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun.egg-info/top_level.txt +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/setup.cfg +0 -0
- {langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/setup.py +0 -0
|
@@ -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)
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/mcp/client_test.py
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
.
|
|
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
|
-
|
|
92
|
+
returns_message: bool = False
|
|
93
93
|
) -> Any:
|
|
94
94
|
"""Calls a MCP tool synchronously."""
|
|
95
|
-
return
|
|
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
|
-
|
|
101
|
+
returns_message: bool = False
|
|
106
102
|
) -> Any:
|
|
107
103
|
"""Calls a MCP tool asynchronously."""
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
|
60
|
-
self,
|
|
61
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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'
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun.egg-info/SOURCES.txt
RENAMED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/__init__.py
RENAMED
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action.py
RENAMED
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_eval.py
RENAMED
|
File without changes
|
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/agentic/action_test.py
RENAMED
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/async_support.py
RENAMED
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/async_support_test.py
RENAMED
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/coding/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/component_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/concurrent_test.py
RENAMED
|
File without changes
|
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/console_test.py
RENAMED
|
File without changes
|
{langfun-0.1.2.dev202510260804 → langfun-0.1.2.dev202510280805}/langfun/core/data/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|