langfun 0.1.2.dev202506190804__tar.gz → 0.1.2.dev202512150805__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.dev202506190804 → langfun-0.1.2.dev202512150805}/PKG-INFO +15 -36
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/README.md +1 -24
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/__init__.py +1 -1
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/__init__.py +36 -0
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/bounding_box_parser.py +195 -0
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +313 -0
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/drawing.py +242 -0
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/drawing_test.py +103 -0
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/location.py +288 -0
- langfun-0.1.2.dev202512150805/langfun/assistant/capabilities/gui/location_test.py +230 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/__init__.py +9 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/agentic/__init__.py +11 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/agentic/action.py +865 -198
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/agentic/action_eval.py +9 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/agentic/action_test.py +253 -27
- langfun-0.1.2.dev202512150805/langfun/core/async_support.py +127 -0
- langfun-0.1.2.dev202512150805/langfun/core/async_support_test.py +62 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/__init__.py +0 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/correction.py +19 -9
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/execution.py +14 -12
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/generation.py +21 -16
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/sandboxing.py +24 -31
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/component.py +42 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/concurrent.py +70 -6
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/concurrent_test.py +17 -6
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/console.py +1 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/data/conversion/anthropic.py +12 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/data/conversion/anthropic_test.py +8 -6
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/data/conversion/gemini.py +11 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/data/conversion/gemini_test.py +48 -9
- langfun-0.1.2.dev202512150805/langfun/core/data/conversion/openai.py +245 -0
- langfun-0.1.2.dev202512150805/langfun/core/data/conversion/openai_test.py +320 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/base.py +48 -44
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/base_test.py +5 -5
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/matching.py +5 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/patching.py +3 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/scoring.py +4 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/__init__.py +3 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/checkpointing.py +149 -51
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/checkpointing_test.py +33 -20
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/config_saver.py +37 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/config_saver_test.py +36 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/eval_test_helper.py +104 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/evaluation.py +122 -24
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/evaluation_test.py +31 -5
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/example.py +50 -40
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/example_test.py +16 -8
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/experiment.py +108 -20
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/experiment_test.py +26 -7
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/metric_values.py +31 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/metric_values_test.py +32 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/metrics.py +157 -44
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/metrics_test.py +39 -18
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/progress.py +31 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/progress_test.py +27 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/progress_tracking.py +13 -5
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/progress_tracking_test.py +12 -4
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/reporting.py +88 -71
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/v2/reporting_test.py +29 -11
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/__init__.py +30 -0
- langfun-0.1.2.dev202506190804/langfun/core/eval/v2/runners.py → langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/base.py +74 -180
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/beam.py +354 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/beam_test.py +153 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/ckpt_monitor.py +350 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/ckpt_monitor_test.py +213 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/debug.py +40 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/debug_test.py +76 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/parallel.py +243 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/parallel_test.py +182 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun-0.1.2.dev202512150805/langfun/core/eval/v2/runners/sequential_test.py +169 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/langfunc.py +45 -130
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/langfunc_test.py +7 -5
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/language_model.py +304 -46
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/language_model_test.py +128 -17
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/__init__.py +21 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/anthropic.py +157 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/azure_openai.py +29 -17
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/cache/base.py +25 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/cache/in_memory.py +48 -7
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/cache/in_memory_test.py +14 -4
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/compositional.py +26 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/deepseek.py +30 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/fake.py +39 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/fake_test.py +9 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/gemini.py +164 -14
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/gemini_test.py +110 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/google_genai.py +75 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/groq.py +28 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/llama_cpp.py +23 -4
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/openai.py +120 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/openai_compatible.py +148 -27
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/openai_compatible_test.py +207 -20
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/openai_test.py +0 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/rest.py +16 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/vertexai.py +105 -10
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/logging.py +1 -1
- langfun-0.1.2.dev202512150805/langfun/core/mcp/__init__.py +10 -0
- langfun-0.1.2.dev202512150805/langfun/core/mcp/client.py +177 -0
- langfun-0.1.2.dev202512150805/langfun/core/mcp/client_test.py +71 -0
- langfun-0.1.2.dev202512150805/langfun/core/mcp/session.py +241 -0
- langfun-0.1.2.dev202512150805/langfun/core/mcp/session_test.py +54 -0
- langfun-0.1.2.dev202506190804/langfun/core/modalities/pdf.py → langfun-0.1.2.dev202512150805/langfun/core/mcp/testing/simple_mcp_client.py +17 -6
- langfun-0.1.2.dev202512150805/langfun/core/mcp/testing/simple_mcp_server.py +33 -0
- langfun-0.1.2.dev202512150805/langfun/core/mcp/tool.py +254 -0
- langfun-0.1.2.dev202512150805/langfun/core/mcp/tool_test.py +197 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/memory.py +1 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/message.py +162 -59
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/message_test.py +71 -87
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/__init__.py +8 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/audio.py +21 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/audio_test.py +4 -4
- langfun-0.1.2.dev202512150805/langfun/core/modalities/image.py +130 -0
- langfun-0.1.2.dev202512150805/langfun/core/modalities/image_test.py +224 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/mime.py +94 -16
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/mime_test.py +59 -0
- langfun-0.1.2.dev202512150805/langfun/core/modalities/pdf.py +40 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/video.py +21 -1
- langfun-0.1.2.dev202512150805/langfun/core/modality.py +271 -0
- langfun-0.1.2.dev202512150805/langfun/core/modality_test.py +117 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/natural_language.py +1 -1
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/sampling.py +4 -4
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/sampling_test.py +20 -4
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/__init__.py +9 -24
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/completion.py +62 -44
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/completion_test.py +59 -43
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/description.py +54 -50
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/function_generation.py +29 -12
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/mapping.py +92 -38
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/parsing.py +170 -77
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/parsing_test.py +15 -3
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/querying.py +284 -156
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/querying_test.py +120 -64
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/__init__.py +49 -0
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/base.py +664 -0
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/base_test.py +531 -0
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/json.py +174 -0
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/json_test.py +121 -0
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/python.py +316 -0
- langfun-0.1.2.dev202512150805/langfun/core/structured/schema/python_test.py +410 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/schema_generation.py +33 -14
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/scoring.py +74 -35
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/scoring_test.py +8 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/tokenization.py +49 -10
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/tokenization_test.py +8 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/subscription.py +2 -2
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/template.py +176 -51
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/template_test.py +123 -17
- langfun-0.1.2.dev202512150805/langfun/env/__init__.py +43 -0
- langfun-0.1.2.dev202512150805/langfun/env/base_environment.py +827 -0
- langfun-0.1.2.dev202512150805/langfun/env/base_environment_test.py +473 -0
- langfun-0.1.2.dev202512150805/langfun/env/base_feature.py +304 -0
- langfun-0.1.2.dev202512150805/langfun/env/base_feature_test.py +228 -0
- langfun-0.1.2.dev202512150805/langfun/env/base_sandbox.py +842 -0
- langfun-0.1.2.dev202512150805/langfun/env/base_sandbox_test.py +1235 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/__init__.py +14 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/chain.py +233 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/chain_test.py +253 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/event_logger.py +472 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/event_logger_test.py +304 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/metric_writer.py +726 -0
- langfun-0.1.2.dev202512150805/langfun/env/event_handlers/metric_writer_test.py +214 -0
- langfun-0.1.2.dev202512150805/langfun/env/interface.py +1640 -0
- langfun-0.1.2.dev202512150805/langfun/env/interface_test.py +153 -0
- langfun-0.1.2.dev202512150805/langfun/env/load_balancers.py +59 -0
- langfun-0.1.2.dev202512150805/langfun/env/load_balancers_test.py +141 -0
- langfun-0.1.2.dev202512150805/langfun/env/test_utils.py +507 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun.egg-info/PKG-INFO +15 -36
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun.egg-info/SOURCES.txt +59 -5
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun.egg-info/requires.txt +10 -9
- langfun-0.1.2.dev202506190804/langfun/core/data/conversion/openai.py +0 -131
- langfun-0.1.2.dev202506190804/langfun/core/data/conversion/openai_test.py +0 -176
- langfun-0.1.2.dev202506190804/langfun/core/eval/v2/runners_test.py +0 -343
- langfun-0.1.2.dev202506190804/langfun/core/modalities/image.py +0 -60
- langfun-0.1.2.dev202506190804/langfun/core/modalities/image_test.py +0 -108
- langfun-0.1.2.dev202506190804/langfun/core/modality.py +0 -133
- langfun-0.1.2.dev202506190804/langfun/core/modality_test.py +0 -87
- langfun-0.1.2.dev202506190804/langfun/core/structured/schema.py +0 -977
- langfun-0.1.2.dev202506190804/langfun/core/structured/schema_test.py +0 -982
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/LICENSE +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/agentic/action_eval_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/correction_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/execution_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/generation_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/parsing.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/parsing_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/coding/python/sandboxing_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/component_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/console_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/data/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/data/conversion/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/matching_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/patching_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/eval/scoring_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/anthropic_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/azure_openai_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/cache/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/compositional_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/deepseek_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/google_genai_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/groq_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/llama_cpp_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/rest_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/llms/vertexai_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/logging_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/memories/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/memories/conversation_history.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/memories/conversation_history_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/pdf_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/modalities/video_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/natural_language_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/description_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/function_generation_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/mapping_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/structured/schema_generation_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/subscription_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/__init__.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/completion.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/completion_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/conversation.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/conversation_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/demonstration.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/demonstration_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/selfplay.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun/core/templates/selfplay_test.py +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun.egg-info/dependency_links.txt +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/langfun.egg-info/top_level.txt +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/setup.cfg +0 -0
- {langfun-0.1.2.dev202506190804 → langfun-0.1.2.dev202512150805}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langfun
|
|
3
|
-
Version: 0.1.2.
|
|
3
|
+
Version: 0.1.2.dev202512150805
|
|
4
4
|
Summary: Langfun: Language as Functions.
|
|
5
5
|
Home-page: https://github.com/google/langfun
|
|
6
6
|
Author: Langfun Authors
|
|
@@ -21,30 +21,32 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist:
|
|
24
|
+
Requires-Dist: anyio>=4.7.0
|
|
25
25
|
Requires-Dist: jinja2>=3.1.2
|
|
26
|
+
Requires-Dist: mcp>=1.17.0
|
|
27
|
+
Requires-Dist: puremagic>=1.20
|
|
28
|
+
Requires-Dist: pyglove>=0.5.0.dev202510170226
|
|
26
29
|
Requires-Dist: requests>=2.31.0
|
|
27
30
|
Provides-Extra: all
|
|
28
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: anyio>=4.7.0; extra == "all"
|
|
29
32
|
Requires-Dist: jinja2>=3.1.2; extra == "all"
|
|
33
|
+
Requires-Dist: mcp>=1.17.0; extra == "all"
|
|
34
|
+
Requires-Dist: puremagic>=1.20; extra == "all"
|
|
35
|
+
Requires-Dist: pyglove>=0.5.0.dev202510170226; extra == "all"
|
|
30
36
|
Requires-Dist: requests>=2.31.0; extra == "all"
|
|
31
|
-
Requires-Dist: termcolor==1.1.0; extra == "all"
|
|
32
|
-
Requires-Dist: tqdm>=4.64.1; extra == "all"
|
|
33
37
|
Requires-Dist: google-auth>=2.16.0; extra == "all"
|
|
34
|
-
Requires-Dist: python-magic>=0.4.27; extra == "all"
|
|
35
38
|
Requires-Dist: pillow>=10.0.0; extra == "all"
|
|
36
|
-
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist: tqdm>=4.64.1; extra == "ui"
|
|
39
|
+
Requires-Dist: termcolor==1.1.0; extra == "all"
|
|
40
|
+
Requires-Dist: tqdm>=4.64.1; extra == "all"
|
|
39
41
|
Provides-Extra: vertexai
|
|
40
42
|
Requires-Dist: google-auth>=2.16.0; extra == "vertexai"
|
|
41
43
|
Provides-Extra: mime
|
|
42
|
-
Requires-Dist: python-magic>=0.4.27; extra == "mime"
|
|
43
44
|
Requires-Dist: pillow>=10.0.0; extra == "mime"
|
|
44
|
-
Provides-Extra: mime-auto
|
|
45
|
-
Requires-Dist: python-magic>=0.4.27; extra == "mime-auto"
|
|
46
45
|
Provides-Extra: mime-pil
|
|
47
46
|
Requires-Dist: pillow>=10.0.0; extra == "mime-pil"
|
|
47
|
+
Provides-Extra: ui
|
|
48
|
+
Requires-Dist: termcolor==1.1.0; extra == "ui"
|
|
49
|
+
Requires-Dist: tqdm>=4.64.1; extra == "ui"
|
|
48
50
|
Dynamic: author
|
|
49
51
|
Dynamic: author-email
|
|
50
52
|
Dynamic: classifier
|
|
@@ -202,37 +204,14 @@ a tag from the list below:
|
|
|
202
204
|
| all | All Langfun features. |
|
|
203
205
|
| vertexai | VertexAI access. |
|
|
204
206
|
| mime | All MIME supports. |
|
|
205
|
-
| mime-auto | Automatic MIME type detection. |
|
|
206
207
|
| mime-pil | Image support for PIL. |
|
|
207
208
|
| ui | UI enhancements |
|
|
208
209
|
|
|
209
210
|
For example, to install a nightly build that includes VertexAI access, full
|
|
210
211
|
modality support, and UI enhancements, use:
|
|
211
|
-
```
|
|
212
|
-
pip install langfun[vertexai,mime,ui] --pre
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Solving import issue with `libmagic`
|
|
216
|
-
|
|
217
|
-
Langfun utilizes `libmagic` for automatic MIME type detection to support
|
|
218
|
-
multi-modal functionalities. However, `pip install libmagic` may not work
|
|
219
|
-
out-of-the-box on all operation systems, sometimes leading to an
|
|
220
|
-
`'ImportError: failed to find libmagic.'` error after Langfun installation.
|
|
221
|
-
|
|
222
|
-
If you encounter this error, you will need to follow the recommendations below
|
|
223
|
-
to fix the installation of `libmagic` library.
|
|
224
212
|
|
|
225
|
-
#### OSX
|
|
226
|
-
|
|
227
|
-
```
|
|
228
|
-
conda install conda-forge::libmagic
|
|
229
213
|
```
|
|
230
|
-
|
|
231
|
-
#### Windows:
|
|
232
|
-
```
|
|
233
|
-
pip install python-magic
|
|
234
|
-
pip uninstall python-magic-bin
|
|
235
|
-
pip install python-magic-bin
|
|
214
|
+
pip install langfun[vertexai,mime,ui] --pre
|
|
236
215
|
```
|
|
237
216
|
|
|
238
217
|
*Disclaimer: this is not an officially supported Google product.*
|
|
@@ -142,37 +142,14 @@ a tag from the list below:
|
|
|
142
142
|
| all | All Langfun features. |
|
|
143
143
|
| vertexai | VertexAI access. |
|
|
144
144
|
| mime | All MIME supports. |
|
|
145
|
-
| mime-auto | Automatic MIME type detection. |
|
|
146
145
|
| mime-pil | Image support for PIL. |
|
|
147
146
|
| ui | UI enhancements |
|
|
148
147
|
|
|
149
148
|
For example, to install a nightly build that includes VertexAI access, full
|
|
150
149
|
modality support, and UI enhancements, use:
|
|
151
|
-
```
|
|
152
|
-
pip install langfun[vertexai,mime,ui] --pre
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Solving import issue with `libmagic`
|
|
156
|
-
|
|
157
|
-
Langfun utilizes `libmagic` for automatic MIME type detection to support
|
|
158
|
-
multi-modal functionalities. However, `pip install libmagic` may not work
|
|
159
|
-
out-of-the-box on all operation systems, sometimes leading to an
|
|
160
|
-
`'ImportError: failed to find libmagic.'` error after Langfun installation.
|
|
161
|
-
|
|
162
|
-
If you encounter this error, you will need to follow the recommendations below
|
|
163
|
-
to fix the installation of `libmagic` library.
|
|
164
150
|
|
|
165
|
-
#### OSX
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
conda install conda-forge::libmagic
|
|
169
151
|
```
|
|
170
|
-
|
|
171
|
-
#### Windows:
|
|
172
|
-
```
|
|
173
|
-
pip install python-magic
|
|
174
|
-
pip uninstall python-magic-bin
|
|
175
|
-
pip install python-magic-bin
|
|
152
|
+
pip install langfun[vertexai,mime,ui] --pre
|
|
176
153
|
```
|
|
177
154
|
|
|
178
155
|
*Disclaimer: this is not an officially supported Google product.*
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
"""GUI understanding."""
|
|
15
|
+
|
|
16
|
+
# pylint: disable=g-bad-import-order
|
|
17
|
+
# pylint: disable=g-importing-member
|
|
18
|
+
# pylint: disable=g-import-not-at-top
|
|
19
|
+
|
|
20
|
+
from langfun.assistant.capabilities.gui.location import Coordinate
|
|
21
|
+
from langfun.assistant.capabilities.gui.location import BBox
|
|
22
|
+
from langfun.assistant.capabilities.gui import bounding_box_parser
|
|
23
|
+
from langfun.assistant.capabilities.gui import drawing
|
|
24
|
+
import pyglove as pg
|
|
25
|
+
|
|
26
|
+
# For backward compatibility.
|
|
27
|
+
pg.JSONConvertible.add_module_alias(
|
|
28
|
+
'langfun.assistant.capabilities.gui', (
|
|
29
|
+
'langfun.agents.gui',
|
|
30
|
+
'langfun.agents.tools.gui',
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# pylint: enable=g-bad-import-order
|
|
35
|
+
# pylint: enable=g-importing-member
|
|
36
|
+
# pylint: enable=g-import-not-at-top
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
"""Customized bounding box related to Gemini Object Detection."""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import re
|
|
18
|
+
from typing import Any, Dict, List, Tuple, Union
|
|
19
|
+
|
|
20
|
+
from langfun.assistant.capabilities.gui import location
|
|
21
|
+
import pyglove as pg
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GeminiBBox(pg.Object):
|
|
25
|
+
"""Customized bounding box.
|
|
26
|
+
|
|
27
|
+
Note: GeminiPro returns a unique JSON response structure for object detection
|
|
28
|
+
tasks, which differs from the standard location.BBox format. To accommodate
|
|
29
|
+
this, we've created this custom class to adapt to GeminiPro's specific
|
|
30
|
+
response structure.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
ymin: int
|
|
34
|
+
xmin: int
|
|
35
|
+
ymax: int
|
|
36
|
+
xmax: int
|
|
37
|
+
|
|
38
|
+
def scale(self,
|
|
39
|
+
target_size: tuple[int, int],
|
|
40
|
+
source_size: tuple[int, int] = (1000, 1000)) -> 'GeminiBBox':
|
|
41
|
+
|
|
42
|
+
def _scale_x(v):
|
|
43
|
+
return (v * target_size[0]) // source_size[0]
|
|
44
|
+
|
|
45
|
+
def _scale_y(v):
|
|
46
|
+
return (v * target_size[1]) // source_size[1]
|
|
47
|
+
|
|
48
|
+
return GeminiBBox(
|
|
49
|
+
ymin=_scale_y(self.ymin),
|
|
50
|
+
xmin=_scale_x(self.xmin),
|
|
51
|
+
ymax=_scale_y(self.ymax),
|
|
52
|
+
xmax=_scale_x(self.xmax),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def resize(
|
|
56
|
+
self, image_size: Tuple[int, int], resize_factor: int = 3
|
|
57
|
+
) -> 'GeminiBBox':
|
|
58
|
+
"""Resize a bounding box from its center.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
image_size: A tuple (width, height) representing the size of the image.
|
|
62
|
+
resize_factor: The factor by which to resize the bounding box. Defaults
|
|
63
|
+
to 3.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
A bounding box with the expanded dimensions.
|
|
67
|
+
"""
|
|
68
|
+
center_x = (self.xmin + self.xmax) // 2
|
|
69
|
+
center_y = (self.ymin + self.ymax) // 2
|
|
70
|
+
width = self.xmax - self.xmin
|
|
71
|
+
height = self.ymax - self.ymin
|
|
72
|
+
|
|
73
|
+
new_width = width * resize_factor
|
|
74
|
+
new_height = height * resize_factor
|
|
75
|
+
|
|
76
|
+
new_xmin = max(0, int(center_x - new_width // 2))
|
|
77
|
+
new_ymin = max(0, int(center_y - new_height // 2))
|
|
78
|
+
new_xmax = min(image_size[0], int(center_x + new_width // 2))
|
|
79
|
+
new_ymax = min(image_size[1], int(center_y + new_height // 2))
|
|
80
|
+
|
|
81
|
+
return GeminiBBox(
|
|
82
|
+
xmin=new_xmin, ymin=new_ymin, xmax=new_xmax, ymax=new_ymax
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def to_gui_bbox(self) -> location.BBox | None:
|
|
86
|
+
try:
|
|
87
|
+
return location.BBox(
|
|
88
|
+
x=self.xmin, y=self.ymin, right=self.xmax, bottom=self.ymax
|
|
89
|
+
)
|
|
90
|
+
except AssertionError:
|
|
91
|
+
# If the bounding box is not valid, return None.
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def extract_json_candidate_from_text(raw_text: str) -> str:
|
|
96
|
+
"""Extracts a JSON candidate string from raw text."""
|
|
97
|
+
# Try to find content within ```json ... ```
|
|
98
|
+
match = re.search(r'```json\s*([\s\S]+?)\s*```', raw_text, re.IGNORECASE)
|
|
99
|
+
if match:
|
|
100
|
+
return match.group(1).strip()
|
|
101
|
+
|
|
102
|
+
# Try to find content within ``` ... ```
|
|
103
|
+
match = re.search(r'```\s*([\s\S]+?)\s*```', raw_text)
|
|
104
|
+
if match:
|
|
105
|
+
return match.group(1).strip()
|
|
106
|
+
|
|
107
|
+
# If no code blocks, return the stripped raw text.
|
|
108
|
+
return raw_text.strip()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def parse_and_convert_json(
|
|
112
|
+
text: str, screen_size: Tuple[int, int] = (1000, 1000)
|
|
113
|
+
) -> Dict[str, location.BBox | None]:
|
|
114
|
+
"""Parse and convert json to bounding box.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
text: The text to parse.
|
|
118
|
+
screen_size: The screen size to scale the bounding box.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
A dictionary of bounding boxes.
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
>>> json_text = '```{"search button": [10, 20, 100, 200]}```'
|
|
125
|
+
>>> bboxes = parse_and_convert_json(json_text, screen_size=(800, 600))
|
|
126
|
+
>>> print(bboxes)
|
|
127
|
+
{'search button': BBox(x=16, y=6, right=160, bottom=60)}
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def parse_json(t: str) -> Union[Dict[str, str], List[str], None]:
|
|
131
|
+
"""Parse text to json."""
|
|
132
|
+
try:
|
|
133
|
+
return json.loads(t)
|
|
134
|
+
except json.JSONDecodeError:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
def can_cast_to_int(obj: Any) -> bool:
|
|
138
|
+
try:
|
|
139
|
+
int(obj)
|
|
140
|
+
return True
|
|
141
|
+
except (ValueError, TypeError):
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
def convert_to_bbox(
|
|
145
|
+
data: Union[Dict[str, Any], List[Any], None], screen_size: Tuple[int, int]
|
|
146
|
+
) -> Dict[str, location.BBox | None]:
|
|
147
|
+
"""Convert data to bounding box."""
|
|
148
|
+
result = {}
|
|
149
|
+
if not data:
|
|
150
|
+
return result
|
|
151
|
+
|
|
152
|
+
if isinstance(data, list):
|
|
153
|
+
if len(data) == 4 and all(can_cast_to_int(item) for item in data):
|
|
154
|
+
return {
|
|
155
|
+
'element': (
|
|
156
|
+
GeminiBBox(
|
|
157
|
+
xmin=int(data[1]),
|
|
158
|
+
ymin=int(data[0]),
|
|
159
|
+
xmax=int(data[3]),
|
|
160
|
+
ymax=int(data[2]),
|
|
161
|
+
)
|
|
162
|
+
.scale(screen_size)
|
|
163
|
+
.to_gui_bbox()
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
for item in data:
|
|
167
|
+
if isinstance(item, dict):
|
|
168
|
+
result.update(convert_to_bbox(item, screen_size))
|
|
169
|
+
return result
|
|
170
|
+
|
|
171
|
+
for key, value in data.items():
|
|
172
|
+
if not value:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
if isinstance(value, list):
|
|
176
|
+
if len(value) == 4 and all(can_cast_to_int(item) for item in value):
|
|
177
|
+
result[key] = (
|
|
178
|
+
GeminiBBox(
|
|
179
|
+
xmin=int(value[1]),
|
|
180
|
+
ymin=int(value[0]),
|
|
181
|
+
xmax=int(value[3]),
|
|
182
|
+
ymax=int(value[2]),
|
|
183
|
+
)
|
|
184
|
+
.scale(screen_size)
|
|
185
|
+
.to_gui_bbox()
|
|
186
|
+
)
|
|
187
|
+
elif isinstance(value, dict):
|
|
188
|
+
result.update(convert_to_bbox(value, screen_size))
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
parsed_data = parse_json(extract_json_candidate_from_text(text))
|
|
192
|
+
if parsed_data is None:
|
|
193
|
+
return {}
|
|
194
|
+
|
|
195
|
+
return convert_to_bbox(parsed_data, screen_size)
|
|
@@ -0,0 +1,313 @@
|
|
|
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
|
+
from typing import Dict, Tuple
|
|
15
|
+
import unittest
|
|
16
|
+
|
|
17
|
+
from langfun.assistant.capabilities.gui import bounding_box_parser
|
|
18
|
+
from langfun.assistant.capabilities.gui import location
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BoundingBoxTest(unittest.TestCase):
|
|
22
|
+
|
|
23
|
+
def assert_bbox_equal(
|
|
24
|
+
self,
|
|
25
|
+
expected: Dict[str, Tuple[int, int, int, int]],
|
|
26
|
+
actual: Dict[str, location.BBox],
|
|
27
|
+
):
|
|
28
|
+
self.assertEqual(len(expected), len(actual))
|
|
29
|
+
for key, value in expected.items():
|
|
30
|
+
self.assertIn(key, actual)
|
|
31
|
+
self.assertEqual(value[0], actual[key].x)
|
|
32
|
+
self.assertEqual(value[1], actual[key].y)
|
|
33
|
+
self.assertEqual(value[2], actual[key].right)
|
|
34
|
+
self.assertEqual(value[3], actual[key].bottom)
|
|
35
|
+
|
|
36
|
+
def test_bbox_basic_functionality(self):
|
|
37
|
+
bbox = bounding_box_parser.GeminiBBox(xmin=10, ymin=20, xmax=50, ymax=80)
|
|
38
|
+
|
|
39
|
+
self.assertEqual(bbox.xmin, 10)
|
|
40
|
+
self.assertEqual(bbox.ymin, 20)
|
|
41
|
+
self.assertEqual(bbox.xmax, 50)
|
|
42
|
+
self.assertEqual(bbox.ymax, 80)
|
|
43
|
+
|
|
44
|
+
# Test the `scale` method
|
|
45
|
+
target_size = (800, 600)
|
|
46
|
+
source_size = (1000, 1000)
|
|
47
|
+
scaled_bbox = bbox.scale(target_size, source_size)
|
|
48
|
+
|
|
49
|
+
self.assertEqual(scaled_bbox.xmin, 8)
|
|
50
|
+
self.assertEqual(scaled_bbox.ymin, 12)
|
|
51
|
+
self.assertEqual(scaled_bbox.xmax, 40)
|
|
52
|
+
self.assertEqual(scaled_bbox.ymax, 48)
|
|
53
|
+
|
|
54
|
+
# Test the `resize` method
|
|
55
|
+
resized_bbox = bbox.resize((1200, 800), 2)
|
|
56
|
+
|
|
57
|
+
self.assertEqual(resized_bbox.xmin, 0)
|
|
58
|
+
self.assertEqual(resized_bbox.ymin, 0)
|
|
59
|
+
self.assertEqual(resized_bbox.xmax, 70)
|
|
60
|
+
self.assertEqual(resized_bbox.ymax, 110)
|
|
61
|
+
|
|
62
|
+
# Test the `to_gui_bbox` method
|
|
63
|
+
gui_bbox = bbox.to_gui_bbox()
|
|
64
|
+
self.assertEqual(gui_bbox.x, 10)
|
|
65
|
+
self.assertEqual(gui_bbox.y, 20)
|
|
66
|
+
self.assertEqual(gui_bbox.right, 50)
|
|
67
|
+
self.assertEqual(gui_bbox.bottom, 80)
|
|
68
|
+
|
|
69
|
+
def test_simple_json(self):
|
|
70
|
+
json_text = '{"search button": [10, 20, 100, 200]}'
|
|
71
|
+
expected = {'search button': (16, 6, 160, 60)}
|
|
72
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
73
|
+
json_text, screen_size=(800, 600)
|
|
74
|
+
)
|
|
75
|
+
self.assert_bbox_equal(expected, result)
|
|
76
|
+
|
|
77
|
+
def test_multiple_objects(self):
|
|
78
|
+
json_text = (
|
|
79
|
+
'{"button1": [10, 20, 100, 200], "button2": [30, 40, 130, 240]}'
|
|
80
|
+
)
|
|
81
|
+
expected = {'button1': (16, 6, 160, 60), 'button2': (32, 18, 192, 78)}
|
|
82
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
83
|
+
json_text, screen_size=(800, 600)
|
|
84
|
+
)
|
|
85
|
+
self.assert_bbox_equal(expected, result)
|
|
86
|
+
|
|
87
|
+
def test_nested_json(self):
|
|
88
|
+
json_text = (
|
|
89
|
+
'{"buttons": {"search": [10, 20, 100, 200], "cancel": [30, 40, 130,'
|
|
90
|
+
' 240]}}'
|
|
91
|
+
)
|
|
92
|
+
expected = {'search': (16, 6, 160, 60), 'cancel': (32, 18, 192, 78)}
|
|
93
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
94
|
+
json_text, screen_size=(800, 600)
|
|
95
|
+
)
|
|
96
|
+
self.assert_bbox_equal(expected, result)
|
|
97
|
+
|
|
98
|
+
def test_json_in_code_block(self):
|
|
99
|
+
json_text = '```\n{"search button": [10, 20, 100, 200]}\n```'
|
|
100
|
+
expected = {'search button': (16, 6, 160, 60)}
|
|
101
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
102
|
+
json_text, screen_size=(800, 600)
|
|
103
|
+
)
|
|
104
|
+
self.assert_bbox_equal(expected, result)
|
|
105
|
+
|
|
106
|
+
def test_extract_json_candidate_from_text(self):
|
|
107
|
+
test_cases = [
|
|
108
|
+
(
|
|
109
|
+
'Some text before ```json\n{"key": "value"}\n``` and after',
|
|
110
|
+
'{"key": "value"}',
|
|
111
|
+
),
|
|
112
|
+
(
|
|
113
|
+
'Some text before ```\n{"key": "value"}\n``` and after',
|
|
114
|
+
'{"key": "value"}',
|
|
115
|
+
),
|
|
116
|
+
('{"key": "value"}', '{"key": "value"}'),
|
|
117
|
+
(
|
|
118
|
+
'```json\n{\n "name": "Test",\n "version": 1\n}\n```',
|
|
119
|
+
'{\n "name": "Test",\n "version": 1\n}',
|
|
120
|
+
),
|
|
121
|
+
(
|
|
122
|
+
'```\n{\n "name": "Test",\n "version": 1\n}\n```',
|
|
123
|
+
'{\n "name": "Test",\n "version": 1\n}',
|
|
124
|
+
),
|
|
125
|
+
(
|
|
126
|
+
' ```json\n {"spaced_json": true} \n``` ',
|
|
127
|
+
'{"spaced_json": true}',
|
|
128
|
+
),
|
|
129
|
+
(
|
|
130
|
+
'No code block here, just plain text.',
|
|
131
|
+
'No code block here, just plain text.',
|
|
132
|
+
),
|
|
133
|
+
(
|
|
134
|
+
'```JSON\n{"case_test": "uppercase_json_tag"}\n```',
|
|
135
|
+
'{"case_test": "uppercase_json_tag"}',
|
|
136
|
+
),
|
|
137
|
+
('', ''),
|
|
138
|
+
(' ', ''),
|
|
139
|
+
(
|
|
140
|
+
(
|
|
141
|
+
'First block: ```json\n{"first": true}\n``` Second block:'
|
|
142
|
+
' ```json\n{"second": false}\n```'
|
|
143
|
+
),
|
|
144
|
+
'{"first": true}',
|
|
145
|
+
),
|
|
146
|
+
(
|
|
147
|
+
(
|
|
148
|
+
'First block: ```\n{"first_code": true}\n``` Second block:'
|
|
149
|
+
' ```\n{"second_code": false}\n```'
|
|
150
|
+
),
|
|
151
|
+
'{"first_code": true}',
|
|
152
|
+
),
|
|
153
|
+
(
|
|
154
|
+
'```json \n {"leading_trailing_space_in_block": "test"} \n ```',
|
|
155
|
+
'{"leading_trailing_space_in_block": "test"}',
|
|
156
|
+
),
|
|
157
|
+
(
|
|
158
|
+
(
|
|
159
|
+
'```\n {"leading_trailing_space_in_block_no_json_tag": "test"}'
|
|
160
|
+
' \n ```'
|
|
161
|
+
),
|
|
162
|
+
'{"leading_trailing_space_in_block_no_json_tag": "test"}',
|
|
163
|
+
),
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
for raw_text, expected_json_str in test_cases:
|
|
167
|
+
with self.subTest(raw_text=raw_text):
|
|
168
|
+
self.assertEqual(
|
|
169
|
+
bounding_box_parser.extract_json_candidate_from_text(raw_text),
|
|
170
|
+
expected_json_str,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def test_dict_in_list(self):
|
|
174
|
+
json_text = '```json\n[\n {"box_2d": [61, 22, 160, 95]}\n]\n```'
|
|
175
|
+
expected = {'box_2d': (22, 61, 95, 160)}
|
|
176
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
177
|
+
self.assert_bbox_equal(expected, result)
|
|
178
|
+
|
|
179
|
+
def test_dict_with_label(self):
|
|
180
|
+
json_text = """```json
|
|
181
|
+
[
|
|
182
|
+
{"box_2d": [634, 416, 820, 482], "label": "the inner vertical side of the rightmost lower protrusion of the green polygon"},
|
|
183
|
+
{"box_2d": [820, 328, 872, 352], "label": "the purple number '1' located to its left"}
|
|
184
|
+
]
|
|
185
|
+
```"""
|
|
186
|
+
expected = {'box_2d': (328, 820, 352, 872)}
|
|
187
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
188
|
+
print('result: ', result)
|
|
189
|
+
self.assert_bbox_equal(expected, result)
|
|
190
|
+
|
|
191
|
+
def test_invalid_json(self):
|
|
192
|
+
json_text = 'This is not a valid JSON'
|
|
193
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
194
|
+
self.assertEqual({}, result)
|
|
195
|
+
|
|
196
|
+
def test_empty_input(self):
|
|
197
|
+
json_text = ''
|
|
198
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
199
|
+
self.assertEqual({}, result)
|
|
200
|
+
|
|
201
|
+
def test_invalid_list_length(self):
|
|
202
|
+
# Test with a list of length 3
|
|
203
|
+
json_text = '{"button": [10, 20, 100]}'
|
|
204
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
205
|
+
self.assertEqual({}, result)
|
|
206
|
+
|
|
207
|
+
# Test with a list of length 5
|
|
208
|
+
json_text = '{"button": [10, 20, 100, 200, 300]}'
|
|
209
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
210
|
+
self.assertEqual({}, result)
|
|
211
|
+
|
|
212
|
+
# Test with an empty list
|
|
213
|
+
json_text = '{"button": []}'
|
|
214
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
215
|
+
self.assertEqual({}, result)
|
|
216
|
+
|
|
217
|
+
def test_list_input(self):
|
|
218
|
+
json_text = '[10, 20, 100, 200]'
|
|
219
|
+
expected = {'element': (20, 10, 200, 100)}
|
|
220
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
221
|
+
self.assert_bbox_equal(expected, result)
|
|
222
|
+
|
|
223
|
+
def test_default_screen_size(self):
|
|
224
|
+
json_text = '{"button": [10, 20, 100, 200]}'
|
|
225
|
+
expected = {'button': (20, 10, 200, 100)}
|
|
226
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
227
|
+
self.assert_bbox_equal(expected, result)
|
|
228
|
+
|
|
229
|
+
def test_float_numbers(self):
|
|
230
|
+
json_text = '{"button": [10.5, 20.2, 100.7, 200.9]}'
|
|
231
|
+
expected = {'button': (20, 10, 200, 100)} # Expected integer coordinates
|
|
232
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
233
|
+
json_text, screen_size=(1000, 1000)
|
|
234
|
+
)
|
|
235
|
+
self.assert_bbox_equal(expected, result)
|
|
236
|
+
|
|
237
|
+
def test_type_error_handling(self):
|
|
238
|
+
json_text = '{"button": ["text", 20, 100, 200]}'
|
|
239
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
240
|
+
json_text, screen_size=(1000, 1000)
|
|
241
|
+
)
|
|
242
|
+
self.assertEqual({}, result)
|
|
243
|
+
json_text = '{"button": [None, 20, 100, 200]}'
|
|
244
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
245
|
+
json_text, screen_size=(1000, 1000)
|
|
246
|
+
)
|
|
247
|
+
self.assertEqual({}, result)
|
|
248
|
+
|
|
249
|
+
def test_malformed_json(self):
|
|
250
|
+
# Missing quotes around keys
|
|
251
|
+
json_text = '{search button: [10, 20, 100, 200]}'
|
|
252
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
253
|
+
self.assertEqual({}, result)
|
|
254
|
+
|
|
255
|
+
# Unbalanced brackets
|
|
256
|
+
json_text = '{"search button": [10, 20, 100, 200}'
|
|
257
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
258
|
+
self.assertEqual({}, result)
|
|
259
|
+
|
|
260
|
+
# Incorrect comma usage
|
|
261
|
+
json_text = '{"search button", [10, 20, 100, 200]}'
|
|
262
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
263
|
+
self.assertEqual({}, result)
|
|
264
|
+
|
|
265
|
+
def test_mixed_data_types(self):
|
|
266
|
+
# String values in coordinates
|
|
267
|
+
json_text = '{"button": ["10", "20", "100", "200"]}'
|
|
268
|
+
expected = {'button': (20, 10, 200, 100)}
|
|
269
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
270
|
+
json_text, screen_size=(1000, 1000)
|
|
271
|
+
)
|
|
272
|
+
self.assert_bbox_equal(expected, result)
|
|
273
|
+
|
|
274
|
+
def test_none_values(self):
|
|
275
|
+
json_text = '{"button": [null, 20, 100, 200]}'
|
|
276
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
277
|
+
json_text, screen_size=(1000, 1000)
|
|
278
|
+
)
|
|
279
|
+
self.assertEqual({}, result)
|
|
280
|
+
|
|
281
|
+
json_text = '{"button": [10, null, 100, 200]}'
|
|
282
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
283
|
+
json_text, screen_size=(1000, 1000)
|
|
284
|
+
)
|
|
285
|
+
self.assertEqual({}, result)
|
|
286
|
+
|
|
287
|
+
def test_large_coordinates(self):
|
|
288
|
+
json_text = '{"button": [10000, 20000, 100000, 200000]}'
|
|
289
|
+
expected = {'button': (20000, 10000, 200000, 100000)}
|
|
290
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
291
|
+
json_text, screen_size=(1000, 1000)
|
|
292
|
+
)
|
|
293
|
+
self.assert_bbox_equal(expected, result)
|
|
294
|
+
|
|
295
|
+
def test_different_string_formats(self):
|
|
296
|
+
# Newlines
|
|
297
|
+
json_text = '{\n"search button": [10, 20, 100, 200]\n}'
|
|
298
|
+
expected = {'search button': (16, 6, 160, 60)}
|
|
299
|
+
result = bounding_box_parser.parse_and_convert_json(
|
|
300
|
+
json_text, screen_size=(800, 600)
|
|
301
|
+
)
|
|
302
|
+
self.assert_bbox_equal(expected, result)
|
|
303
|
+
|
|
304
|
+
def test_deeply_nested_json(self):
|
|
305
|
+
json_text = (
|
|
306
|
+
'{"layer1": {"layer2": {"layer3": {"button": [10, 20, 100, 200]}}}}'
|
|
307
|
+
)
|
|
308
|
+
expected = {'button': (20, 10, 200, 100)}
|
|
309
|
+
result = bounding_box_parser.parse_and_convert_json(json_text)
|
|
310
|
+
self.assert_bbox_equal(expected, result)
|
|
311
|
+
|
|
312
|
+
if __name__ == '__main__':
|
|
313
|
+
unittest.main()
|