agentbyte 0.30.0__tar.gz → 0.31.0__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 (332) hide show
  1. {agentbyte-0.30.0 → agentbyte-0.31.0}/CHANGELOG.md +53 -0
  2. {agentbyte-0.30.0 → agentbyte-0.31.0}/PKG-INFO +4 -2
  3. {agentbyte-0.30.0 → agentbyte-0.31.0}/README.md +1 -1
  4. {agentbyte-0.30.0 → agentbyte-0.31.0}/pyproject.toml +3 -0
  5. agentbyte-0.31.0/src/agentbyte/__about__.py +2 -0
  6. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/agents/agent.py +143 -47
  7. agentbyte-0.31.0/src/agentbyte/compaction/__init__.py +41 -0
  8. agentbyte-0.31.0/src/agentbyte/compaction/counters.py +192 -0
  9. agentbyte-0.31.0/src/agentbyte/compaction/errors.py +44 -0
  10. agentbyte-0.31.0/src/agentbyte/compaction/grouping.py +112 -0
  11. agentbyte-0.31.0/src/agentbyte/compaction/strategies.py +188 -0
  12. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/sqlite.py +7 -2
  13. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/sqlite_db.py +7 -2
  14. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/llm.py +13 -8
  15. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/pairwise.py +11 -3
  16. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/trajectory.py +11 -7
  17. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/validation.py +29 -0
  18. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/pairwise.py +7 -2
  19. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/runner.py +15 -1
  20. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/model.py +3 -0
  21. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/types.py +10 -0
  22. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/messages.py +8 -1
  23. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/middleware/__init__.py +2 -0
  24. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/middleware/base.py +226 -68
  25. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/core_tools.py +1 -1
  26. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/types.py +35 -3
  27. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/__init__.py +8 -0
  28. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/core/checkpoint.py +88 -15
  29. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/core/models.py +36 -5
  30. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/core/runner.py +176 -94
  31. agentbyte-0.31.0/src/agentbyte/workflow/errors.py +13 -0
  32. agentbyte-0.31.0/src/agentbyte/workflow/sqlite_checkpoint.py +166 -0
  33. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/step.py +31 -2
  34. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/subworkflow.py +62 -53
  35. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_as_tool.py +48 -0
  36. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_tool_approval.py +7 -0
  37. agentbyte-0.31.0/tests/agents/test_tool_batch_resolution.py +274 -0
  38. agentbyte-0.31.0/tests/agents/test_tool_metadata.py +121 -0
  39. agentbyte-0.31.0/tests/compaction/test_compaction.py +360 -0
  40. agentbyte-0.31.0/tests/eval/test_score_integrity.py +409 -0
  41. agentbyte-0.31.0/tests/examples/test_compaction_webapp.py +109 -0
  42. agentbyte-0.31.0/tests/middleware/test_context_compaction.py +607 -0
  43. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/middleware/test_middleware_chain.py +0 -103
  44. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/tools/test_tools.py +11 -0
  45. agentbyte-0.31.0/tests/workflow/recovery_worker.py +47 -0
  46. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_subworkflow_step.py +6 -7
  47. agentbyte-0.31.0/tests/workflow/test_workflow_reliability.py +561 -0
  48. agentbyte-0.30.0/src/agentbyte/__about__.py +0 -2
  49. agentbyte-0.30.0/tests/eval/test_score_integrity.py +0 -194
  50. {agentbyte-0.30.0 → agentbyte-0.31.0}/.gitignore +0 -0
  51. {agentbyte-0.30.0 → agentbyte-0.31.0}/LICENSE +0 -0
  52. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/__init__.py +0 -0
  53. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/agents/__init__.py +0 -0
  54. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/agents/agent_as_tool.py +0 -0
  55. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/agents/base.py +0 -0
  56. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/agents/embedding_agent.py +0 -0
  57. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/agents/types.py +0 -0
  58. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/cancellation_token.py +0 -0
  59. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/catalog.py +0 -0
  60. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/cli/__init__.py +0 -0
  61. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/cli/main.py +0 -0
  62. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/component.py +0 -0
  63. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/context.py +0 -0
  64. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/context_providers/__init__.py +0 -0
  65. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/context_providers/base.py +0 -0
  66. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/context_providers/skill_tools.py +0 -0
  67. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/context_providers/skills.py +0 -0
  68. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/__init__.py +0 -0
  69. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/base.py +0 -0
  70. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/config.py +0 -0
  71. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/importer.py +0 -0
  72. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/json.py +0 -0
  73. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/loader.py +0 -0
  74. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/publish.py +0 -0
  75. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/publish_config.py +0 -0
  76. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/publishers.py +0 -0
  77. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/sources.py +0 -0
  78. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/write_config.py +0 -0
  79. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/dataset/writers.py +0 -0
  80. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/entity.py +0 -0
  81. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/__init__.py +0 -0
  82. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/base.py +0 -0
  83. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/__init__.py +0 -0
  84. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/decorator.py +0 -0
  85. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/keyword.py +0 -0
  86. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/local.py +0 -0
  87. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/process.py +0 -0
  88. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/tool.py +0 -0
  89. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/checks/types.py +0 -0
  90. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/comparison.py +0 -0
  91. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/eval_dataset.py +0 -0
  92. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/__init__.py +0 -0
  93. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/base.py +0 -0
  94. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/composite.py +0 -0
  95. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/judges/reference.py +0 -0
  96. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/report.py +0 -0
  97. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/splitting.py +0 -0
  98. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/__init__.py +0 -0
  99. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/agent.py +0 -0
  100. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/multi_turn.py +0 -0
  101. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/orchestrator.py +0 -0
  102. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/runtime.py +0 -0
  103. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/eval/targets/workflow.py +0 -0
  104. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/execution_trace/__init__.py +0 -0
  105. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/execution_trace/collector.py +0 -0
  106. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/execution_trace/models.py +0 -0
  107. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/__init__.py +0 -0
  108. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/_retry_observability.py +0 -0
  109. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/auth.py +0 -0
  110. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure/__init__.py +0 -0
  111. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure/auth.py +0 -0
  112. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure/chat.py +0 -0
  113. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure/embedding.py +0 -0
  114. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure/settings.py +0 -0
  115. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure_openai.py +0 -0
  116. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/azure_openai_embedding.py +0 -0
  117. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/base.py +0 -0
  118. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/embeddings_base.py +0 -0
  119. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/openai/__init__.py +0 -0
  120. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/openai/chat.py +0 -0
  121. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/openai/embedding.py +0 -0
  122. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/openai/settings.py +0 -0
  123. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/openai_embedding.py +0 -0
  124. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/pricing.py +0 -0
  125. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/retry_policy.py +0 -0
  126. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/settings.py +0 -0
  127. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/llm/types.py +0 -0
  128. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/logger.py +0 -0
  129. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/memory/__init__.py +0 -0
  130. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/memory/base.py +0 -0
  131. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/middleware/otel.py +0 -0
  132. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/middleware/retry.py +0 -0
  133. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/middleware/sql_usage.py +0 -0
  134. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/middleware/usage_logger.py +0 -0
  135. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/notebook.py +0 -0
  136. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/__init__.py +0 -0
  137. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/base.py +0 -0
  138. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/config.py +0 -0
  139. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/gepa.py +0 -0
  140. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/mipro.py +0 -0
  141. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/pareto.py +0 -0
  142. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/reflective.py +0 -0
  143. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/spec.py +0 -0
  144. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/optim/trace.py +0 -0
  145. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/__init__.py +0 -0
  146. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/ai.py +0 -0
  147. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/base.py +0 -0
  148. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/handoff.py +0 -0
  149. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/plan.py +0 -0
  150. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/policies.py +0 -0
  151. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/orchestration/round_robin.py +0 -0
  152. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/__init__.py +0 -0
  153. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/agents.py +0 -0
  154. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/clients.py +0 -0
  155. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/instruction_registry.py +0 -0
  156. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/instructions/orchestrator.yaml +0 -0
  157. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/instructions/query_rewriter.yaml +0 -0
  158. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/instructions/researcher.yaml +0 -0
  159. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/instructions/reviewer.yaml +0 -0
  160. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/instructions/writer.yaml +0 -0
  161. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/orchestration.py +0 -0
  162. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/skills/contracts-analyst/SKILL.md +0 -0
  163. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/skills/hr-analyst/SKILL.md +0 -0
  164. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/skills/hr-analyst/resources/departments.md +0 -0
  165. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/skills/hr-analyst/resources/employees.md +0 -0
  166. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/skills/hr-analyst/resources/payroll.md +0 -0
  167. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/streaming.py +0 -0
  168. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/presets/workflow.py +0 -0
  169. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/session_store.py +0 -0
  170. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/skills/__init__.py +0 -0
  171. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/skills/base.py +0 -0
  172. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/skills/resources.py +0 -0
  173. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/skills/scripts.py +0 -0
  174. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/skills/sources.py +0 -0
  175. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/skills/validation.py +0 -0
  176. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/__init__.py +0 -0
  177. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/base.py +0 -0
  178. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/cancellation.py +0 -0
  179. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/composite.py +0 -0
  180. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/consecutive_agent.py +0 -0
  181. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/external.py +0 -0
  182. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/function_call.py +0 -0
  183. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/handoff.py +0 -0
  184. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/max_message.py +0 -0
  185. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/predicate.py +0 -0
  186. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/source.py +0 -0
  187. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/text_mention.py +0 -0
  188. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/timeout.py +0 -0
  189. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/termination/token_usage.py +0 -0
  190. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/__init__.py +0 -0
  191. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/base.py +0 -0
  192. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/coding_tools.py +0 -0
  193. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/decorator.py +0 -0
  194. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/memory_tool.py +0 -0
  195. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/tools/research_tools.py +0 -0
  196. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/__init__.py +0 -0
  197. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/discovery.py +0 -0
  198. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/execution.py +0 -0
  199. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/models.py +0 -0
  200. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/registry.py +0 -0
  201. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/server.py +0 -0
  202. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/session_store.py +0 -0
  203. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/sessions.py +0 -0
  204. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/ui/assets/index-BF3DwXaF.js +0 -0
  205. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/ui/assets/index-ar5tOeqt.css +0 -0
  206. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/ui/index.html +0 -0
  207. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/webui/ui/vite.svg +0 -0
  208. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/agent.py +0 -0
  209. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/core/__init__.py +0 -0
  210. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/core/_structure_hash.py +0 -0
  211. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/core/workflow.py +0 -0
  212. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/defaults.py +0 -0
  213. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/loader.py +0 -0
  214. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/schema.py +0 -0
  215. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/schema_utils.py +0 -0
  216. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/__init__.py +0 -0
  217. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/agent.py +0 -0
  218. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/echo.py +0 -0
  219. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/function.py +0 -0
  220. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/http.py +0 -0
  221. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/steps/transform.py +0 -0
  222. {agentbyte-0.30.0 → agentbyte-0.31.0}/src/agentbyte/workflow/visualizer.py +0 -0
  223. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_basic.py +0 -0
  224. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_context_providers.py +0 -0
  225. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_error_response.py +0 -0
  226. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_event_types.py +0 -0
  227. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_memory_integration.py +0 -0
  228. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_middleware_integration.py +0 -0
  229. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_response_accessors.py +0 -0
  230. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_retry_middleware.py +0 -0
  231. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_agent_stream_events.py +0 -0
  232. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/agents/test_embedding_agent.py +0 -0
  233. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/cli/test_registry_check.py +0 -0
  234. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/context_providers/__init__.py +0 -0
  235. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/context_providers/test_skill_tools.py +0 -0
  236. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/context_providers/test_skills_provider.py +0 -0
  237. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/dataset/test_loader.py +0 -0
  238. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/dataset/test_multi_table.py +0 -0
  239. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/dataset/test_publish.py +0 -0
  240. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/dataset/test_sqlite_db.py +0 -0
  241. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_eval_dataset.py +0 -0
  242. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_execution_trajectories.py +0 -0
  243. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_multi_turn.py +0 -0
  244. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_pairwise.py +0 -0
  245. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_phase1_runner_and_targets.py +0 -0
  246. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_phase2_checks_and_reports.py +0 -0
  247. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_splitting.py +0 -0
  248. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/eval/test_types_and_judges.py +0 -0
  249. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_azure_client.py +0 -0
  250. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_azure_embedding_client.py +0 -0
  251. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_llm_types.py +0 -0
  252. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_openai_client.py +0 -0
  253. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_openai_embedding_client.py +0 -0
  254. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_pricing.py +0 -0
  255. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_retry_observability.py +0 -0
  256. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_retry_policy_api.py +0 -0
  257. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/llm/test_retryable_error_substrings.py +0 -0
  258. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/memory/test_memory.py +0 -0
  259. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/middleware/test_deduplicate_tool_result.py +0 -0
  260. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/middleware/test_otel.py +0 -0
  261. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/middleware/test_retry_middleware.py +0 -0
  262. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/middleware/test_sql_usage.py +0 -0
  263. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/middleware/test_usage_logger.py +0 -0
  264. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/__init__.py +0 -0
  265. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_base.py +0 -0
  266. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_base_integration.py +0 -0
  267. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_config.py +0 -0
  268. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_gepa.py +0 -0
  269. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_mipro.py +0 -0
  270. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_pareto.py +0 -0
  271. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_reflective.py +0 -0
  272. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_score_integrity.py +0 -0
  273. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_spec.py +0 -0
  274. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/optim/test_trace.py +0 -0
  275. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/orchestration/test_ai_orchestrator.py +0 -0
  276. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/orchestration/test_base_orchestrator.py +0 -0
  277. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/orchestration/test_handoff_orchestrator.py +0 -0
  278. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/orchestration/test_orchestrator_finalization.py +0 -0
  279. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/orchestration/test_plan_orchestrator.py +0 -0
  280. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/orchestration/test_round_robin.py +0 -0
  281. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/presets/test_agents.py +0 -0
  282. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/presets/test_clients.py +0 -0
  283. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/presets/test_instruction_registry.py +0 -0
  284. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/presets/test_orchestration.py +0 -0
  285. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/presets/test_streaming.py +0 -0
  286. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/presets/test_workflow.py +0 -0
  287. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/skills/__init__.py +0 -0
  288. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/skills/test_base.py +0 -0
  289. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/skills/test_resources.py +0 -0
  290. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/skills/test_scripts.py +0 -0
  291. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/skills/test_sources.py +0 -0
  292. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_base.py +0 -0
  293. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_cancellation.py +0 -0
  294. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_composite.py +0 -0
  295. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_consecutive_agent.py +0 -0
  296. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_external.py +0 -0
  297. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_function_call.py +0 -0
  298. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_handoff.py +0 -0
  299. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_max_message.py +0 -0
  300. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_predicate.py +0 -0
  301. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_source.py +0 -0
  302. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_text_mention.py +0 -0
  303. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_timeout.py +0 -0
  304. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/termination/test_token_usage.py +0 -0
  305. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_cancellation_token.py +0 -0
  306. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_context.py +0 -0
  307. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_logger.py +0 -0
  308. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_messages.py +0 -0
  309. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_package_api.py +0 -0
  310. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_session_store.py +0 -0
  311. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_types.py +0 -0
  312. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/test_vanilla_chunker.py +0 -0
  313. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/tools/test_coding_tools.py +0 -0
  314. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/tools/test_memory_tool.py +0 -0
  315. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/tools/test_research_tools.py +0 -0
  316. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/__init__.py +0 -0
  317. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/helpers.py +0 -0
  318. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/test_execution.py +0 -0
  319. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/test_package_api.py +0 -0
  320. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/test_registry.py +0 -0
  321. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/test_server.py +0 -0
  322. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/test_sessions.py +0 -0
  323. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/webui/test_workflow_streaming.py +0 -0
  324. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_agent_step_imports.py +0 -0
  325. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_checkpoint.py +0 -0
  326. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_agent.py +0 -0
  327. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_class.py +0 -0
  328. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_models.py +0 -0
  329. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_runner.py +0 -0
  330. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_schema.py +0 -0
  331. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_steps.py +0 -0
  332. {agentbyte-0.30.0 → agentbyte-0.31.0}/tests/workflow/test_workflow_visualizer.py +0 -0
@@ -4,6 +4,59 @@ All notable changes to Agentbyte are documented in this file.
4
4
 
5
5
  The format follows Keep a Changelog principles and semantic versioning.
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [0.31.0] - 2026-09-30
10
+
11
+ Upgrade notes (breaking):
12
+
13
+ - `ContextCompactionMiddleware(max_tokens=..., keep_last=...)` → `ContextCompactionMiddleware(max_input_tokens=...)`; read counts from `metadata["compaction"]`.
14
+ - Middleware that rewrites a `model_call` payload must keep its keys: `context.data = {**context.data, "messages": new}`.
15
+ - Multi-call approval batches return every approval request at once; loop over `response.approval_requests`.
16
+ - Workflow steps must write back changed state explicitly (`context.set(...)`); in-place mutation of a value read from `WorkflowContext` no longer persists.
17
+
18
+ ### Added
19
+
20
+ - Workflow reliability (spec 0058): optional native-async `SQLiteCheckpointStore` (new `workflow-sqlite` extra, lazily imported driver) with immutable checkpoint IDs and per-run retention; checkpoint schema v2 with staged continuation and accepted responses (v1 checkpoints still load); `CheckpointStore.load_latest(..., execution_id=...)`; multiple nested input requests per step with batch validation and partial resume (`BaseStep.resume_from_requests`, `validate_resume_responses`); `WorkflowStateIsolationError`, `WorkflowCheckpointError`, `CheckpointConflictError`.
21
+ - Context compaction (spec 0049): new `agentbyte.compaction` package with `HeadTailCompaction`, `SlidingWindowCompaction`, `CharacterTokenCounter`, optional `TiktokenCounter` and `CompactionResult`. Tool-call batches and their results are kept or removed whole; system messages and the current turn are always kept; the budget covers messages, tools and output schema. Mandatory content that cannot fit raises `ContextBudgetExceeded` before any provider call.
22
+ - `BaseMiddleware.finalize_request` and `validate_request` hooks: after every `process_request`, the chain runs all `finalize_request` hooks (may change the payload), then all read-only `validate_request` hooks, immediately before the operation. A validator that changes the payload raises `MiddlewareContractError`.
23
+ - Tool message metadata (spec 0056): `ToolMessage.metadata` keeps an independent copy of the final `ToolResult.metadata` (e.g. `AgentAsTool` child usage) across run, streaming, approval resume and failed results. Sessions and checkpoints preserve it; model-provider requests never include it. Records without the field load as `{}`.
24
+
25
+ ### Changed
26
+
27
+ - **Breaking:** the agent no longer falls back to its own messages, tools or output format when middleware removes those keys from a `model_call` payload; a missing key raises. Set a key to `None` to omit it.
28
+ - Passing a new task while the conversation ends with an unresolved tool-call batch now ends the run with `finish_reason="error"` instead of sending an invalid history; resume with `task=None` first.
29
+ - **Breaking:** `ContextCompactionMiddleware` now takes keyword-only `max_input_tokens` (required), `strategy` and `counter`; `max_tokens` and `keep_last` were removed without aliases, and the fabricated summary placeholder is gone. Malformed, orphan or unresolved tool exchanges now raise `ToolExchangeError`/`PendingToolExchangeError` instead of being sent. Metadata moved to `metadata["compaction"]`, with per-pass counts in `passes`.
30
+ - **Breaking:** `ToolResult.metadata` and `ToolMessage.metadata` accept JSON values only (string-keyed objects, lists, strings, finite numbers, booleans, null). Tuples, enums, non-string keys, `Path`, `datetime`, models, NaN/inf raise `ToolMetadataError` (a `TypeError`); a custom tool producing such metadata now fails its call instead of succeeding.
31
+ - Tool metadata is now present on every `ToolMessage` sent by the WebUI server and stored in sessions, not only in verbose tool-response events.
32
+ - `CalculatorTool` no longer duplicates the computed value in metadata, so non-finite and complex results still succeed.
33
+ - **Breaking:** workflow staged state is isolated: `WorkflowContext` reads return detached copies, so steps must write changes back explicitly; values that cannot be isolated raise `WorkflowStateIsolationError`. Checkpoints are taken at quiescent recovery boundaries, and persistence failures stop the run with `WorkflowCheckpointError` instead of continuing unsaved.
34
+ - `FileCheckpointStore` is deprecated (legacy blocking backend); use `SQLiteCheckpointStore` or an application durable store. Migration guidance is in the workflow skill and checkpoint study topic. FastAPI still persists through its session store.
35
+
36
+ ### Documentation
37
+
38
+ - Context compaction notebook (`notebooks/concepts/agent/10-context-compaction.ipynb`) and FastAPI example (`examples/compaction/`) showing budgets, 413/409 mapping, turn rollback, SSE and a context meter.
39
+ - Proposed spec 0059 (summarizing compaction).
40
+
41
+ ### Fixed
42
+
43
+ - Multi-call tool batches stay complete across approval pauses: calls after the first approval-gated call are no longer skipped (previously the model received tool calls without results after resume). All gated calls in a batch request approval in one round; resume runs the rest in original order; missing decisions keep `approval_needed` without a model call.
44
+ - Cancelling mid-batch records a failed `ToolMessage` (`error="Cancelled"`) for each call that did not run, so the session stays valid for the next task.
45
+
46
+ ## [0.30.1] - 2026-09-30
47
+
48
+ ### Fixed
49
+
50
+ - Judge score integrity follow-ups (spec 0055 v0.3.0): judges re-check cancellation after each model call, and `EvalRunner`/`PairwiseRunner` record a run as `cancelled` without judging it when the token is cancelled or the trajectory has `execution_status="cancelled"`.
51
+ - Raw-JSON judge responses with duplicate keys are rejected (`duplicate_criterion` for `LLMEvalJudge`, `invalid_response` for `PairwiseJudge`) instead of keeping the last value.
52
+ - When a target raises, `EvalRunner` and `PairwiseRunner` store only the exception type in `trajectory.error`, never the exception message.
53
+ - `ModelEvalTarget` records `execution_status` on failed and cancelled trajectories.
54
+
55
+ ### Changed
56
+
57
+ - **Breaking:** `criteria=None` selects judge defaults; an explicit empty list raises `JudgeScoringError("invalid_criteria")`. An empty `default_criteria` (`LLMEvalJudge`, `LLMTrajectoryJudge`) or `criteria` (`PairwiseJudge`) raises `ValueError` at construction.
58
+ - **Breaking:** a scored `EvalScore` carrying legacy `fallback` or `judge_failed` metadata fails validation.
59
+
7
60
  ## [0.30.0] - 2026-09-30
8
61
 
9
62
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentbyte
3
- Version: 0.30.0
3
+ Version: 0.31.0
4
4
  Summary: A toolkit for designing multiagent systems
5
5
  Author-email: MrDataPsycho <mr.data.psycho@gmail.com>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -80,13 +80,15 @@ Requires-Dist: graphviz>=0.21; extra == 'viz'
80
80
  Provides-Extra: webui
81
81
  Requires-Dist: fastapi>=0.135.2; extra == 'webui'
82
82
  Requires-Dist: uvicorn[standard]>=0.44.0; extra == 'webui'
83
+ Provides-Extra: workflow-sqlite
84
+ Requires-Dist: aiosqlite>=0.22.1; extra == 'workflow-sqlite'
83
85
  Description-Content-Type: text/markdown
84
86
 
85
87
  # Agentbyte
86
88
 
87
89
  Agentbyte is an observability-first agentic AI framework for building and studying multiagent systems with a learning-first, implementation-oriented workflow.
88
90
 
89
- Current release: **0.30.0**
91
+ Current release: **0.31.0**
90
92
 
91
93
  ## Building an Agent
92
94
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Agentbyte is an observability-first agentic AI framework for building and studying multiagent systems with a learning-first, implementation-oriented workflow.
4
4
 
5
- Current release: **0.30.0**
5
+ Current release: **0.31.0**
6
6
 
7
7
  ## Building an Agent
8
8
 
@@ -41,6 +41,9 @@ webui = [
41
41
  dataset = [
42
42
  "aiosqlite>=0.22.1",
43
43
  ]
44
+ workflow-sqlite = [
45
+ "aiosqlite>=0.22.1",
46
+ ]
44
47
  sql = [
45
48
  "sqlalchemy>=2.0.43",
46
49
  "sqlmodel>=0.0.38",
@@ -0,0 +1,2 @@
1
+ __version__ = "0.31.0"
2
+ VERSION = __version__
@@ -234,6 +234,7 @@ class Agent(BaseAgent):
234
234
  tool_name=tool_call.tool_name,
235
235
  success=streamed_result.success,
236
236
  error=streamed_result.error,
237
+ metadata=streamed_result.metadata,
237
238
  )
238
239
  working_context.add_message(tool_message)
239
240
  yield tool_message
@@ -317,6 +318,7 @@ class Agent(BaseAgent):
317
318
  tool_name=tool_call.tool_name,
318
319
  success=result.success,
319
320
  error=result.error,
321
+ metadata=result.metadata,
320
322
  )
321
323
  except Exception as exc:
322
324
  message = ToolMessage(
@@ -339,6 +341,102 @@ class Agent(BaseAgent):
339
341
 
340
342
  yield message
341
343
 
344
+ @staticmethod
345
+ def _unresolved_tail_calls(working_context: AgentContext) -> List[ToolCallRequest]:
346
+ """Calls of the newest tool-call batch that have no result yet, in order.
347
+
348
+ Only a batch followed exclusively by tool results counts; results are
349
+ matched to that batch occurrence, not to earlier reuses of a call ID.
350
+ """
351
+ resolved: set[str] = set()
352
+ for message in reversed(working_context.messages):
353
+ if isinstance(message, ToolMessage):
354
+ resolved.add(message.tool_call_id)
355
+ continue
356
+ if isinstance(message, AssistantMessage) and message.tool_calls:
357
+ return [c for c in message.tool_calls if c.call_id not in resolved]
358
+ return []
359
+ return []
360
+
361
+ def _awaits_approval(
362
+ self,
363
+ tool_call: ToolCallRequest,
364
+ working_context: AgentContext,
365
+ extra_tools: Optional[List[BaseTool]],
366
+ ) -> bool:
367
+ """Whether *tool_call* needs an approval decision that does not exist yet."""
368
+ tool = self._find_tool(tool_call.tool_name, extra_tools=extra_tools)
369
+ return (
370
+ tool is not None
371
+ and tool.approval_mode == ApprovalMode.ALWAYS
372
+ and working_context.get_approval_response(tool_call.call_id) is None
373
+ )
374
+
375
+ async def _resolve_tool_batch(
376
+ self,
377
+ working_context: AgentContext,
378
+ cancellation_token: Optional[CancellationToken],
379
+ verbose: bool,
380
+ extra_tools: Optional[List[BaseTool]],
381
+ outcome: dict[str, int | bool],
382
+ ) -> AsyncGenerator[Union[Message, AgentEvent], None]:
383
+ """Execute the newest batch's unresolved calls in their original order.
384
+
385
+ Execution stops before the first call still awaiting approval; approval
386
+ requests for every such call in the batch are registered together, so a
387
+ single decision round resolves the batch. Calls left unexecuted because
388
+ of cancellation receive an explicit failed result, keeping the batch
389
+ complete. ``outcome`` reports ``executed`` and ``paused``.
390
+ """
391
+ pending = self._unresolved_tail_calls(working_context)
392
+ awaiting = [
393
+ c for c in pending if self._awaits_approval(c, working_context, extra_tools)
394
+ ]
395
+ stop_at = awaiting[0].call_id if awaiting else None
396
+
397
+ for tool_call in pending:
398
+ if tool_call.call_id == stop_at:
399
+ break
400
+ outcome["executed"] = int(outcome["executed"]) + 1
401
+ try:
402
+ async for item in self._execute_tool_call(
403
+ tool_call,
404
+ working_context,
405
+ cancellation_token,
406
+ verbose,
407
+ extra_tools=extra_tools,
408
+ ):
409
+ yield item
410
+ if isinstance(item, ToolApprovalEvent):
411
+ outcome["paused"] = True
412
+ return
413
+ except asyncio.CancelledError:
414
+ for skipped in self._unresolved_tail_calls(working_context):
415
+ working_context.add_message(
416
+ ToolMessage(
417
+ content="Tool execution cancelled before it ran",
418
+ source=self.name,
419
+ tool_call_id=skipped.call_id,
420
+ tool_name=skipped.tool_name,
421
+ success=False,
422
+ error="Cancelled",
423
+ )
424
+ )
425
+ raise
426
+
427
+ if not awaiting:
428
+ return
429
+
430
+ outcome["paused"] = True
431
+ for tool_call in awaiting:
432
+ if tool_call.call_id in working_context.pending_tool_calls:
433
+ continue # already requested in an earlier run
434
+ request = working_context.add_approval_request(
435
+ tool_call=tool_call,
436
+ tool_name=tool_call.tool_name,
437
+ )
438
+ yield ToolApprovalEvent(source=self.name, approval_request=request)
439
+
342
440
  @trace_stream("agent")
343
441
  async def run_stream(
344
442
  self,
@@ -428,6 +526,11 @@ class Agent(BaseAgent):
428
526
 
429
527
  try:
430
528
  task_messages = self._convert_task_to_messages(task) if task else []
529
+ if task_messages and self._unresolved_tail_calls(working_context):
530
+ raise AgentExecutionError(
531
+ "The conversation ends with unresolved tool calls; resolve pending "
532
+ "approvals by resuming with task=None before sending a new task."
533
+ )
431
534
  for message in task_messages:
432
535
  working_context.add_message(message)
433
536
 
@@ -453,32 +556,22 @@ class Agent(BaseAgent):
453
556
  if cancellation_token and cancellation_token.is_cancelled():
454
557
  raise asyncio.CancelledError()
455
558
 
456
- approved_calls = working_context.get_approved_tool_calls()
457
- rejected_calls = working_context.get_rejected_tool_calls()
458
-
459
- for approved_call in approved_calls:
460
- tool_calls += 1
461
- async for item in self._execute_tool_call(
462
- approved_call,
559
+ # Resume: finish the newest tool-call batch (approved, denied and
560
+ # not-yet-run siblings, in order) before asking the model again.
561
+ if self._unresolved_tail_calls(working_context):
562
+ outcome: dict[str, int | bool] = {"executed": 0, "paused": False}
563
+ async for item in self._resolve_tool_batch(
463
564
  working_context,
464
565
  cancellation_token,
465
566
  verbose,
466
- skip_approval_check=True,
467
- extra_tools=provider_tools,
567
+ provider_tools,
568
+ outcome,
468
569
  ):
469
570
  yield item
470
-
471
- for rejected_call_id, rejected_call in rejected_calls:
472
- denial_message = ToolMessage(
473
- content="Tool execution denied: User declined approval",
474
- source=self.name,
475
- tool_call_id=rejected_call_id,
476
- tool_name=rejected_call.tool_name,
477
- success=False,
478
- error="Approval denied",
479
- )
480
- working_context.add_message(denial_message)
481
- yield denial_message
571
+ tool_calls += int(outcome["executed"])
572
+ if outcome["paused"]:
573
+ finish_reason = "approval_needed"
574
+ break
482
575
 
483
576
  llm_messages = await self._prepare_llm_messages([], working_context, extra_instructions=extra_instructions)
484
577
  if self.memory is not None:
@@ -560,11 +653,25 @@ class Agent(BaseAgent):
560
653
  break
561
654
 
562
655
  async def _execute_model_call(payload: dict[str, object]):
563
- payload_messages = payload.get("messages", llm_messages)
656
+ # Send exactly the final middleware payload: falling back to
657
+ # the agent's own values would bypass request validation
658
+ # (e.g. compaction budgets) done on that payload.
659
+ missing = [
660
+ key
661
+ for key in ("messages", "tools", "output_format")
662
+ if key not in payload
663
+ ]
664
+ if missing:
665
+ raise ValueError(
666
+ f"model_call payload is missing {missing}; middleware "
667
+ "must preserve payload keys (set a key to None to omit it)"
668
+ )
669
+
670
+ payload_messages = payload["messages"]
564
671
  if not isinstance(payload_messages, list):
565
672
  raise ValueError("model_call payload messages must be a list")
566
673
 
567
- payload_tools = payload.get("tools", tools)
674
+ payload_tools = payload["tools"]
568
675
  if payload_tools is not None and not isinstance(
569
676
  payload_tools, list
570
677
  ):
@@ -572,9 +679,7 @@ class Agent(BaseAgent):
572
679
  "model_call payload tools must be a list or None"
573
680
  )
574
681
 
575
- payload_output_format = payload.get(
576
- "output_format", self.output_format
577
- )
682
+ payload_output_format = payload["output_format"]
578
683
  if payload_output_format is not None and not isinstance(
579
684
  payload_output_format, type
580
685
  ):
@@ -667,27 +772,18 @@ class Agent(BaseAgent):
667
772
  finish_reason = "max_iterations_finalized"
668
773
  break
669
774
 
670
- approval_pause = False
671
- for tool_call in assistant_message.tool_calls:
672
- tool_calls += 1
673
- async for item in self._execute_tool_call(
674
- tool_call,
675
- working_context,
676
- cancellation_token,
677
- verbose,
678
- extra_tools=provider_tools,
679
- ):
680
- yield item
681
-
682
- if isinstance(item, ToolApprovalEvent):
683
- finish_reason = "approval_needed"
684
- approval_pause = True
685
- break
686
-
687
- if approval_pause:
688
- break
689
-
690
- if approval_pause:
775
+ outcome = {"executed": 0, "paused": False}
776
+ async for item in self._resolve_tool_batch(
777
+ working_context,
778
+ cancellation_token,
779
+ verbose,
780
+ provider_tools,
781
+ outcome,
782
+ ):
783
+ yield item
784
+ tool_calls += int(outcome["executed"])
785
+ if outcome["paused"]:
786
+ finish_reason = "approval_needed"
691
787
  break
692
788
 
693
789
  if not self.summarize_tool_result:
@@ -0,0 +1,41 @@
1
+ """Context compaction that keeps outbound conversations provider-valid.
2
+
3
+ Strategies select whole message groups (a tool request and all its results are
4
+ one group) so that a complete model-call request fits an input-token budget,
5
+ or raise :class:`ContextBudgetExceeded` before any provider I/O.
6
+ """
7
+
8
+ from agentbyte.compaction.counters import (
9
+ CharacterTokenCounter,
10
+ TiktokenCounter,
11
+ TokenCounter,
12
+ )
13
+ from agentbyte.compaction.errors import (
14
+ CompactionError,
15
+ ContextBudgetExceeded,
16
+ PendingToolExchangeError,
17
+ ToolExchangeError,
18
+ )
19
+ from agentbyte.compaction.grouping import MessageGroup, group_messages
20
+ from agentbyte.compaction.strategies import (
21
+ CompactionResult,
22
+ CompactionStrategy,
23
+ HeadTailCompaction,
24
+ SlidingWindowCompaction,
25
+ )
26
+
27
+ __all__ = [
28
+ "CharacterTokenCounter",
29
+ "CompactionError",
30
+ "CompactionResult",
31
+ "CompactionStrategy",
32
+ "ContextBudgetExceeded",
33
+ "HeadTailCompaction",
34
+ "MessageGroup",
35
+ "PendingToolExchangeError",
36
+ "SlidingWindowCompaction",
37
+ "TiktokenCounter",
38
+ "TokenCounter",
39
+ "ToolExchangeError",
40
+ "group_messages",
41
+ ]
@@ -0,0 +1,192 @@
1
+ """Token counters used to budget complete model-call requests.
2
+
3
+ Counts are estimates: they approximate, but never promise, provider-exact
4
+ token usage. Text is counted as text (Unicode code points / tokenizer tokens),
5
+ never as escaped JSON sequences, and internal metadata that is not sent to
6
+ providers (for example ``ToolMessage.metadata``) is excluded.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import math
13
+ from abc import ABC, abstractmethod
14
+ from typing import Any
15
+
16
+ from pydantic import BaseModel
17
+
18
+ from agentbyte.messages import (
19
+ AssistantMessage,
20
+ MultiModalMessage,
21
+ SystemMessage,
22
+ ToolMessage,
23
+ UserMessage,
24
+ )
25
+
26
+ _KNOWN_MESSAGE_TYPES = (
27
+ SystemMessage,
28
+ UserMessage,
29
+ AssistantMessage,
30
+ ToolMessage,
31
+ MultiModalMessage,
32
+ )
33
+
34
+
35
+ def _to_json_text(value: Any) -> str:
36
+ """Serialize *value* compactly without escaping non-ASCII characters."""
37
+ return json.dumps(value, ensure_ascii=False, sort_keys=True, default=str)
38
+
39
+
40
+ class TokenCounter(ABC):
41
+ """Counts tokens for every component of a model-call request.
42
+
43
+ Subclasses implement :meth:`count_text`; message, tool and schema counting
44
+ is shared so that every counter budgets the same request components.
45
+ """
46
+
47
+ #: Label recorded in results and metadata (e.g. ``"character_estimate"``).
48
+ kind: str = "custom"
49
+
50
+ def __init__(self, *, message_overhead: int = 4, media_tokens: int = 1_000):
51
+ if message_overhead < 0:
52
+ raise ValueError("message_overhead must be non-negative")
53
+ if media_tokens < 0:
54
+ raise ValueError("media_tokens must be non-negative")
55
+ self.message_overhead = message_overhead
56
+ self.media_tokens = media_tokens
57
+
58
+ @abstractmethod
59
+ def count_text(self, text: str) -> int:
60
+ """Return the token count for plain *text*."""
61
+
62
+ def count_message(self, message: Any) -> int:
63
+ """Count one outbound message, including tool-call arguments and media.
64
+
65
+ Messages of unknown types are never interpreted: their whole serialized
66
+ form is counted, so the budget errs towards overcounting.
67
+ """
68
+ if not isinstance(message, _KNOWN_MESSAGE_TYPES):
69
+ opaque = (
70
+ message.model_dump_json()
71
+ if isinstance(message, BaseModel)
72
+ else str(message)
73
+ )
74
+ return self.message_overhead + self.count_text(opaque)
75
+
76
+ tokens = self.message_overhead
77
+ content = getattr(message, "content", None)
78
+ if isinstance(content, str):
79
+ tokens += self.count_text(content)
80
+ elif content is not None:
81
+ tokens += self.count_text(_to_json_text(content))
82
+
83
+ if isinstance(message, AssistantMessage) and message.tool_calls:
84
+ for call in message.tool_calls:
85
+ tokens += self.count_text(call.tool_name)
86
+ tokens += self.count_text(call.call_id)
87
+ tokens += self.count_text(_to_json_text(call.parameters))
88
+ elif isinstance(message, ToolMessage):
89
+ tokens += self.count_text(message.tool_call_id)
90
+ elif isinstance(message, MultiModalMessage):
91
+ if message.is_text() and message.data is not None:
92
+ data = message.data
93
+ text = data.decode("utf-8", errors="ignore") if isinstance(data, bytes) else data
94
+ if text != message.content:
95
+ tokens += self.count_text(text)
96
+ else:
97
+ # Binary media is budgeted with a declared flat estimate; its
98
+ # base64 payload is not text and is never counted as such.
99
+ tokens += self.media_tokens
100
+ return tokens
101
+
102
+ def count_messages(self, messages: list[Any]) -> int:
103
+ """Count a list of outbound messages."""
104
+ return sum(self.count_message(message) for message in messages)
105
+
106
+ def count_tools(self, tools: list[dict[str, Any]] | None) -> int:
107
+ """Count provider tool definitions."""
108
+ if not tools:
109
+ return 0
110
+ return sum(self.count_text(_to_json_text(tool)) for tool in tools)
111
+
112
+ def count_output_schema(self, output_schema: Any) -> int:
113
+ """Count a structured-output schema (Pydantic model type or JSON schema)."""
114
+ if output_schema is None:
115
+ return 0
116
+ if isinstance(output_schema, type) and issubclass(output_schema, BaseModel):
117
+ return self.count_text(_to_json_text(output_schema.model_json_schema()))
118
+ return self.count_text(_to_json_text(output_schema))
119
+
120
+ def count_request(
121
+ self,
122
+ messages: list[Any],
123
+ *,
124
+ tools: list[dict[str, Any]] | None = None,
125
+ output_schema: Any = None,
126
+ ) -> int:
127
+ """Count the complete request input: messages, tools and output schema."""
128
+ return (
129
+ self.count_messages(messages)
130
+ + self.count_tools(tools)
131
+ + self.count_output_schema(output_schema)
132
+ )
133
+
134
+
135
+ class CharacterTokenCounter(TokenCounter):
136
+ """Dependency-free estimate: Unicode characters divided by ``chars_per_token``.
137
+
138
+ Rounds up per text component, so any nonempty text costs at least one token.
139
+ Accuracy varies by language and tokenizer; treat counts as estimates.
140
+ """
141
+
142
+ kind = "character_estimate"
143
+
144
+ def __init__(
145
+ self,
146
+ *,
147
+ chars_per_token: float = 4.0,
148
+ message_overhead: int = 4,
149
+ media_tokens: int = 1_000,
150
+ ):
151
+ super().__init__(message_overhead=message_overhead, media_tokens=media_tokens)
152
+ if chars_per_token <= 0:
153
+ raise ValueError("chars_per_token must be greater than zero")
154
+ self.chars_per_token = chars_per_token
155
+
156
+ def count_text(self, text: str) -> int:
157
+ if not text:
158
+ return 0
159
+ return math.ceil(len(text) / self.chars_per_token)
160
+
161
+
162
+ class TiktokenCounter(TokenCounter):
163
+ """Estimate using a ``tiktoken`` encoding (optional dependency).
164
+
165
+ Tokenizer counts are closer to OpenAI-family billing but still exclude
166
+ provider-specific framing, so they remain estimates.
167
+ """
168
+
169
+ kind = "tiktoken_estimate"
170
+
171
+ def __init__(
172
+ self,
173
+ encoding_name: str = "o200k_base",
174
+ *,
175
+ message_overhead: int = 4,
176
+ media_tokens: int = 1_000,
177
+ ):
178
+ super().__init__(message_overhead=message_overhead, media_tokens=media_tokens)
179
+ try:
180
+ import tiktoken
181
+ except ImportError as exc:
182
+ raise ImportError(
183
+ "TiktokenCounter requires the optional 'tiktoken' package; "
184
+ "install it or use CharacterTokenCounter."
185
+ ) from exc
186
+ self.encoding_name = encoding_name
187
+ self._encoding = tiktoken.get_encoding(encoding_name)
188
+
189
+ def count_text(self, text: str) -> int:
190
+ if not text:
191
+ return 0
192
+ return len(self._encoding.encode(text, disallowed_special=()))
@@ -0,0 +1,44 @@
1
+ """Exceptions raised by context compaction."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class CompactionError(Exception):
7
+ """Base class for context-compaction failures."""
8
+
9
+
10
+ class ToolExchangeError(CompactionError):
11
+ """Outbound history contains a malformed or orphaned tool exchange.
12
+
13
+ Raised before provider I/O. Compaction never repairs history by inventing
14
+ tool calls or results.
15
+ """
16
+
17
+
18
+ class PendingToolExchangeError(ToolExchangeError):
19
+ """A tool-call batch has no result for one or more calls.
20
+
21
+ The unresolved batch stays in session history, but it cannot be sent for a
22
+ new model generation until every call has a result or approval resolution.
23
+ """
24
+
25
+ def __init__(self, message: str, *, pending_call_ids: list[str]):
26
+ super().__init__(message)
27
+ self.pending_call_ids = pending_call_ids
28
+
29
+
30
+ class ContextBudgetExceeded(CompactionError):
31
+ """Mandatory request content cannot fit the configured input budget."""
32
+
33
+ def __init__(
34
+ self,
35
+ message: str,
36
+ *,
37
+ required_tokens: int,
38
+ max_input_tokens: int,
39
+ counter_kind: str,
40
+ ):
41
+ super().__init__(message)
42
+ self.required_tokens = required_tokens
43
+ self.max_input_tokens = max_input_tokens
44
+ self.counter_kind = counter_kind