flowra 0.0.90__tar.gz → 0.0.92.dev108__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.
Files changed (357) hide show
  1. {flowra-0.0.90 → flowra-0.0.92.dev108}/.agents/skills/source-command-update-pricing/SKILL.md +3 -2
  2. {flowra-0.0.90 → flowra-0.0.92.dev108}/.claude/commands/update-pricing.md +3 -2
  3. {flowra-0.0.90 → flowra-0.0.92.dev108}/AGENTS.md +2 -2
  4. {flowra-0.0.90 → flowra-0.0.92.dev108}/CHANGELOG.md +50 -0
  5. {flowra-0.0.90 → flowra-0.0.92.dev108}/CLAUDE.md +2 -2
  6. {flowra-0.0.90 → flowra-0.0.92.dev108}/PKG-INFO +7 -2
  7. {flowra-0.0.90 → flowra-0.0.92.dev108}/README.md +2 -1
  8. {flowra-0.0.90 → flowra-0.0.92.dev108}/context7.json +6 -6
  9. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/agents.md +5 -2
  10. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/architecture.md +3 -2
  11. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/getting-started.md +2 -1
  12. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/llm.md +51 -6
  13. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/observability.md +33 -2
  14. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/__init__.py +2 -0
  15. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/agent.py +52 -5
  16. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/hook_events.py +24 -2
  17. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/errors.py +5 -2
  18. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/__init__.py +12 -2
  19. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/bundled.py +3 -1
  20. flowra-0.0.92.dev108/flowra/llm/pricing/data/generated/bedrock.json +163 -0
  21. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/anthropic.py +27 -6
  22. flowra-0.0.92.dev108/flowra/llm/providers/anthropic_bedrock.py +82 -0
  23. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/version.py +1 -1
  24. {flowra-0.0.90 → flowra-0.0.92.dev108}/pyproject.toml +3 -1
  25. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_turn_agent.py +381 -2
  26. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/pricing/test_bundled.py +6 -2
  27. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/_mock_http.py +41 -0
  28. flowra-0.0.92.dev108/tests/llm/providers/test_anthropic_bedrock.py +175 -0
  29. flowra-0.0.92.dev108/tests/llm/providers/test_anthropic_bedrock_e2e.py +151 -0
  30. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_cross_provider_e2e.py +55 -1
  31. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_errors.py +87 -0
  32. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/scripts/test_sync_pricing.py +64 -0
  33. {flowra-0.0.90 → flowra-0.0.92.dev108}/tools/sync_pricing.py +41 -0
  34. {flowra-0.0.90 → flowra-0.0.92.dev108}/uv.lock +62 -2
  35. {flowra-0.0.90 → flowra-0.0.92.dev108}/.env.example +0 -0
  36. {flowra-0.0.90 → flowra-0.0.92.dev108}/.github/CODEOWNERS +0 -0
  37. {flowra-0.0.90 → flowra-0.0.92.dev108}/.github/workflows/master.yml +0 -0
  38. {flowra-0.0.90 → flowra-0.0.92.dev108}/.github/workflows/publish.yml +0 -0
  39. {flowra-0.0.90 → flowra-0.0.92.dev108}/.github/workflows/pull_request.yml +0 -0
  40. {flowra-0.0.90 → flowra-0.0.92.dev108}/.gitignore +0 -0
  41. {flowra-0.0.90 → flowra-0.0.92.dev108}/.python-version +0 -0
  42. {flowra-0.0.90 → flowra-0.0.92.dev108}/LICENSE +0 -0
  43. {flowra-0.0.90 → flowra-0.0.92.dev108}/Makefile +0 -0
  44. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/patterns.md +0 -0
  45. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/agent_async_init.md +0 -0
  46. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/agent_tool_errors.md +0 -0
  47. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/anthropic_web_search.md +0 -0
  48. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/e2e_security.md +0 -0
  49. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/enum_slot_values.md +0 -0
  50. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/final_tools.md +0 -0
  51. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/langchain_comparison.md +0 -0
  52. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/lifecycle_hooks_and_conversation.md +0 -0
  53. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/llm_retry_backoff.md +0 -0
  54. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/named_scope_lazy_hydration.md +0 -0
  55. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/named_scope_type_registry.md +0 -0
  56. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/named_scopes.md +0 -0
  57. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/pricing_complexity.md +0 -0
  58. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/pydantic_ai_comparison.md +0 -0
  59. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/strands_comparison.md +0 -0
  60. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/tool_error_signals.md +0 -0
  61. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/tool_errors_vs_failures.md +0 -0
  62. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/tool_registry_redesign.md +0 -0
  63. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/tool_search_tool.md +0 -0
  64. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/research/voice_stt.md +0 -0
  65. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/review_prompts/step1_structure.md +0 -0
  66. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/review_prompts/step2_code_style.md +0 -0
  67. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/review_prompts/step3_documentation.md +0 -0
  68. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/review_prompts/step4_doc_readability.md +0 -0
  69. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/review_prompts/step5_doc_audit.md +0 -0
  70. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/review_prompts/step6_tests.md +0 -0
  71. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/todo.md +0 -0
  72. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/tools.md +0 -0
  73. {flowra-0.0.90 → flowra-0.0.92.dev108}/docs/video_script.md +0 -0
  74. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/TRACING_COMBINATIONS.md +0 -0
  75. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/__init__.py +0 -0
  76. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/agent_as_tool.py +0 -0
  77. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/app_agent.py +0 -0
  78. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/console_chat.py +0 -0
  79. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/__init__.py +0 -0
  80. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/agents_custom.py +0 -0
  81. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/agents_parallel.py +0 -0
  82. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/getting_started_chat.py +0 -0
  83. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/getting_started_streaming.py +0 -0
  84. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/getting_started_tools.py +0 -0
  85. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/llm_streaming.py +0 -0
  86. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/llm_structured_output.py +0 -0
  87. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/docs/tools_service_injection.py +0 -0
  88. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/escalation.py +0 -0
  89. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/llm_logging.py +0 -0
  90. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/llm_routing.py +0 -0
  91. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/menu_agent.py +0 -0
  92. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/menu_agent_class.py +0 -0
  93. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/mlflow_demo.py +0 -0
  94. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/mlflow_dual_export_demo.py +0 -0
  95. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/mlflow_nested_demo.py +0 -0
  96. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/mlflow_otel_both_demo.py +0 -0
  97. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/mlflow_otel_nested_demo.py +0 -0
  98. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/mlflow_parallel_demo.py +0 -0
  99. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/model_registry.py +0 -0
  100. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/otel_demo.py +0 -0
  101. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/otel_jaeger_demo.py +0 -0
  102. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/otel_nested_demo.py +0 -0
  103. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/otel_visualize.py +0 -0
  104. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/race.py +0 -0
  105. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/span_crash_demo.py +0 -0
  106. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/span_demo.py +0 -0
  107. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/system_prompt.txt +0 -0
  108. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/tools/__init__.py +0 -0
  109. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/tools/calculator.py +0 -0
  110. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/tools/random_numbers.py +0 -0
  111. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/tools/switch_model.py +0 -0
  112. {flowra-0.0.90 → flowra-0.0.92.dev108}/examples/tui_chat.py +0 -0
  113. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/__init__.py +0 -0
  114. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/_sentinel.py +0 -0
  115. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/_typing.py +0 -0
  116. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/__init__.py +0 -0
  117. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/__init__.py +0 -0
  118. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/_path.py +0 -0
  119. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/actions.py +0 -0
  120. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/agent.py +0 -0
  121. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/agent_arg.py +0 -0
  122. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/__init__.py +0 -0
  123. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/compiler.py +0 -0
  124. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/contract.py +0 -0
  125. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/init_params.py +0 -0
  126. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/instance.py +0 -0
  127. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/step_params.py +0 -0
  128. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/step_validation.py +0 -0
  129. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/steps.py +0 -0
  130. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/compile/type_registry.py +0 -0
  131. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/model.py +0 -0
  132. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/registry.py +0 -0
  133. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/shared_persistent.py +0 -0
  134. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/spawn.py +0 -0
  135. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/step.py +0 -0
  136. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/definition/step_helpers.py +0 -0
  137. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/__init__.py +0 -0
  138. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/context.py +0 -0
  139. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/flowing_registry.py +0 -0
  140. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/hooks.py +0 -0
  141. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/interrupt.py +0 -0
  142. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/interrupt_helpers.py +0 -0
  143. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/flow/timeout.py +0 -0
  144. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/__init__.py +0 -0
  145. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/engine.py +0 -0
  146. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/execution.py +0 -0
  147. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/instance_factory.py +0 -0
  148. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/key_locks.py +0 -0
  149. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/runtime.py +0 -0
  150. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/scope.py +0 -0
  151. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/serialization.py +0 -0
  152. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/spans.py +0 -0
  153. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/spawn_satisfaction.py +0 -0
  154. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/spawn_tree.py +0 -0
  155. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/runtime/visibility.py +0 -0
  156. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/services.py +0 -0
  157. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/session_state.py +0 -0
  158. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/state/__init__.py +0 -0
  159. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/state/markers.py +0 -0
  160. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/state/persistent.py +0 -0
  161. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/state/store.py +0 -0
  162. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/state/values.py +0 -0
  163. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/storage/__init__.py +0 -0
  164. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/storage/file.py +0 -0
  165. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/storage/in_memory.py +0 -0
  166. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/agent/storage/session_storage.py +0 -0
  167. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/ext/__init__.py +0 -0
  168. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/ext/_session.py +0 -0
  169. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/ext/mlflow.py +0 -0
  170. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/ext/otel.py +0 -0
  171. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/__init__.py +0 -0
  172. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/chat/__init__.py +0 -0
  173. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/chat/agent.py +0 -0
  174. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/chat/config.py +0 -0
  175. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/chat/conversation.py +0 -0
  176. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/chat/hook_events.py +0 -0
  177. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/chat/spec.py +0 -0
  178. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/config_value.py +0 -0
  179. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/llm_call/__init__.py +0 -0
  180. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/llm_call/agent.py +0 -0
  181. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/llm_call/config.py +0 -0
  182. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/llm_call/spec.py +0 -0
  183. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/llm_caller.py +0 -0
  184. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/llm_config.py +0 -0
  185. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/observability/__init__.py +0 -0
  186. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/observability/llm_hooks.py +0 -0
  187. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/config.py +0 -0
  188. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/context.py +0 -0
  189. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/spec.py +0 -0
  190. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/tool_call/__init__.py +0 -0
  191. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/tool_call/agent.py +0 -0
  192. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/tool_call/agent_tool.py +0 -0
  193. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/lib/turn/tool_call/context.py +0 -0
  194. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/__init__.py +0 -0
  195. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/_schema_nodes.py +0 -0
  196. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/base.py +0 -0
  197. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/blocks.py +0 -0
  198. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/__init__.py +0 -0
  199. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/anthropic/__init__.py +0 -0
  200. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/anthropic/cache.py +0 -0
  201. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/anthropic/presets.py +0 -0
  202. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/anthropic/tool_search.py +0 -0
  203. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/anthropic/web_search.py +0 -0
  204. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/openai/__init__.py +0 -0
  205. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/ext/openai/tool_search.py +0 -0
  206. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/gemini_schema.py +0 -0
  207. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/messages.py +0 -0
  208. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/openai_schema.py +0 -0
  209. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/custom/anthropic.json +0 -0
  210. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/custom/vertex.json +0 -0
  211. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/generated/anthropic.json +0 -0
  212. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/generated/azure_openai.json +0 -0
  213. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/generated/gemini_api.json +0 -0
  214. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/generated/inception.json +0 -0
  215. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/generated/openai.json +0 -0
  216. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/data/generated/vertex.json +0 -0
  217. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/model_pricing.py +0 -0
  218. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/pricing/price_list.py +0 -0
  219. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/provider.py +0 -0
  220. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/__init__.py +0 -0
  221. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/_openai_common.py +0 -0
  222. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/anthropic_foundry.py +0 -0
  223. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/anthropic_vertex.py +0 -0
  224. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/azure_openai.py +0 -0
  225. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/google_vertex.py +0 -0
  226. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/openai.py +0 -0
  227. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/providers/openai_responses.py +0 -0
  228. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/request.py +0 -0
  229. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/response.py +0 -0
  230. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/schema_formatting.py +0 -0
  231. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/schema_validation.py +0 -0
  232. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/stream.py +0 -0
  233. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/llm/tools.py +0 -0
  234. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/py.typed +0 -0
  235. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/__init__.py +0 -0
  236. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/_mcp_content.py +0 -0
  237. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/_mcp_headers.py +0 -0
  238. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/local_tool.py +0 -0
  239. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/mcp_connection.py +0 -0
  240. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/result_format.py +0 -0
  241. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/tool_arg.py +0 -0
  242. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/tool_filter.py +0 -0
  243. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/tool_registry.py +0 -0
  244. {flowra-0.0.90 → flowra-0.0.92.dev108}/flowra/tools/types.py +0 -0
  245. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/__init__.py +0 -0
  246. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/__init__.py +0 -0
  247. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/__init__.py +0 -0
  248. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/compile/__init__.py +0 -0
  249. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/compile/test_compile.py +0 -0
  250. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/test_actions.py +0 -0
  251. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/test_agent.py +0 -0
  252. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/test_registry.py +0 -0
  253. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/test_spawn.py +0 -0
  254. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/definition/test_step_helpers.py +0 -0
  255. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/__init__.py +0 -0
  256. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_context.py +0 -0
  257. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_flowing_registry.py +0 -0
  258. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_flowing_registry_tasks.py +0 -0
  259. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_flowing_sync.py +0 -0
  260. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_hooks.py +0 -0
  261. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_interrupt.py +0 -0
  262. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_interruptible.py +0 -0
  263. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_spans.py +0 -0
  264. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_timeout.py +0 -0
  265. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/flow/test_with_interrupt.py +0 -0
  266. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/__init__.py +0 -0
  267. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_engine.py +0 -0
  268. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_engine_spans.py +0 -0
  269. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_ephemeral_isolation.py +0 -0
  270. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_hook_context.py +0 -0
  271. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_named_scope_persistence.py +0 -0
  272. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_named_scope_registry.py +0 -0
  273. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_named_scopes.py +0 -0
  274. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_parallel_failure.py +0 -0
  275. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_persistence.py +0 -0
  276. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_primitive_join.py +0 -0
  277. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_runtime.py +0 -0
  278. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_scope.py +0 -0
  279. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_serialization.py +0 -0
  280. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/runtime/test_spec_in_constructor.py +0 -0
  281. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/state/__init__.py +0 -0
  282. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/state/test_values.py +0 -0
  283. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/storage/__init__.py +0 -0
  284. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/storage/test_file.py +0 -0
  285. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/storage/test_in_memory.py +0 -0
  286. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/test_missing_scenarios.py +0 -0
  287. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/agent/test_session_state.py +0 -0
  288. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/examples/__init__.py +0 -0
  289. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/examples/test_menu_agent.py +0 -0
  290. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/ext/__init__.py +0 -0
  291. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/ext/_mlflow.py +0 -0
  292. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/ext/test_mlflow.py +0 -0
  293. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/ext/test_mlflow_cross_task.py +0 -0
  294. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/ext/test_otel.py +0 -0
  295. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/__init__.py +0 -0
  296. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/_helpers.py +0 -0
  297. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/ext/__init__.py +0 -0
  298. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/ext/anthropic/__init__.py +0 -0
  299. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/ext/anthropic/test_anthropic.py +0 -0
  300. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/ext/openai/__init__.py +0 -0
  301. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/ext/openai/test_openai_tool_search.py +0 -0
  302. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_chat_agent.py +0 -0
  303. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_chat_spec.py +0 -0
  304. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_config_value.py +0 -0
  305. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_conversation.py +0 -0
  306. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_llm_call_agent.py +0 -0
  307. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_llm_caller.py +0 -0
  308. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_matches_tool_filter.py +0 -0
  309. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_tool_call_agent.py +0 -0
  310. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/test_tool_call_agent_call_agent.py +0 -0
  311. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/turn/__init__.py +0 -0
  312. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/turn/test_per_call_headers.py +0 -0
  313. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/turn/test_render.py +0 -0
  314. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/lib/turn/test_tool_errors.py +0 -0
  315. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/__init__.py +0 -0
  316. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/pricing/__init__.py +0 -0
  317. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/pricing/test_model_pricing.py +0 -0
  318. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/pricing/test_price_list.py +0 -0
  319. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/__init__.py +0 -0
  320. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/_raw_body.py +0 -0
  321. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/_tool_result_blocks.py +0 -0
  322. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_anthropic.py +0 -0
  323. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_anthropic_e2e.py +0 -0
  324. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_anthropic_foundry.py +0 -0
  325. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_anthropic_foundry_e2e.py +0 -0
  326. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_anthropic_vertex.py +0 -0
  327. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_anthropic_vertex_locations_e2e.py +0 -0
  328. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_azure_openai_e2e.py +0 -0
  329. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_fallback_e2e.py +0 -0
  330. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_google_vertex.py +0 -0
  331. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_google_vertex_e2e.py +0 -0
  332. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_history_across_models.py +0 -0
  333. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_openai_e2e.py +0 -0
  334. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_openai_provider.py +0 -0
  335. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_openai_responses.py +0 -0
  336. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_openai_responses_e2e.py +0 -0
  337. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_refusals.py +0 -0
  338. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/providers/test_schemas_e2e.py +0 -0
  339. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_blocks.py +0 -0
  340. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_cost_breakdown.py +0 -0
  341. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_gemini_schema.py +0 -0
  342. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_metadata.py +0 -0
  343. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_openai_schema.py +0 -0
  344. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_response.py +0 -0
  345. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_schema_formatting.py +0 -0
  346. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_schema_nodes.py +0 -0
  347. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_schema_validation.py +0 -0
  348. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/llm/test_stream.py +0 -0
  349. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/scripts/__init__.py +0 -0
  350. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/test_typing.py +0 -0
  351. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/__init__.py +0 -0
  352. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/test_local_tool.py +0 -0
  353. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/test_mcp_connection.py +0 -0
  354. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/test_mcp_headers.py +0 -0
  355. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/test_result_format.py +0 -0
  356. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/test_tool_registry.py +0 -0
  357. {flowra-0.0.90 → flowra-0.0.92.dev108}/tests/tools/test_types.py +0 -0
@@ -29,6 +29,7 @@ The price lists, their constants in `flowra.llm.pricing` and the sources `LISTS`
29
29
  | List | Constant | Supplier | Sources |
30
30
  |---|---|---|---|
31
31
  | `anthropic` | `ANTHROPIC` | Anthropic's API; Microsoft Foundry bills Claude at it | `anthropic` (fields filled from `vertex_ai-anthropic_models`) |
32
+ | `bedrock` | `BEDROCK` | Claude on Amazon Bedrock | `bedrock_converse`'s `anthropic.claude-…` entries: rates from the bare / `global.` ids, `regional_uplift` from the geography profiles (`au.`, `eu.`, …; `us-gov.` excluded: GovCloud has its own price). Fields filled from `anthropic` |
32
33
  | `vertex` | `VERTEX` | Claude and Gemini on Vertex AI | `vertex_ai-anthropic_models`, `vertex_ai`, `vertex_ai-language-models` (fields filled from `anthropic`, `gemini`) |
33
34
  | `gemini_api` | `GEMINI_API` | Gemini Developer API (AI Studio) | `gemini` |
34
35
  | `openai` | `OPENAI` | OpenAI | `openai` |
@@ -69,7 +70,7 @@ Available pricing fields:
69
70
  - `reasoning_output` — rate for the reasoning share of the output tokens (if different from output; providers count reasoning inside `output_tokens`)
70
71
  - `long_prompt_threshold` — for models priced by prompt length: the prompt size in tokens, where the prompt is input + cache read + cache write tokens. A longer prompt bills every token of the request, output included, at the `*_long_prompt` rates. Examples: 100000 for Claude Haiku 5.5, 200000 for Gemini 2.5 Pro, 272000 for GPT-5.4.
71
72
  - `input_long_prompt`, `output_long_prompt`, `cache_read_long_prompt`, `cache_creation_long_prompt`, `cache_creation_1h_long_prompt` — the rates past that threshold. An omitted one keeps the base rate. They come from litellm's `input_cost_per_token_above_{N}k_tokens` etc.; `sync_pricing.py` sets `long_prompt_threshold` to N×1000.
72
- - `regional_uplift` — this list's supplier's factor on the whole cost of a call it serves regionally, e.g. 1.1: in `vertex` a location other than `global` (from litellm's `regional_endpoint_uplift_multiplier`), in `openai` the `eu.` / `us.api.openai.com` data residency (`regional_processing_uplift_multiplier_eu|us`), in `anthropic` `inference_geo: "us"` and Foundry US Data Zone deployments (Claude 4.6 and later, kept in `custom/anthropic.json`: litellm has none). The uplift of one list never comes from another supplier's data.
73
+ - `regional_uplift` — this list's supplier's factor on the whole cost of a call it serves regionally, e.g. 1.1: in `vertex` a location other than `global` (from litellm's `regional_endpoint_uplift_multiplier`), in `openai` the `eu.` / `us.api.openai.com` data residency (`regional_processing_uplift_multiplier_eu|us`), in `bedrock` a geography's cross-region profile (the ratio of its input rate to the `global.` one), in `anthropic` `inference_geo: "us"` and Foundry US Data Zone deployments (Claude 4.6 and later, kept in `custom/anthropic.json`: litellm has none). The uplift of one list never comes from another supplier's data.
73
74
 
74
75
  ### Steps to follow
75
76
 
@@ -80,7 +81,7 @@ Available pricing fields:
80
81
  - If no arguments were provided, refresh pricing for models in the `custom/` files.
81
82
 
82
83
  3. **Web search for current pricing**: Use WebSearch to find the most up-to-date pricing:
83
- - Search official pricing pages: Anthropic (platform.claude.com/docs/en/about-claude/pricing), OpenAI (openai.com/api/pricing), Google Cloud (cloud.google.com/vertex-ai/generative-ai/pricing), the Gemini Developer API (ai.google.dev/gemini-api/docs/pricing), Azure OpenAI (azure.microsoft.com/pricing/details/cognitive-services/openai-service/). Lists synced from a models API (Inception) are not searched: rerun the sync instead
84
+ - Search official pricing pages: Anthropic (platform.claude.com/docs/en/about-claude/pricing), OpenAI (openai.com/api/pricing), Google Cloud (cloud.google.com/vertex-ai/generative-ai/pricing), the Gemini Developer API (ai.google.dev/gemini-api/docs/pricing), Azure OpenAI (azure.microsoft.com/pricing/details/cognitive-services/openai-service/), Amazon Bedrock (aws.amazon.com/bedrock/pricing/). Lists synced from a models API (Inception) are not searched: rerun the sync instead
84
85
  - Focus on models/fields the `generated/` files are missing (check litellm gaps)
85
86
 
86
87
  4. **Update the `custom/<list>.json` files** of the supplier whose price it is:
@@ -18,6 +18,7 @@ The price lists, their constants in `flowra.llm.pricing` and the sources `LISTS`
18
18
  | List | Constant | Supplier | Sources |
19
19
  |---|---|---|---|
20
20
  | `anthropic` | `ANTHROPIC` | Anthropic's API; Microsoft Foundry bills Claude at it | `anthropic` (fields filled from `vertex_ai-anthropic_models`) |
21
+ | `bedrock` | `BEDROCK` | Claude on Amazon Bedrock | `bedrock_converse`'s `anthropic.claude-…` entries: rates from the bare / `global.` ids, `regional_uplift` from the geography profiles (`au.`, `eu.`, …; `us-gov.` excluded: GovCloud has its own price). Fields filled from `anthropic` |
21
22
  | `vertex` | `VERTEX` | Claude and Gemini on Vertex AI | `vertex_ai-anthropic_models`, `vertex_ai`, `vertex_ai-language-models` (fields filled from `anthropic`, `gemini`) |
22
23
  | `gemini_api` | `GEMINI_API` | Gemini Developer API (AI Studio) | `gemini` |
23
24
  | `openai` | `OPENAI` | OpenAI | `openai` |
@@ -58,7 +59,7 @@ Available pricing fields:
58
59
  - `reasoning_output` — rate for the reasoning share of the output tokens (if different from output; providers count reasoning inside `output_tokens`)
59
60
  - `long_prompt_threshold` — for models priced by prompt length: the prompt size in tokens, where the prompt is input + cache read + cache write tokens. A longer prompt bills every token of the request, output included, at the `*_long_prompt` rates. Examples: 100000 for Claude Haiku 5.5, 200000 for Gemini 2.5 Pro, 272000 for GPT-5.4.
60
61
  - `input_long_prompt`, `output_long_prompt`, `cache_read_long_prompt`, `cache_creation_long_prompt`, `cache_creation_1h_long_prompt` — the rates past that threshold. An omitted one keeps the base rate. They come from litellm's `input_cost_per_token_above_{N}k_tokens` etc.; `sync_pricing.py` sets `long_prompt_threshold` to N×1000.
61
- - `regional_uplift` — this list's supplier's factor on the whole cost of a call it serves regionally, e.g. 1.1: in `vertex` a location other than `global` (from litellm's `regional_endpoint_uplift_multiplier`), in `openai` the `eu.` / `us.api.openai.com` data residency (`regional_processing_uplift_multiplier_eu|us`), in `anthropic` `inference_geo: "us"` and Foundry US Data Zone deployments (Claude 4.6 and later, kept in `custom/anthropic.json`: litellm has none). The uplift of one list never comes from another supplier's data.
62
+ - `regional_uplift` — this list's supplier's factor on the whole cost of a call it serves regionally, e.g. 1.1: in `vertex` a location other than `global` (from litellm's `regional_endpoint_uplift_multiplier`), in `openai` the `eu.` / `us.api.openai.com` data residency (`regional_processing_uplift_multiplier_eu|us`), in `bedrock` a geography's cross-region profile (the ratio of its input rate to the `global.` one), in `anthropic` `inference_geo: "us"` and Foundry US Data Zone deployments (Claude 4.6 and later, kept in `custom/anthropic.json`: litellm has none). The uplift of one list never comes from another supplier's data.
62
63
 
63
64
  ### Steps to follow
64
65
 
@@ -69,7 +70,7 @@ Available pricing fields:
69
70
  - If no arguments were provided, refresh pricing for models in the `custom/` files.
70
71
 
71
72
  3. **Web search for current pricing**: Use WebSearch to find the most up-to-date pricing:
72
- - Search official pricing pages: Anthropic (platform.claude.com/docs/en/about-claude/pricing), OpenAI (openai.com/api/pricing), Google Cloud (cloud.google.com/vertex-ai/generative-ai/pricing), the Gemini Developer API (ai.google.dev/gemini-api/docs/pricing), Azure OpenAI (azure.microsoft.com/pricing/details/cognitive-services/openai-service/). Lists synced from a models API (Inception) are not searched: rerun the sync instead
73
+ - Search official pricing pages: Anthropic (platform.claude.com/docs/en/about-claude/pricing), OpenAI (openai.com/api/pricing), Google Cloud (cloud.google.com/vertex-ai/generative-ai/pricing), the Gemini Developer API (ai.google.dev/gemini-api/docs/pricing), Azure OpenAI (azure.microsoft.com/pricing/details/cognitive-services/openai-service/), Amazon Bedrock (aws.amazon.com/bedrock/pricing/). Lists synced from a models API (Inception) are not searched: rerun the sync instead
73
74
  - Focus on models/fields the `generated/` files are missing (check litellm gaps)
74
75
 
75
76
  4. **Update the `custom/<list>.json` files** of the supplier whose price it is:
@@ -55,9 +55,9 @@ Provider-agnostic interface for calling LLMs:
55
55
  - History across models: providers adapt a stored history to the model they call. Anthropic sends Claude's signed or redacted thinking to any Claude model and drops other thinking; Google sends Gemini's thoughts (`extra["google"]` with `thought` or a `thought_signature`) to any Gemini model, drops other thinking, and signs a message's first function call that has no Google signature with `skip_thought_signature_validator`; an assistant message left empty is not sent, and empty text blocks (Gemini's signature carriers) are not sent to Claude or OpenAI
56
56
  - Span types (in `flowra.lib.observability`): `LLMCallSpan` (request + response), `ToolCallSpan` (tool_use + result + error_kind) — mutable, for observability via span-hooks
57
57
 
58
- Providers live in `flowra/llm/providers/`. Currently: `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`.
58
+ Providers live in `flowra/llm/providers/`. Currently: `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `AnthropicBedrockProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`.
59
59
 
60
- Pricing lives in `flowra/llm/pricing/` — suppliers' price lists. A `PriceList` maps model names to `ModelPricing` rates; `find(served, requested)` returns the rates of the first name with a price (a name matches the longest key it contains), and `ModelPricing.cost(...)` → `CostBreakdown` prices a call. Bundled lists (`ANTHROPIC` — Anthropic API and Foundry, `VERTEX`, `GEMINI_API`, `OPENAI`, `AZURE_OPENAI`, `INCEPTION`) load from `data/generated/<name>.json` (from litellm via `tools/sync_pricing.py`, whose `LISTS` maps each list to its litellm providers — or, for Inception, to the supplier's own `/models` API, which then decides the chat models and every rate it states) and `data/custom/<name>.json` (manual overrides, fields merged on load). Every provider takes `price_list=` (`None` = no cost) with a default of whoever bills its endpoint; `AnthropicProvider` requires one. Supports long-prompt pricing at a per-model threshold (`long_prompt_threshold`; past it the whole request is billed at the `*_long_prompt` rates), reasoning tokens (counted inside `output_tokens`; `reasoning_output` prices their share), cache creation TTL variants, and a per-model `regional_uplift` — the list's supplier's premium — that providers apply with `regional=True`: a Vertex location other than `global`, a Foundry US Data Zone (`data_zone=True`), OpenAI `eu`/`us` data residency.
60
+ Pricing lives in `flowra/llm/pricing/` — suppliers' price lists. A `PriceList` maps model names to `ModelPricing` rates; `find(served, requested)` returns the rates of the first name with a price (a name matches the longest key it contains), and `ModelPricing.cost(...)` → `CostBreakdown` prices a call. Bundled lists (`ANTHROPIC` — Anthropic API and Foundry, `BEDROCK` — Claude on Bedrock, rates from the bare / `global.` ids and the uplift from the geography profiles, `VERTEX`, `GEMINI_API`, `OPENAI`, `AZURE_OPENAI`, `INCEPTION`) load from `data/generated/<name>.json` (from litellm via `tools/sync_pricing.py`, whose `LISTS` maps each list to its litellm providers — or, for Inception, to the supplier's own `/models` API, which then decides the chat models and every rate it states) and `data/custom/<name>.json` (manual overrides, fields merged on load). Every provider takes `price_list=` (`None` = no cost) with a default of whoever bills its endpoint; `AnthropicProvider` requires one. Supports long-prompt pricing at a per-model threshold (`long_prompt_threshold`; past it the whole request is billed at the `*_long_prompt` rates), reasoning tokens (counted inside `output_tokens`; `reasoning_output` prices their share), cache creation TTL variants, and a per-model `regional_uplift` — the list's supplier's premium — that providers apply with `regional=True`: a Vertex location other than `global`, a Foundry US Data Zone (`data_zone=True`), a Bedrock geography profile (`au.`, `eu.`, ... model ids), OpenAI `eu`/`us` data residency.
61
61
 
62
62
  ### Flowing context (`flowra/agent/flow/flowing_registry.py`)
63
63
 
@@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **`BeforeToolCallsEvent`: answer a model message's tool calls before any
12
+ runs.** Emitted once per model message with tool calls, a single call
13
+ included, with every call of the message in `tool_uses`, a call the response cut off
14
+ included (an answer under its id is not used). A handler puts a
15
+ `ToolResult` into `results` under a call's id to answer that call itself:
16
+ it does not run, the other calls run as usual, and its result reaches the
17
+ model in the message's order. An answered call gets no
18
+ `BeforeToolCallEvent` and no `ToolCallSpan`; it goes through
19
+ `AfterToolCallEvent` and counts toward `max_consecutive_errors` like any
20
+ result. An error answered without an `error_kind` gets
21
+ `ToolErrorKind.EXECUTION`; a key that is not the id of a call in the message
22
+ raises `ValueError`. Answered calls are kept in the turn's state, so they
23
+ survive a resume before the results are collected.
24
+ - **`AfterToolCallEvent.prefilled`**: true for a call answered in
25
+ `BeforeToolCallsEvent`, so a handler can tell that the tool did not run.
26
+
27
+ ## [0.0.91] - 2026-10-20
28
+
29
+ ### Added
30
+ - **`AnthropicBedrockProvider`: Claude on Amazon Bedrock** (#135, closes #134). An
31
+ `AnthropicProvider` over the SDK's `AsyncAnthropicBedrock`, so blocks, tools,
32
+ streaming, errors and usage work as on Vertex and Foundry. Pass a Bedrock API
33
+ key and `aws_region`, or a configured client (AWS access keys, a profile).
34
+ `LLMRequest.model` is Bedrock's model id or inference profile, e.g.
35
+ `au.anthropic.claude-haiku-5-5`; the response reports the served model
36
+ (`claude-haiku-5-5`). Install with the new `bedrock` extra
37
+ (`pip install flowra[bedrock]`, also in `providers`): botocore decodes
38
+ Bedrock's event streams, and importing the provider without it says so. Claude's signed thinking moves
39
+ between Bedrock and Foundry either way.
40
+ - **`pricing.BEDROCK`**, the default for `AnthropicBedrockProvider`: Claude's
41
+ rates on Bedrock, synced from litellm's `bedrock_converse` entries (AWS's
42
+ price index). A geography's cross-region profile (`au.`, `eu.`, `us.`,
43
+ `apac.`, `jp.`) bills 10% above `global.` for Claude 4.5 and later, Haiku 4.5
44
+ and Sonnet 4.5 included, where Anthropic's own premium starts at 4.6; the
45
+ provider reads the profile from the model id, or from the end of a system
46
+ inference profile's ARN. GovCloud's profiles (`us-gov.`) have prices of their
47
+ own, which the list does not hold: their calls report no cost.
48
+ - **Bedrock's errors are typed.** Its HTTP errors are a bare
49
+ `{"message": ...}`, now read, so a prompt too long raises
50
+ `ContextTooLongError`. The exceptions of its event stream go by their type:
51
+ `throttlingException` is a `RateLimitedError`, `validationException` a
52
+ `BadRequestError` (or `ContextTooLongError`), `modelTimeoutException` an
53
+ `LLMTimeoutError`, the others an `UnavailableError`. `request_id` is read
54
+ from `x-amzn-RequestId`. An unknown model id, which Bedrock answers with a
55
+ 400 "The provided model identifier is invalid", is a `NotFoundError`, so a
56
+ fallback moves on to its next config as it does on Vertex and Foundry.
57
+
8
58
  ## [0.0.90] - 2026-10-19
9
59
 
10
60
  ### Fixed
@@ -56,9 +56,9 @@ Provider-agnostic interface for calling LLMs:
56
56
  - History across models: providers adapt a stored history to the model they call. Anthropic sends Claude's signed or redacted thinking to any Claude model and drops other thinking; Google sends Gemini's thoughts (`extra["google"]` with `thought` or a `thought_signature`) to any Gemini model, drops other thinking, and signs a message's first function call that has no Google signature with `skip_thought_signature_validator`; an assistant message left empty is not sent, and empty text blocks (Gemini's signature carriers) are not sent to Claude or OpenAI
57
57
  - Span types (in `flowra.lib.observability`): `LLMCallSpan` (request + response), `ToolCallSpan` (tool_use + result + error_kind) — mutable, for observability via span-hooks
58
58
 
59
- Providers live in `flowra/llm/providers/`. Currently: `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`.
59
+ Providers live in `flowra/llm/providers/`. Currently: `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `AnthropicBedrockProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`.
60
60
 
61
- Pricing lives in `flowra/llm/pricing/` — suppliers' price lists. A `PriceList` maps model names to `ModelPricing` rates; `find(served, requested)` returns the rates of the first name with a price (a name matches the longest key it contains), and `ModelPricing.cost(...)` → `CostBreakdown` prices a call. Bundled lists (`ANTHROPIC` — Anthropic API and Foundry, `VERTEX`, `GEMINI_API`, `OPENAI`, `AZURE_OPENAI`, `INCEPTION`) load from `data/generated/<name>.json` (from litellm via `tools/sync_pricing.py`, whose `LISTS` maps each list to its litellm providers — or, for Inception, to the supplier's own `/models` API, which then decides the chat models and every rate it states) and `data/custom/<name>.json` (manual overrides, fields merged on load). Every provider takes `price_list=` (`None` = no cost) with a default of whoever bills its endpoint; `AnthropicProvider` requires one. Supports long-prompt pricing at a per-model threshold (`long_prompt_threshold`; past it the whole request is billed at the `*_long_prompt` rates), reasoning tokens (counted inside `output_tokens`; `reasoning_output` prices their share), cache creation TTL variants, and a per-model `regional_uplift` — the list's supplier's premium — that providers apply with `regional=True`: a Vertex location other than `global`, a Foundry US Data Zone (`data_zone=True`), OpenAI `eu`/`us` data residency.
61
+ Pricing lives in `flowra/llm/pricing/` — suppliers' price lists. A `PriceList` maps model names to `ModelPricing` rates; `find(served, requested)` returns the rates of the first name with a price (a name matches the longest key it contains), and `ModelPricing.cost(...)` → `CostBreakdown` prices a call. Bundled lists (`ANTHROPIC` — Anthropic API and Foundry, `BEDROCK` — Claude on Bedrock, rates from the bare / `global.` ids and the uplift from the geography profiles, `VERTEX`, `GEMINI_API`, `OPENAI`, `AZURE_OPENAI`, `INCEPTION`) load from `data/generated/<name>.json` (from litellm via `tools/sync_pricing.py`, whose `LISTS` maps each list to its litellm providers — or, for Inception, to the supplier's own `/models` API, which then decides the chat models and every rate it states) and `data/custom/<name>.json` (manual overrides, fields merged on load). Every provider takes `price_list=` (`None` = no cost) with a default of whoever bills its endpoint; `AnthropicProvider` requires one. Supports long-prompt pricing at a per-model threshold (`long_prompt_threshold`; past it the whole request is billed at the `*_long_prompt` rates), reasoning tokens (counted inside `output_tokens`; `reasoning_output` prices their share), cache creation TTL variants, and a per-model `regional_uplift` — the list's supplier's premium — that providers apply with `regional=True`: a Vertex location other than `global`, a Foundry US Data Zone (`data_zone=True`), a Bedrock geography profile (`au.`, `eu.`, ... model ids), OpenAI `eu`/`us` data residency.
62
62
 
63
63
  ### Flowing context (`flowra/agent/flow/flowing_registry.py`)
64
64
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowra
3
- Version: 0.0.90
3
+ Version: 0.0.92.dev108
4
4
  Summary: Flowra — flow infrastructure for building stateful LLM agents
5
5
  Project-URL: Repository, https://github.com/anna-money/flowra
6
6
  Project-URL: Changelog, https://github.com/anna-money/flowra/blob/master/CHANGELOG.md
@@ -22,6 +22,9 @@ Requires-Dist: marshmallow-recipe>=0.0.95
22
22
  Provides-Extra: anthropic
23
23
  Requires-Dist: anthropic[vertex]; extra == 'anthropic'
24
24
  Requires-Dist: httpx2>=2.13; extra == 'anthropic'
25
+ Provides-Extra: bedrock
26
+ Requires-Dist: anthropic[bedrock]; extra == 'bedrock'
27
+ Requires-Dist: httpx2>=2.13; extra == 'bedrock'
25
28
  Provides-Extra: google
26
29
  Requires-Dist: google-genai; extra == 'google'
27
30
  Provides-Extra: mlflow
@@ -33,6 +36,7 @@ Provides-Extra: otel
33
36
  Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
34
37
  Requires-Dist: opentelemetry-sdk>=1.20; extra == 'otel'
35
38
  Provides-Extra: providers
39
+ Requires-Dist: anthropic[bedrock]; extra == 'providers'
36
40
  Requires-Dist: anthropic[vertex]; extra == 'providers'
37
41
  Requires-Dist: google-genai; extra == 'providers'
38
42
  Requires-Dist: httpx2>=2.13; extra == 'providers'
@@ -58,7 +62,7 @@ parallel execution, and crash recovery. Requires Python 3.12+.
58
62
  - **Tool integration** — `@tool` decorator for local functions, MCP server support,
59
63
  DI into tool handlers, agents as tools for LLM-driven delegation
60
64
  - **LLM abstraction** — provider-agnostic `LLMProvider` interface with immutable
61
- message types and real-time streaming (ships `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`)
65
+ message types and real-time streaming (ships `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `AnthropicBedrockProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`)
62
66
  - **Agents as tools** — `@agent_tool` decorator exposes an agent as a tool the
63
67
  LLM can call autonomously; sub-agent runs its own system prompt and tool loop
64
68
  - **Cooperative interrupts** — `InterruptToken` for graceful cancellation across
@@ -74,6 +78,7 @@ pip install flowra
74
78
 
75
79
  # With specific providers
76
80
  pip install flowra[anthropic]
81
+ pip install flowra[bedrock]
77
82
  pip install flowra[openai]
78
83
  pip install flowra[google]
79
84
 
@@ -17,7 +17,7 @@ parallel execution, and crash recovery. Requires Python 3.12+.
17
17
  - **Tool integration** — `@tool` decorator for local functions, MCP server support,
18
18
  DI into tool handlers, agents as tools for LLM-driven delegation
19
19
  - **LLM abstraction** — provider-agnostic `LLMProvider` interface with immutable
20
- message types and real-time streaming (ships `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`)
20
+ message types and real-time streaming (ships `AnthropicVertexProvider`, `AnthropicFoundryProvider`, `AnthropicBedrockProvider`, `OpenAIProvider`, `AzureOpenAIProvider`, `OpenAIResponsesProvider`, `GoogleVertexProvider`)
21
21
  - **Agents as tools** — `@agent_tool` decorator exposes an agent as a tool the
22
22
  LLM can call autonomously; sub-agent runs its own system prompt and tool loop
23
23
  - **Cooperative interrupts** — `InterruptToken` for graceful cancellation across
@@ -33,6 +33,7 @@ pip install flowra
33
33
 
34
34
  # With specific providers
35
35
  pip install flowra[anthropic]
36
+ pip install flowra[bedrock]
36
37
  pip install flowra[openai]
37
38
  pip install flowra[google]
38
39
 
@@ -26,17 +26,17 @@
26
26
  "LLMData base: metadata (dict, not sent to LLM), extra (dict, provider-namespaced — extra['anthropic'], extra['openai'] (Chat Completions), extra['openai_responses'], extra['google']), transient (bool, non-permanent content hint). ProviderExtra subclasses are frozen dataclasses with mr.MISSING defaults. Three operations: build_extra(MyExtra(field=value)) to construct, MyExtra(field=value).write_to(obj) to mutate, MyExtra.read_from(obj) to read. None clears a field. Extras: AnthropicCacheable (cache_control), AnthropicToolExtra (defer_loading, type), AnthropicBlockExtra (signature, redacted_data, cache_control), AnthropicRequestExtra (thinking_budget_tokens, thinking, output_config), OpenAIRequestExtra (reasoning_effort), OpenAIResponsesToolExtra (defer_loading, type, namespace), OpenAIResponsesRequestExtra (namespaces, reasoning_effort), GoogleBlockExtra (thought_signature, thought), GoogleRequestExtra (thinking_level, thinking_budget)",
27
27
  "ThinkingBlock holds reasoning/thinking text from models with extended thinking (Anthropic Claude, Google Gemini); text may be empty (Claude's redacted_thinking, kept with extra['anthropic']['redacted_data']; a Gemini thought that carries only a signature). Each provider sends only its own family's thinking: Anthropic signed or redacted Claude thinking to any Claude model, Google Gemini thoughts (extra['google'] thought=True or a thought_signature) to any Gemini model; thinking from elsewhere is dropped. OpenAI sends none",
28
28
  "Tool definition for LLM: Tool inherits LLMData, fields: name, description, input_schema, output_schema (plus metadata, extra, transient from LLMData)",
29
- "Six built-in providers: AnthropicVertexProvider (Claude via Vertex AI), AnthropicFoundryProvider (Claude via Azure AI Foundry), OpenAIProvider (OpenAI Chat Completions), AzureOpenAIProvider (OpenAI models via Azure), OpenAIResponsesProvider (OpenAI Responses API — supports tool search with defer_loading), GoogleVertexProvider (Gemini via Vertex AI). All six implement stream() with real-time deltas",
29
+ "Seven built-in providers: AnthropicVertexProvider (Claude via Vertex AI), AnthropicFoundryProvider (Claude via Azure AI Foundry), AnthropicBedrockProvider (Claude via Amazon Bedrock, extra flowra[bedrock]; model = a Bedrock id or inference profile such as au.anthropic.claude-haiku-5-5; api_key + aws_region or a configured AsyncAnthropicBedrock client), OpenAIProvider (OpenAI Chat Completions), AzureOpenAIProvider (OpenAI models via Azure), OpenAIResponsesProvider (OpenAI Responses API — supports tool search with defer_loading), GoogleVertexProvider (Gemini via Vertex AI). All seven implement stream() with real-time deltas",
30
30
  "Long-prompt pricing: some models bill the whole request, output included, at higher rates once the prompt (input + cache read + cache write tokens) exceeds a per-model threshold (ModelPricing.long_prompt_threshold + *_long_prompt rates): Claude Haiku 5.5 100k, Claude Sonnet 4/4.5 and Gemini 2.5/3 Pro 200k, GPT-5.4+ 272k. usage.cost_usd is None for models missing from the provider's price list, or when price_list=None",
31
- "Pricing: flowra.llm.pricing has suppliers' price lists — PriceList (model -> ModelPricing; find(served_model, requested_model) returns the first name's rates, a name matching the longest key it contains) and ModelPricing.cost(input_tokens=..., output_tokens=..., cache_read_tokens=..., cache_creation_tokens=..., cache_creation_1h_tokens=..., reasoning_tokens=..., regional=...) -> CostBreakdown; PriceList.cost(served_model, requested_model, input_tokens=..., ...) -> CostBreakdown | None does both. Bundled lists: ANTHROPIC (Anthropic API; Foundry bills Claude at it), VERTEX (Claude and Gemini on Vertex), GEMINI_API (AI Studio), OPENAI, AZURE_OPENAI, INCEPTION. Every provider takes price_list= (None = no cost); defaults: Vertex providers VERTEX (GoogleVertexProvider GEMINI_API for an API-key client), Foundry ANTHROPIC, OpenAIProvider/OpenAIResponsesProvider OPENAI, AzureOpenAIProvider AZURE_OPENAI. OpenAIProvider for Inception: price_list=pricing.INCEPTION. Build your own: PriceList('name', {**pricing.VERTEX.prices, 'model': ModelPricing(input=..., output=...)}) or PriceList.from_json(name, *paths). Regional uplift is per list and model (Vertex non-global: Claude 4.5+, newer Gemini; Anthropic/Foundry Data Zone: Claude 4.6+; OpenAI eu/us)",
31
+ "Pricing: flowra.llm.pricing has suppliers' price lists — PriceList (model -> ModelPricing; find(served_model, requested_model) returns the first name's rates, a name matching the longest key it contains) and ModelPricing.cost(input_tokens=..., output_tokens=..., cache_read_tokens=..., cache_creation_tokens=..., cache_creation_1h_tokens=..., reasoning_tokens=..., regional=...) -> CostBreakdown; PriceList.cost(served_model, requested_model, input_tokens=..., ...) -> CostBreakdown | None does both. Bundled lists: ANTHROPIC (Anthropic API; Foundry bills Claude at it), BEDROCK (Claude on Amazon Bedrock), VERTEX (Claude and Gemini on Vertex), GEMINI_API (AI Studio), OPENAI, AZURE_OPENAI, INCEPTION. Every provider takes price_list= (None = no cost); defaults: Vertex providers VERTEX (GoogleVertexProvider GEMINI_API for an API-key client), Foundry ANTHROPIC, Bedrock BEDROCK, OpenAIProvider/OpenAIResponsesProvider OPENAI, AzureOpenAIProvider AZURE_OPENAI. OpenAIProvider for Inception: price_list=pricing.INCEPTION. Build your own: PriceList('name', {**pricing.VERTEX.prices, 'model': ModelPricing(input=..., output=...)}) or PriceList.from_json(name, *paths). Regional uplift is per list and model (Vertex non-global: Claude 4.5+, newer Gemini; Anthropic/Foundry Data Zone: Claude 4.6+; Bedrock geography profiles au./eu./us./apac.: Claude 4.5+; OpenAI eu/us)",
32
32
  "AnthropicVertexProvider(project=..., location=..., credentials=..., model_locations={'claude-sonnet-5-5': 'eu'}) routes models by id (without @revision) to another Vertex location than the default, one SDK client per location; newer Claude models (Sonnet 5.5, Sonnet 5, Opus 4.7+) are served only on global/us/eu and a single region like europe-west1 answers 429. model_locations needs the credentials form, not client=. usage.cost_usd from AnthropicVertexProvider and GoogleVertexProvider includes Vertex's 10% premium on every location but global for the models it applies to (Claude 4.5+, Gemini 3.1 Flash-Lite and 3.5+ Flash / Flash-Lite); OpenAIProvider and OpenAIResponsesProvider add OpenAI's 10% data-residency premium for most GPT-5.4+ models when base_url is eu.api.openai.com or us.api.openai.com. GoogleVertexProvider counts Gemini thinking tokens in usage.output_tokens and bills them",
33
- "Import providers: from flowra.llm.providers.anthropic_vertex import AnthropicVertexProvider, from flowra.llm.providers.anthropic_foundry import AnthropicFoundryProvider, from flowra.llm.providers.openai import OpenAIProvider, from flowra.llm.providers.azure_openai import AzureOpenAIProvider, from flowra.llm.providers.openai_responses import OpenAIResponsesProvider, from flowra.llm.providers.google_vertex import GoogleVertexProvider",
33
+ "Import providers: from flowra.llm.providers.anthropic_vertex import AnthropicVertexProvider, from flowra.llm.providers.anthropic_foundry import AnthropicFoundryProvider, from flowra.llm.providers.anthropic_bedrock import AnthropicBedrockProvider, from flowra.llm.providers.openai import OpenAIProvider, from flowra.llm.providers.azure_openai import AzureOpenAIProvider, from flowra.llm.providers.openai_responses import OpenAIResponsesProvider, from flowra.llm.providers.google_vertex import GoogleVertexProvider",
34
34
  "Import stream types: from flowra.llm import TextDelta, ThinkingDelta, ContentComplete. Import observability types: from flowra.lib.observability import TextDeltaEvent, ThinkingDeltaEvent, LLMCallSpan, ToolCallSpan",
35
35
  "AnthropicRequestExtra(thinking_budget_tokens=4000) enables extended thinking on Claude models before 4.6. For Claude 4.6+ pass the API parameters as-is: AnthropicRequestExtra(thinking={'type': 'adaptive'}, output_config={'effort': 'low'}); an explicit thinking wins over thinking_budget_tokens. Each is sent only when set and is not validated against the model (same for temperature/top_p)",
36
36
  "GoogleRequestExtra(thinking_level='medium') or GoogleRequestExtra(thinking_budget=4096) configures Gemini thinking. Pass via extra=build_extra(GoogleRequestExtra(thinking_level='medium'))",
37
37
  "OpenAIResponsesRequestExtra(namespaces={'crm': 'CRM tools'}) (also reasoning_effort, see OpenAI reasoning effort) defines namespace descriptions for OpenAI Responses API tool search. Tools with OpenAIResponsesToolExtra(namespace='crm') are grouped into namespace objects. Both live under extra['openai_responses']",
38
38
  "SCHEMAS: any JSON schema (recursive $ref, oneOf, allOf, const, additionalProperties, type arrays) works with every provider at every level, except an allOf that cannot be merged (conflicting parts, or a $ref next to other constraints), which OpenAI strict mode rejects. Chat Completions and Gemini adapt json_schema and a tool's input_schema to their API; the Responses API adapts json_schema and sends a tool's schema as is (strict false); Anthropic puts json_schema into the prompt as text and sends a tool's schema as is. The helpers are exported from flowra.llm for a provider of your own: make_schema_strict (OpenAI strict mode: additionalProperties false, all properties required, optional as anyOf with null, no default on a property; oneOf as anyOf, const as a typed one-value enum, allOf merged when its parts do not conflict), drop_strict_nulls(value, schema) (an answer to make_schema_strict(schema) refitted to schema: a null for an optional property whose schema does not allow null is dropped; the OpenAI providers apply it to a json_schema answer and Chat tool arguments), adapt_schema_for_gemini (const as enum, allOf merged when its parts do not conflict; Gemini gets json_schema as response_json_schema and a tool's as parameters_json_schema, full JSON Schema), format_schema_for_llm (schema as text for a prompt, Anthropic's json_schema; a recursive type written once as Name = {...} and named elsewhere), format_output_schema_for_description (a tool's output_schema in its description), validate_json_schema (returns (error, the answer without a markdown code fence)). make_schema_strict and adapt_schema_for_gemini return a new schema and leave the given one unchanged",
39
- "Anthropic providers (Vertex, Foundry) fall back to non-streaming when json_schema is set (retry loop requires full responses)",
39
+ "Anthropic providers (Vertex, Foundry, Bedrock) fall back to non-streaming when json_schema is set (retry loop requires full responses)",
40
40
 
41
41
  "SPAN-HOOKS: observability wrappers with guaranteed enter/exit. Register via hooks.on_span(SpanType, handler). Handler receives Event[T] (same envelope as regular hooks, with execution context) — access span via event.data. Returns SpanExitHandler(error: BaseException | None) or None. Span is mutable — input fields set at open, result fields set before close. Later subscribers are outer (among normal on_span handlers). open_span/close_span for long-lived spans, async with emitter.span() for simple cases. hooks.on_self_span(SpanType, handler) subscribes to span events emitted by the agent itself (the baseline excludes self-emits for on_span); the handler is bound to the agent's own hooks instance and fires only for that instance's own emits (not other agents, not other instances of the same agent); self-emit handlers fire after normal handlers on enter and before them on exit (innermost). ToolCallAgent uses on_self_span(AgentSpan, ...) to emit ToolCallSpan around the lifetime of its own AgentSpan, so the tool span and any child agents spawned by the tool nest correctly across Spawn boundaries",
42
42
  "Span types: AgentSpan/StepSpan (engine lifecycle, from flowra.agent import AgentSpan, StepSpan), LLMCallSpan/ToolCallSpan (flowra.lib.observability). All mutable dataclasses with input + result fields. ToolCallSpan has error_kind (ToolErrorKind | None)",
@@ -129,12 +129,12 @@
129
129
 
130
130
  "PRE-BUILT AGENTS — TurnAgent: single-turn LLM tool loop with hooks and caching",
131
131
  "TurnAgent sends messages to LLM, executes tool calls, feeds results back, repeats until done",
132
- "TurnAgent stop handling: a response without tool calls ends the turn with TurnStatus.COMPLETED and TurnResult.stop_reason (END_TURN, MAX_TOKENS, STOP_SEQUENCE, OTHER, SCHEMA_VALIDATION_FAILED, REFUSAL). On REFUSAL TurnResult.refusal is set, result_message is None and messages keep the turn so far (tool calls included) with no empty assistant message. If a response stopped with anything but END_TURN/TOOL_USE ends on a tool call, that call was cut off: it is not run and gets an error ToolResultBlock (no Before/AfterToolCallEvent, no ToolCallSpan, not counted in max_consecutive_errors), the complete calls before it run, and the loop continues so the model can retry (bounded by max_llm_calls). History never holds an unanswered tool_use",
132
+ "TurnAgent stop handling: a response without tool calls ends the turn with TurnStatus.COMPLETED and TurnResult.stop_reason (END_TURN, MAX_TOKENS, STOP_SEQUENCE, OTHER, SCHEMA_VALIDATION_FAILED, REFUSAL). On REFUSAL TurnResult.refusal is set, result_message is None and messages keep the turn so far (tool calls included) with no empty assistant message. If a response stopped with anything but END_TURN/TOOL_USE ends on a tool call, that call was cut off: it is not run and gets an error ToolResultBlock (it appears in BeforeToolCallsEvent.tool_uses; no BeforeToolCallEvent/AfterToolCallEvent, no ToolCallSpan, not counted in max_consecutive_errors), the complete calls before it run, and the loop continues so the model can retry (bounded by max_llm_calls). History never holds an unanswered tool_use",
133
133
  "TurnConfig configures TurnAgent: TurnConfig(llm_config=LLMConfig(model='...'), system=[...], history=[...])",
134
134
  "RENDER: ChatConfig.render / TurnConfig.render (PromptRender = Callable[[Prompt], Prompt | Awaitable[Prompt]]; Prompt from flowra.lib.turn has system and messages) is called once per LLM call of the turn (not per fallback attempt), before LLMCaller, with the system messages and the conversation (history, then turn messages). What it returns is sent to every fallback link; each link's request_extensions apply after it, and each attempt's LLMCallSpan carries the request with them applied. Stored history and turn messages are not changed, so a marker block can be rendered from live state on each call; build new messages, never edit the given ones. Messages left with no blocks are not sent, none left at all is a ValueError before the call; a tool call must still be followed straight away by its results (Claude and OpenAI Chat Completions reject anything in between); an error in render is raised from the turn as is; an interrupt stops an async render and ends the turn INTERRUPTED",
135
135
  "TurnAgent lifecycle: __post_init__ emits TurnReadyEvent(context, turn_messages) — fires on both creation and crash recovery. ChatAgent subscribes and emits ConversationReadyEvent(conversation, context) with a Conversation view combining history + turn messages",
136
136
  "Conversation class (from flowra.lib.chat): read-only Sequence view over history + turn_messages. Supports len(), indexing, slicing, forward/reverse iteration. Live — reflects changes to underlying lists. Properties: history_len, turn_len. Method without_pending_tool_use() -> list[UserMessage | AssistantMessage] returns messages with ToolUseBlocks stripped from the last AssistantMessage (drops the message entirely if it becomes empty) — use it inside an agent-as-tool to feed the parent conversation to a child LLM call without the unresolved tool-use that spawned it",
137
- "TurnAgent hook events: StartTurnEvent (messages, metadata — mutate messages to augment input), MessageAcceptedEvent, StartIterationEvent, TextReasoningEvent (each TextBlock with text in response), ThinkingEvent (each ThinkingBlock with text; not redacted or signature-only thoughts), ServerToolCallEvent (server-side tool call from intermediate messages, e.g. tool search), ServerToolResultEvent (server-side tool result), ResultMessageEvent, BeforeToolCallEvent (has tool_definition: ToolDefinition | None; mutable tool_use, headers, meta — see ToolCallInfo), AfterToolCallEvent (has error_kind; result is a single ToolResultBlock; append TextBlock/MediaBlock to additional_blocks to attach them to this call's result — they go at the end of its content, also when max_consecutive_errors replaces it, so they stay with their call and never land between the results of parallel calls; mark one transient to keep it out of saved history; being part of the result, a MediaBlock there takes the providers' rules for media in a tool result (Chat Completions raises, Gemini before 3 does not see it) and a transient block ends the non-transient cache prefix before the result). Delta events: TextDeltaEvent, ThinkingDeltaEvent (from flowra.lib.observability — shared with LLMCallAgent). UsageEvent emitted after each LLM call + subscribed to accumulate usage from child agents (e.g. agent_as_tool). Span-hooks: LLMCallSpan, ToolCallSpan. Request transforms (prompt caching, tool search) are NOT hooks — they live on LLMConfig.request_extensions. A conversation transform for every fallback link goes in ChatConfig.render / TurnConfig.render (see RENDER)",
137
+ "TurnAgent hook events: StartTurnEvent (messages, metadata — mutate messages to augment input), MessageAcceptedEvent, StartIterationEvent, TextReasoningEvent (each TextBlock with text in response), ThinkingEvent (each ThinkingBlock with text; not redacted or signature-only thoughts), ServerToolCallEvent (server-side tool call from intermediate messages, e.g. tool search), ServerToolResultEvent (server-side tool result), ResultMessageEvent, BeforeToolCallsEvent (once per model message with tool calls, before any runs; tool_uses is every call of the message, a cut-off call included (an answer under its id is not used); put a ToolResult into results under a call's id to answer it without running it — that call gets no BeforeToolCallEvent and no ToolCallSpan, its result goes through AfterToolCallEvent with prefilled=True and max_consecutive_errors in message order, an error without error_kind gets EXECUTION; a key not among the message's call ids raises ValueError), BeforeToolCallEvent (has tool_definition: ToolDefinition | None; mutable tool_use, headers, meta — see ToolCallInfo; not emitted for a call answered in BeforeToolCallsEvent), AfterToolCallEvent (fires after execution, and for a call answered in BeforeToolCallsEvent; has error_kind; prefilled is true for a call answered in BeforeToolCallsEvent; result is a single ToolResultBlock; append TextBlock/MediaBlock to additional_blocks to attach them to this call's result — they go at the end of its content, also when max_consecutive_errors replaces it, so they stay with their call and never land between the results of parallel calls; mark one transient to keep it out of saved history; being part of the result, a MediaBlock there takes the providers' rules for media in a tool result (Chat Completions raises, Gemini before 3 does not see it) and a transient block ends the non-transient cache prefix before the result). Delta events: TextDeltaEvent, ThinkingDeltaEvent (from flowra.lib.observability — shared with LLMCallAgent). UsageEvent emitted after each LLM call + subscribed to accumulate usage from child agents (e.g. agent_as_tool). Span-hooks: LLMCallSpan, ToolCallSpan. Request transforms (prompt caching, tool search) are NOT hooks — they live on LLMConfig.request_extensions. A conversation transform for every fallback link goes in ChatConfig.render / TurnConfig.render (see RENDER)",
138
138
  "When TextDeltaEvent or ThinkingDeltaEvent handlers are registered, TurnAgent automatically uses provider.stream() instead of provider.call()",
139
139
 
140
140
  "AGENTS AS TOOLS: Tools can delegate to sub-agents via ToolCallAgentContext.call_agent(). make_agent_tool(AgentClass, name, description) creates an ExecutableTool with input_schema from spec type and output_schema from result type (ToolResult excluded). @agent_tool decorator marks an agent class as a tool, get_agent_tool() extracts the ExecutableTool. ToolCallAgent spawns the sub-agent and serializes its result as a ToolResultBlock. Agent tools can return ToolResult directly to signal errors to the parent LLM or answer with blocks (e.g. return ToolResult(content='...', is_error=True)) — the agent completes normally; with is_error=True the parent LLM sees a tool error and can retry. Compile-time validation: step return types must be a subset of Agent's generic result type parameter. The sub-agent must be declared by the agent running the tool or one of its registry ancestors (top level works); it receives services of its own registry ancestors, not the calling chat's (unless the chat is one of them)",
@@ -802,11 +802,14 @@ Lifecycle events (from `flowra.lib.turn`):
802
802
  | `ThinkingEvent` | Non-empty thinking block in LLM response | — |
803
803
  | `ServerToolCallEvent` | Server-side tool call (e.g. tool search) | — |
804
804
  | `ServerToolResultEvent` | Server-side tool result | — |
805
+ | `BeforeToolCallsEvent` | Message with tool calls, before any runs | `results` |
805
806
  | `BeforeToolCallEvent` | Before tool execution | `tool_use`, `headers`, `meta` |
806
- | `AfterToolCallEvent` | After tool execution | `result`, `additional_blocks`, `additional_messages` |
807
+ | `AfterToolCallEvent` | After tool execution or answer | `result`, `additional_blocks`, `additional_messages` |
807
808
  | `ResultMessageEvent` | LLM finished (END_TURN) | `continue_messages` |
808
809
 
809
- Tool calls cut off by the output limit fire no tool events; see
810
+ A tool call cut off by the output limit appears in `BeforeToolCallsEvent` but
811
+ fires no `BeforeToolCallEvent` or `AfterToolCallEvent`, and a call answered in
812
+ `BeforeToolCallsEvent` does not run; see
810
813
  [Pre-built events](observability.md#pre-built-events) in Observability.
811
814
 
812
815
  Streaming events (from `flowra.lib.observability`):
@@ -29,7 +29,7 @@ ext (agent, llm, lib)
29
29
  Code imports another package's public names from its `__init__`, never from its
30
30
  internal modules. One deliberate exception: `flowra.llm.providers` exports
31
31
  nothing, because each provider needs its own optional SDK (`flowra[anthropic]`,
32
- `flowra[openai]`, `flowra[google]`). Each provider module
32
+ `flowra[bedrock]`, `flowra[openai]`, `flowra[google]`). Each provider module
33
33
  (`flowra.llm.providers.anthropic_vertex`, …) is that provider's public entry
34
34
  point, and code imports from it directly.
35
35
 
@@ -44,8 +44,9 @@ import inside a function.
44
44
  Protocol layer between the SDK and any LLM. The core abstraction is `LLMProvider` —
45
45
  `call(LLMRequest) → LLMResponse` for full responses, and `stream(LLMRequest) →
46
46
  AsyncIterator[StreamEvent]` for real-time text/thinking deltas. Request and response use
47
- a shared set of message and block types. Ships six providers:
47
+ a shared set of message and block types. Ships seven providers:
48
48
  `AnthropicVertexProvider` (Claude via Vertex AI), `AnthropicFoundryProvider` (Claude via Azure AI Foundry),
49
+ `AnthropicBedrockProvider` (Claude via Amazon Bedrock),
49
50
  `OpenAIProvider` (OpenAI Chat Completions), `AzureOpenAIProvider` (OpenAI models via Azure),
50
51
  `OpenAIResponsesProvider` (OpenAI Responses API), `GoogleVertexProvider` (Gemini via Vertex AI). → [docs/llm.md](llm.md)
51
52
 
@@ -6,7 +6,8 @@ Flowra — from a single LLM call to a multi-turn chatbot with tools.
6
6
  ## Installation
7
7
 
8
8
  ```bash
9
- pip install flowra[anthropic] # Claude via Vertex AI
9
+ pip install flowra[anthropic] # Claude via Vertex AI or Microsoft Foundry
10
+ pip install flowra[bedrock] # Claude via Amazon Bedrock
10
11
  pip install flowra[openai] # OpenAI-compatible APIs
11
12
  pip install flowra[google] # Gemini via Vertex AI
12
13
  pip install flowra[providers] # all providers
@@ -9,7 +9,7 @@ For the basics (making a call, using ChatAgent), see
9
9
 
10
10
  ## Providers
11
11
 
12
- Flowra ships six providers. Each lives in its own module to avoid pulling in
12
+ Flowra ships seven providers. Each lives in its own module to avoid pulling in
13
13
  unnecessary dependencies.
14
14
 
15
15
  ### AnthropicVertexProvider (Claude via Vertex AI)
@@ -127,6 +127,45 @@ async with AnthropicFoundryProvider(resource="my-azure-resource", api_key="...",
127
127
  ...
128
128
  ```
129
129
 
130
+ ### AnthropicBedrockProvider (Claude via Amazon Bedrock)
131
+
132
+ ```bash
133
+ pip install flowra[bedrock]
134
+ ```
135
+
136
+ ```python
137
+ from flowra.llm.providers.anthropic_bedrock import AnthropicBedrockProvider
138
+
139
+ async with AnthropicBedrockProvider(
140
+ api_key="...", # a Bedrock API key, sent as a bearer token
141
+ aws_region="ap-southeast-2",
142
+ ) as provider:
143
+ ...
144
+
145
+ # Or with a pre-configured client, e.g. for AWS access keys or a profile:
146
+ from anthropic import AsyncAnthropicBedrock
147
+ async with AnthropicBedrockProvider(client=AsyncAnthropicBedrock(aws_profile="...")) as provider:
148
+ ...
149
+ ```
150
+
151
+ Set `LLMRequest.model` to a Bedrock model id or inference profile. A profile's
152
+ prefix says where Bedrock may run the call: `global.anthropic.claude-sonnet-5-5`
153
+ in any region, `au.anthropic.claude-haiku-5-5` only within one geography (`au.`,
154
+ `eu.`, `us.`, `apac.`, `jp.`, ...). Older models carry a date and version:
155
+ `au.anthropic.claude-haiku-4-5-20251001-v1:0`. `aws_region` is the region
156
+ requests go to; call a geography's profile from one of its regions (`au.` from
157
+ `ap-southeast-2`). Bedrock names the underlying model in its response
158
+ (`claude-haiku-5-5`); flowra prices the call as that model and returns it in
159
+ `response.model`.
160
+
161
+ Calls are priced from the `BEDROCK` price list (see [Price lists](#price-lists)).
162
+ A geography's profile bills 10% above a `global.` one for Claude 4.5 and later;
163
+ flowra reads the profile from the model id, or from the end of a system
164
+ inference profile's ARN. A bare model id (`anthropic.claude-…`) or an
165
+ application inference profile's ARN is priced at the base rate. GovCloud's
166
+ profiles (`us-gov.`) have prices of their own, which the list does not hold:
167
+ their calls report no cost (`cost_usd=None`).
168
+
130
169
  ### OpenAIProvider (OpenAI and compatible APIs)
131
170
 
132
171
  ```bash
@@ -284,7 +323,7 @@ when the result carries media:
284
323
 
285
324
  | Provider | Text only | With media |
286
325
  |-------------------------------------------|----------------------------|--------------------------------------------------------------------------------|
287
- | Anthropic (Vertex, Foundry, API) | `result.text` | a list of `text` / `image` / `document` blocks; the API takes JPEG, PNG, GIF, WebP and PDF |
326
+ | Anthropic (Vertex, Foundry, Bedrock, API) | `result.text` | a list of `text` / `image` / `document` blocks; the API takes JPEG, PNG, GIF, WebP and PDF |
288
327
  | `OpenAIProvider`, `AzureOpenAIProvider` | `result.text` | `ValueError`: Chat Completions takes only text in a tool message |
289
328
  | `OpenAIResponsesProvider` | `result.text` | a list of `input_text` / `input_image` / `input_file` items; the API takes PNG, JPEG, WebP and GIF images and document or text files, not audio, video, archives or `application/octet-stream` |
290
329
  | `GoogleVertexProvider` | `{"result": result.text}` | `{"result": result.text}` plus the media in the function response's `parts`, which Gemini 3 models see; Gemini 2.5 takes the request but does not see the media |
@@ -395,7 +434,7 @@ Every error carries:
395
434
  | `model` | `str` | The requested model |
396
435
  | `status_code` | `int \| None` | The HTTP status; `None` when no error status came back: a connection error or timeout, an error event inside a stream, a failed Responses API response, a request flowra stops before sending because Gemini's SDK would refuse it (nothing to send) |
397
436
  | `retry_after` | `float \| None` | Seconds to wait, from the response's `retry-after` header, if any |
398
- | `request_id` | `str \| None` | The provider's id of the request (`request-id`, `apim-request-id` or `x-request-id`), to quote to its support; Gemini sends none |
437
+ | `request_id` | `str \| None` | The provider's id of the request (`request-id`, `apim-request-id`, `x-request-id` or `x-amzn-RequestId`), to quote to its support; Gemini sends none |
399
438
  | `partial` | `bool` | `True` when a stream had already yielded events before failing |
400
439
 
401
440
  `str(error)` names the provider and model and keeps the provider's own message.
@@ -441,7 +480,9 @@ except TransientLLMError as error:
441
480
 
442
481
  Errors that arrive inside a stream — Anthropic's and OpenAI's error events,
443
482
  `OpenAIResponsesProvider`'s `error` and `response.failed` events — are typed by
444
- what they report (an `overloaded_error` event is an `OverloadedError`). Without
483
+ what they report (an `overloaded_error` event is an `OverloadedError`; Bedrock's
484
+ `throttlingException` a `RateLimitedError`, its `validationException` a
485
+ `BadRequestError`, or a `ContextTooLongError` when the prompt is too long). Without
445
486
  a status, OpenAI's error goes by its code, then its type, and one flowra does not
446
487
  know is an `UnavailableError`. A bad request — its code is one of the request-blaming codes below, or,
447
488
  with no code flowra knows, its type is `invalid_request_error` (vLLM:
@@ -621,7 +662,7 @@ last column:
621
662
 
622
663
  | Provider | What is a refusal | `categories` | `explanation` | Keys in `response.extra` |
623
664
  |---|---|---|---|---|
624
- | Anthropic (Vertex, Foundry) | `stop_reason: "refusal"`: Claude's safety classifiers | `stop_details.category`: `cyber`, `bio`, `frontier_llm`, `reasoning_extraction`, `general_harms` | `stop_details.explanation` | `stop_details` |
665
+ | Anthropic (Vertex, Foundry, Bedrock) | `stop_reason: "refusal"`: Claude's safety classifiers | `stop_details.category`: `cyber`, `bio`, `frontier_llm`, `reasoning_extraction`, `general_harms` | `stop_details.explanation` | `stop_details` |
625
666
  | OpenAI Chat Completions | `finish_reason: "content_filter"` on the choice, or on the separate stream event Azure's asynchronous filter sends when it blocks a stream; the model's `message.refusal` (structured outputs) | Azure's `content_filter_results` entries with `filtered: true` | `message.refusal` | `content_filter_results` (Azure) |
626
667
  | Azure (Chat and Responses) | a 400 with code `content_filter`: the prompt filter | `innererror.content_filter_result` entries with `filtered: true`, e.g. `jailbreak`, `hate` | the error's message | none |
627
668
  | OpenAI Responses | `incomplete_details.reason: "content_filter"`; a `refusal` output part; a failed response, or a stream's `error` event, with `bio_policy`, `misalignment_policy_violation` or `image_content_policy_violation` | the policy code | the refusal part's text, or the failure's message | `incomplete_reason`, or `provider_status` for a failed response |
@@ -806,7 +847,7 @@ and the history keeps the thinking each model produced. Each provider sends
806
847
  only the part of it that the model it calls accepts:
807
848
 
808
849
  - **Anthropic** sends Claude's thinking, signed or redacted, to any Claude model,
809
- on any cloud (Vertex and Foundry accept each other's signatures). It drops
850
+ on any cloud (Vertex, Foundry and Bedrock accept each other's signatures). It drops
810
851
  thinking from another provider or written by hand: the API rejects thinking
811
852
  without Claude's signature.
812
853
  - **Google** sends Gemini's thoughts and thought signatures to any Gemini model,
@@ -1125,6 +1166,7 @@ litellm or the supplier's own models API, with manual overrides, in
1125
1166
  | List | Supplier | Used by default by |
1126
1167
  |---|---|---|
1127
1168
  | `ANTHROPIC` | Anthropic's API; Microsoft Foundry bills Claude at these prices | `AnthropicFoundryProvider` |
1169
+ | `BEDROCK` | Claude on Amazon Bedrock | `AnthropicBedrockProvider` |
1128
1170
  | `VERTEX` | Google Cloud Vertex AI: Claude and Gemini | `AnthropicVertexProvider`, `GoogleVertexProvider` on Vertex |
1129
1171
  | `GEMINI_API` | The Gemini Developer API (Google AI Studio) | `GoogleVertexProvider` with `client=genai.Client(api_key=...)` |
1130
1172
  | `OPENAI` | OpenAI | `OpenAIProvider`, `OpenAIResponsesProvider` |
@@ -1213,6 +1255,9 @@ needs none of this: each child prices its own calls, and their `Usage` add up.
1213
1255
  (see [Locations](#locations)); `GoogleVertexProvider` by its `location`.
1214
1256
  - `ANTHROPIC`, on a Foundry US Data Zone deployment
1215
1257
  (`AnthropicFoundryProvider(data_zone=True)`): Claude 4.6 and later.
1258
+ - `BEDROCK`, on a geography's cross-region profile (`au.anthropic.…`,
1259
+ `eu.anthropic.…`, but not `global.anthropic.…`): Claude 4.5 and later.
1260
+ `AnthropicBedrockProvider` reads the profile from the model id.
1216
1261
  - `OPENAI`, when `base_url` is `https://eu.api.openai.com/v1` or
1217
1262
  `https://us.api.openai.com/v1`: most GPT-5.4 and later models. Applies to
1218
1263
  `OpenAIProvider` and `OpenAIResponsesProvider`.
@@ -35,8 +35,9 @@ Events from `TurnAgent` (and `ChatAgent`, which uses it internally):
35
35
  | `ThinkingEvent` | Non-empty thinking block in LLM response | — |
36
36
  | `ServerToolCallEvent` | Server-side tool call (e.g. tool search) | — |
37
37
  | `ServerToolResultEvent` | Server-side tool result | — |
38
+ | `BeforeToolCallsEvent` | Message with tool calls, before any runs | `results` |
38
39
  | `BeforeToolCallEvent` | Before tool execution | `tool_use`, `headers`, `meta` |
39
- | `AfterToolCallEvent` | After tool execution | `result`, `additional_blocks`, `additional_messages` |
40
+ | `AfterToolCallEvent` | After tool execution or answer | `result`, `additional_blocks`, `additional_messages` |
40
41
  | `ResultMessageEvent` | LLM finished (END_TURN) | `continue_messages` |
41
42
 
42
43
  `AfterToolCallEvent.additional_blocks` go at the end of that call's result, so a
@@ -71,7 +72,37 @@ runtime.hooks.on(AfterToolCallEvent, add_hint)
71
72
  A tool call cut off by the output limit (see [TurnAgent](agents.md#turnagent)) is
72
73
  never executed. It gets an error result directly, with no `BeforeToolCallEvent`,
73
74
  `AfterToolCallEvent` or `ToolCallSpan`, so hooks that audit or rewrite tool
74
- results don't see it.
75
+ results don't see it; it does appear in `BeforeToolCallsEvent.tool_uses`, and an
76
+ answer put under its id is not used.
77
+
78
+ `BeforeToolCallsEvent` fires once per model message with tool calls, a single
79
+ call included, before any of them runs. A handler answers a call itself by
80
+ putting a `ToolResult` into `results` under the call's id: that call does not
81
+ run, and the others run as usual. An answered call gets no `BeforeToolCallEvent`
82
+ and no `ToolCallSpan`; its result goes through `AfterToolCallEvent` (with
83
+ `prefilled=True`) and `max_consecutive_errors` like any other, in the message's
84
+ order. An error answered without an `error_kind` arrives with
85
+ `ToolErrorKind.EXECUTION`. A key that is not the id of a call in the message
86
+ fails the step with `ValueError`.
87
+
88
+ ```python
89
+ from flowra.agent import Event
90
+ from flowra.lib.turn import BeforeToolCallsEvent
91
+ from flowra.tools import ToolResult
92
+
93
+
94
+ def reply_alone(event: Event[BeforeToolCallsEvent]) -> None:
95
+ if len(event.data.tool_uses) < 2:
96
+ return
97
+ for tool_use in event.data.tool_uses:
98
+ if tool_use.name == "send_reply":
99
+ event.data.results[tool_use.id] = ToolResult(
100
+ content="Call send_reply on its own, in a message with no other tool calls.", is_error=True
101
+ )
102
+
103
+
104
+ runtime.hooks.on(BeforeToolCallsEvent, reply_alone)
105
+ ```
75
106
 
76
107
  A tool call that fails the run (an exception raised around a tool's executor,
77
108
  see [Error handling](tools.md#what-fails-the-run)) has no result:
@@ -4,6 +4,7 @@ from .context import TurnAgentContext
4
4
  from .hook_events import (
5
5
  AfterToolCallEvent,
6
6
  BeforeToolCallEvent,
7
+ BeforeToolCallsEvent,
7
8
  MessageAcceptedEvent,
8
9
  ResultMessageEvent,
9
10
  ServerToolCallEvent,
@@ -20,6 +21,7 @@ from .tool_call import ToolCallAgentContext, agent_tool, get_agent_tool, make_ag
20
21
  __all__ = [
21
22
  "AfterToolCallEvent",
22
23
  "BeforeToolCallEvent",
24
+ "BeforeToolCallsEvent",
23
25
  "MessageAcceptedEvent",
24
26
  "Prompt",
25
27
  "PromptRender",