langfun 0.1.2.dev202504140804__tar.gz → 0.1.2.dev202511200805__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.dev202504140804 → langfun-0.1.2.dev202511200805}/PKG-INFO +19 -17
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/README.md +5 -5
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/__init__.py +4 -1
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/__init__.py +36 -0
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/bounding_box_parser.py +195 -0
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +313 -0
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/drawing.py +242 -0
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/drawing_test.py +103 -0
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/location.py +288 -0
- langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/location_test.py +230 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/__init__.py +11 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/agentic/__init__.py +11 -1
- langfun-0.1.2.dev202511200805/langfun/core/agentic/action.py +2377 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/agentic/action_eval.py +23 -7
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/agentic/action_eval_test.py +0 -1
- langfun-0.1.2.dev202511200805/langfun/core/agentic/action_test.py +663 -0
- langfun-0.1.2.dev202511200805/langfun/core/async_support.py +127 -0
- langfun-0.1.2.dev202511200805/langfun/core/async_support_test.py +62 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/__init__.py +0 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/correction.py +23 -9
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/execution.py +14 -12
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/generation.py +21 -16
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/sandboxing.py +30 -5
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/component.py +42 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/concurrent.py +70 -6
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/concurrent_test.py +17 -6
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/console.py +7 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/anthropic.py +12 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/anthropic_test.py +8 -6
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/gemini.py +27 -9
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/gemini_test.py +13 -22
- langfun-0.1.2.dev202511200805/langfun/core/data/conversion/openai.py +245 -0
- langfun-0.1.2.dev202511200805/langfun/core/data/conversion/openai_test.py +320 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/base.py +48 -44
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/base_test.py +11 -11
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/matching.py +5 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/matching_test.py +3 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/patching.py +3 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/scoring.py +4 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/scoring_test.py +4 -6
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/__init__.py +1 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/checkpointing.py +65 -11
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/checkpointing_test.py +33 -20
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/eval_test_helper.py +104 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/evaluation.py +219 -33
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/evaluation_test.py +33 -5
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/example.py +52 -41
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/example_test.py +18 -10
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/experiment.py +101 -8
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/experiment_test.py +26 -7
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metric_values.py +31 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metric_values_test.py +32 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metrics.py +157 -44
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metrics_test.py +43 -22
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress.py +30 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress_test.py +27 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress_tracking.py +12 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress_tracking_test.py +12 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/reporting.py +90 -71
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/reporting_test.py +29 -11
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/__init__.py +30 -0
- langfun-0.1.2.dev202504140804/langfun/core/eval/v2/runners.py → langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/base.py +83 -141
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/beam.py +341 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/beam_test.py +131 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/debug.py +40 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/debug_test.py +76 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/parallel.py +100 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/parallel_test.py +95 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/sequential_test.py +172 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/langfunc.py +45 -130
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/langfunc_test.py +8 -5
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/language_model.py +359 -48
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/language_model_test.py +97 -17
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/__init__.py +34 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/anthropic.py +288 -10
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/anthropic_test.py +49 -13
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/azure_openai.py +29 -17
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/base.py +25 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/in_memory.py +48 -7
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/in_memory_test.py +14 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/compositional.py +26 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/deepseek.py +30 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/fake.py +39 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/fake_test.py +9 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/gemini.py +243 -14
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/gemini_test.py +133 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/google_genai.py +77 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/groq.py +28 -3
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/llama_cpp.py +23 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/openai.py +184 -3
- langfun-0.1.2.dev202511200805/langfun/core/llms/openai_compatible.py +292 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/openai_compatible_test.py +224 -19
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/openai_test.py +0 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/rest.py +21 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/vertexai.py +131 -12
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/vertexai_test.py +22 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/logging.py +73 -17
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/logging_test.py +9 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/__init__.py +10 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/client.py +177 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/client_test.py +71 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/session.py +241 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/session_test.py +54 -0
- langfun-0.1.2.dev202504140804/langfun/core/modalities/pdf.py → langfun-0.1.2.dev202511200805/langfun/core/mcp/testing/simple_mcp_client.py +17 -6
- langfun-0.1.2.dev202511200805/langfun/core/mcp/testing/simple_mcp_server.py +33 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/tool.py +254 -0
- langfun-0.1.2.dev202511200805/langfun/core/mcp/tool_test.py +197 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memories/conversation_history.py +6 -6
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memories/conversation_history_test.py +31 -27
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memory.py +13 -21
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/message.py +167 -61
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/message_test.py +80 -87
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/__init__.py +8 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/audio.py +21 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/audio_test.py +4 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/image.py +19 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/mime.py +82 -15
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/mime_test.py +11 -0
- langfun-0.1.2.dev202511200805/langfun/core/modalities/pdf.py +40 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/video.py +21 -1
- langfun-0.1.2.dev202511200805/langfun/core/modality.py +271 -0
- langfun-0.1.2.dev202511200805/langfun/core/modality_test.py +117 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/natural_language.py +1 -1
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/sampling.py +4 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/sampling_test.py +20 -4
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/__init__.py +12 -24
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/completion.py +62 -44
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/completion_test.py +59 -43
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/description.py +54 -50
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/function_generation.py +29 -12
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/mapping.py +103 -40
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/parsing.py +170 -77
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/parsing_test.py +16 -4
- langfun-0.1.2.dev202511200805/langfun/core/structured/querying.py +1360 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/querying_test.py +666 -109
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/__init__.py +49 -0
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/base.py +664 -0
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/base_test.py +531 -0
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/json.py +174 -0
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/json_test.py +121 -0
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/python.py +316 -0
- langfun-0.1.2.dev202511200805/langfun/core/structured/schema/python_test.py +410 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/schema_generation.py +34 -14
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/scoring.py +74 -35
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/scoring_test.py +8 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/tokenization.py +49 -10
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/tokenization_test.py +8 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/subscription.py +2 -2
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/template.py +176 -51
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/template_test.py +126 -20
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/conversation.py +1 -1
- langfun-0.1.2.dev202511200805/langfun/env/__init__.py +43 -0
- langfun-0.1.2.dev202511200805/langfun/env/base_environment.py +827 -0
- langfun-0.1.2.dev202511200805/langfun/env/base_environment_test.py +473 -0
- langfun-0.1.2.dev202511200805/langfun/env/base_feature.py +304 -0
- langfun-0.1.2.dev202511200805/langfun/env/base_feature_test.py +228 -0
- langfun-0.1.2.dev202511200805/langfun/env/base_sandbox.py +842 -0
- langfun-0.1.2.dev202511200805/langfun/env/base_sandbox_test.py +1235 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/__init__.py +14 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/chain.py +233 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/chain_test.py +253 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/event_logger.py +472 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/event_logger_test.py +304 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/metric_writer.py +726 -0
- langfun-0.1.2.dev202511200805/langfun/env/event_handlers/metric_writer_test.py +214 -0
- langfun-0.1.2.dev202511200805/langfun/env/interface.py +1640 -0
- langfun-0.1.2.dev202511200805/langfun/env/interface_test.py +153 -0
- langfun-0.1.2.dev202511200805/langfun/env/load_balancers.py +59 -0
- langfun-0.1.2.dev202511200805/langfun/env/load_balancers_test.py +141 -0
- langfun-0.1.2.dev202511200805/langfun/env/test_utils.py +507 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/PKG-INFO +19 -17
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/SOURCES.txt +57 -5
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/requires.txt +10 -9
- langfun-0.1.2.dev202504140804/langfun/core/agentic/action.py +0 -1103
- langfun-0.1.2.dev202504140804/langfun/core/agentic/action_test.py +0 -158
- langfun-0.1.2.dev202504140804/langfun/core/data/conversion/openai.py +0 -131
- langfun-0.1.2.dev202504140804/langfun/core/data/conversion/openai_test.py +0 -176
- langfun-0.1.2.dev202504140804/langfun/core/eval/v2/runners_test.py +0 -334
- langfun-0.1.2.dev202504140804/langfun/core/llms/openai_compatible.py +0 -161
- langfun-0.1.2.dev202504140804/langfun/core/modality.py +0 -133
- langfun-0.1.2.dev202504140804/langfun/core/modality_test.py +0 -87
- langfun-0.1.2.dev202504140804/langfun/core/structured/querying.py +0 -779
- langfun-0.1.2.dev202504140804/langfun/core/structured/schema.py +0 -951
- langfun-0.1.2.dev202504140804/langfun/core/structured/schema_test.py +0 -927
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/LICENSE +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/correction_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/execution_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/generation_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/parsing.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/parsing_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/sandboxing_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/component_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/console_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/patching_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/azure_openai_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/compositional_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/deepseek_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/google_genai_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/groq_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/llama_cpp_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/rest_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memories/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/image_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/pdf_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/video_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/natural_language_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/description_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/function_generation_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/mapping_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/schema_generation_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/subscription_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/__init__.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/completion.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/completion_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/conversation_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/demonstration.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/demonstration_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/selfplay.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/selfplay_test.py +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/dependency_links.txt +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/top_level.txt +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/setup.cfg +0 -0
- {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/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.dev202511200805
|
|
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
|
|
@@ -193,21 +195,21 @@ To install a nightly build, include the `--pre` flag, like this:
|
|
|
193
195
|
pip install langfun[all] --pre
|
|
194
196
|
```
|
|
195
197
|
|
|
196
|
-
If you want to customize your installation, you can select specific features
|
|
198
|
+
If you want to customize your installation, you can select specific features
|
|
199
|
+
using package names like `langfun[X1, X2, ..., Xn]`, where `Xi` corresponds to
|
|
200
|
+
a tag from the list below:
|
|
197
201
|
|
|
198
202
|
| Tag | Description |
|
|
199
203
|
| ------------------- | ---------------------------------------- |
|
|
200
204
|
| all | All Langfun features. |
|
|
201
205
|
| vertexai | VertexAI access. |
|
|
202
206
|
| mime | All MIME supports. |
|
|
203
|
-
| mime-auto | Automatic MIME type detection. |
|
|
204
|
-
| mime-docx | DocX format support. |
|
|
205
207
|
| mime-pil | Image support for PIL. |
|
|
206
|
-
| mime-xlsx | XlsX format support. |
|
|
207
208
|
| ui | UI enhancements |
|
|
208
209
|
|
|
210
|
+
For example, to install a nightly build that includes VertexAI access, full
|
|
211
|
+
modality support, and UI enhancements, use:
|
|
209
212
|
|
|
210
|
-
For example, to install a nightly build that includes VertexAI access, full modality support, and UI enhancements, use:
|
|
211
213
|
```
|
|
212
214
|
pip install langfun[vertexai,mime,ui] --pre
|
|
213
215
|
```
|
|
@@ -133,21 +133,21 @@ To install a nightly build, include the `--pre` flag, like this:
|
|
|
133
133
|
pip install langfun[all] --pre
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
If you want to customize your installation, you can select specific features
|
|
136
|
+
If you want to customize your installation, you can select specific features
|
|
137
|
+
using package names like `langfun[X1, X2, ..., Xn]`, where `Xi` corresponds to
|
|
138
|
+
a tag from the list below:
|
|
137
139
|
|
|
138
140
|
| Tag | Description |
|
|
139
141
|
| ------------------- | ---------------------------------------- |
|
|
140
142
|
| all | All Langfun features. |
|
|
141
143
|
| vertexai | VertexAI access. |
|
|
142
144
|
| mime | All MIME supports. |
|
|
143
|
-
| mime-auto | Automatic MIME type detection. |
|
|
144
|
-
| mime-docx | DocX format support. |
|
|
145
145
|
| mime-pil | Image support for PIL. |
|
|
146
|
-
| mime-xlsx | XlsX format support. |
|
|
147
146
|
| ui | UI enhancements |
|
|
148
147
|
|
|
148
|
+
For example, to install a nightly build that includes VertexAI access, full
|
|
149
|
+
modality support, and UI enhancements, use:
|
|
149
150
|
|
|
150
|
-
For example, to install a nightly build that includes VertexAI access, full modality support, and UI enhancements, use:
|
|
151
151
|
```
|
|
152
152
|
pip install langfun[vertexai,mime,ui] --pre
|
|
153
153
|
```
|
|
@@ -37,6 +37,9 @@ generate_class = structured.generate_class
|
|
|
37
37
|
|
|
38
38
|
track_queries = structured.track_queries
|
|
39
39
|
|
|
40
|
+
# Context manager for setting the query protocol for the scope.
|
|
41
|
+
query_protocol = structured.query_protocol
|
|
42
|
+
|
|
40
43
|
# Helper function for map-reduce style querying.
|
|
41
44
|
query_and_reduce = structured.query_and_reduce
|
|
42
45
|
|
|
@@ -63,8 +66,8 @@ from langfun.core import agentic
|
|
|
63
66
|
Action = agentic.Action
|
|
64
67
|
Session = agentic.Session
|
|
65
68
|
|
|
69
|
+
from langfun.core import mcp
|
|
66
70
|
from langfun.core import memories
|
|
67
|
-
|
|
68
71
|
from langfun.core import modalities
|
|
69
72
|
|
|
70
73
|
Mime = modalities.Mime
|
|
@@ -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()
|