timbal 2.2.3__tar.gz → 2.2.5__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 (224) hide show
  1. {timbal-2.2.3 → timbal-2.2.5}/PKG-INFO +1 -1
  2. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/_version.py +2 -2
  3. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/flow.py +63 -0
  4. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/workflow.py +32 -8
  5. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/__init__.py +7 -0
  6. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/elasticsearch.py +5 -2
  7. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/elevenlabs.py +3 -2
  8. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/fal.py +9 -6
  9. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/gemini_images.py +3 -2
  10. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_analytics.py +8 -2
  11. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_docs.py +1 -1
  12. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_drive.py +2 -2
  13. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/higgsfield.py +5 -1
  14. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/jira.py +4 -1
  15. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/onedrive.py +2 -2
  16. timbal-2.2.5/python/timbal/tools/parallel.py +191 -0
  17. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/pinecone.py +4 -1
  18. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/quiver_ai.py +5 -3
  19. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/replicate.py +5 -3
  20. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/sharepoint.py +2 -2
  21. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/stripe.py +4 -1
  22. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/zendesk.py +8 -2
  23. {timbal-2.2.3 → timbal-2.2.5}/.gitignore +0 -0
  24. {timbal-2.2.3 → timbal-2.2.5}/LICENSE +0 -0
  25. {timbal-2.2.3 → timbal-2.2.5}/README.md +0 -0
  26. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/chat-completions.mdx +0 -0
  27. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/create-from-workforce.mdx +0 -0
  28. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/jobs/cancel.mdx +0 -0
  29. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/jobs/get.mdx +0 -0
  30. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/jobs/list.mdx +0 -0
  31. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/jobs/retry.mdx +0 -0
  32. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/link.mdx +0 -0
  33. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/list-context-vars.mdx +0 -0
  34. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/list-policies.mdx +0 -0
  35. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/messages.mdx +0 -0
  36. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/responses.mdx +0 -0
  37. {timbal-2.2.3 → timbal-2.2.5}/docs/api-reference/ace/unlink.mdx +0 -0
  38. {timbal-2.2.3 → timbal-2.2.5}/pyproject.toml +0 -0
  39. {timbal-2.2.3 → timbal-2.2.5}/pyrightconfig.json +0 -0
  40. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/__init__.py +0 -0
  41. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/__init__.py +0 -0
  42. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/__main__.py +0 -0
  43. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/cli_utils.py +0 -0
  44. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/cst_utils.py +0 -0
  45. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/format.py +0 -0
  46. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/model_discovery.py +0 -0
  47. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/test.py +0 -0
  48. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/tool_discovery.py +0 -0
  49. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/__init__.py +0 -0
  50. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/add_edge.py +0 -0
  51. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/add_step.py +0 -0
  52. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/add_tool.py +0 -0
  53. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/remove_edge.py +0 -0
  54. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/remove_step.py +0 -0
  55. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/remove_tool.py +0 -0
  56. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/set_config.py +0 -0
  57. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/set_param.py +0 -0
  58. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/codegen/transformers/set_position.py +0 -0
  59. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/__init__.py +0 -0
  60. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/base.py +0 -0
  61. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/__init__.py +0 -0
  62. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/anthropic.py +0 -0
  63. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/default.py +0 -0
  64. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/message.py +0 -0
  65. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/openai.py +0 -0
  66. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/string.py +0 -0
  67. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/impl/timbal.py +0 -0
  68. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/collectors/registry.py +0 -0
  69. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/__init__.py +0 -0
  70. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/agent.py +0 -0
  71. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/fallback_model.py +0 -0
  72. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/llm_router.py +0 -0
  73. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/mcp.py +0 -0
  74. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/memory_compaction.py +0 -0
  75. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/models.py +0 -0
  76. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/runnable.py +0 -0
  77. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/skill.py +0 -0
  78. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/test_model.py +0 -0
  79. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/tool.py +0 -0
  80. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/core/tool_set.py +0 -0
  81. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/errors.py +0 -0
  82. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/__init__.py +0 -0
  83. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/cli.py +0 -0
  84. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/display.py +0 -0
  85. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/agent.py +0 -0
  86. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/eval_examples.yaml +0 -0
  87. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/eval_flow_validators.yaml +0 -0
  88. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/eval_negations.yaml +0 -0
  89. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/eval_parallel.yaml +0 -0
  90. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/eval_subagent.yaml +0 -0
  91. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/eval_transforms.yaml +0 -0
  92. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/fixtures/test_invalid_validator.yaml +0 -0
  93. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/models.py +0 -0
  94. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/runner.py +0 -0
  95. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/utils.py +0 -0
  96. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/__init__.py +0 -0
  97. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/base.py +0 -0
  98. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/comparison_base.py +0 -0
  99. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/contains.py +0 -0
  100. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/contains_all.py +0 -0
  101. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/contains_any.py +0 -0
  102. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/context.py +0 -0
  103. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/email.py +0 -0
  104. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/ends_with.py +0 -0
  105. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/eq.py +0 -0
  106. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/gt.py +0 -0
  107. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/gte.py +0 -0
  108. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/json.py +0 -0
  109. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/language.py +0 -0
  110. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/length.py +0 -0
  111. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/llm_base.py +0 -0
  112. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/lt.py +0 -0
  113. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/lte.py +0 -0
  114. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/max_length.py +0 -0
  115. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/min_length.py +0 -0
  116. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/not_null.py +0 -0
  117. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/parallel.py +0 -0
  118. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/pattern.py +0 -0
  119. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/prompt.py +0 -0
  120. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/semantic.py +0 -0
  121. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/seq.py +0 -0
  122. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/starts_with.py +0 -0
  123. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/evals/validators/type.py +0 -0
  124. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/logs.py +0 -0
  125. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/models.yaml +0 -0
  126. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/__init__.py +0 -0
  127. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/integrations.py +0 -0
  128. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/knowledge_bases/__init__.py +0 -0
  129. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/knowledge_bases/query.py +0 -0
  130. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/tool_proxy.py +0 -0
  131. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/types.py +0 -0
  132. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/platform/utils.py +0 -0
  133. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/server/__init__.py +0 -0
  134. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/server/__main__.py +0 -0
  135. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/server/http.py +0 -0
  136. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/server/jobs.py +0 -0
  137. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/server/voice.html +0 -0
  138. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/server/voice.py +0 -0
  139. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/__init__.py +0 -0
  140. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/config.py +0 -0
  141. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/config_loader.py +0 -0
  142. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/context.py +0 -0
  143. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/dependency_analyzer.py +0 -0
  144. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/__init__.py +0 -0
  145. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/exporters/__init__.py +0 -0
  146. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/exporters/otel.py +0 -0
  147. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/providers/__init__.py +0 -0
  148. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/providers/base.py +0 -0
  149. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/providers/in_memory.py +0 -0
  150. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/providers/jsonl.py +0 -0
  151. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/providers/platform.py +0 -0
  152. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/providers/sqlite.py +0 -0
  153. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/span.py +0 -0
  154. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/state/tracing/trace.py +0 -0
  155. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/_creds.py +0 -0
  156. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/asana.py +0 -0
  157. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/bash.py +0 -0
  158. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/cala.py +0 -0
  159. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/cloudflare.py +0 -0
  160. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/dynamics_business_central.py +0 -0
  161. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/dynamics_sales.py +0 -0
  162. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/edit.py +0 -0
  163. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/excel.py +0 -0
  164. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/fathom.py +0 -0
  165. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/firecrawl.py +0 -0
  166. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/gmail.py +0 -0
  167. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_business.py +0 -0
  168. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_calendar.py +0 -0
  169. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_maps.py +0 -0
  170. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_merchant_center.py +0 -0
  171. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_search_console.py +0 -0
  172. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/google_sheets.py +0 -0
  173. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/happy_scribe.py +0 -0
  174. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/hubspot.py +0 -0
  175. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/interaction.py +0 -0
  176. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/klaviyo.py +0 -0
  177. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/knowledge_base.py +0 -0
  178. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/krea.py +0 -0
  179. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/lancedb.py +0 -0
  180. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/linkedin.py +0 -0
  181. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/mongodb.py +0 -0
  182. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/netsuite.py +0 -0
  183. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/outlook.py +0 -0
  184. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/powerbi.py +0 -0
  185. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/read.py +0 -0
  186. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/resend.py +0 -0
  187. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/salesforce.py +0 -0
  188. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/scraperapi.py +0 -0
  189. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/shopify.py +0 -0
  190. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/slack.py +0 -0
  191. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/tavily.py +0 -0
  192. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/web_search.py +0 -0
  193. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/write.py +0 -0
  194. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/xai.py +0 -0
  195. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/tools/zoho_crm.py +0 -0
  196. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/__init__.py +0 -0
  197. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/approval.py +0 -0
  198. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/__init__.py +0 -0
  199. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/base.py +0 -0
  200. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/custom.py +0 -0
  201. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/file.py +0 -0
  202. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/text.py +0 -0
  203. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/thinking.py +0 -0
  204. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/tool_result.py +0 -0
  205. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/content/tool_use.py +0 -0
  206. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/__init__.py +0 -0
  207. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/approval.py +0 -0
  208. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/base.py +0 -0
  209. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/delta.py +0 -0
  210. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/interaction.py +0 -0
  211. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/output.py +0 -0
  212. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/events/start.py +0 -0
  213. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/file.py +0 -0
  214. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/message.py +0 -0
  215. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/types/run_status.py +0 -0
  216. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/utils/__init__.py +0 -0
  217. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/utils/import_spec.py +0 -0
  218. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/utils/model.py +0 -0
  219. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/utils/net.py +0 -0
  220. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/utils/schema.py +0 -0
  221. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/utils/serialization.py +0 -0
  222. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/voice/__init__.py +0 -0
  223. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/voice/elevenlabs.py +0 -0
  224. {timbal-2.2.3 → timbal-2.2.5}/python/timbal/voice/session.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timbal
3
- Version: 2.2.3
3
+ Version: 2.2.5
4
4
  Summary: Simple, performant, battle-tested framework for building reliable AI applications
5
5
  Project-URL: Source, https://github.com/timbal-ai/timbal
6
6
  Author-email: Timbal <team@timbal.ai>
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.2.3'
22
- __version_tuple__ = version_tuple = (2, 2, 3)
21
+ __version__ = version = '2.2.5'
22
+ __version_tuple__ = version_tuple = (2, 2, 5)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -203,6 +203,24 @@ def _build_node(runnable: Any, *, include_tools: bool = True) -> dict[str, Any]:
203
203
  return node
204
204
 
205
205
 
206
+ def _edge_kind(kinds: set[str] | None) -> str:
207
+ """Collapse a set of edge-origin kinds into a single label for JSON output.
208
+
209
+ A dependency can arise from several sources at once (e.g. an explicit
210
+ ``depends_on`` that also reads the step's output via a param map). Precedence
211
+ is ``ordering > when > param`` so explicit sequencing is never masked by
212
+ param wiring. Unknown/missing (e.g. edges from a direct ``_link``) default to
213
+ ``ordering`` — the safe choice, since ordering edges are never reduced away.
214
+ """
215
+ if not kinds:
216
+ return "ordering"
217
+ if "ordering" in kinds:
218
+ return "ordering"
219
+ if "when" in kinds:
220
+ return "when"
221
+ return "param"
222
+
223
+
206
224
  def get_flow(workspace_path: str | Path) -> dict[str, Any]:
207
225
  """Return a ReactFlow-compatible graph for the workspace entry point.
208
226
 
@@ -239,12 +257,14 @@ def get_flow(workspace_path: str | Path) -> dict[str, Any]:
239
257
 
240
258
  for _step_name, step in runnable._steps.items():
241
259
  when_source = _get_when_source(step) if step.when else None
260
+ kinds_map = getattr(step, "previous_steps_kinds", None) or {}
242
261
  for prev_name in step.previous_steps:
243
262
  prev_step = runnable._steps[prev_name]
244
263
  edge: dict[str, Any] = {
245
264
  "id": f"{prev_step._path}->{step._path}",
246
265
  "source": prev_step._path,
247
266
  "target": step._path,
267
+ "kind": _edge_kind(kinds_map.get(prev_name)),
248
268
  }
249
269
  if when_source is not None:
250
270
  edge["when"] = when_source
@@ -390,6 +410,48 @@ def _fmt_node_lines(node: dict, indent: str) -> list[str]:
390
410
  return lines
391
411
 
392
412
 
413
+ def _reduce_param_edges(edges: list[dict]) -> list[dict]:
414
+ """Drop param-induced edges that are transitively implied by another path.
415
+
416
+ A param dependency ``A → C`` (the handler/agent on C reads A's output) is a
417
+ real await-gate at runtime, but for an execution-order view it is redundant
418
+ when A already reaches C through some other path (e.g. ``A → B → C``). Left
419
+ in, such edges turn a linear pipeline into a misleading fan-out from the
420
+ first step.
421
+
422
+ Only edges whose single ``kind`` is ``param`` are eligible for removal —
423
+ explicit ordering (``depends_on`` / hooks) and ``when`` edges are always
424
+ kept so user-added structure never silently disappears from the compact
425
+ view. The full edge set (including these) is retained in the JSON output.
426
+ """
427
+ adj: dict[str, list[str]] = defaultdict(list)
428
+ for e in edges:
429
+ adj[e["source"]].append(e["target"])
430
+
431
+ def _reaches(start: str, goal: str, skip: tuple[str, str]) -> bool:
432
+ """True if ``goal`` is reachable from ``start`` without using the direct ``skip`` edge."""
433
+ stack = [start]
434
+ seen = {start}
435
+ while stack:
436
+ node = stack.pop()
437
+ for nxt in adj.get(node, []):
438
+ if (node, nxt) == skip:
439
+ continue
440
+ if nxt == goal:
441
+ return True
442
+ if nxt not in seen:
443
+ seen.add(nxt)
444
+ stack.append(nxt)
445
+ return False
446
+
447
+ kept: list[dict] = []
448
+ for e in edges:
449
+ if e.get("kind") == "param" and _reaches(e["source"], e["target"], (e["source"], e["target"])):
450
+ continue
451
+ kept.append(e)
452
+ return kept
453
+
454
+
393
455
  def _build_edge_lines(edges: list[dict]) -> list[str]:
394
456
  """Render edges grouped by source, one line per source node."""
395
457
  targets_of: dict[str, list[tuple[str, str | None]]] = defaultdict(list)
@@ -472,6 +534,7 @@ def format_compact(flow: dict) -> str:
472
534
  for line in _fmt_node_lines(node, indent=" "):
473
535
  lines.append(line)
474
536
 
537
+ edges = _reduce_param_edges(edges)
475
538
  if edges:
476
539
  lines.append("")
477
540
  lines.append("EDGES")
@@ -142,29 +142,53 @@ class Workflow(Runnable):
142
142
  self._steps[runnable.name] = runnable
143
143
  runnable.previous_steps = set()
144
144
  runnable.next_steps = set()
145
+ # Per-source edge kinds: maps a previous step name -> set of kinds
146
+ # ("ordering" | "when" | "param"). Used by introspection (get_flow) to
147
+ # distinguish explicit sequencing from param/when-induced dependencies.
148
+ # Runtime sequencing only ever looks at ``previous_steps``.
149
+ runnable.previous_steps_kinds = {}
145
150
  runnable.when = None
146
151
 
147
152
  # Explicit dependencies
148
153
  if depends_on and not isinstance(depends_on, list):
149
154
  raise ValueError("depends_on must be a list of step names")
150
- depends_on = set(depends_on or []) # Deduplicate here to avoid duplicate _is_dag calls
151
155
 
152
- depends_on.update(runnable._dependencies)
153
- depends_on.update(runnable._pre_hook_dependencies)
154
- depends_on.update(runnable._post_hook_dependencies)
156
+ # Track dependency origins so introspection can tell explicit ordering
157
+ # (depends_on / hooks) apart from param- and when-induced wiring. These
158
+ # all merge into ``previous_steps`` for execution, but the distinction is
159
+ # lossy once merged (a step can be both an explicit dep and a param dep),
160
+ # so we record it here while the sources are still separate.
161
+ ordering_deps = set(depends_on or [])
162
+ # Hooks read sibling outputs via step_span(); treat as ordering so they
163
+ # are never silently collapsed in compact views.
164
+ ordering_deps.update(runnable._pre_hook_dependencies)
165
+ ordering_deps.update(runnable._post_hook_dependencies)
166
+ # Handler-body step_span() references are data wiring, not explicit ordering.
167
+ param_deps = set(runnable._dependencies)
168
+ when_deps: set[str] = set()
155
169
 
156
170
  # Optional handler to determine whether to execute the step, and inspect it to automatically link steps
157
171
  if when:
158
172
  inspect_result = runnable._inspect_callable(when)
159
173
  runnable.when = {"callable": when, **inspect_result}
160
- depends_on.update(inspect_result["dependencies"])
174
+ when_deps.update(inspect_result["dependencies"])
161
175
 
162
176
  # Use kwargs as default params for the runnable, and inspect callables to automatically link steps
163
177
  runnable._prepare_default_params(kwargs)
164
178
  for v in runnable._default_runtime_params.values():
165
- depends_on.update(v["dependencies"])
166
-
167
- for dep in depends_on:
179
+ param_deps.update(v["dependencies"])
180
+
181
+ edge_kinds: dict[str, set[str]] = {}
182
+ for dep in ordering_deps:
183
+ edge_kinds.setdefault(dep, set()).add("ordering")
184
+ for dep in when_deps:
185
+ edge_kinds.setdefault(dep, set()).add("when")
186
+ for dep in param_deps:
187
+ edge_kinds.setdefault(dep, set()).add("param")
188
+ runnable.previous_steps_kinds = edge_kinds
189
+
190
+ # Deduplicate (set union) to avoid duplicate _is_dag calls per shared dep.
191
+ for dep in edge_kinds:
168
192
  logger.info("Linking steps", previous_step=dep, next_step=runnable.name)
169
193
  self._link(dep, runnable.name)
170
194
 
@@ -493,6 +493,7 @@ if TYPE_CHECKING:
493
493
  OutlookTrash,
494
494
  OutlookUpdateEmail,
495
495
  )
496
+ from .parallel import ParallelExtract, ParallelSearch, ParallelTaskRun
496
497
  from .pinecone import (
497
498
  PineconeCreateIndex,
498
499
  PineconeDeleteVectors,
@@ -1330,6 +1331,9 @@ __all__ = [
1330
1331
  "TavilyExtract",
1331
1332
  "TavilyMap",
1332
1333
  "TavilySearch",
1334
+ "ParallelSearch",
1335
+ "ParallelExtract",
1336
+ "ParallelTaskRun",
1333
1337
  "WebSearch",
1334
1338
  "Write",
1335
1339
  "XSearch",
@@ -2400,6 +2404,9 @@ _LAZY_IMPORTS = {
2400
2404
  "TavilyExtract": ".tavily",
2401
2405
  "TavilyMap": ".tavily",
2402
2406
  "TavilySearch": ".tavily",
2407
+ "ParallelSearch": ".parallel",
2408
+ "ParallelExtract": ".parallel",
2409
+ "ParallelTaskRun": ".parallel",
2403
2410
  "WebSearch": ".web_search",
2404
2411
  "Write": ".write",
2405
2412
  "XSearch": ".xai",
@@ -33,8 +33,11 @@ async def _resolve_credentials(tool: Any) -> tuple[str, str | None]:
33
33
  class ElasticsearchIngestAttachment(Tool):
34
34
  name: str = "elasticsearch_ingest_attachment"
35
35
  description: str | None = (
36
- "Ingest a base64-encoded file attachment into an Elasticsearch index "
37
- "using the ingest-attachment pipeline to extract text content."
36
+ "Ingest and parse a file attachment PDF, Word DOCX, PowerPoint PPTX, "
37
+ "Excel, HTML, RTF, or plain text document — into searchable full text in "
38
+ "an Elasticsearch index using the ingest-attachment (Apache Tika) pipeline. "
39
+ "Extract text and metadata from a base64-encoded document or file for "
40
+ "full-text search."
38
41
  )
39
42
  integration: Annotated[str, Integration("elasticsearch")] | None = None
40
43
  api_key: SecretStr | None = None
@@ -13,8 +13,9 @@ _ELEVENLABS_BASE = "https://api.elevenlabs.io/v1"
13
13
  class ElevenLabsTextToSpeech(Tool):
14
14
  name: str = "elevenlabs_text_to_speech"
15
15
  description: str | None = (
16
- "Convert text to speech using a specified ElevenLabs voice. "
17
- "Returns the audio encoded as a base64 string."
16
+ "Generate a voiceover or narration from text: text-to-speech (TTS) that "
17
+ "produces natural spoken audio in a chosen ElevenLabs voice. Returns the "
18
+ "audio as a base64 string."
18
19
  )
19
20
  integration: Annotated[str, Integration("elevenlabs")] | None = None
20
21
  api_key: SecretStr | None = None
@@ -37,12 +37,15 @@ def _queue_model_path_from_submit_response(data: dict[str, Any]) -> str | None:
37
37
  class FalQueueSubmit(Tool):
38
38
  name: str = "fal_queue_submit"
39
39
  description: str | None = (
40
- "Submit an async inference job to fal's queue for a model (e.g. fal-ai/flux/schnell). "
41
- "Returns request_id, status_url, response_url, cancel_url, and queue_model_path (canonical path for "
42
- "follow-up calls). Important: fal may rewrite the route—e.g. you submit fal-ai/flux/schnell but URLs use "
43
- "fal-ai/flux. For fal_queue_status, fal_queue_result, and fal_queue_cancel always pass model_id="
44
- "queue_model_path from this response (or the path segment from status_url before /requests/), not the "
45
- "original submit string, or status polling can return HTTP 405."
40
+ "Run an AI model on fal (e.g. FLUX) to generate images, video, or audio — "
41
+ "submits an async inference/generation job to fal's queue. General-purpose "
42
+ "model runner. Returns request_id, status_url, response_url, cancel_url, and "
43
+ "queue_model_path (canonical path for follow-up calls). Important: fal may "
44
+ "rewrite the route—e.g. you submit fal-ai/flux/schnell but URLs use fal-ai/flux. "
45
+ "For fal_queue_status, fal_queue_result, and fal_queue_cancel always pass "
46
+ "model_id=queue_model_path from this response (or the path segment from "
47
+ "status_url before /requests/), not the original submit string, or status "
48
+ "polling can return HTTP 405."
46
49
  )
47
50
  integration: Annotated[str, Integration("fal")] | None = None
48
51
  api_key: SecretStr | None = None
@@ -148,8 +148,9 @@ class GeminiImagesGenerateImage(Tool):
148
148
  class GeminiImagesAnalyzeImage(Tool):
149
149
  name: str = "gemini_analyze_image"
150
150
  description: str | None = (
151
- "Analyze, describe, or extract information from an image using Gemini vision. "
152
- "Returns a text response based on your question or instruction."
151
+ "Analyze an image with the Gemini vision model: describe a picture, photo, "
152
+ "or screenshot, answer questions about its contents, and OCR — read, "
153
+ "extract, and transcribe text from an image."
153
154
  )
154
155
  integration: Annotated[str, Integration("gemini")] | None = None
155
156
  api_key: SecretStr | None = None
@@ -74,7 +74,10 @@ class GoogleAnalyticsListAccountSummaries(Tool):
74
74
 
75
75
  class GoogleAnalyticsRunReport(Tool):
76
76
  name: str = "google_analytics_run_report"
77
- description: str | None = "Run a standard GA4 report with dimensions, metrics, and date ranges."
77
+ description: str | None = (
78
+ "Run a standard GA4 analytics report — website traffic, visitors, sessions, "
79
+ "pageviews, and conversions — with dimensions, metrics, and date ranges."
80
+ )
78
81
  integration: Annotated[str, Integration("google_analytics")] | None = None
79
82
  token: SecretStr | None = None
80
83
  default_property_id: str | None = None
@@ -131,7 +134,10 @@ class GoogleAnalyticsRunReport(Tool):
131
134
 
132
135
  class GoogleAnalyticsRunRealtimeReport(Tool):
133
136
  name: str = "google_analytics_run_realtime_report"
134
- description: str | None = "Run a GA4 realtime report for active users and events."
137
+ description: str | None = (
138
+ "Run a GA4 realtime report: active users and visitors on your site right "
139
+ "now — live website traffic and events."
140
+ )
135
141
  integration: Annotated[str, Integration("google_analytics")] | None = None
136
142
  token: SecretStr | None = None
137
143
  default_property_id: str | None = None
@@ -505,7 +505,7 @@ class GoogleDocsGetTabContent(Tool):
505
505
  class GoogleDocsGetDocument(Tool):
506
506
  name: str = "google_docs_get_document"
507
507
  description: str | None = (
508
- "Get the contents of the latest version of a Google Doc. "
508
+ "Read and return the text contents of a Google Docs document. "
509
509
  "Set include_tabs_content=true for multi-tab documents."
510
510
  )
511
511
  integration: Annotated[str, Integration("google_docs")] | None = None
@@ -143,7 +143,7 @@ class GoogleDriveGetDownloadLink(Tool):
143
143
 
144
144
  class GoogleDriveGetFile(Tool):
145
145
  name: str = "google_drive_get_file"
146
- description: str | None = "Download and return content of a Google Drive file."
146
+ description: str | None = "Download and return the content of a Google Drive file or document (PDF, Word, etc.) by id."
147
147
  integration: Annotated[str, Integration("google_drive")] | None = None
148
148
  token: SecretStr | None = None
149
149
 
@@ -308,7 +308,7 @@ class GoogleDriveSearchFolders(Tool):
308
308
 
309
309
  class GoogleDriveSearchFiles(Tool):
310
310
  name: str = "google_drive_search_files"
311
- description: str | None = "Search for files in Google Drive with optional query. Supports pagination, filtering by folder, name, and trashed status."
311
+ description: str | None = "Search Google Drive to find a file, document, or folder by name or query. Locate documents in Drive. Supports pagination, filtering by folder, name, and trashed status."
312
312
  integration: Annotated[str, Integration("google_drive")] | None = None
313
313
  token: SecretStr | None = None
314
314
 
@@ -653,7 +653,11 @@ class HiggsfieldSoulTrain(_HiggsfieldTool):
653
653
 
654
654
  class HiggsfieldSoulGenerate(_HiggsfieldTool):
655
655
  name: str = "higgsfield_soul_generate"
656
- description: str | None = "Generate image or video using a trained Soul character."
656
+ description: str | None = (
657
+ "Generate photorealistic images of a person or character with Higgsfield "
658
+ "Soul — UGC-style content, avatars, and character image generation from a "
659
+ "prompt, using a trained Soul character."
660
+ )
657
661
 
658
662
  def __init__(self, **kwargs: Any) -> None:
659
663
  async def _soul_generate(
@@ -495,7 +495,10 @@ class JiraGetIssueTypesForProject(Tool):
495
495
 
496
496
  class JiraCreateIssue(Tool):
497
497
  name: str = "jira_create_issue"
498
- description: str | None = "Create an issue with a Jira fields payload (project, issuetype, summary, etc.)."
498
+ description: str | None = (
499
+ "Create a Jira issue — a bug, task, story, or ticket — from a fields "
500
+ "payload (project, issuetype, summary, etc.)."
501
+ )
499
502
  integration: Annotated[str, Integration("jira")] | None = None
500
503
  token: SecretStr | None = None
501
504
 
@@ -127,7 +127,7 @@ class OneDriveListFiles(Tool):
127
127
 
128
128
  class OneDriveGetFile(Tool):
129
129
  name: str = "onedrive_get_file"
130
- description: str | None = "Get file metadata and content by file ID from OneDrive."
130
+ description: str | None = "Get the content and metadata of a OneDrive file or document by id."
131
131
  integration: Annotated[str, Integration("onedrive")] | None = None
132
132
 
133
133
  def get_config(self) -> dict[str, Any]:
@@ -210,7 +210,7 @@ class OneDriveFindFile(Tool):
210
210
 
211
211
  class OneDriveDownloadFile(Tool):
212
212
  name: str = "onedrive_download_file"
213
- description: str | None = "Download a file from OneDrive by file ID."
213
+ description: str | None = "Download a OneDrive file or document (Word, Excel, PDF, etc.) by id."
214
214
  integration: Annotated[str, Integration("onedrive")] | None = None
215
215
 
216
216
  def get_config(self) -> dict[str, Any]:
@@ -0,0 +1,191 @@
1
+ from typing import Annotated, Any
2
+
3
+ from pydantic import Field, SecretStr
4
+
5
+ from ..core.tool import Tool
6
+ from ..platform.integrations import Integration
7
+ from ._creds import resolve_api_key
8
+
9
+ _BASE_URL = "https://api.parallel.ai"
10
+
11
+
12
+ class ParallelSearch(Tool):
13
+ name: str = "parallel_search"
14
+ description: str | None = (
15
+ "Search the web with Parallel. Takes a natural-language objective and/or keyword "
16
+ "queries and returns ranked results with LLM-optimized excerpts, titles, and URLs."
17
+ )
18
+ integration: Annotated[str, Integration("parallel")] | None = None
19
+ api_key: SecretStr | None = None
20
+
21
+ def get_config(self) -> dict[str, Any]:
22
+ """See base class."""
23
+ return {
24
+ **super().get_config(),
25
+ **self._annotate_config({"integration": self.integration, "api_key": self.api_key}),
26
+ }
27
+
28
+ def __init__(self, **kwargs: Any) -> None:
29
+ async def _parallel_search(
30
+ search_queries: list[str] = Field(..., description="Concise keyword search queries, 3-6 words each"),
31
+ objective: str | None = Field(None, description="Natural-language description of the underlying question or goal"),
32
+ mode: str = Field("advanced", description='Search mode: "turbo", "basic", or "advanced"'),
33
+ max_results: int = Field(10, description="Maximum number of results to return"),
34
+ max_chars_total: int | None = Field(None, description="Upper bound on total excerpt characters across results"),
35
+ include_domains: list[str] | None = Field(None, description="Only include results from these domains"),
36
+ exclude_domains: list[str] | None = Field(None, description="Exclude results from these domains"),
37
+ location: str | None = Field(None, description="ISO 3166-1 alpha-2 country code to bias results (e.g. 'US')"),
38
+ ) -> dict:
39
+ api_key = await resolve_api_key(tool=self, provider_name="Parallel", env_var="PARALLEL_API_KEY")
40
+ import httpx
41
+
42
+ advanced_settings: dict[str, Any] = {"max_results": max_results}
43
+ if location:
44
+ advanced_settings["location"] = location
45
+ source_policy: dict[str, Any] = {}
46
+ if include_domains:
47
+ source_policy["include_domains"] = include_domains
48
+ if exclude_domains:
49
+ source_policy["exclude_domains"] = exclude_domains
50
+ if source_policy:
51
+ advanced_settings["source_policy"] = source_policy
52
+
53
+ payload: dict[str, Any] = {
54
+ "search_queries": search_queries,
55
+ "mode": mode,
56
+ "advanced_settings": advanced_settings,
57
+ }
58
+ if objective:
59
+ payload["objective"] = objective
60
+ if max_chars_total is not None:
61
+ payload["max_chars_total"] = max_chars_total
62
+
63
+ async with httpx.AsyncClient(timeout=httpx.Timeout(30.0, connect=10.0)) as client:
64
+ response = await client.post(
65
+ f"{_BASE_URL}/v1/search",
66
+ headers={"x-api-key": api_key, "Content-Type": "application/json"},
67
+ json=payload,
68
+ timeout=httpx.Timeout(60.0, read=None),
69
+ )
70
+ response.raise_for_status()
71
+ return response.json()
72
+
73
+ super().__init__(handler=_parallel_search, **kwargs)
74
+
75
+
76
+ class ParallelExtract(Tool):
77
+ name: str = "parallel_extract"
78
+ description: str | None = (
79
+ "Extract content from specific web pages with Parallel. Provide up to 20 URLs and get back "
80
+ "LLM-optimized excerpts (and optionally full page content) for each, ranked against an "
81
+ "optional objective and queries."
82
+ )
83
+ integration: Annotated[str, Integration("parallel")] | None = None
84
+ api_key: SecretStr | None = None
85
+
86
+ def get_config(self) -> dict[str, Any]:
87
+ """See base class."""
88
+ return {
89
+ **super().get_config(),
90
+ **self._annotate_config({"integration": self.integration, "api_key": self.api_key}),
91
+ }
92
+
93
+ def __init__(self, **kwargs: Any) -> None:
94
+ async def _parallel_extract(
95
+ urls: list[str] = Field(..., description="URLs to extract content from (up to 20)"),
96
+ objective: str | None = Field(None, description="Natural-language description of what to extract from the pages"),
97
+ search_queries: list[str] | None = Field(None, description="Optional keyword queries to rank excerpts against"),
98
+ max_chars_total: int | None = Field(None, description="Upper bound on total characters across all excerpts"),
99
+ max_chars_per_result: int | None = Field(None, description="Upper bound on excerpt characters per URL"),
100
+ full_content: bool = Field(False, description="Return the full cleaned page content in addition to excerpts"),
101
+ ) -> dict:
102
+ api_key = await resolve_api_key(tool=self, provider_name="Parallel", env_var="PARALLEL_API_KEY")
103
+ import httpx
104
+
105
+ advanced_settings: dict[str, Any] = {}
106
+ if max_chars_per_result is not None:
107
+ advanced_settings["excerpt_settings"] = {"max_chars_per_result": max_chars_per_result}
108
+ if full_content:
109
+ advanced_settings["full_content"] = True
110
+
111
+ payload: dict[str, Any] = {"urls": urls}
112
+ if objective:
113
+ payload["objective"] = objective
114
+ if search_queries:
115
+ payload["search_queries"] = search_queries
116
+ if max_chars_total is not None:
117
+ payload["max_chars_total"] = max_chars_total
118
+ if advanced_settings:
119
+ payload["advanced_settings"] = advanced_settings
120
+
121
+ async with httpx.AsyncClient(timeout=httpx.Timeout(30.0, connect=10.0)) as client:
122
+ response = await client.post(
123
+ f"{_BASE_URL}/v1/extract",
124
+ headers={"x-api-key": api_key, "Content-Type": "application/json"},
125
+ json=payload,
126
+ timeout=httpx.Timeout(120.0, read=None),
127
+ )
128
+ response.raise_for_status()
129
+ return response.json()
130
+
131
+ super().__init__(handler=_parallel_extract, **kwargs)
132
+
133
+
134
+ class ParallelTaskRun(Tool):
135
+ name: str = "parallel_task_run"
136
+ description: str | None = (
137
+ "Run a Parallel research task: give a plain-language objective and Parallel performs web "
138
+ "research and returns an answer with citations. Optionally pass a JSON schema to get "
139
+ "structured output. Blocks until the run completes."
140
+ )
141
+ integration: Annotated[str, Integration("parallel")] | None = None
142
+ api_key: SecretStr | None = None
143
+
144
+ def get_config(self) -> dict[str, Any]:
145
+ """See base class."""
146
+ return {
147
+ **super().get_config(),
148
+ **self._annotate_config({"integration": self.integration, "api_key": self.api_key}),
149
+ }
150
+
151
+ def __init__(self, **kwargs: Any) -> None:
152
+ async def _parallel_task_run(
153
+ input: str | dict = Field(..., description="The research objective as text, or a structured input object"),
154
+ processor: str = Field("base", description='Depth/latency tier: "lite", "base", "core", "pro", or "ultra" (append "-fast" for low-latency variants, e.g. "ultra-fast")'),
155
+ output_schema: dict | None = Field(None, description="Optional JSON schema for structured output. Omit to get a text answer."),
156
+ output_description: str | None = Field(None, description="Natural-language description of the desired output when not using a JSON schema"),
157
+ ) -> dict:
158
+ api_key = await resolve_api_key(tool=self, provider_name="Parallel", env_var="PARALLEL_API_KEY")
159
+ import httpx
160
+
161
+ task_spec: dict[str, Any] = {}
162
+ if output_schema is not None:
163
+ task_spec["output_schema"] = {"type": "json", "json_schema": output_schema}
164
+ elif output_description is not None:
165
+ task_spec["output_schema"] = {"type": "text", "description": output_description}
166
+
167
+ payload: dict[str, Any] = {"input": input, "processor": processor}
168
+ if task_spec:
169
+ payload["task_spec"] = task_spec
170
+
171
+ headers = {"x-api-key": api_key, "Content-Type": "application/json"}
172
+ async with httpx.AsyncClient(timeout=httpx.Timeout(30.0, connect=10.0)) as client:
173
+ create = await client.post(
174
+ f"{_BASE_URL}/v1/tasks/runs",
175
+ headers=headers,
176
+ json=payload,
177
+ timeout=httpx.Timeout(60.0, read=None),
178
+ )
179
+ create.raise_for_status()
180
+ run_id = create.json()["run_id"]
181
+
182
+ # Blocks until the run is completed.
183
+ result = await client.get(
184
+ f"{_BASE_URL}/v1/tasks/runs/{run_id}/result",
185
+ headers=headers,
186
+ timeout=httpx.Timeout(600.0, read=None),
187
+ )
188
+ result.raise_for_status()
189
+ return result.json()
190
+
191
+ super().__init__(handler=_parallel_task_run, **kwargs)
@@ -160,7 +160,10 @@ class PineconeUpsertVectors(Tool):
160
160
 
161
161
  class PineconeQuery(Tool):
162
162
  name: str = "pinecone_query"
163
- description: str | None = "Query a Pinecone index for nearest neighbors."
163
+ description: str | None = (
164
+ "Query a Pinecone vector index for nearest neighbors — semantic similarity "
165
+ "search / vector search over stored embeddings."
166
+ )
164
167
  integration: Annotated[str, Integration("pinecone")] | None = None
165
168
  api_key: SecretStr | None = None
166
169
 
@@ -39,9 +39,11 @@ def _normalize_image(image: str | dict[str, str]) -> dict[str, str]:
39
39
  class QuiverAIGenerateSVG(Tool):
40
40
  name: str = "quiver_ai_generate_svg"
41
41
  description: str | None = (
42
- "Generate one or more SVG graphics from a text prompt using QuiverAI's Arrow models. "
43
- "Supports optional reference images to guide style/composition. "
44
- "Returns raw SVG markup along with request metadata and credit usage."
42
+ "Generate SVG vector graphics logos, icons, and illustrations from a "
43
+ "text prompt with QuiverAI's Arrow models. Produces scalable vector (SVG) "
44
+ "output, not raster images. Supports optional reference images to guide "
45
+ "style/composition. Returns raw SVG markup along with request metadata and "
46
+ "credit usage."
45
47
  )
46
48
  integration: Annotated[str, Integration("quiver_ai")] | None = None
47
49
  api_key: SecretStr | None = None
@@ -22,9 +22,11 @@ def _replicate_headers(api_token: str, extra: dict[str, str] | None = None) -> d
22
22
  class ReplicateCreatePrediction(Tool):
23
23
  name: str = "replicate_create_prediction"
24
24
  description: str | None = (
25
- "Create a Replicate prediction for a model version and inputs. "
26
- "Use official model refs like owner/name or a full version id. "
27
- "Optionally wait up to N seconds for completion via prefer_wait_seconds."
25
+ "Run any AI model on Replicate (owner/name model refs) to generate images, "
26
+ "video, audio, or text from inputs. General-purpose model runner / inference "
27
+ "call for open-source and community models. Use official model refs like "
28
+ "owner/name or a full version id. Optionally wait up to N seconds for "
29
+ "completion via prefer_wait_seconds."
28
30
  )
29
31
  integration: Annotated[str, Integration("replicate")] | None = None
30
32
  api_token: SecretStr | None = None
@@ -252,8 +252,8 @@ class SharePointGetFile(Tool):
252
252
  class SharePointDownloadFile(Tool):
253
253
  name: str = "sharepoint_download_file"
254
254
  description: str | None = (
255
- "Download a file from a SharePoint site by file ID. Returns base64-encoded content "
256
- "(safe for Word, Excel, PDF and other binaries)."
255
+ "Download a SharePoint file or document (Word, Excel, PDF, etc.) by file id. "
256
+ "Returns base64-encoded content (safe for Word, Excel, PDF and other binaries)."
257
257
  )
258
258
  integration: Annotated[str, Integration("sharepoint")] | None = None
259
259
 
@@ -147,7 +147,10 @@ class SearchCustomer(Tool):
147
147
 
148
148
  class CreatePayment(Tool):
149
149
  name: str = "stripe_create_payment"
150
- description: str | None = "Create a Stripe PaymentIntent to collect a payment."
150
+ description: str | None = (
151
+ "Charge a customer / collect a payment — create a Stripe PaymentIntent to "
152
+ "bill, take, or process a payment."
153
+ )
151
154
  integration: Annotated[str, Integration("stripe")] | None = None
152
155
  api_key: SecretStr | None = None
153
156