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.

Files changed (231) hide show
  1. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/PKG-INFO +19 -17
  2. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/README.md +5 -5
  3. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/__init__.py +4 -1
  4. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/__init__.py +36 -0
  5. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/bounding_box_parser.py +195 -0
  6. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +313 -0
  7. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/drawing.py +242 -0
  8. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/drawing_test.py +103 -0
  9. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/location.py +288 -0
  10. langfun-0.1.2.dev202511200805/langfun/assistant/capabilities/gui/location_test.py +230 -0
  11. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/__init__.py +11 -0
  12. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/agentic/__init__.py +11 -1
  13. langfun-0.1.2.dev202511200805/langfun/core/agentic/action.py +2377 -0
  14. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/agentic/action_eval.py +23 -7
  15. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/agentic/action_eval_test.py +0 -1
  16. langfun-0.1.2.dev202511200805/langfun/core/agentic/action_test.py +663 -0
  17. langfun-0.1.2.dev202511200805/langfun/core/async_support.py +127 -0
  18. langfun-0.1.2.dev202511200805/langfun/core/async_support_test.py +62 -0
  19. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/__init__.py +0 -1
  20. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/correction.py +23 -9
  21. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/execution.py +14 -12
  22. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/generation.py +21 -16
  23. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/sandboxing.py +30 -5
  24. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/component.py +42 -3
  25. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/concurrent.py +70 -6
  26. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/concurrent_test.py +17 -6
  27. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/console.py +7 -4
  28. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/anthropic.py +12 -3
  29. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/anthropic_test.py +8 -6
  30. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/gemini.py +27 -9
  31. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/gemini_test.py +13 -22
  32. langfun-0.1.2.dev202511200805/langfun/core/data/conversion/openai.py +245 -0
  33. langfun-0.1.2.dev202511200805/langfun/core/data/conversion/openai_test.py +320 -0
  34. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/base.py +48 -44
  35. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/base_test.py +11 -11
  36. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/matching.py +5 -2
  37. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/matching_test.py +3 -2
  38. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/patching.py +3 -3
  39. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/scoring.py +4 -3
  40. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/scoring_test.py +4 -6
  41. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/__init__.py +1 -0
  42. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/checkpointing.py +65 -11
  43. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/checkpointing_test.py +33 -20
  44. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/eval_test_helper.py +104 -3
  45. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/evaluation.py +219 -33
  46. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/evaluation_test.py +33 -5
  47. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/example.py +52 -41
  48. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/example_test.py +18 -10
  49. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/experiment.py +101 -8
  50. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/experiment_test.py +26 -7
  51. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metric_values.py +31 -3
  52. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metric_values_test.py +32 -0
  53. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metrics.py +157 -44
  54. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/metrics_test.py +43 -22
  55. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress.py +30 -1
  56. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress_test.py +27 -0
  57. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress_tracking.py +12 -3
  58. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/progress_tracking_test.py +12 -4
  59. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/reporting.py +90 -71
  60. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/v2/reporting_test.py +29 -11
  61. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/__init__.py +30 -0
  62. 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
  63. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/beam.py +341 -0
  64. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/beam_test.py +131 -0
  65. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
  66. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -0
  67. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/debug.py +40 -0
  68. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/debug_test.py +76 -0
  69. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/parallel.py +100 -0
  70. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/parallel_test.py +95 -0
  71. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/sequential.py +47 -0
  72. langfun-0.1.2.dev202511200805/langfun/core/eval/v2/runners/sequential_test.py +172 -0
  73. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/langfunc.py +45 -130
  74. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/langfunc_test.py +8 -5
  75. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/language_model.py +359 -48
  76. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/language_model_test.py +97 -17
  77. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/__init__.py +34 -2
  78. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/anthropic.py +288 -10
  79. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/anthropic_test.py +49 -13
  80. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/azure_openai.py +29 -17
  81. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/base.py +25 -3
  82. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/in_memory.py +48 -7
  83. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/in_memory_test.py +14 -4
  84. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/compositional.py +26 -2
  85. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/deepseek.py +30 -2
  86. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/fake.py +39 -1
  87. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/fake_test.py +9 -0
  88. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/gemini.py +243 -14
  89. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/gemini_test.py +133 -2
  90. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/google_genai.py +77 -1
  91. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/groq.py +28 -3
  92. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/llama_cpp.py +23 -4
  93. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/openai.py +184 -3
  94. langfun-0.1.2.dev202511200805/langfun/core/llms/openai_compatible.py +292 -0
  95. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/openai_compatible_test.py +224 -19
  96. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/openai_test.py +0 -2
  97. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/rest.py +21 -1
  98. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/vertexai.py +131 -12
  99. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/vertexai_test.py +22 -0
  100. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/logging.py +73 -17
  101. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/logging_test.py +9 -0
  102. langfun-0.1.2.dev202511200805/langfun/core/mcp/__init__.py +10 -0
  103. langfun-0.1.2.dev202511200805/langfun/core/mcp/client.py +177 -0
  104. langfun-0.1.2.dev202511200805/langfun/core/mcp/client_test.py +71 -0
  105. langfun-0.1.2.dev202511200805/langfun/core/mcp/session.py +241 -0
  106. langfun-0.1.2.dev202511200805/langfun/core/mcp/session_test.py +54 -0
  107. 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
  108. langfun-0.1.2.dev202511200805/langfun/core/mcp/testing/simple_mcp_server.py +33 -0
  109. langfun-0.1.2.dev202511200805/langfun/core/mcp/tool.py +254 -0
  110. langfun-0.1.2.dev202511200805/langfun/core/mcp/tool_test.py +197 -0
  111. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memories/conversation_history.py +6 -6
  112. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memories/conversation_history_test.py +31 -27
  113. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memory.py +13 -21
  114. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/message.py +167 -61
  115. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/message_test.py +80 -87
  116. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/__init__.py +8 -0
  117. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/audio.py +21 -1
  118. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/audio_test.py +4 -4
  119. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/image.py +19 -1
  120. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/mime.py +82 -15
  121. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/mime_test.py +11 -0
  122. langfun-0.1.2.dev202511200805/langfun/core/modalities/pdf.py +40 -0
  123. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/video.py +21 -1
  124. langfun-0.1.2.dev202511200805/langfun/core/modality.py +271 -0
  125. langfun-0.1.2.dev202511200805/langfun/core/modality_test.py +117 -0
  126. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/natural_language.py +1 -1
  127. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/sampling.py +4 -4
  128. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/sampling_test.py +20 -4
  129. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/__init__.py +12 -24
  130. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/completion.py +62 -44
  131. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/completion_test.py +59 -43
  132. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/description.py +54 -50
  133. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/function_generation.py +29 -12
  134. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/mapping.py +103 -40
  135. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/parsing.py +170 -77
  136. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/parsing_test.py +16 -4
  137. langfun-0.1.2.dev202511200805/langfun/core/structured/querying.py +1360 -0
  138. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/querying_test.py +666 -109
  139. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/__init__.py +49 -0
  140. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/base.py +664 -0
  141. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/base_test.py +531 -0
  142. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/json.py +174 -0
  143. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/json_test.py +121 -0
  144. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/python.py +316 -0
  145. langfun-0.1.2.dev202511200805/langfun/core/structured/schema/python_test.py +410 -0
  146. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/schema_generation.py +34 -14
  147. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/scoring.py +74 -35
  148. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/scoring_test.py +8 -0
  149. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/tokenization.py +49 -10
  150. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/tokenization_test.py +8 -0
  151. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/subscription.py +2 -2
  152. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/template.py +176 -51
  153. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/template_test.py +126 -20
  154. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/conversation.py +1 -1
  155. langfun-0.1.2.dev202511200805/langfun/env/__init__.py +43 -0
  156. langfun-0.1.2.dev202511200805/langfun/env/base_environment.py +827 -0
  157. langfun-0.1.2.dev202511200805/langfun/env/base_environment_test.py +473 -0
  158. langfun-0.1.2.dev202511200805/langfun/env/base_feature.py +304 -0
  159. langfun-0.1.2.dev202511200805/langfun/env/base_feature_test.py +228 -0
  160. langfun-0.1.2.dev202511200805/langfun/env/base_sandbox.py +842 -0
  161. langfun-0.1.2.dev202511200805/langfun/env/base_sandbox_test.py +1235 -0
  162. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/__init__.py +14 -0
  163. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/chain.py +233 -0
  164. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/chain_test.py +253 -0
  165. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/event_logger.py +472 -0
  166. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/event_logger_test.py +304 -0
  167. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/metric_writer.py +726 -0
  168. langfun-0.1.2.dev202511200805/langfun/env/event_handlers/metric_writer_test.py +214 -0
  169. langfun-0.1.2.dev202511200805/langfun/env/interface.py +1640 -0
  170. langfun-0.1.2.dev202511200805/langfun/env/interface_test.py +153 -0
  171. langfun-0.1.2.dev202511200805/langfun/env/load_balancers.py +59 -0
  172. langfun-0.1.2.dev202511200805/langfun/env/load_balancers_test.py +141 -0
  173. langfun-0.1.2.dev202511200805/langfun/env/test_utils.py +507 -0
  174. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/PKG-INFO +19 -17
  175. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/SOURCES.txt +57 -5
  176. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/requires.txt +10 -9
  177. langfun-0.1.2.dev202504140804/langfun/core/agentic/action.py +0 -1103
  178. langfun-0.1.2.dev202504140804/langfun/core/agentic/action_test.py +0 -158
  179. langfun-0.1.2.dev202504140804/langfun/core/data/conversion/openai.py +0 -131
  180. langfun-0.1.2.dev202504140804/langfun/core/data/conversion/openai_test.py +0 -176
  181. langfun-0.1.2.dev202504140804/langfun/core/eval/v2/runners_test.py +0 -334
  182. langfun-0.1.2.dev202504140804/langfun/core/llms/openai_compatible.py +0 -161
  183. langfun-0.1.2.dev202504140804/langfun/core/modality.py +0 -133
  184. langfun-0.1.2.dev202504140804/langfun/core/modality_test.py +0 -87
  185. langfun-0.1.2.dev202504140804/langfun/core/structured/querying.py +0 -779
  186. langfun-0.1.2.dev202504140804/langfun/core/structured/schema.py +0 -951
  187. langfun-0.1.2.dev202504140804/langfun/core/structured/schema_test.py +0 -927
  188. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/LICENSE +0 -0
  189. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/__init__.py +0 -0
  190. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/correction_test.py +0 -0
  191. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/execution_test.py +0 -0
  192. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/generation_test.py +0 -0
  193. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/parsing.py +0 -0
  194. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/parsing_test.py +0 -0
  195. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/coding/python/sandboxing_test.py +0 -0
  196. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/component_test.py +0 -0
  197. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/console_test.py +0 -0
  198. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/__init__.py +0 -0
  199. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/data/conversion/__init__.py +0 -0
  200. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/__init__.py +0 -0
  201. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/eval/patching_test.py +0 -0
  202. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/azure_openai_test.py +0 -0
  203. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/cache/__init__.py +0 -0
  204. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/compositional_test.py +0 -0
  205. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/deepseek_test.py +0 -0
  206. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/google_genai_test.py +0 -0
  207. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/groq_test.py +0 -0
  208. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/llama_cpp_test.py +0 -0
  209. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/llms/rest_test.py +0 -0
  210. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/memories/__init__.py +0 -0
  211. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/image_test.py +0 -0
  212. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/pdf_test.py +0 -0
  213. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/modalities/video_test.py +0 -0
  214. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/natural_language_test.py +0 -0
  215. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/description_test.py +0 -0
  216. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/function_generation_test.py +0 -0
  217. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/mapping_test.py +0 -0
  218. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/structured/schema_generation_test.py +0 -0
  219. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/subscription_test.py +0 -0
  220. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/__init__.py +0 -0
  221. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/completion.py +0 -0
  222. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/completion_test.py +0 -0
  223. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/conversation_test.py +0 -0
  224. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/demonstration.py +0 -0
  225. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/demonstration_test.py +0 -0
  226. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/selfplay.py +0 -0
  227. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun/core/templates/selfplay_test.py +0 -0
  228. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/dependency_links.txt +0 -0
  229. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/langfun.egg-info/top_level.txt +0 -0
  230. {langfun-0.1.2.dev202504140804 → langfun-0.1.2.dev202511200805}/setup.cfg +0 -0
  231. {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.dev202504140804
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: pyglove>=0.4.5.dev202409110000
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: pyglove>=0.4.5.dev202409110000; extra == "all"
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
- Provides-Extra: ui
37
- Requires-Dist: termcolor==1.1.0; extra == "ui"
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 using package names like `langfun[X1, X2, ..., Xn]`, where `Xi` corresponds to a tag from the list below:
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 using package names like `langfun[X1, X2, ..., Xn]`, where `Xi` corresponds to a tag from the list below:
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()