agentseek-api 0.2.0__tar.gz → 0.2.1__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 (226) hide show
  1. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/CHANGELOG.md +15 -0
  2. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/PKG-INFO +2 -2
  3. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/README.md +1 -1
  4. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/README.zh-CN.md +1 -1
  5. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/pyproject.toml +1 -1
  6. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test-redis-runtime.sh +25 -0
  7. agentseek_api-0.2.1/src/agentseek_api/__init__.py +1 -0
  8. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/run_executor.py +16 -0
  9. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/stream_persistence.py +20 -5
  10. agentseek_api-0.2.1/tests/integration/test_live_redis_stream_persistence.py +75 -0
  11. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_stream_persistence.py +85 -1
  12. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_run_executor.py +136 -1
  13. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/uv.lock +1 -1
  14. agentseek_api-0.2.0/src/agentseek_api/__init__.py +0 -1
  15. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.dockerignore +0 -0
  16. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.env.example +0 -0
  17. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  18. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.github/workflows/ci.yml +0 -0
  19. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.github/workflows/live-provider-streaming.yml +0 -0
  20. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.github/workflows/release.yml +0 -0
  21. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/.gitignore +0 -0
  22. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/AGENTS.md +0 -0
  23. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/CONTRIBUTING.md +0 -0
  24. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/LICENSE +0 -0
  25. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/Makefile +0 -0
  26. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/README.md +0 -0
  27. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/__init__.py +0 -0
  28. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/assistant_config/agentseek.json +0 -0
  29. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/assistant_config/graph.py +0 -0
  30. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/auth/api_key_auth.py +0 -0
  31. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/auth/bearer_token_auth.py +0 -0
  32. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/auth/custom_backend.py +0 -0
  33. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/auth/jwt.md +0 -0
  34. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/auth/jwt_auth.py +0 -0
  35. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/custom_routes/app.py +0 -0
  36. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/docker_ci_auth/auth_backend.py +0 -0
  37. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/docker_ci_auth/manifest.json +0 -0
  38. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/external_graph/__init__.py +0 -0
  39. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/external_graph/graph.py +0 -0
  40. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/external_graph/manifest.json +0 -0
  41. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/external_graph/run.py +0 -0
  42. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/__init__.py +0 -0
  43. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/react_agent/__init__.py +0 -0
  44. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/react_agent/graph.py +0 -0
  45. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/store_memory/graph.py +0 -0
  46. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/stress_test/__init__.py +0 -0
  47. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/stress_test/graph.py +0 -0
  48. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/stress_tool_agent/__init__.py +0 -0
  49. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/stress_tool_agent/graph.py +0 -0
  50. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/subgraph_agent/__init__.py +0 -0
  51. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/subgraph_agent/graph.py +0 -0
  52. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/subgraph_hitl_agent/__init__.py +0 -0
  53. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/graphs/subgraph_hitl_agent/graph.py +0 -0
  54. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/live_provider_graphs/__init__.py +0 -0
  55. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/live_provider_graphs/graph.py +0 -0
  56. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/live_provider_graphs/manifest.json +0 -0
  57. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/minimal_agentseek/agentseek.json +0 -0
  58. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/minimal_agentseek/graph.py +0 -0
  59. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/run_sample_graphs.py +0 -0
  60. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/examples/sample_graphs_manifest.json +0 -0
  61. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/build_live_provider_matrix.py +0 -0
  62. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/seekdb_checkpoint_smoke.py +0 -0
  63. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/seekdb_embed_launcher.py +0 -0
  64. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test-checkpoints.sh +0 -0
  65. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test-cli-dev-samples.sh +0 -0
  66. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test-cli-docker.sh +0 -0
  67. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test-live-provider.sh +0 -0
  68. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test-seekdb.sh +0 -0
  69. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test_cli_config_autodiscovery.py +0 -0
  70. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test_cli_dev_studio_smoke.py +0 -0
  71. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test_cli_embed_serve_smoke.py +0 -0
  72. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/test_cli_serve_smoke.py +0 -0
  73. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/scripts/verify_docker_api.py +0 -0
  74. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/a2a_server.py +0 -0
  75. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/__init__.py +0 -0
  76. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/assistants.py +0 -0
  77. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/crons.py +0 -0
  78. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/runs.py +0 -0
  79. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/stateless_runs.py +0 -0
  80. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/store.py +0 -0
  81. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/streaming.py +0 -0
  82. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/api/threads.py +0 -0
  83. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/cli.py +0 -0
  84. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/__init__.py +0 -0
  85. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/a2a_config.py +0 -0
  86. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/app_loader.py +0 -0
  87. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/auth_deps.py +0 -0
  88. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/auth_middleware.py +0 -0
  89. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/config_file.py +0 -0
  90. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/content_type_fix.py +0 -0
  91. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/cors_config.py +0 -0
  92. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/database.py +0 -0
  93. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/http_config.py +0 -0
  94. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/mcp_config.py +0 -0
  95. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/oceanbase_checkpointer.py +0 -0
  96. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/orm.py +0 -0
  97. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/runtime_store.py +0 -0
  98. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/core/store_config.py +0 -0
  99. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/main.py +0 -0
  100. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/mcp_server.py +0 -0
  101. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/models/__init__.py +0 -0
  102. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/models/api.py +0 -0
  103. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/models/auth.py +0 -0
  104. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/models/protocol.py +0 -0
  105. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/scheduler.py +0 -0
  106. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/__init__.py +0 -0
  107. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/cron_models.py +0 -0
  108. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/cron_rrule.py +0 -0
  109. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/cron_scheduler.py +0 -0
  110. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/cron_service.py +0 -0
  111. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/cron_webhooks.py +0 -0
  112. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/default_assistants.py +0 -0
  113. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/executor.py +0 -0
  114. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/langgraph_service.py +0 -0
  115. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/redis_queue.py +0 -0
  116. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/run_jobs.py +0 -0
  117. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/run_preparation.py +0 -0
  118. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/run_state.py +0 -0
  119. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/sample_graphs.py +0 -0
  120. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/sse.py +0 -0
  121. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/stream_modes.py +0 -0
  122. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/thread_checkpoint_store.py +0 -0
  123. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/thread_protocol.py +0 -0
  124. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/services/thread_service.py +0 -0
  125. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/settings.py +0 -0
  126. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/src/agentseek_api/worker.py +0 -0
  127. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/__init__.py +0 -0
  128. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/conftest.py +0 -0
  129. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/__init__.py +0 -0
  130. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/conftest.py +0 -0
  131. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/e2e_inprocess_flow.py +0 -0
  132. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/e2e_live_http_flow.py +0 -0
  133. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/e2e_live_http_multi_graph.py +0 -0
  134. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/e2e_live_http_resume_flow.py +0 -0
  135. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/fixtures/langgraph.store-e2e.json +0 -0
  136. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/live_provider_helpers.py +0 -0
  137. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_a2a_live.py +0 -0
  138. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_cron_api_live.py +0 -0
  139. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_cron_scheduler_live.py +0 -0
  140. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_langsmith_compat_live.py +0 -0
  141. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_live_provider_api.py +0 -0
  142. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_live_provider_mcp.py +0 -0
  143. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_mcp_live.py +0 -0
  144. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/e2e/test_real_api_flow.py +0 -0
  145. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_a2a_endpoint.py +0 -0
  146. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_agent_card.py +0 -0
  147. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_agents_aliases.py +0 -0
  148. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_assistants_compat.py +0 -0
  149. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_assistants_crud.py +0 -0
  150. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_assistants_ordering.py +0 -0
  151. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_auth_coverage_gaps.py +0 -0
  152. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_auth_handlers.py +0 -0
  153. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_auth_route_enforcement.py +0 -0
  154. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_auth_scope_matrix.py +0 -0
  155. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_core_api_flow.py +0 -0
  156. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_cron_api.py +0 -0
  157. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_langsmith_compat_coverage.py +0 -0
  158. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_langsmith_compat_extra.py +0 -0
  159. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_live_provider_streaming.py +0 -0
  160. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_mcp_endpoint.py +0 -0
  161. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_metadata_db_config.py +0 -0
  162. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_protocol_v2_streaming.py +0 -0
  163. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_run_cancellation_async.py +0 -0
  164. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_compat.py +0 -0
  165. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_crud.py +0 -0
  166. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_ordering.py +0 -0
  167. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_resume.py +0 -0
  168. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_streaming.py +0 -0
  169. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_streaming_errors.py +0 -0
  170. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_validation.py +0 -0
  171. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_wait_and_get.py +0 -0
  172. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_runs_wait_timeout.py +0 -0
  173. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_scheduler_runtime.py +0 -0
  174. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_stateless_runs_crud.py +0 -0
  175. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_store_api.py +0 -0
  176. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_system_endpoints.py +0 -0
  177. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_threads_compat.py +0 -0
  178. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_threads_crud.py +0 -0
  179. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/integration/test_threads_ordering.py +0 -0
  180. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_a2a_config.py +0 -0
  181. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_a2a_server.py +0 -0
  182. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_app_loader.py +0 -0
  183. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_assistant_routes_unit.py +0 -0
  184. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_auth.py +0 -0
  185. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_auth_deps.py +0 -0
  186. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_auth_internals.py +0 -0
  187. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_checkpoint_guards.py +0 -0
  188. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_cli.py +0 -0
  189. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_content_type_fix.py +0 -0
  190. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_cron_rrule.py +0 -0
  191. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_cron_service.py +0 -0
  192. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_cron_webhooks.py +0 -0
  193. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_custom_routers.py +0 -0
  194. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_database_manager.py +0 -0
  195. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_default_assistants.py +0 -0
  196. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_examples.py +0 -0
  197. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_executor.py +0 -0
  198. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_graph_manifest.py +0 -0
  199. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_http_config.py +0 -0
  200. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_langgraph_service.py +0 -0
  201. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_live_provider_e2e_config.py +0 -0
  202. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_live_provider_graphs.py +0 -0
  203. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_live_provider_matrix.py +0 -0
  204. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_mcp_config.py +0 -0
  205. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_mcp_server.py +0 -0
  206. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_oceanbase_checkpointer.py +0 -0
  207. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_openapi_auth_config.py +0 -0
  208. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_redis_queue.py +0 -0
  209. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_run_jobs.py +0 -0
  210. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_run_preparation.py +0 -0
  211. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_run_state.py +0 -0
  212. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_runtime_store.py +0 -0
  213. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_sample_graphs.py +0 -0
  214. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_scheduler.py +0 -0
  215. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_scheduler_process.py +0 -0
  216. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_seekdb_checkpoint_smoke.py +0 -0
  217. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_seekdb_embed_launcher.py +0 -0
  218. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_sse_keepalive.py +0 -0
  219. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_store_config.py +0 -0
  220. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_stream_modes.py +0 -0
  221. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_stream_persistence_helpers.py +0 -0
  222. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_thread_checkpoint_store.py +0 -0
  223. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_thread_protocol.py +0 -0
  224. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_thread_routes_unit.py +0 -0
  225. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_thread_service.py +0 -0
  226. {agentseek_api-0.2.0 → agentseek_api-0.2.1}/tests/unit/test_worker.py +0 -0
@@ -2,6 +2,21 @@
2
2
 
3
3
  Notable changes to AgentSeek API are documented in this file.
4
4
 
5
+ ## 0.2.1 - 2026-07-14
6
+
7
+ ### Fixed
8
+
9
+ - Preserved empty JSON arrays in Redis thread-stream events by avoiding a Lua
10
+ cjson decode/encode round trip, while keeping generated envelope fields
11
+ authoritative.
12
+ - Mirrored completed tool, human, and system messages to `messages-tuple` when
13
+ requested so LangGraph SDK clients receive tool results before final-answer
14
+ streaming completes.
15
+
16
+ ### Upgrade notes
17
+
18
+ - No schema or configuration migration is required from 0.2.0.
19
+
5
20
  ## 0.2.0 - 2026-07-12
6
21
 
7
22
  ### Highlights
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentseek-api
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: AgentSeek API core runtime with OceanBase checkpoints.
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -216,7 +216,7 @@ When the server starts it prints the banner and local URLs:
216
216
  ╠═╣│ ┬├┤ │││ │ ╚═╗├┤ ├┤ ├┴┐
217
217
  ╩ ╩└─┘└─┘┘└┘ ┴ ╚═╝└─┘└─┘┴ ┴
218
218
 
219
- AgentSeek v0.2.0
219
+ AgentSeek v0.2.1
220
220
 
221
221
  - 🚀 API: http://localhost:2024
222
222
  - 📚 Docs: http://localhost:2024/docs
@@ -185,7 +185,7 @@ When the server starts it prints the banner and local URLs:
185
185
  ╠═╣│ ┬├┤ │││ │ ╚═╗├┤ ├┤ ├┴┐
186
186
  ╩ ╩└─┘└─┘┘└┘ ┴ ╚═╝└─┘└─┘┴ ┴
187
187
 
188
- AgentSeek v0.2.0
188
+ AgentSeek v0.2.1
189
189
 
190
190
  - 🚀 API: http://localhost:2024
191
191
  - 📚 Docs: http://localhost:2024/docs
@@ -181,7 +181,7 @@ uv run agentseek-api dev --config ./langgraph.json
181
181
  ╠═╣│ ┬├┤ │││ │ ╚═╗├┤ ├┤ ├┴┐
182
182
  ╩ ╩└─┘└─┘┘└┘ ┴ ╚═╝└─┘└─┘┴ ┴
183
183
 
184
- AgentSeek v0.2.0
184
+ AgentSeek v0.2.1
185
185
 
186
186
  - 🚀 API: http://localhost:2024
187
187
  - 📚 Docs: http://localhost:2024/docs
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentseek-api"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "AgentSeek API core runtime with OceanBase checkpoints."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -9,6 +9,7 @@ CONFIG_PATH="${CONFIG_PATH:-examples/docker_ci_auth/manifest.json}"
9
9
  NETWORK_NAME="${NETWORK_NAME:-agentseek-redis-runtime}"
10
10
  BACKEND_CONTAINER="${BACKEND_CONTAINER:-agentseek-redis-backend}"
11
11
  REDIS_CONTAINER="${REDIS_CONTAINER:-agentseek-redis}"
12
+ REDIS_HOST_PORT="${REDIS_HOST_PORT:-6391}"
12
13
  API_CONTAINER="${API_CONTAINER:-agentseek-redis-api}"
13
14
  WORKER_CONTAINER="${WORKER_CONTAINER:-agentseek-redis-worker}"
14
15
  API_PORT="${API_PORT:-8126}"
@@ -113,6 +114,21 @@ PY
113
114
  fi
114
115
  }
115
116
 
117
+ wait_for_redis() {
118
+ local attempt
119
+
120
+ for ((attempt = 1; attempt <= 30; attempt++)); do
121
+ if docker exec "$REDIS_CONTAINER" redis-cli ping >/dev/null 2>&1; then
122
+ return 0
123
+ fi
124
+ sleep 1
125
+ done
126
+
127
+ echo "Redis did not become ready" >&2
128
+ print_logs
129
+ return 1
130
+ }
131
+
116
132
  ensure_database_exists() {
117
133
  uv run python - <<'PY'
118
134
  import os
@@ -252,8 +268,17 @@ docker rm -f "$REDIS_CONTAINER" >/dev/null 2>&1 || true
252
268
  docker run -d \
253
269
  --name "$REDIS_CONTAINER" \
254
270
  --network "$NETWORK_NAME" \
271
+ -p "127.0.0.1:${REDIS_HOST_PORT}:6379" \
255
272
  redis:7-alpine >/dev/null
256
273
 
274
+ wait_for_redis
275
+
276
+ if ! AGENTSEEK_TEST_REDIS_URL="redis://127.0.0.1:${REDIS_HOST_PORT}/0" \
277
+ uv run pytest tests/integration/test_live_redis_stream_persistence.py -q; then
278
+ print_logs
279
+ exit 1
280
+ fi
281
+
257
282
  wait_for_backend
258
283
  ensure_database_exists
259
284
 
@@ -0,0 +1 @@
1
+ __version__ = "0.2.1"
@@ -952,6 +952,22 @@ async def execute_run(
952
952
  namespace=protocol_namespace,
953
953
  run_id=run_id,
954
954
  )
955
+ # LangGraph SDK 1.x useStream subscribes to
956
+ # messages-tuple, not messages/complete. Mirror the
957
+ # completed non-AI message onto that requested
958
+ # channel so ToolMessage resolves the pending tool
959
+ # call while the run is still streaming.
960
+ if _want_messages_tuple:
961
+ event_metadata = (
962
+ _normalize_stream_value(stream_event.get("metadata", {})) or {}
963
+ )
964
+ await apublish_messages_tuple(
965
+ thread_id,
966
+ chunk=msg_dump,
967
+ metadata=event_metadata,
968
+ namespace=protocol_namespace,
969
+ run_id=run_id,
970
+ )
955
971
  continue
956
972
  if _want_messages_tuple:
957
973
  chunk_dump = _normalize_stream_value(message)
@@ -17,6 +17,7 @@ _RUN_STREAM_SEQ_KEY_PREFIX = "agentseek:runs:stream-seq"
17
17
  _THREAD_STREAM_SEQ_KEY_PREFIX = "agentseek:threads:stream-seq"
18
18
  _RUN_STREAM_KEY_PREFIX = "agentseek:runs:stream"
19
19
  _THREAD_STREAM_KEY_PREFIX = "agentseek:threads:stream"
20
+ _THREAD_STREAM_ENVELOPE_FIELDS = frozenset({"type", "event_id", "seq"})
20
21
  _redis_client: Redis | None = None
21
22
  logger = logging.getLogger(__name__)
22
23
 
@@ -24,11 +25,23 @@ _APPEND_REDIS_STREAM_EVENT_SCRIPT = """
24
25
  local seq = redis.call('INCR', KEYS[1])
25
26
  local payload = ARGV[1]
26
27
  if ARGV[4] ~= '' then
27
- local event = cjson.decode(payload)
28
- event['type'] = 'event'
29
- event['event_id'] = ARGV[4] .. ':' .. tostring(seq)
30
- event['seq'] = seq
31
- payload = cjson.encode(event)
28
+ -- Inject type/event_id/seq WITHOUT a cjson decode/encode round-trip.
29
+ -- Redis' bundled lua-cjson cannot distinguish an empty array from an empty
30
+ -- object, so cjson.encode(cjson.decode('{"tool_calls":[]}')) returns
31
+ -- '{"tool_calls":{}}'. That silently corrupts every streamed message
32
+ -- (tool_calls / invalid_tool_calls become {}), and langgraph-sdk's
33
+ -- convertToChunk() then throws on `{}.map`, so the client cannot concat
34
+ -- message chunks by id and each token replaces the previous one instead of
35
+ -- accumulating. Splice the header in as a string to keep the original
36
+ -- payload (and its empty arrays) byte-for-byte intact.
37
+ local rest = string.sub(payload, 2)
38
+ local event_id = cjson.encode(ARGV[4] .. ':' .. tostring(seq))
39
+ local head = '{"type":"event","event_id":' .. event_id .. ',"seq":' .. tostring(seq)
40
+ if rest == '}' then
41
+ payload = head .. '}'
42
+ else
43
+ payload = head .. ',' .. rest
44
+ end
32
45
  end
33
46
  redis.call('XADD', KEYS[2], 'MAXLEN', '~', ARGV[2], tostring(seq) .. '-0', 'payload', payload)
34
47
  redis.call('EXPIRE', KEYS[2], ARGV[3])
@@ -70,6 +83,8 @@ async def _append_redis_stream_event_atomic(
70
83
  payload: dict[str, Any],
71
84
  event_prefix: str = "",
72
85
  ) -> tuple[int, dict[str, Any]]:
86
+ if event_prefix:
87
+ payload = {key: value for key, value in payload.items() if key not in _THREAD_STREAM_ENVELOPE_FIELDS}
73
88
  result = await _get_redis_client().eval(
74
89
  _APPEND_REDIS_STREAM_EVENT_SCRIPT,
75
90
  2,
@@ -0,0 +1,75 @@
1
+ import json
2
+ import os
3
+ from uuid import uuid4
4
+
5
+ import pytest
6
+ from redis.asyncio import from_url
7
+
8
+ from agentseek_api.services import stream_persistence as stream_module
9
+
10
+
11
+ _TEST_REDIS_URL = os.getenv("AGENTSEEK_TEST_REDIS_URL")
12
+ pytestmark = pytest.mark.skipif(
13
+ not _TEST_REDIS_URL,
14
+ reason="AGENTSEEK_TEST_REDIS_URL is required for live Redis script tests",
15
+ )
16
+
17
+
18
+ @pytest.mark.parametrize(
19
+ "payload",
20
+ [
21
+ pytest.param(
22
+ {
23
+ "method": "messages/partial",
24
+ "params": {
25
+ "data": {
26
+ "tool_calls": [],
27
+ "invalid_tool_calls": [],
28
+ }
29
+ },
30
+ },
31
+ id="message-with-empty-arrays",
32
+ ),
33
+ pytest.param({}, id="empty-object"),
34
+ pytest.param(
35
+ {
36
+ "type": "payload-event",
37
+ "event_id": "payload-event-id",
38
+ "seq": 0,
39
+ "method": "values",
40
+ },
41
+ id="reserved-envelope-fields",
42
+ ),
43
+ ],
44
+ )
45
+ @pytest.mark.asyncio
46
+ async def test_thread_stream_lua_splices_header_without_reencoding_payload(
47
+ monkeypatch: pytest.MonkeyPatch,
48
+ payload: dict[str, object],
49
+ ) -> None:
50
+ assert _TEST_REDIS_URL is not None
51
+ redis = from_url(_TEST_REDIS_URL, decode_responses=True)
52
+ thread_id = 'thread-"' + "\\" + uuid4().hex[:8]
53
+ sequence_key = f"agentseek:threads:stream-seq:{thread_id}"
54
+ stream_key = f"agentseek:threads:stream:{thread_id}"
55
+ monkeypatch.setattr(stream_module, "_redis_client", redis)
56
+
57
+ try:
58
+ seq, event = await stream_module.append_redis_thread_stream_event(thread_id, payload)
59
+ rows = await redis.xrange(stream_key, min="-", max="+")
60
+ finally:
61
+ await redis.delete(sequence_key, stream_key)
62
+ await redis.aclose()
63
+
64
+ assert seq == 1
65
+ assert event["type"] == "event"
66
+ assert event["event_id"] == f"{thread_id}:1"
67
+ assert event["seq"] == 1
68
+ payload_body = {key: value for key, value in payload.items() if key not in {"type", "event_id", "seq"}}
69
+ expected_event = {"type": "event", "event_id": f"{thread_id}:1", "seq": 1, **payload_body}
70
+ assert event == expected_event
71
+ assert len(rows) == 1
72
+ stored_payload = rows[0][1]["payload"]
73
+ stored_event = json.loads(stored_payload)
74
+ assert stored_event == event
75
+ assert stored_payload == json.dumps(expected_event, ensure_ascii=False, separators=(",", ":"))
@@ -16,7 +16,12 @@ from agentseek_api.services import run_jobs as run_jobs_module
16
16
  from agentseek_api.services import sse as sse_module
17
17
  from agentseek_api.services import stream_persistence as stream_module
18
18
  from agentseek_api.services.run_state import run_broker
19
- from agentseek_api.services.thread_protocol import publish_values_event, thread_protocol_broker
19
+ from agentseek_api.services.thread_protocol import (
20
+ apublish_messages_complete,
21
+ apublish_messages_tuple,
22
+ publish_values_event,
23
+ thread_protocol_broker,
24
+ )
20
25
 
21
26
 
22
27
  class FakeRedisCounter:
@@ -566,6 +571,85 @@ def test_create_run_stream_sends_keepalive_while_protocol_broker_is_idle(client:
566
571
  assert events[1]["data"] == {"phase": "done"}
567
572
 
568
573
 
574
+ def test_redis_create_run_stream_replays_tool_message_tuple_for_sdk_subscribers(
575
+ client: TestClient,
576
+ monkeypatch,
577
+ ) -> None:
578
+ fake_redis = FakeRedisCounter()
579
+ monkeypatch.setattr(stream_module.settings, "EXECUTOR_BACKEND", "redis")
580
+ monkeypatch.setattr(stream_module, "_redis_client", fake_redis)
581
+ monkeypatch.setattr(runs_api, "REDIS_STREAM_POLL_INTERVAL_SECONDS", 0)
582
+
583
+ thread_id, run_id = client.portal.call(lambda: _seed_run(status="success"))
584
+ created = RunRead(
585
+ run_id=run_id,
586
+ thread_id=thread_id,
587
+ assistant_id="assistant",
588
+ status="success",
589
+ output={},
590
+ interrupts=None,
591
+ last_error=None,
592
+ created_at=None,
593
+ updated_at=None,
594
+ metadata={},
595
+ kwargs={},
596
+ multitask_strategy="enqueue",
597
+ )
598
+ tool_message = {
599
+ "content": "42 characters",
600
+ "additional_kwargs": {},
601
+ "response_metadata": {},
602
+ "type": "tool",
603
+ "name": None,
604
+ "id": "tool-message-1",
605
+ "tool_call_id": "call-character-count",
606
+ "artifact": None,
607
+ "status": "success",
608
+ }
609
+ metadata = {
610
+ "langgraph_node": "tools",
611
+ "langgraph_checkpoint_ns": "tools:task-1",
612
+ "provider": "deterministic",
613
+ }
614
+
615
+ client.portal.call(
616
+ lambda: apublish_messages_complete(
617
+ thread_id,
618
+ messages=[tool_message],
619
+ namespace=["tools:task-1"],
620
+ run_id=run_id,
621
+ )
622
+ )
623
+ client.portal.call(
624
+ lambda: apublish_messages_tuple(
625
+ thread_id,
626
+ chunk=tool_message,
627
+ metadata=metadata,
628
+ namespace=["tools:task-1"],
629
+ run_id=run_id,
630
+ )
631
+ )
632
+ thread_protocol_broker.delete_thread(thread_id)
633
+
634
+ response = runs_api._build_create_run_stream_response(
635
+ thread_id=thread_id,
636
+ created=created,
637
+ user=User(identity="default_user", is_authenticated=True),
638
+ stream_modes=["messages-tuple", "values"],
639
+ after_seq=0,
640
+ location=(
641
+ f"/threads/{thread_id}/runs/{run_id}/stream"
642
+ "?stream_mode=messages-tuple&stream_mode=values"
643
+ ),
644
+ content_location=f"/threads/{thread_id}/runs/{run_id}",
645
+ )
646
+
647
+ body = client.portal.call(_collect_stream_body, response)
648
+ events = _parse_sse(body)
649
+ assert [event["event"] for event in events] == ["metadata", "messages"]
650
+ assert events[1]["data"] == [tool_message, metadata]
651
+
652
+
569
653
  def test_run_stream_persistence_uses_shared_seq_after_broker_reset(client: TestClient, monkeypatch) -> None:
570
654
  fake_redis = FakeRedisCounter()
571
655
  monkeypatch.setattr(stream_module.settings, "EXECUTOR_BACKEND", "redis")
@@ -1,7 +1,7 @@
1
1
  from typing import Any
2
2
 
3
3
  import pytest
4
- from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage
4
+ from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage, ToolMessage
5
5
  from langgraph.constants import CONF, CONFIG_KEY_CHECKPOINTER
6
6
 
7
7
  from agentseek_api.core.runtime_store import UserScopedStore
@@ -752,6 +752,141 @@ class FakeProtocolMultiMessageLangGraphService(FakeLangGraphService):
752
752
  return FakeProtocolMultiMessageEntry()
753
753
 
754
754
 
755
+ class FakeProtocolToolMessageGraph(FakeGraph):
756
+ async def astream_events(self, prepared_input: dict, config: dict, version: str = "v2", **kwargs):
757
+ self.configs.append(config)
758
+ tool_message = ToolMessage(
759
+ content="42 characters",
760
+ tool_call_id="call-character-count",
761
+ id="tool-message-1",
762
+ )
763
+ tool_event = {
764
+ "event": "on_chain_stream",
765
+ "name": "tools",
766
+ "run_id": "tool-run",
767
+ "parent_ids": ["root-run"],
768
+ "metadata": {
769
+ "langgraph_node": "tools",
770
+ "langgraph_checkpoint_ns": "tools:task-1",
771
+ "provider": "deterministic",
772
+ },
773
+ "tags": ["graph:step:2"],
774
+ "data": {"chunk": {"messages": [tool_message]}},
775
+ }
776
+ yield tool_event
777
+ yield tool_event
778
+ yield {
779
+ "event": "on_chat_model_stream",
780
+ "name": "chat-model",
781
+ "run_id": "chat-run",
782
+ "parent_ids": ["root-run"],
783
+ "metadata": {"langgraph_node": "call_model"},
784
+ "tags": ["graph:step:3"],
785
+ "data": {"chunk": AIMessageChunk(content="Final answer", id="ai-message-1")},
786
+ }
787
+ yield {
788
+ "event": "on_chain_end",
789
+ "name": "fake-graph",
790
+ "run_id": "root-run",
791
+ "parent_ids": [],
792
+ "metadata": {},
793
+ "tags": [],
794
+ "data": {
795
+ "output": {
796
+ "output": {
797
+ "messages": [tool_message, AIMessage(content="Final answer", id="ai-message-1")]
798
+ }
799
+ }
800
+ },
801
+ }
802
+
803
+
804
+ class FakeProtocolToolMessageEntry(FakeEntry):
805
+ graph = FakeProtocolToolMessageGraph()
806
+
807
+ @staticmethod
808
+ def build_graph(_checkpointer=None) -> FakeProtocolToolMessageGraph:
809
+ return FakeProtocolToolMessageEntry.graph
810
+
811
+
812
+ class FakeProtocolToolMessageLangGraphService(FakeLangGraphService):
813
+ def get_entry(self, _graph_id: str | None) -> FakeProtocolToolMessageEntry:
814
+ return FakeProtocolToolMessageEntry()
815
+
816
+
817
+ async def _execute_protocol_tool_message_graph(
818
+ monkeypatch: pytest.MonkeyPatch,
819
+ *,
820
+ stream_modes: list[str],
821
+ ) -> list[dict[str, Any]]:
822
+ fake_db = FakeDBManager()
823
+ protocol_broker = ThreadProtocolEventBroker()
824
+ FakeProtocolToolMessageEntry.graph = FakeProtocolToolMessageGraph()
825
+ monkeypatch.setattr(
826
+ "agentseek_api.services.run_executor.get_langgraph_service",
827
+ lambda: FakeProtocolToolMessageLangGraphService(),
828
+ )
829
+ monkeypatch.setattr("agentseek_api.services.run_executor.db_manager", fake_db)
830
+ monkeypatch.setattr("agentseek_api.services.thread_protocol.thread_protocol_broker", protocol_broker)
831
+
832
+ await execute_run(
833
+ thread_id="t1",
834
+ run_id="r1",
835
+ payload={"hello": "world"},
836
+ user_id="user-1",
837
+ kwargs={"stream_modes": stream_modes},
838
+ )
839
+ return protocol_broker._events["t1"]
840
+
841
+
842
+ @pytest.mark.asyncio
843
+ async def test_execute_run_mirrors_tool_message_to_requested_tuple_stream_once_and_before_final_answer(
844
+ monkeypatch: pytest.MonkeyPatch,
845
+ ) -> None:
846
+ thread_events = await _execute_protocol_tool_message_graph(
847
+ monkeypatch,
848
+ stream_modes=["messages-tuple", "values"],
849
+ )
850
+ complete_events = [event for event in thread_events if event["method"] == "messages/complete"]
851
+ tuple_events = [event for event in thread_events if event["method"] == "messages-tuple"]
852
+ tool_tuples = [event for event in tuple_events if event["params"]["data"][0]["type"] == "tool"]
853
+
854
+ assert len(complete_events) == 1
855
+ assert len(tool_tuples) == 1
856
+
857
+ tool_tuple = tool_tuples[0]
858
+ tool_payload, metadata = tool_tuple["params"]["data"]
859
+ assert tool_payload["content"] == "42 characters"
860
+ assert tool_payload["id"] == "tool-message-1"
861
+ assert tool_payload["tool_call_id"] == "call-character-count"
862
+ assert metadata == {
863
+ "langgraph_node": "tools",
864
+ "langgraph_checkpoint_ns": "tools:task-1",
865
+ "provider": "deterministic",
866
+ }
867
+ assert tool_tuple["params"]["namespace"] == ["tools:task-1"]
868
+ assert tool_tuple["params"]["run_id"] == "r1"
869
+ assert complete_events[0]["params"]["data"] == [tool_payload]
870
+
871
+ final_answer_tuple = next(
872
+ event for event in tuple_events if event["params"]["data"][0].get("id") == "ai-message-1"
873
+ )
874
+ assert thread_events.index(tool_tuple) < thread_events.index(final_answer_tuple)
875
+
876
+
877
+ @pytest.mark.asyncio
878
+ async def test_execute_run_keeps_tool_message_complete_without_unrequested_tuple_mirror(
879
+ monkeypatch: pytest.MonkeyPatch,
880
+ ) -> None:
881
+ thread_events = await _execute_protocol_tool_message_graph(monkeypatch, stream_modes=["values"])
882
+ complete_events = [event for event in thread_events if event["method"] == "messages/complete"]
883
+ tuple_events = [event for event in thread_events if event["method"] == "messages-tuple"]
884
+
885
+ assert len(complete_events) == 1
886
+ assert complete_events[0]["params"]["data"][0]["id"] == "tool-message-1"
887
+ assert tuple_events == []
888
+
889
+
755
890
  @pytest.mark.asyncio
756
891
  async def test_execute_run_publishes_incremental_protocol_messages_and_values(monkeypatch: pytest.MonkeyPatch) -> None:
757
892
  fake_db = FakeDBManager()
@@ -29,7 +29,7 @@ wheels = [
29
29
 
30
30
  [[package]]
31
31
  name = "agentseek-api"
32
- version = "0.2.0"
32
+ version = "0.2.1"
33
33
  source = { editable = "." }
34
34
  dependencies = [
35
35
  { name = "aiomysql" },
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
File without changes
File without changes
File without changes
File without changes