timbal 2.2.8__tar.gz → 2.2.10__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 (234) hide show
  1. {timbal-2.2.8 → timbal-2.2.10}/PKG-INFO +1 -1
  2. timbal-2.2.10/docs/api-reference/ace/jobs/create.mdx +4 -0
  3. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/_version.py +2 -2
  4. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/__main__.py +100 -1
  5. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/model_discovery.py +12 -11
  6. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/tool_discovery.py +1 -1
  7. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/openai.py +86 -11
  8. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/llm_router.py +27 -3
  9. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/models.py +22 -10
  10. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/runnable.py +15 -5
  11. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/cli.py +69 -30
  12. timbal-2.2.10/python/timbal/evals/reporters.py +80 -0
  13. timbal-2.2.10/python/timbal/evals/runner.py +304 -0
  14. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/utils.py +124 -14
  15. timbal-2.2.10/python/timbal/models.yaml +1094 -0
  16. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/server/voice.html +96 -5
  17. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/server/voice.py +29 -2
  18. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/__init__.py +32 -0
  19. timbal-2.2.10/python/timbal/tools/aircall.py +512 -0
  20. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/base.py +5 -2
  21. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/thinking.py +7 -6
  22. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/message.py +25 -5
  23. timbal-2.2.10/python/timbal/voice/__init__.py +87 -0
  24. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/voice/elevenlabs.py +10 -0
  25. timbal-2.2.10/python/timbal/voice/eou.py +130 -0
  26. timbal-2.2.10/python/timbal/voice/metrics.py +48 -0
  27. timbal-2.2.10/python/timbal/voice/playback.py +149 -0
  28. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/voice/session.py +580 -237
  29. timbal-2.2.10/python/timbal/voice/turn_detection.py +658 -0
  30. timbal-2.2.8/docs/api-reference/ace/messages.mdx +0 -4
  31. timbal-2.2.8/docs/api-reference/ace/responses.mdx +0 -4
  32. timbal-2.2.8/python/timbal/evals/runner.py +0 -185
  33. timbal-2.2.8/python/timbal/models.yaml +0 -1058
  34. timbal-2.2.8/python/timbal/voice/__init__.py +0 -41
  35. {timbal-2.2.8 → timbal-2.2.10}/.gitignore +0 -0
  36. {timbal-2.2.8 → timbal-2.2.10}/LICENSE +0 -0
  37. {timbal-2.2.8 → timbal-2.2.10}/README.md +0 -0
  38. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/chat-completions.mdx +0 -0
  39. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/create-from-workforce.mdx +0 -0
  40. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/jobs/cancel.mdx +0 -0
  41. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/jobs/get.mdx +0 -0
  42. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/jobs/list.mdx +0 -0
  43. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/jobs/retry.mdx +0 -0
  44. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/link.mdx +0 -0
  45. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/list-context-vars.mdx +0 -0
  46. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/list-policies.mdx +0 -0
  47. {timbal-2.2.8 → timbal-2.2.10}/docs/api-reference/ace/unlink.mdx +0 -0
  48. {timbal-2.2.8 → timbal-2.2.10}/pyproject.toml +0 -0
  49. {timbal-2.2.8 → timbal-2.2.10}/pyrightconfig.json +0 -0
  50. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/__init__.py +0 -0
  51. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/__init__.py +0 -0
  52. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/cli_utils.py +0 -0
  53. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/cst_utils.py +0 -0
  54. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/flow.py +0 -0
  55. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/format.py +0 -0
  56. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/test.py +0 -0
  57. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/__init__.py +0 -0
  58. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/add_edge.py +0 -0
  59. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/add_step.py +0 -0
  60. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/add_tool.py +0 -0
  61. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/remove_edge.py +0 -0
  62. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/remove_step.py +0 -0
  63. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/remove_tool.py +0 -0
  64. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/set_config.py +0 -0
  65. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/set_param.py +0 -0
  66. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/codegen/transformers/set_position.py +0 -0
  67. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/__init__.py +0 -0
  68. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/base.py +0 -0
  69. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/__init__.py +0 -0
  70. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/anthropic.py +0 -0
  71. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/default.py +0 -0
  72. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/message.py +0 -0
  73. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/string.py +0 -0
  74. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/impl/timbal.py +0 -0
  75. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/collectors/registry.py +0 -0
  76. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/__init__.py +0 -0
  77. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/agent.py +0 -0
  78. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/fallback_model.py +0 -0
  79. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/mcp.py +0 -0
  80. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/memory_compaction.py +0 -0
  81. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/skill.py +0 -0
  82. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/test_model.py +0 -0
  83. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/tool.py +0 -0
  84. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/tool_set.py +0 -0
  85. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/core/workflow.py +0 -0
  86. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/errors.py +0 -0
  87. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/__init__.py +0 -0
  88. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/display.py +0 -0
  89. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/agent.py +0 -0
  90. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/eval_examples.yaml +0 -0
  91. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/eval_flow_validators.yaml +0 -0
  92. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/eval_negations.yaml +0 -0
  93. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/eval_parallel.yaml +0 -0
  94. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/eval_subagent.yaml +0 -0
  95. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/eval_transforms.yaml +0 -0
  96. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/fixtures/test_invalid_validator.yaml +0 -0
  97. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/models.py +0 -0
  98. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/__init__.py +0 -0
  99. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/base.py +0 -0
  100. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/comparison_base.py +0 -0
  101. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/contains.py +0 -0
  102. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/contains_all.py +0 -0
  103. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/contains_any.py +0 -0
  104. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/context.py +0 -0
  105. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/email.py +0 -0
  106. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/ends_with.py +0 -0
  107. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/eq.py +0 -0
  108. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/gt.py +0 -0
  109. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/gte.py +0 -0
  110. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/json.py +0 -0
  111. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/language.py +0 -0
  112. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/length.py +0 -0
  113. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/llm_base.py +0 -0
  114. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/lt.py +0 -0
  115. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/lte.py +0 -0
  116. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/max_length.py +0 -0
  117. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/min_length.py +0 -0
  118. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/not_null.py +0 -0
  119. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/parallel.py +0 -0
  120. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/pattern.py +0 -0
  121. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/prompt.py +0 -0
  122. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/semantic.py +0 -0
  123. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/seq.py +0 -0
  124. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/starts_with.py +0 -0
  125. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/evals/validators/type.py +0 -0
  126. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/logs.py +0 -0
  127. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/__init__.py +0 -0
  128. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/integrations.py +0 -0
  129. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/knowledge_bases/__init__.py +0 -0
  130. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/knowledge_bases/query.py +0 -0
  131. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/tool_proxy.py +0 -0
  132. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/types.py +0 -0
  133. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/platform/utils.py +0 -0
  134. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/server/__init__.py +0 -0
  135. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/server/__main__.py +0 -0
  136. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/server/http.py +0 -0
  137. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/server/jobs.py +0 -0
  138. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/__init__.py +0 -0
  139. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/config.py +0 -0
  140. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/config_loader.py +0 -0
  141. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/context.py +0 -0
  142. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/dependency_analyzer.py +0 -0
  143. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/__init__.py +0 -0
  144. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/exporters/__init__.py +0 -0
  145. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/exporters/otel.py +0 -0
  146. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/providers/__init__.py +0 -0
  147. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/providers/base.py +0 -0
  148. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/providers/in_memory.py +0 -0
  149. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/providers/jsonl.py +0 -0
  150. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/providers/platform.py +0 -0
  151. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/providers/sqlite.py +0 -0
  152. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/span.py +0 -0
  153. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/state/tracing/trace.py +0 -0
  154. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/_creds.py +0 -0
  155. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/asana.py +0 -0
  156. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/bash.py +0 -0
  157. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/cala.py +0 -0
  158. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/cloudflare.py +0 -0
  159. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/dynamics_business_central.py +0 -0
  160. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/dynamics_sales.py +0 -0
  161. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/edit.py +0 -0
  162. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/elasticsearch.py +0 -0
  163. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/elevenlabs.py +0 -0
  164. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/excel.py +0 -0
  165. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/fal.py +0 -0
  166. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/fathom.py +0 -0
  167. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/firecrawl.py +0 -0
  168. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/gemini_images.py +0 -0
  169. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/gmail.py +0 -0
  170. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_analytics.py +0 -0
  171. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_business.py +0 -0
  172. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_calendar.py +0 -0
  173. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_docs.py +0 -0
  174. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_drive.py +0 -0
  175. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_maps.py +0 -0
  176. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_merchant_center.py +0 -0
  177. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_search_console.py +0 -0
  178. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/google_sheets.py +0 -0
  179. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/happy_scribe.py +0 -0
  180. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/higgsfield.py +0 -0
  181. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/hubspot.py +0 -0
  182. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/interaction.py +0 -0
  183. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/jira.py +0 -0
  184. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/klaviyo.py +0 -0
  185. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/knowledge_base.py +0 -0
  186. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/krea.py +0 -0
  187. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/lancedb.py +0 -0
  188. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/linkedin.py +0 -0
  189. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/mongodb.py +0 -0
  190. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/netsuite.py +0 -0
  191. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/onedrive.py +0 -0
  192. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/outlook.py +0 -0
  193. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/parallel.py +0 -0
  194. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/pinecone.py +0 -0
  195. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/powerbi.py +0 -0
  196. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/quiver_ai.py +0 -0
  197. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/read.py +0 -0
  198. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/replicate.py +0 -0
  199. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/resend.py +0 -0
  200. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/salesforce.py +0 -0
  201. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/scraperapi.py +0 -0
  202. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/sharepoint.py +0 -0
  203. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/shopify.py +0 -0
  204. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/slack.py +0 -0
  205. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/stripe.py +0 -0
  206. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/tavily.py +0 -0
  207. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/web_search.py +0 -0
  208. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/write.py +0 -0
  209. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/xai.py +0 -0
  210. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/zendesk.py +0 -0
  211. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/tools/zoho_crm.py +0 -0
  212. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/__init__.py +0 -0
  213. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/approval.py +0 -0
  214. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/__init__.py +0 -0
  215. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/custom.py +0 -0
  216. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/file.py +0 -0
  217. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/text.py +0 -0
  218. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/tool_result.py +0 -0
  219. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/content/tool_use.py +0 -0
  220. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/__init__.py +0 -0
  221. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/approval.py +0 -0
  222. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/base.py +0 -0
  223. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/delta.py +0 -0
  224. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/interaction.py +0 -0
  225. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/output.py +0 -0
  226. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/events/start.py +0 -0
  227. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/file.py +0 -0
  228. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/types/run_status.py +0 -0
  229. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/utils/__init__.py +0 -0
  230. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/utils/import_spec.py +0 -0
  231. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/utils/model.py +0 -0
  232. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/utils/net.py +0 -0
  233. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/utils/schema.py +0 -0
  234. {timbal-2.2.8 → timbal-2.2.10}/python/timbal/utils/serialization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: timbal
3
- Version: 2.2.8
3
+ Version: 2.2.10
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>
@@ -0,0 +1,4 @@
1
+ ---
2
+ title: "Enqueue Job"
3
+ openapi: "POST /ace/{ace_uid}/jobs"
4
+ ---
@@ -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.8'
22
- __version_tuple__ = version_tuple = (2, 2, 8)
21
+ __version__ = version = '2.2.10'
22
+ __version_tuple__ = version_tuple = (2, 2, 10)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -83,10 +83,42 @@ def main() -> None:
83
83
  "--stream", "-s", action="store_true", help="Print every event instead of only the final output event."
84
84
  )
85
85
 
86
+ # Evals operation
87
+ evals_parser = subparsers.add_parser(
88
+ "evals",
89
+ help="Run evals against the workspace entry point.",
90
+ )
91
+ evals_parser.add_argument(
92
+ "evals_path",
93
+ nargs="?",
94
+ default=None,
95
+ help="Path to an eval file or directory (defaults to the workspace path). "
96
+ "Use ::eval_name to run a single eval.",
97
+ )
98
+ evals_parser.add_argument(
99
+ "--tags",
100
+ default=None,
101
+ help="Comma-separated tag filter; evals matching ANY tag will run.",
102
+ )
103
+ evals_parser.add_argument(
104
+ "--jobs",
105
+ "-j",
106
+ type=int,
107
+ default=1,
108
+ help="Run up to N evals concurrently (default 1). Results are emitted as they complete.",
109
+ )
110
+ evals_parser.add_argument(
111
+ "--format",
112
+ choices=["json", "pretty"],
113
+ default="json",
114
+ help="Output format: 'json' (default) streams one JSON event per line to stdout as each eval "
115
+ "completes; 'pretty' is the rich terminal report.",
116
+ )
117
+
86
118
  # Defer transformer module loading (pulls in libcst + timbal.codegen which
87
119
  # are expensive) — only needed for transformer operations, not for
88
120
  # list-tools, get-flow, or test.
89
- _lightweight_ops = {"get-models", "get-tools", "get-flow", "test"}
121
+ _lightweight_ops = {"get-models", "get-tools", "get-flow", "test", "evals"}
90
122
  if not (_lightweight_ops & set(sys.argv[1:])):
91
123
  from timbal.codegen.transformers import load_modules
92
124
 
@@ -230,6 +262,73 @@ def main() -> None:
230
262
  asyncio.run(run_test(import_spec, params, run_context=run_context, stream=args.stream))
231
263
  return
232
264
 
265
+ if operation == "evals":
266
+ import asyncio
267
+ import os
268
+
269
+ os.environ.setdefault("TIMBAL_SUPPRESS_EVENTS", "tracing_setup")
270
+ os.environ["TIMBAL_LOG_LEVEL"] = "CRITICAL"
271
+
272
+ from ..logs import setup_logging
273
+
274
+ setup_logging()
275
+
276
+ from dotenv import load_dotenv
277
+
278
+ load_dotenv(override=True)
279
+
280
+ from . import parse_fqn
281
+
282
+ # The workspace entry point is the default runnable for all evals;
283
+ # individual evals can still override it with their own 'runnable' key.
284
+ try:
285
+ runnable = parse_fqn(workspace_path).load()
286
+ except (FileNotFoundError, ValueError) as e:
287
+ print(f"error: {e}", file=sys.stderr)
288
+ sys.exit(1)
289
+ except Exception as e:
290
+ print(f"error: failed to load entry point: {e}", file=sys.stderr)
291
+ sys.exit(1)
292
+
293
+ from timbal.evals.utils import collect_evals
294
+
295
+ evals_path_arg = args.evals_path if args.evals_path is not None else str(workspace_path)
296
+ eval_name = None
297
+ if "::" in evals_path_arg:
298
+ evals_path_arg, eval_name = evals_path_arg.rsplit("::", 1)
299
+ tags = {t.strip() for t in args.tags.split(",")} if args.tags else None
300
+
301
+ # Resolve relative eval paths against the workspace (--path), not the
302
+ # process cwd, so running codegen from outside the workspace works.
303
+ evals_path = Path(evals_path_arg)
304
+ if not evals_path.is_absolute():
305
+ evals_path = workspace_path / evals_path
306
+
307
+ try:
308
+ evals = collect_evals(evals_path, runnable, eval_name, tags)
309
+ except ValueError as e:
310
+ print(f"error: {e}", file=sys.stderr)
311
+ sys.exit(1)
312
+
313
+ if not evals:
314
+ # Warn but keep going, matching the evals CLI: an empty run must
315
+ # still emit the start/summary events consumers expect.
316
+ print(f"warning: no evals found in {evals_path}", file=sys.stderr)
317
+
318
+ from timbal.evals.runner import run_evals
319
+
320
+ if args.format == "pretty":
321
+ from timbal.evals.reporters import PrettyReporter
322
+
323
+ reporter = PrettyReporter()
324
+ else:
325
+ from timbal.evals.reporters import JsonReporter
326
+
327
+ reporter = JsonReporter()
328
+
329
+ summary = asyncio.run(run_evals(evals, reporter=reporter, max_concurrency=args.jobs))
330
+ sys.exit(0 if summary.failed == 0 else 1)
331
+
233
332
  # Transformer operations — heavy imports already loaded above.
234
333
  from timbal.codegen import parse_fqn
235
334
  from timbal.codegen.transformers import apply_operation
@@ -5,17 +5,18 @@ import yaml
5
5
  _MODELS_YAML = Path(__file__).parent.parent / "models.yaml"
6
6
 
7
7
  _PROVIDER_LOGOS = {
8
- "anthropic": "https://content.timbal.ai/assets/anthropic_favicon.svg",
9
- "openai": "https://content.timbal.ai/assets/openai_favicon.svg",
10
- "google": "https://content.timbal.ai/assets/google_favicon.svg",
11
- "togetherai": "https://content.timbal.ai/assets/togetherai_favicon.svg",
12
- "xai": "https://content.timbal.ai/assets/xai_favicon.svg",
13
- "groq": "https://content.timbal.ai/assets/groq_favicon.svg",
14
- "fireworks": "https://content.timbal.ai/assets/fireworks_favicon.svg",
15
- "xiaomi": "https://content.timbal.ai/assets/xiaomi_favicon.svg",
16
- "byteplus": "https://content.timbal.ai/assets/byteplus_favicon.svg",
17
- "cerebras": "https://content.timbal.ai/assets/cerebras_favicon.svg",
18
- "sambanova": "https://content.timbal.ai/assets/sambanova_favicon.svg",
8
+ "anthropic": "https://timbalusercontent.com/assets/anthropic_favicon.svg",
9
+ "openai": "https://timbalusercontent.com/assets/openai_favicon.svg",
10
+ "google": "https://timbalusercontent.com/assets/google_favicon.svg",
11
+ "togetherai": "https://timbalusercontent.com/assets/togetherai_favicon.svg",
12
+ "xai": "https://timbalusercontent.com/assets/xai_favicon.svg",
13
+ "groq": "https://timbalusercontent.com/assets/groq_favicon.svg",
14
+ "fireworks": "https://timbalusercontent.com/assets/fireworks_favicon.svg",
15
+ "xiaomi": "https://timbalusercontent.com/assets/xiaomi_favicon.svg",
16
+ "byteplus": "https://timbalusercontent.com/assets/byteplus_favicon.svg",
17
+ "cerebras": "https://timbalusercontent.com/assets/cerebras_favicon.svg",
18
+ "sambanova": "https://timbalusercontent.com/assets/sambanova_favicon.svg",
19
+ "moonshot": "https://timbalusercontent.com/assets/moonshot_favicon.svg",
19
20
  }
20
21
 
21
22
 
@@ -132,7 +132,7 @@ def _discover_framework_tools() -> dict[str, FrameworkTool]:
132
132
  for meta in typing.get_args(candidate)[1:]:
133
133
  if isinstance(meta, _Integration):
134
134
  provider = meta.provider
135
- provider_logo = f"https://content.timbal.ai/assets/{provider}_favicon.svg"
135
+ provider_logo = f"https://timbalusercontent.com/assets/{provider}_favicon.svg"
136
136
  break
137
137
  if provider:
138
138
  break
@@ -1,4 +1,5 @@
1
1
  import time
2
+ from collections import deque
2
3
  from typing import Any
3
4
 
4
5
  # `override` was introduced in Python 3.12; use `typing_extensions` for compatibility with older versions
@@ -98,17 +99,37 @@ ResponseEvent = (
98
99
  logger = structlog.get_logger("timbal.collectors.impl.openai")
99
100
 
100
101
 
102
+ def _delta_reasoning_content(delta: Any) -> str | None:
103
+ """Extract chat-completions reasoning text (Fireworks / DeepSeek / etc.).
104
+
105
+ OpenAI's typed ``ChoiceDelta`` does not declare ``reasoning_content``; providers
106
+ put it on the delta as an extra field (kept in ``model_extra``).
107
+ """
108
+ rc = getattr(delta, "reasoning_content", None)
109
+ if isinstance(rc, str) and rc:
110
+ return rc
111
+ extra = getattr(delta, "model_extra", None) or {}
112
+ if isinstance(extra, dict):
113
+ for key in ("reasoning_content", "reasoning"):
114
+ val = extra.get(key)
115
+ if isinstance(val, str) and val:
116
+ return val
117
+ return None
118
+
119
+
101
120
  @register_collector
102
121
  class ChatCompletionCollector(BaseCollector):
103
122
  """Collector for OpenAI chat completions streaming events."""
104
123
 
105
124
  # Content block ID for text content (chat completions only have one text block)
106
125
  TEXT_BLOCK_ID = "text_0"
126
+ THINKING_BLOCK_ID = "thinking_0"
107
127
 
108
128
  def __init__(self, start: float, **kwargs: Any):
109
129
  super().__init__(**kwargs)
110
130
  self._start = start
111
131
  self._content: str = ""
132
+ self._thinking: str = ""
112
133
  # `_current_tool_call` is appended to `_tool_calls` by reference (same dict).
113
134
  # Subsequent mutations of `_current_tool_call` propagate to the entry in
114
135
  # `_tool_calls` without needing to re-append.
@@ -118,15 +139,40 @@ class ChatCompletionCollector(BaseCollector):
118
139
  self._first_token: float | None = None
119
140
  self._output_tokens: int = 0
120
141
  self._text_block_started: bool = False
142
+ self._thinking_block_started: bool = False
121
143
  self._content_blocks: set[str] = set()
122
144
  self._stop_reason: str | None = None
123
145
  self._pending_usage: Any | None = None # Last usage event, written once in result()
146
+ # One source chunk can produce multiple stream items (e.g. reasoning + text).
147
+ # process() returns the first; the rest drain via __anext__ / pop_pending_stream_item().
148
+ self._pending_stream_items: deque[Any] = deque()
124
149
 
125
150
  @classmethod
126
151
  @override
127
152
  def can_handle(cls, event: Any) -> bool:
128
153
  return isinstance(event, ChatCompletionEvent)
129
154
 
155
+ def pop_pending_stream_item(self) -> Any | None:
156
+ """Return the next queued stream item, if any (used after process() on the peek chunk)."""
157
+ if self._pending_stream_items:
158
+ return self._pending_stream_items.popleft()
159
+ return None
160
+
161
+ async def __anext__(self):
162
+ pending = self.pop_pending_stream_item()
163
+ if pending is not None:
164
+ return pending
165
+ return await super().__anext__()
166
+
167
+ def _emit_stream_items(self, items: list[Any]) -> Any:
168
+ """Return the first stream item and queue any remaining for later yields."""
169
+ filtered = [item for item in items if item is not None]
170
+ if not filtered:
171
+ return None
172
+ first, *rest = filtered
173
+ self._pending_stream_items.extend(rest)
174
+ return first
175
+
130
176
  @override
131
177
  def process(self, event: ChatCompletionEvent) -> Any:
132
178
  """Processes OpenAI streaming events."""
@@ -143,15 +189,24 @@ class ChatCompletionCollector(BaseCollector):
143
189
  # 'length' indicates max_tokens was reached
144
190
  if event.choices[0].finish_reason:
145
191
  self._stop_reason = event.choices[0].finish_reason
146
- # Calculate TTFT (Time To First Token) on first token
147
- if self._first_token is None:
192
+ delta = event.choices[0].delta
193
+ has_tool_calls = bool(delta.tool_calls)
194
+ reasoning = _delta_reasoning_content(delta)
195
+ has_text = bool(delta.content)
196
+ # Calculate TTFT on first visible / reasoning / tool token
197
+ if self._first_token is None and (has_tool_calls or reasoning or has_text):
148
198
  self._first_token = time.perf_counter()
149
- # Handle tool calls
150
- if event.choices[0].delta.tool_calls:
151
- return self._handle_tool_calls(event)
152
- # Handle text content
153
- if event.choices[0].delta.content:
154
- return self._handle_text_content(event)
199
+
200
+ # Fireworks / Moonshot / DeepSeek-style: reasoning may co-arrive with
201
+ # visible content or tool_calls on the same chunk. Emit all of them.
202
+ items: list[Any] = []
203
+ if reasoning:
204
+ items.append(self._handle_reasoning_content(reasoning))
205
+ if has_tool_calls:
206
+ items.append(self._handle_tool_calls(event))
207
+ elif has_text:
208
+ items.append(self._handle_text_content(event))
209
+ return self._emit_stream_items(items)
155
210
 
156
211
  @staticmethod
157
212
  def _usage_billing_id(api_model: str) -> str:
@@ -296,6 +351,21 @@ class ChatCompletionCollector(BaseCollector):
296
351
  )
297
352
  return None
298
353
 
354
+ def _handle_reasoning_content(self, reasoning_chunk: str) -> TimbalThinking | TimbalThinkingDelta:
355
+ """Handle ``delta.reasoning_content`` from OpenAI-compatible reasoning models."""
356
+ self._thinking += reasoning_chunk
357
+ if not self._thinking_block_started:
358
+ self._thinking_block_started = True
359
+ self._content_blocks.add(self.THINKING_BLOCK_ID)
360
+ return TimbalThinking(
361
+ id=self.THINKING_BLOCK_ID,
362
+ thinking=reasoning_chunk,
363
+ )
364
+ return TimbalThinkingDelta(
365
+ id=self.THINKING_BLOCK_ID,
366
+ thinking_delta=reasoning_chunk,
367
+ )
368
+
299
369
  def _handle_text_content(self, event: ChatCompletionEvent) -> TimbalText | TimbalTextDelta:
300
370
  """Handle text content from OpenAI events."""
301
371
  text_chunk = event.choices[0].delta.content
@@ -325,12 +395,17 @@ class ChatCompletionCollector(BaseCollector):
325
395
  self._handle_usage(self._pending_usage)
326
396
 
327
397
  span = get_run_context().current_span()
328
- ttft = self._first_token - self._start
398
+ # finish_reason-only chunks never set _first_token keep metrics defined.
399
+ first = self._first_token if self._first_token is not None else time.perf_counter()
400
+ ttft = first - self._start
329
401
  span.metadata["ttft"] = ttft
330
- tps = self._output_tokens / (time.perf_counter() - self._first_token)
402
+ elapsed = time.perf_counter() - first
403
+ tps = self._output_tokens / elapsed if elapsed > 0 else 0.0
331
404
  span.metadata["tps"] = tps
332
405
 
333
- content = []
406
+ content: list[Any] = []
407
+ if self._thinking:
408
+ content.append({"type": "thinking", "thinking": self._thinking})
334
409
  if self._content:
335
410
  content.append(self._content)
336
411
 
@@ -118,6 +118,15 @@ class _ProviderConfig:
118
118
  supports_stream_options: bool = True
119
119
  """Whether the provider supports ``stream_options`` in Chat Completions."""
120
120
 
121
+ supports_platform_proxy: bool = True
122
+ """If False, never fall back to the Timbal platform proxy — require the provider API key."""
123
+
124
+ supports_chat_reasoning_content: bool = False
125
+ """If True, serialize ThinkingContent as top-level ``reasoning_content`` (Moonshot/Fireworks-style).
126
+
127
+ Otherwise thinking is omitted from outbound chat-completions messages (Vercel/LiteLLM default).
128
+ """
129
+
121
130
 
122
131
  _PROVIDERS: dict[str, _ProviderConfig] = {
123
132
  "openai": _ProviderConfig(
@@ -137,6 +146,7 @@ _PROVIDERS: dict[str, _ProviderConfig] = {
137
146
  "togetherai": _ProviderConfig(
138
147
  env_key="TOGETHER_API_KEY",
139
148
  default_base_url="https://api.together.xyz/v1/",
149
+ supports_chat_reasoning_content=True,
140
150
  ),
141
151
  "xai": _ProviderConfig(
142
152
  env_key="XAI_API_KEY",
@@ -150,10 +160,12 @@ _PROVIDERS: dict[str, _ProviderConfig] = {
150
160
  "fireworks": _ProviderConfig(
151
161
  env_key="FIREWORKS_API_KEY",
152
162
  default_base_url="https://api.fireworks.ai/inference/v1",
163
+ supports_chat_reasoning_content=True,
153
164
  ),
154
165
  "byteplus": _ProviderConfig(
155
166
  env_key="BYTEPLUS_API_KEY",
156
167
  default_base_url="https://ark.ap-southeast.bytepluses.com/api/v3",
168
+ supports_chat_reasoning_content=True,
157
169
  ),
158
170
  "xiaomi": _ProviderConfig(
159
171
  env_key="XIAOMI_API_KEY",
@@ -165,6 +177,12 @@ _PROVIDERS: dict[str, _ProviderConfig] = {
165
177
  env_key="CEREBRAS_API_KEY",
166
178
  default_base_url="https://api.cerebras.ai/v1",
167
179
  ),
180
+ "moonshot": _ProviderConfig(
181
+ env_key="MOONSHOT_API_KEY",
182
+ default_base_url="https://api.moonshot.ai/v1",
183
+ supports_platform_proxy=False,
184
+ supports_chat_reasoning_content=True,
185
+ ),
168
186
  "sambanova": _ProviderConfig(
169
187
  env_key="SAMBANOVA_API_KEY",
170
188
  default_base_url="https://api.sambanova.ai/v1",
@@ -188,7 +206,11 @@ def _resolve_client(
188
206
  if not api_key:
189
207
  api_key = os.getenv(config.env_key)
190
208
  if not api_key:
191
- if run_context.platform_config is not None and run_context.platform_config.subject is not None:
209
+ if (
210
+ config.supports_platform_proxy
211
+ and run_context.platform_config is not None
212
+ and run_context.platform_config.subject is not None
213
+ ):
192
214
  api_key = run_context.platform_config.auth.header_value
193
215
  base_url = (
194
216
  f"https://{run_context.platform_config.host}"
@@ -416,8 +438,9 @@ async def _llm_router(
416
438
  # Per-request headers: change every call, so passed via extra_headers on each .create().
417
439
  request_headers: dict[str, str] = {
418
440
  "x-timbal-run-id": run_context.id,
419
- "x-timbal-call-id": call_id,
420
441
  }
442
+ if call_id:
443
+ request_headers["x-timbal-call-id"] = call_id
421
444
  if run_context.platform_config and run_context.platform_config.subject:
422
445
  if run_context.platform_config.subject.app_id:
423
446
  request_headers["x-timbal-app-id"] = run_context.platform_config.subject.app_id
@@ -528,8 +551,9 @@ async def _llm_router(
528
551
  chat_completions_messages = []
529
552
  if system_prompt:
530
553
  chat_completions_messages.append({"role": "system", "content": system_prompt})
554
+ reasoning_as = "reasoning_content" if config.supports_chat_reasoning_content else "omit"
531
555
  for message in messages:
532
- chat_completions_message = message.to_openai_chat_completions_input()
556
+ chat_completions_message = message.to_openai_chat_completions_input(reasoning_as=reasoning_as)
533
557
  chat_completions_messages.append(chat_completions_message)
534
558
 
535
559
  # Some providers have incomplete OpenAI chat completions support.
@@ -54,7 +54,12 @@ Model = Literal[
54
54
  "anthropic/claude-sonnet-4-5",
55
55
  "anthropic/claude-haiku-4-5",
56
56
  "openai/gpt-5.5",
57
+ "openai/gpt-5.5-pro",
58
+ "openai/gpt-5.6-sol",
59
+ "openai/gpt-5.6-terra",
60
+ "openai/gpt-5.6-luna",
57
61
  "openai/gpt-5.4",
62
+ "openai/gpt-5.4-pro",
58
63
  "openai/gpt-5.4-mini",
59
64
  "openai/gpt-5.4-nano",
60
65
  "openai/gpt-5.3-chat-latest",
@@ -88,9 +93,12 @@ Model = Literal[
88
93
  "togetherai/deepseek-ai/DeepSeek-V3.1",
89
94
  "togetherai/deepseek-ai/DeepSeek-V4-Pro",
90
95
  "togetherai/moonshotai/Kimi-K2.6",
96
+ "togetherai/moonshotai/Kimi-K2.7-Code",
91
97
  "togetherai/MiniMaxAI/MiniMax-M2.7",
98
+ "togetherai/MiniMaxAI/MiniMax-M3",
92
99
  "togetherai/zai-org/GLM-5",
93
100
  "togetherai/zai-org/GLM-5.1",
101
+ "togetherai/zai-org/GLM-5.2",
94
102
  "togetherai/zai-org/GLM-4.7",
95
103
  "togetherai/openai/gpt-oss-120b",
96
104
  "togetherai/openai/gpt-oss-20b",
@@ -108,28 +116,27 @@ Model = Literal[
108
116
  "google/gemini-2.5-flash-lite",
109
117
  "google/gemini-2.5-flash-image",
110
118
  "google/gemini-2.5-flash-preview-tts",
111
- "xai/grok-4",
112
- "xai/grok-4-fast-reasoning",
113
- "xai/grok-4-fast-non-reasoning",
114
- "xai/grok-4-1-fast-reasoning",
115
- "xai/grok-4-1-fast-non-reasoning",
116
- "groq/meta-llama/llama-4-scout-17b-16e-instruct",
119
+ "xai/grok-4.5",
120
+ "xai/grok-4.3",
117
121
  "groq/llama-3.3-70b-versatile",
118
122
  "groq/llama-3.1-8b-instant",
119
- "groq/qwen/qwen3-32b",
123
+ "groq/qwen/qwen3.6-27b",
120
124
  "groq/openai/gpt-oss-120b",
121
125
  "groq/openai/gpt-oss-20b",
122
126
  "fireworks/accounts/fireworks/models/deepseek-v4-pro",
127
+ "fireworks/accounts/fireworks/models/deepseek-v4-flash",
123
128
  "fireworks/accounts/fireworks/models/qwen3p6-plus",
129
+ "fireworks/accounts/fireworks/models/qwen3p7-plus",
124
130
  "fireworks/accounts/fireworks/models/kimi-k2p6",
131
+ "fireworks/accounts/fireworks/models/kimi-k2p7-code",
125
132
  "fireworks/accounts/fireworks/models/kimi-k2p5",
126
133
  "fireworks/accounts/fireworks/models/minimax-m2p5",
134
+ "fireworks/accounts/fireworks/models/minimax-m2p7",
135
+ "fireworks/accounts/fireworks/models/minimax-m3",
127
136
  "fireworks/accounts/fireworks/models/gpt-oss-120b",
128
137
  "fireworks/accounts/fireworks/models/gpt-oss-20b",
129
138
  "fireworks/accounts/fireworks/models/glm-5p1",
130
- "xiaomi/mimo-v2-pro",
131
- "xiaomi/mimo-v2-omni",
132
- "xiaomi/mimo-v2-flash",
139
+ "fireworks/accounts/fireworks/models/glm-5p2",
133
140
  "xiaomi/mimo-v2.5",
134
141
  "xiaomi/mimo-v2.5-pro",
135
142
  "byteplus/seed-2-0-lite-260228",
@@ -156,4 +163,9 @@ Model = Literal[
156
163
  "sambanova/MiniMax-M2.7",
157
164
  "sambanova/gemma-3-12b-it",
158
165
  "sambanova/gemma-4-31B-it",
166
+ "moonshot/kimi-k3",
167
+ "moonshot/kimi-k2.7-code",
168
+ "moonshot/kimi-k2.7-code-highspeed",
169
+ "moonshot/kimi-k2.6",
170
+ "moonshot/kimi-k2.5",
159
171
  ]
@@ -1022,12 +1022,22 @@ class Runnable(ABC, BaseModel):
1022
1022
  event_queue.put_nowait(event)
1023
1023
  return event
1024
1024
 
1025
- # We need to manually process the first chunk, since we removed it from the generator
1025
+ # We need to manually process the first chunk, since we removed it from the generator.
1026
+ # Some collectors (e.g. ChatCompletionCollector) can emit multiple stream items
1027
+ # per source chunk — process() returns the first and queues the rest.
1026
1028
  first_event = collector.process(first_chunk)
1027
- if first_event is not None:
1028
- first_event = process_event(first_event)
1029
- if first_event is not None:
1030
- yield (first_event, None, collector)
1029
+ pending_events = [first_event] if first_event is not None else []
1030
+ pop_pending = getattr(collector, "pop_pending_stream_item", None)
1031
+ if callable(pop_pending):
1032
+ while True:
1033
+ pending_item = pop_pending()
1034
+ if pending_item is None:
1035
+ break
1036
+ pending_events.append(pending_item)
1037
+ for raw_event in pending_events:
1038
+ event = process_event(raw_event)
1039
+ if event is not None:
1040
+ yield (event, None, collector)
1031
1041
  # Process remaining events
1032
1042
  async for event in collector:
1033
1043
  event = process_event(event)