langfun 0.1.2.dev202503240804__tar.gz → 0.1.2.dev202503260804__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.
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/PKG-INFO +1 -1
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/__init__.py +1 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/__init__.py +2 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/__init__.py +19 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/__init__.py +23 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/anthropic.py +131 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/anthropic_test.py +267 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/gemini.py +168 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/gemini_test.py +256 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/openai.py +131 -0
- langfun-0.1.2.dev202503260804/langfun/core/data/conversion/openai_test.py +176 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/anthropic.py +10 -52
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/gemini.py +15 -62
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/gemini_test.py +0 -32
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/openai_compatible.py +15 -19
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/message.py +232 -27
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/message_test.py +130 -16
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/image.py +1 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun.egg-info/PKG-INFO +1 -1
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun.egg-info/SOURCES.txt +8 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/LICENSE +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/README.md +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/agentic/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/agentic/action.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/agentic/action_eval.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/agentic/action_eval_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/agentic/action_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/correction.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/correction_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/execution.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/execution_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/generation.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/generation_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/parsing.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/parsing_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/sandboxing.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/coding/python/sandboxing_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/component.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/component_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/concurrent.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/concurrent_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/console.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/console_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/base.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/base_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/matching.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/matching_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/patching.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/patching_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/scoring.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/scoring_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/checkpointing.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/checkpointing_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/eval_test_helper.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/evaluation.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/evaluation_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/example.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/example_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/experiment.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/experiment_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/metric_values.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/metric_values_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/metrics.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/metrics_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/progress.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/progress_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/progress_tracking.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/progress_tracking_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/reporting.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/reporting_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/runners.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/eval/v2/runners_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/langfunc.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/langfunc_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/language_model.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/language_model_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/anthropic_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/azure_openai.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/azure_openai_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/cache/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/cache/base.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/cache/in_memory.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/cache/in_memory_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/compositional.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/compositional_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/deepseek.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/deepseek_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/fake.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/fake_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/google_genai.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/google_genai_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/groq.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/groq_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/llama_cpp.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/llama_cpp_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/openai.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/openai_compatible_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/openai_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/rest.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/rest_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/vertexai.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/llms/vertexai_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/logging.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/logging_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/memories/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/memories/conversation_history.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/memories/conversation_history_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/memory.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/audio.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/audio_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/image_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/mime.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/mime_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/pdf.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/pdf_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/video.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modalities/video_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modality.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/modality_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/natural_language.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/natural_language_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/sampling.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/sampling_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/completion.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/completion_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/description.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/description_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/function_generation.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/function_generation_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/mapping.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/mapping_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/parsing.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/parsing_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/querying.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/querying_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/schema.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/schema_generation.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/schema_generation_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/schema_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/scoring.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/scoring_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/tokenization.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/structured/tokenization_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/subscription.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/subscription_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/template.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/template_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/__init__.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/completion.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/completion_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/conversation.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/conversation_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/demonstration.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/demonstration_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/selfplay.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun/core/templates/selfplay_test.py +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun.egg-info/dependency_links.txt +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun.egg-info/requires.txt +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/langfun.egg-info/top_level.txt +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/setup.cfg +0 -0
- {langfun-0.1.2.dev202503240804 → langfun-0.1.2.dev202503260804}/setup.py +0 -0
@@ -48,6 +48,7 @@ query_output = structured.query_output
|
|
48
48
|
source_form = structured.source_form
|
49
49
|
function_gen = structured.function_gen
|
50
50
|
|
51
|
+
from langfun.core import data
|
51
52
|
from langfun.core import eval # pylint: disable=redefined-builtin
|
52
53
|
from langfun.core import templates
|
53
54
|
from langfun.core import coding
|
@@ -87,6 +87,8 @@ from langfun.core.message import AIMessage
|
|
87
87
|
from langfun.core.message import SystemMessage
|
88
88
|
from langfun.core.message import MemoryRecord
|
89
89
|
|
90
|
+
from langfun.core.message import MessageConverter
|
91
|
+
|
90
92
|
# Interface for modality.
|
91
93
|
from langfun.core.modality import Modality
|
92
94
|
from langfun.core.modality import ModalityRef
|
@@ -0,0 +1,19 @@
|
|
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
|
+
"""langfun data sub-module."""
|
15
|
+
|
16
|
+
# pylint: disable=g-importing-member
|
17
|
+
# pylint: disable=g-bad-import-order
|
18
|
+
|
19
|
+
from langfun.core.data import conversion
|
@@ -0,0 +1,23 @@
|
|
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
|
+
"""langfun data conversions."""
|
15
|
+
|
16
|
+
# pylint: disable=g-importing-member
|
17
|
+
# pylint: disable=g-bad-import-order
|
18
|
+
|
19
|
+
from langfun.core.data.conversion import anthropic
|
20
|
+
from langfun.core.data.conversion import gemini
|
21
|
+
from langfun.core.data.conversion import openai
|
22
|
+
|
23
|
+
# Placeholder for Google-internal imports.
|
@@ -0,0 +1,131 @@
|
|
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
|
+
"""Anthropic API message conversion."""
|
15
|
+
|
16
|
+
import base64
|
17
|
+
from typing import Annotated, Any, Callable
|
18
|
+
|
19
|
+
import langfun.core as lf
|
20
|
+
from langfun.core import modalities as lf_modalities
|
21
|
+
|
22
|
+
|
23
|
+
class AnthropicMessageConverter(lf.MessageConverter):
|
24
|
+
"""Converter to Anthropic public API."""
|
25
|
+
|
26
|
+
FORMAT_ID = 'anthropic'
|
27
|
+
|
28
|
+
chunk_preprocessor: Annotated[
|
29
|
+
Callable[[str | lf.Modality], Any] | None,
|
30
|
+
(
|
31
|
+
'Chunk preprocessor for Langfun chunk to Anthropic chunk conversion. '
|
32
|
+
'It will be applied before each Langfun chunk is converted. '
|
33
|
+
'If returns None, the chunk will be skipped.'
|
34
|
+
)
|
35
|
+
] = None
|
36
|
+
|
37
|
+
def to_value(self, message: lf.Message) -> dict[str, Any]:
|
38
|
+
"""Converts a Langfun message to Gemini API."""
|
39
|
+
content = []
|
40
|
+
for chunk in message.chunk():
|
41
|
+
if self.chunk_preprocessor:
|
42
|
+
chunk = self.chunk_preprocessor(chunk)
|
43
|
+
if chunk is None:
|
44
|
+
continue
|
45
|
+
|
46
|
+
if isinstance(chunk, str):
|
47
|
+
content.append({'type': 'text', 'text': chunk})
|
48
|
+
elif isinstance(chunk, lf_modalities.Mime):
|
49
|
+
if isinstance(chunk, lf_modalities.Image):
|
50
|
+
content.append(
|
51
|
+
dict(
|
52
|
+
type='image',
|
53
|
+
source=dict(
|
54
|
+
type='base64',
|
55
|
+
media_type=chunk.mime_type,
|
56
|
+
data=base64.b64encode(chunk.to_bytes()).decode(),
|
57
|
+
),
|
58
|
+
)
|
59
|
+
)
|
60
|
+
elif isinstance(chunk, lf_modalities.PDF):
|
61
|
+
content.append(
|
62
|
+
dict(
|
63
|
+
type='document',
|
64
|
+
source=dict(
|
65
|
+
type='base64',
|
66
|
+
media_type=chunk.mime_type,
|
67
|
+
data=base64.b64encode(chunk.to_bytes()).decode(),
|
68
|
+
),
|
69
|
+
)
|
70
|
+
)
|
71
|
+
else:
|
72
|
+
raise NotImplementedError(
|
73
|
+
f'Modality conversion not implemented: {chunk!r}'
|
74
|
+
)
|
75
|
+
return dict(role=self.get_role(message), content=content)
|
76
|
+
|
77
|
+
def from_value(self, value: dict[str, Any]) -> lf.Message:
|
78
|
+
"""Returns a Langfun message from Anthropic message."""
|
79
|
+
message_cls = self.get_message_cls(
|
80
|
+
self._safe_read(value, 'role', default='assistant')
|
81
|
+
)
|
82
|
+
content = self._safe_read(value, 'content', default=[])
|
83
|
+
assert isinstance(content, list)
|
84
|
+
|
85
|
+
chunks = []
|
86
|
+
thought_chunks = []
|
87
|
+
for part in content:
|
88
|
+
t = self._safe_read(part, 'type')
|
89
|
+
if t == 'text':
|
90
|
+
chunks.append(self._safe_read(part, 'text'))
|
91
|
+
elif t == 'thinking':
|
92
|
+
thought_chunks.append(self._safe_read(part, 'thinking'))
|
93
|
+
elif t in ('image', 'document'):
|
94
|
+
source = self._safe_read(part, 'source')
|
95
|
+
chunks.append(
|
96
|
+
lf_modalities.Mime.class_from_mime_type(
|
97
|
+
self._safe_read(source, 'media_type')
|
98
|
+
).from_bytes(base64.b64decode(self._safe_read(source, 'data')))
|
99
|
+
)
|
100
|
+
else:
|
101
|
+
raise ValueError(f'Unsupported content part: {part!r}.')
|
102
|
+
message = message_cls.from_chunks(chunks)
|
103
|
+
if thought_chunks:
|
104
|
+
message.set('thought', message_cls.from_chunks(thought_chunks))
|
105
|
+
return message
|
106
|
+
|
107
|
+
|
108
|
+
def _as_anthropic_format(
|
109
|
+
self,
|
110
|
+
chunk_preprocessor: Callable[[str | lf.Modality], Any] | None = None,
|
111
|
+
**kwargs
|
112
|
+
) -> dict[str, Any]:
|
113
|
+
"""Returns an Anthropic format message."""
|
114
|
+
return AnthropicMessageConverter(
|
115
|
+
chunk_preprocessor=chunk_preprocessor, **kwargs
|
116
|
+
).to_value(self)
|
117
|
+
|
118
|
+
|
119
|
+
@classmethod
|
120
|
+
def _from_anthropic_format(
|
121
|
+
cls,
|
122
|
+
anthropic_message: dict[str, Any],
|
123
|
+
**kwargs
|
124
|
+
) -> lf.Message:
|
125
|
+
"""Creates a Langfun message from the Anthropic format message."""
|
126
|
+
del cls
|
127
|
+
return AnthropicMessageConverter(**kwargs).from_value(anthropic_message)
|
128
|
+
|
129
|
+
# Set shortcut methods in lf.Message.
|
130
|
+
lf.Message.as_anthropic_format = _as_anthropic_format
|
131
|
+
lf.Message.from_anthropic_format = _from_anthropic_format
|
@@ -0,0 +1,267 @@
|
|
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
|
+
import base64
|
15
|
+
import unittest
|
16
|
+
import langfun.core as lf
|
17
|
+
from langfun.core import modalities as lf_modalities
|
18
|
+
from langfun.core.data.conversion import anthropic # pylint: disable=unused-import
|
19
|
+
|
20
|
+
|
21
|
+
image_content = (
|
22
|
+
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\x00\x00\x00\x18\x04'
|
23
|
+
b'\x03\x00\x00\x00\x12Y \xcb\x00\x00\x00\x18PLTE\x00\x00'
|
24
|
+
b'\x00fff_chaag_cg_ch^ci_ciC\xedb\x94\x00\x00\x00\x08tRNS'
|
25
|
+
b'\x00\n\x9f*\xd4\xff_\xf4\xe4\x8b\xf3a\x00\x00\x00>IDATx'
|
26
|
+
b'\x01c \x05\x08)"\xd8\xcc\xae!\x06pNz\x88k\x19\\Q\xa8"\x10'
|
27
|
+
b'\xc1\x14\x95\x01%\xc1\n\xa143Ta\xa8"D-\x84\x03QM\x98\xc3'
|
28
|
+
b'\x1a\x1a\x1a@5\x0e\x04\xa0q\x88\x05\x00\x07\xf8\x18\xf9'
|
29
|
+
b'\xdao\xd0|\x00\x00\x00\x00IEND\xaeB`\x82'
|
30
|
+
)
|
31
|
+
|
32
|
+
pdf_content = (
|
33
|
+
b'%PDF-1.1\n%\xc2\xa5\xc2\xb1\xc3\xab\n\n1 0 obj\n'
|
34
|
+
b'<< /Type /Catalog\n /Pages 2 0 R\n >>\nendobj\n\n2 0 obj\n '
|
35
|
+
b'<< /Type /Pages\n /Kids [3 0 R]\n '
|
36
|
+
b'/Count 1\n /MediaBox [0 0 300 144]\n '
|
37
|
+
b'>>\nendobj\n\n3 0 obj\n '
|
38
|
+
b'<< /Type /Page\n /Parent 2 0 R\n /Resources\n '
|
39
|
+
b'<< /Font\n'
|
40
|
+
b'<< /F1\n'
|
41
|
+
b'<< /Type /Font\n'
|
42
|
+
b'/Subtype /Type1\n'
|
43
|
+
b'/BaseFont /Times-Roman\n'
|
44
|
+
b'>>\n>>\n>>\n '
|
45
|
+
b'/Contents 4 0 R\n >>\nendobj\n\n4 0 obj\n '
|
46
|
+
b'<< /Length 55 >>\nstream\n BT\n /F1 18 Tf\n 0 0 Td\n '
|
47
|
+
b'(Hello World) Tj\n ET\nendstream\nendobj\n\nxref\n0 5\n0000000000 '
|
48
|
+
b'65535 f \n0000000018 00000 n \n0000000077 00000 n \n0000000178 00000 n '
|
49
|
+
b'\n0000000457 00000 n \ntrailer\n << /Root 1 0 R\n /Size 5\n '
|
50
|
+
b'>>\nstartxref\n565\n%%EOF\n'
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
class AnthropicConversionTest(unittest.TestCase):
|
55
|
+
|
56
|
+
def test_as_format_with_role(self):
|
57
|
+
self.assertEqual(
|
58
|
+
lf.UserMessage('hi').as_format('anthropic'),
|
59
|
+
{
|
60
|
+
'role': 'user',
|
61
|
+
'content': [{'type': 'text', 'text': 'hi'}],
|
62
|
+
},
|
63
|
+
)
|
64
|
+
self.assertEqual(
|
65
|
+
lf.AIMessage('hi').as_format('anthropic'),
|
66
|
+
{
|
67
|
+
'role': 'assistant',
|
68
|
+
'content': [{'type': 'text', 'text': 'hi'}],
|
69
|
+
},
|
70
|
+
)
|
71
|
+
self.assertEqual(
|
72
|
+
lf.SystemMessage('hi').as_format('anthropic'),
|
73
|
+
{
|
74
|
+
'role': 'system',
|
75
|
+
'content': [{'type': 'text', 'text': 'hi'}],
|
76
|
+
},
|
77
|
+
)
|
78
|
+
|
79
|
+
def test_as_format_with_image(self):
|
80
|
+
self.assertEqual(
|
81
|
+
lf.Template(
|
82
|
+
'What are the common words from {{image}} and {{pdf}}?',
|
83
|
+
image=lf_modalities.Image.from_bytes(image_content),
|
84
|
+
pdf=lf_modalities.PDF.from_bytes(pdf_content),
|
85
|
+
).render().as_anthropic_format(),
|
86
|
+
{
|
87
|
+
'role': 'user',
|
88
|
+
'content': [
|
89
|
+
{
|
90
|
+
'type': 'text',
|
91
|
+
'text': 'What are the common words from'
|
92
|
+
},
|
93
|
+
{
|
94
|
+
'type': 'image',
|
95
|
+
'source': {
|
96
|
+
'type': 'base64',
|
97
|
+
'media_type': 'image/png',
|
98
|
+
'data': base64.b64encode(
|
99
|
+
image_content
|
100
|
+
).decode('utf-8'),
|
101
|
+
}
|
102
|
+
},
|
103
|
+
{
|
104
|
+
'type': 'text',
|
105
|
+
'text': 'and'
|
106
|
+
},
|
107
|
+
{
|
108
|
+
'type': 'document',
|
109
|
+
'source': {
|
110
|
+
'type': 'base64',
|
111
|
+
'media_type': 'application/pdf',
|
112
|
+
'data': base64.b64encode(
|
113
|
+
pdf_content
|
114
|
+
).decode('utf-8'),
|
115
|
+
}
|
116
|
+
},
|
117
|
+
{
|
118
|
+
'type': 'text',
|
119
|
+
'text': '?'
|
120
|
+
},
|
121
|
+
],
|
122
|
+
},
|
123
|
+
)
|
124
|
+
|
125
|
+
def test_as_format_with_chunk_preprocessor(self):
|
126
|
+
self.assertEqual(
|
127
|
+
lf.Template(
|
128
|
+
'What is this {{image}}?',
|
129
|
+
image=lf_modalities.Image.from_bytes(image_content)
|
130
|
+
).render().as_format(
|
131
|
+
'anthropic',
|
132
|
+
chunk_preprocessor=lambda x: x if isinstance(x, str) else None
|
133
|
+
),
|
134
|
+
{
|
135
|
+
'role': 'user',
|
136
|
+
'content': [
|
137
|
+
{
|
138
|
+
'type': 'text', 'text': 'What is this'
|
139
|
+
},
|
140
|
+
{
|
141
|
+
'type': 'text', 'text': '?'
|
142
|
+
}
|
143
|
+
],
|
144
|
+
},
|
145
|
+
)
|
146
|
+
|
147
|
+
def test_from_value_with_simple_text(self):
|
148
|
+
self.assertEqual(
|
149
|
+
lf.Message.from_value(
|
150
|
+
{
|
151
|
+
'content': [{'type': 'text', 'text': 'this is a text'}],
|
152
|
+
},
|
153
|
+
format='anthropic',
|
154
|
+
),
|
155
|
+
lf.AIMessage('this is a text'),
|
156
|
+
)
|
157
|
+
|
158
|
+
def test_from_value_with_role(self):
|
159
|
+
self.assertEqual(
|
160
|
+
lf.Message.from_value(
|
161
|
+
{
|
162
|
+
'role': 'user',
|
163
|
+
'content': [{'type': 'text', 'text': 'this is a text'}],
|
164
|
+
},
|
165
|
+
format='anthropic',
|
166
|
+
),
|
167
|
+
lf.UserMessage('this is a text'),
|
168
|
+
)
|
169
|
+
self.assertEqual(
|
170
|
+
lf.Message.from_value(
|
171
|
+
{
|
172
|
+
'role': 'assistant',
|
173
|
+
'content': [{'type': 'text', 'text': 'this is a text'}],
|
174
|
+
},
|
175
|
+
format='anthropic',
|
176
|
+
),
|
177
|
+
lf.AIMessage('this is a text'),
|
178
|
+
)
|
179
|
+
self.assertEqual(
|
180
|
+
lf.Message.from_value(
|
181
|
+
{
|
182
|
+
'role': 'system',
|
183
|
+
'content': [{'type': 'text', 'text': 'this is a text'}],
|
184
|
+
},
|
185
|
+
format='anthropic',
|
186
|
+
),
|
187
|
+
lf.SystemMessage('this is a text'),
|
188
|
+
)
|
189
|
+
with self.assertRaisesRegex(ValueError, 'Unsupported role: .*'):
|
190
|
+
lf.Message.from_value(
|
191
|
+
{
|
192
|
+
'role': 'function',
|
193
|
+
'content': [{'type': 'text', 'text': 'this is a text'}],
|
194
|
+
},
|
195
|
+
format='anthropic',
|
196
|
+
)
|
197
|
+
|
198
|
+
def test_from_value_with_thoughts(self):
|
199
|
+
message = lf.Message.from_anthropic_format(
|
200
|
+
{
|
201
|
+
'role': 'user',
|
202
|
+
'content': [
|
203
|
+
{
|
204
|
+
'type': 'thinking',
|
205
|
+
'thinking': 'this is a red round object',
|
206
|
+
},
|
207
|
+
{
|
208
|
+
'type': 'text',
|
209
|
+
'text': 'this is a apple',
|
210
|
+
},
|
211
|
+
],
|
212
|
+
},
|
213
|
+
)
|
214
|
+
self.assertEqual(message.text, 'this is a apple')
|
215
|
+
self.assertEqual(message.thought, 'this is a red round object')
|
216
|
+
|
217
|
+
def test_from_value_with_modalities(self):
|
218
|
+
m = lf.Message.from_value(
|
219
|
+
{
|
220
|
+
'role': 'user',
|
221
|
+
'content': [
|
222
|
+
{
|
223
|
+
'type': 'text',
|
224
|
+
'text': 'What are the common words from'
|
225
|
+
},
|
226
|
+
{
|
227
|
+
'type': 'image',
|
228
|
+
'source': {
|
229
|
+
'type': 'base64',
|
230
|
+
'media_type': 'image/png',
|
231
|
+
'data': base64.b64encode(image_content).decode('utf-8'),
|
232
|
+
}
|
233
|
+
},
|
234
|
+
{
|
235
|
+
'type': 'text',
|
236
|
+
'text': 'and'
|
237
|
+
},
|
238
|
+
{
|
239
|
+
'type': 'document',
|
240
|
+
'source': {
|
241
|
+
'type': 'base64',
|
242
|
+
'media_type': 'application/pdf',
|
243
|
+
'data': base64.b64encode(pdf_content).decode('utf-8'),
|
244
|
+
}
|
245
|
+
},
|
246
|
+
{
|
247
|
+
'type': 'text',
|
248
|
+
'text': '?'
|
249
|
+
},
|
250
|
+
],
|
251
|
+
},
|
252
|
+
format='anthropic',
|
253
|
+
)
|
254
|
+
self.assertEqual(
|
255
|
+
m.text,
|
256
|
+
'What are the common words from <<[[obj0]]>> and <<[[obj1]]>> ?'
|
257
|
+
)
|
258
|
+
self.assertIsInstance(m.obj0, lf_modalities.Image)
|
259
|
+
self.assertEqual(m.obj0.mime_type, 'image/png')
|
260
|
+
self.assertEqual(m.obj0.to_bytes(), image_content)
|
261
|
+
|
262
|
+
self.assertIsInstance(m.obj1, lf_modalities.PDF)
|
263
|
+
self.assertEqual(m.obj1.to_bytes(), pdf_content)
|
264
|
+
|
265
|
+
|
266
|
+
if __name__ == '__main__':
|
267
|
+
unittest.main()
|
@@ -0,0 +1,168 @@
|
|
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
|
+
"""Gemini API message conversion."""
|
15
|
+
|
16
|
+
import base64
|
17
|
+
from typing import Annotated, Any, Callable
|
18
|
+
|
19
|
+
import langfun.core as lf
|
20
|
+
from langfun.core import modalities as lf_modalities
|
21
|
+
|
22
|
+
|
23
|
+
class GeminiMessageConverter(lf.MessageConverter):
|
24
|
+
"""Converter to Gemini public API."""
|
25
|
+
|
26
|
+
FORMAT_ID = 'gemini'
|
27
|
+
|
28
|
+
chunk_preprocessor: Annotated[
|
29
|
+
Callable[[str | lf.Modality], Any] | None,
|
30
|
+
(
|
31
|
+
'Chunk preprocessor for Langfun chunk to Gemini chunk conversion. '
|
32
|
+
'It will be applied before each Langfun chunk is converted. '
|
33
|
+
'If returns None, the chunk will be skipped.'
|
34
|
+
)
|
35
|
+
] = None
|
36
|
+
|
37
|
+
def to_value(self, message: lf.Message) -> dict[str, Any]:
|
38
|
+
"""Converts a Langfun message to Gemini API."""
|
39
|
+
parts = []
|
40
|
+
for chunk in message.chunk():
|
41
|
+
if self.chunk_preprocessor:
|
42
|
+
chunk = self.chunk_preprocessor(chunk)
|
43
|
+
if chunk is None:
|
44
|
+
continue
|
45
|
+
|
46
|
+
if isinstance(chunk, str):
|
47
|
+
parts.append(self._convert_chunk(chunk))
|
48
|
+
else:
|
49
|
+
if isinstance(chunk, lf_modalities.Mime):
|
50
|
+
modalities = [chunk]
|
51
|
+
# NOTE(daiyip): preprocessing may convert a single chunk into
|
52
|
+
# a list of chunks
|
53
|
+
elif isinstance(chunk, list):
|
54
|
+
modalities = chunk
|
55
|
+
else:
|
56
|
+
raise ValueError(f'Unsupported content type: {chunk!r}.')
|
57
|
+
parts.extend(self._convert_chunk(c) for c in modalities)
|
58
|
+
return dict(role=self.get_role(message), parts=parts)
|
59
|
+
|
60
|
+
def _convert_chunk(self, chunk: str | lf.Modality) -> Any:
|
61
|
+
"""Converts a Langfun chunk to Gemini chunk."""
|
62
|
+
if isinstance(chunk, str):
|
63
|
+
return {'text': chunk}
|
64
|
+
if not isinstance(chunk, lf_modalities.Mime):
|
65
|
+
raise ValueError(f'Unsupported content chunk: {chunk!r}.')
|
66
|
+
# NOTE(daiyip): special handling for YouTube video.
|
67
|
+
if chunk.uri and chunk.uri.startswith('https://www.youtube.com/watch?v='):
|
68
|
+
return {
|
69
|
+
'fileData': {
|
70
|
+
'mimeType': 'video/*',
|
71
|
+
'fileUri': chunk.uri
|
72
|
+
}
|
73
|
+
}
|
74
|
+
if chunk.is_text:
|
75
|
+
return {'text': chunk.to_text()}
|
76
|
+
if chunk.uri and chunk.uri.lower().startswith(
|
77
|
+
('http:', 'https:', 'ftp:')
|
78
|
+
):
|
79
|
+
return {
|
80
|
+
'fileData': {
|
81
|
+
'mimeType': chunk.mime_type,
|
82
|
+
'fileUri': chunk.uri,
|
83
|
+
}
|
84
|
+
}
|
85
|
+
return {
|
86
|
+
'inlineData': {
|
87
|
+
'data': base64.b64encode(chunk.to_bytes()).decode(),
|
88
|
+
'mimeType': chunk.mime_type,
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
def from_value(self, value: dict[str, Any]) -> lf.Message:
|
93
|
+
"""Returns a Langfun message from Gemini message."""
|
94
|
+
message_cls = self.get_message_cls(
|
95
|
+
self._safe_read(value, 'role', default='model')
|
96
|
+
)
|
97
|
+
parts = self._safe_read(value, 'parts', default=[])
|
98
|
+
assert isinstance(parts, list)
|
99
|
+
|
100
|
+
chunks = []
|
101
|
+
thought_chunks = []
|
102
|
+
for part in parts:
|
103
|
+
if 'text' in part:
|
104
|
+
text = self._safe_read(part, 'text')
|
105
|
+
if 'thought' in part:
|
106
|
+
thought_chunks.append(text)
|
107
|
+
else:
|
108
|
+
chunks.append(text)
|
109
|
+
elif 'inlineData' in part:
|
110
|
+
data = self._safe_read(part, 'inlineData')
|
111
|
+
chunks.append(
|
112
|
+
lf_modalities.Mime.class_from_mime_type(
|
113
|
+
self._safe_read(data, 'mimeType')
|
114
|
+
).from_bytes(base64.b64decode(self._safe_read(data, 'data')))
|
115
|
+
)
|
116
|
+
elif 'fileData' in part:
|
117
|
+
data = self._safe_read(part, 'fileData')
|
118
|
+
chunks.append(
|
119
|
+
lf_modalities.Mime.class_from_mime_type(
|
120
|
+
self._safe_read(data, 'mimeType')
|
121
|
+
).from_uri(self._safe_read(data, 'fileUri'))
|
122
|
+
)
|
123
|
+
else:
|
124
|
+
raise ValueError(f'Unsupported content part: {part!r}.')
|
125
|
+
message = message_cls.from_chunks(chunks)
|
126
|
+
if thought_chunks:
|
127
|
+
message.set('thought', message_cls.from_chunks(thought_chunks))
|
128
|
+
return message
|
129
|
+
|
130
|
+
@classmethod
|
131
|
+
def get_role(cls, message: lf.Message) -> str:
|
132
|
+
"""Returns the role of the message."""
|
133
|
+
if isinstance(message, lf.AIMessage):
|
134
|
+
return 'model'
|
135
|
+
return super().get_role(message)
|
136
|
+
|
137
|
+
@classmethod
|
138
|
+
def get_message_cls(cls, role: str) -> type[lf.Message]:
|
139
|
+
"""Returns the message class of the message."""
|
140
|
+
if role == 'model':
|
141
|
+
return lf.AIMessage
|
142
|
+
return super().get_message_cls(role)
|
143
|
+
|
144
|
+
|
145
|
+
def _as_gemini_format(
|
146
|
+
self,
|
147
|
+
chunk_preprocessor: Callable[[str | lf.Modality], Any] | None = None,
|
148
|
+
**kwargs
|
149
|
+
) -> dict[str, Any]:
|
150
|
+
"""Returns a Gemini (REST) format message."""
|
151
|
+
return GeminiMessageConverter(
|
152
|
+
chunk_preprocessor=chunk_preprocessor, **kwargs
|
153
|
+
).to_value(self)
|
154
|
+
|
155
|
+
|
156
|
+
@classmethod
|
157
|
+
def _from_gemini_format(
|
158
|
+
cls,
|
159
|
+
gemini_message: dict[str, Any],
|
160
|
+
**kwargs
|
161
|
+
) -> lf.Message:
|
162
|
+
"""Creates a Langfun message from the Gemini (REST) format message."""
|
163
|
+
del cls
|
164
|
+
return GeminiMessageConverter(**kwargs).from_value(gemini_message)
|
165
|
+
|
166
|
+
# Set shortcut methods in lf.Message.
|
167
|
+
lf.Message.as_gemini_format = _as_gemini_format
|
168
|
+
lf.Message.from_gemini_format = _from_gemini_format
|