langgraph-api 0.15.4__tar.gz → 0.16.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 (329) hide show
  1. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/Makefile +4 -0
  2. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/PKG-INFO +2 -2
  3. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/README.md +53 -10
  4. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/benchmark_profiles.ts +67 -0
  5. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/benchmarks.ts +3 -0
  6. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/delta-turns-lib.test.js +13 -0
  7. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/delta-turns-lib.ts +10 -0
  8. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/delta_turns.ts +83 -8
  9. langgraph_api-0.16.0/benchmark/benchmark-runners/interrupt_resume.ts +119 -0
  10. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/types.ts +3 -0
  11. langgraph_api-0.16.0/benchmark/capacity-ci.js +1025 -0
  12. langgraph_api-0.16.0/benchmark/capacity-ci.test.js +404 -0
  13. langgraph_api-0.16.0/benchmark/daily-report.js +503 -0
  14. langgraph_api-0.16.0/benchmark/daily-report.test.js +463 -0
  15. langgraph_api-0.16.0/benchmark/delta-report.js +599 -0
  16. langgraph_api-0.16.0/benchmark/delta-report.test.js +191 -0
  17. langgraph_api-0.16.0/benchmark/history.js +204 -0
  18. langgraph_api-0.16.0/benchmark/history.test.js +123 -0
  19. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/host-v2.js +23 -0
  20. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/ramp.js +23 -27
  21. langgraph_api-0.16.0/benchmark/runner-descriptions.js +58 -0
  22. langgraph_api-0.16.0/benchmark/server-metrics.js +95 -0
  23. langgraph_api-0.16.0/benchmark/server-metrics.test.js +63 -0
  24. langgraph_api-0.16.0/benchmark/slack.js +49 -0
  25. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/staircase.py +59 -2
  26. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/staircase_step_k6.js +28 -3
  27. langgraph_api-0.16.0/langgraph_api/__init__.py +1 -0
  28. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/__init__.py +53 -8
  29. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2a.py +8 -11
  30. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/runs.py +57 -43
  31. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/threads.py +24 -0
  32. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/asyncio.py +30 -5
  33. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/langsmith/backend.py +2 -0
  34. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/studio_user.py +6 -1
  35. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/config/__init__.py +7 -0
  36. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/cron_scheduler.py +16 -1
  37. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/encryption/middleware.py +4 -0
  38. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/errors.py +2 -2
  39. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/graph.py +14 -2
  40. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/ops/crons.py +3 -2
  41. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/ops/runs.py +16 -2
  42. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/ops/threads.py +2 -0
  43. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/client.mts +2 -0
  44. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/package.json +2 -2
  45. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/remote.py +15 -6
  46. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/yarn.lock +66 -126
  47. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/metadata.py +28 -0
  48. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/metrics_otlp.py +25 -0
  49. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/models/run.py +5 -0
  50. langgraph_api-0.16.0/langgraph_api/runner/__init__.py +10 -0
  51. langgraph_api-0.16.0/langgraph_api/runner/__main__.py +275 -0
  52. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/schema.py +4 -0
  53. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/self_hosted_logs.py +4 -0
  54. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/serde.py +7 -0
  55. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/state.py +11 -5
  56. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/stream.py +9 -10
  57. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/future.py +1 -1
  58. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/worker.py +17 -0
  59. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/checkpoint.py +19 -14
  60. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/__init__.py +4 -0
  61. langgraph_api-0.16.0/langgraph_grpc_common/proto/core_api_pb2.py +276 -0
  62. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/core_api_pb2.pyi +51 -7
  63. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/core_api_pb2_grpc.py +3 -3
  64. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/core_api_pb2_grpc.pyi +3 -3
  65. langgraph_api-0.16.0/langgraph_grpc_common/proto/runner_pb2.py +50 -0
  66. langgraph_api-0.16.0/langgraph_grpc_common/proto/runner_pb2.pyi +167 -0
  67. langgraph_api-0.16.0/langgraph_grpc_common/proto/runner_pb2_grpc.py +108 -0
  68. langgraph_api-0.16.0/langgraph_grpc_common/proto/runner_pb2_grpc.pyi +73 -0
  69. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/openapi.json +124 -0
  70. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/pyproject.toml +1 -1
  71. langgraph_api-0.15.4/benchmark/capacity-ci.js +0 -658
  72. langgraph_api-0.15.4/benchmark/capacity-ci.test.js +0 -138
  73. langgraph_api-0.15.4/benchmark/daily-report.js +0 -263
  74. langgraph_api-0.15.4/benchmark/daily-report.test.js +0 -242
  75. langgraph_api-0.15.4/langgraph_api/__init__.py +0 -1
  76. langgraph_api-0.15.4/langgraph_grpc_common/proto/core_api_pb2.py +0 -274
  77. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/.gitignore +0 -0
  78. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/LICENSE +0 -0
  79. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/README.md +0 -0
  80. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/.gitignore +0 -0
  81. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/Makefile +0 -0
  82. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/assistant.ts +0 -0
  83. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/benchmark-runner.ts +0 -0
  84. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/cancel_first_second_completes.ts +0 -0
  85. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/classify-error.test.js +0 -0
  86. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/classify-error.ts +0 -0
  87. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/enqueued_runs_order.ts +0 -0
  88. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/log-failure.ts +0 -0
  89. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/meta_workload.ts +0 -0
  90. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/store.ts +0 -0
  91. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/stream_write.ts +0 -0
  92. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/thread.ts +0 -0
  93. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/thread_runs_metadata_search.ts +0 -0
  94. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/threads_search_metadata.ts +0 -0
  95. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/benchmark-runners/wait_write.ts +0 -0
  96. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/clean-cli.js +0 -0
  97. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/clean.js +0 -0
  98. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/continuous/README.md +0 -0
  99. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/continuous/pyproject.toml +0 -0
  100. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/continuous/runner.py +0 -0
  101. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/continuous/uv.lock +0 -0
  102. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/graphs.js +0 -0
  103. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/host-v2.test.js +0 -0
  104. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/multer.test.js +0 -0
  105. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/package.json +0 -0
  106. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/reporting/dd_reporting.py +0 -0
  107. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/tsconfig.json +0 -0
  108. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/update-revision.js +0 -0
  109. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/weather.js +0 -0
  110. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/benchmark/yarn.lock +0 -0
  111. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/constraints.txt +0 -0
  112. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/custom_store.sql +0 -0
  113. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/forbidden.txt +0 -0
  114. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/hatch_build.py +0 -0
  115. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/healthcheck.py +0 -0
  116. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph-cloud-debugging-20260210132856.zip +0 -0
  117. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/_checkpointer/__init__.py +0 -0
  118. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/_checkpointer/_adapter.py +0 -0
  119. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/_checkpointer/protocol.py +0 -0
  120. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/_factory_utils.py +0 -0
  121. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2ui.py +0 -0
  122. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2ui_schemas/README.md +0 -0
  123. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2ui_schemas/client_capabilities.json +0 -0
  124. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2ui_schemas/client_data_model.json +0 -0
  125. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2ui_schemas/client_to_server.json +0 -0
  126. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/a2ui_schemas/server_to_client.json +0 -0
  127. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/assistants.py +0 -0
  128. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/event_streaming.py +0 -0
  129. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/mcp/__init__.py +0 -0
  130. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/mcp/_constants.py +0 -0
  131. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/mcp/_handlers.py +0 -0
  132. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/mcp/_models.py +0 -0
  133. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/mcp/_routes.py +0 -0
  134. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/mcp/_sanitizers.py +0 -0
  135. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/meta.py +0 -0
  136. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/openapi.py +0 -0
  137. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/profile.py +0 -0
  138. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/store.py +0 -0
  139. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/api/ui.py +0 -0
  140. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/asgi_transport.py +0 -0
  141. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/__init__.py +0 -0
  142. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/custom.py +0 -0
  143. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/errors.py +0 -0
  144. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/langsmith/__init__.py +0 -0
  145. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/langsmith/client.py +0 -0
  146. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/middleware.py +0 -0
  147. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/auth/noop.py +0 -0
  148. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/beacon_data_plane_jwt_auth.py +0 -0
  149. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/cache.py +0 -0
  150. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/cli.py +0 -0
  151. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/command.py +0 -0
  152. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/config/_parse.py +0 -0
  153. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/config/schemas.py +0 -0
  154. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/encryption/__init__.py +0 -0
  155. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/encryption/aes_json.py +0 -0
  156. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/encryption/context.py +0 -0
  157. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/encryption/custom.py +0 -0
  158. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/encryption/shared.py +0 -0
  159. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/__init__.py +0 -0
  160. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/capabilities.py +0 -0
  161. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/constants.py +0 -0
  162. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/event_normalizers.py +0 -0
  163. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/namespace.py +0 -0
  164. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/service.py +0 -0
  165. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/session.py +0 -0
  166. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/state_normalizers.py +0 -0
  167. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/event_streaming/types.py +0 -0
  168. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/feature_flags.py +0 -0
  169. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/__init__.py +0 -0
  170. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/client.py +0 -0
  171. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/generated/core_api_pb2.pyi +0 -0
  172. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/ops/__init__.py +0 -0
  173. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/ops/assistants.py +0 -0
  174. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/ops/cache.py +0 -0
  175. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/server.py +0 -0
  176. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/servicers/__init__.py +0 -0
  177. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/servicers/checkpointer.py +0 -0
  178. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/grpc/servicers/encryption.py +0 -0
  179. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/http.py +0 -0
  180. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/http_metrics.py +0 -0
  181. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/http_metrics_utils.py +0 -0
  182. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/.gitignore +0 -0
  183. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/.prettierrc +0 -0
  184. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/.yarnclean +0 -0
  185. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/__init__.py +0 -0
  186. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/base.py +0 -0
  187. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/build.mts +0 -0
  188. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/client.http.mts +0 -0
  189. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/errors.py +0 -0
  190. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/global.d.ts +0 -0
  191. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/schema.py +0 -0
  192. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/auth-mutations.mts +0 -0
  193. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/graph.mts +0 -0
  194. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/load.hooks.mjs +0 -0
  195. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/preload.mjs +0 -0
  196. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/utils/experiment-tracing.mts +0 -0
  197. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/utils/files.mts +0 -0
  198. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/utils/importMap.mts +0 -0
  199. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
  200. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/src/utils/serde.mts +0 -0
  201. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/sse.py +0 -0
  202. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/traceblock.mts +0 -0
  203. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/tsconfig.json +0 -0
  204. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/js/ui.py +0 -0
  205. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/lc_security/__init__.py +0 -0
  206. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/lc_security/exceptions.py +0 -0
  207. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/lc_security/policy.py +0 -0
  208. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/lc_security/transport.py +0 -0
  209. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/logging.py +0 -0
  210. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/metrics_collector.py +0 -0
  211. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/middleware/__init__.py +0 -0
  212. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/middleware/ensure_store.py +0 -0
  213. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/middleware/http_logger.py +0 -0
  214. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/middleware/private_network.py +0 -0
  215. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/middleware/request_id.py +0 -0
  216. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/models/__init__.py +0 -0
  217. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/otel_context.py +0 -0
  218. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/patch.py +0 -0
  219. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/queue_entrypoint.py +0 -0
  220. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/release_tags.py +0 -0
  221. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/route.py +0 -0
  222. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/self_hosted_metrics.py +0 -0
  223. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/server.py +0 -0
  224. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/sse.py +0 -0
  225. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/store.py +0 -0
  226. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/stream_v2.py +0 -0
  227. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/timing/__init__.py +0 -0
  228. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/timing/profiler.py +0 -0
  229. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/timing/timer.py +0 -0
  230. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/traceblock.py +0 -0
  231. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/tracing_session.py +0 -0
  232. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/tunneling/cloudflare.py +0 -0
  233. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/__init__.py +0 -0
  234. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/cache.py +0 -0
  235. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/config.py +0 -0
  236. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/errors.py +0 -0
  237. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/extract.py +0 -0
  238. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/headers.py +0 -0
  239. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/network.py +0 -0
  240. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/retriable_client.py +0 -0
  241. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/stream_codec.py +0 -0
  242. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/utils/uuids.py +0 -0
  243. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/validation.py +0 -0
  244. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/webhook.py +0 -0
  245. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_api/worker_runtime.py +0 -0
  246. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/__init__.py +0 -0
  247. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/checkpointer.py +0 -0
  248. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/__init__.py +0 -0
  249. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/_compat.py +0 -0
  250. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/config.py +0 -0
  251. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/durability.py +0 -0
  252. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/store.py +0 -0
  253. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/struct.py +0 -0
  254. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/conversion/value.py +0 -0
  255. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/checkpointer_pb2.py +0 -0
  256. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/checkpointer_pb2.pyi +0 -0
  257. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/checkpointer_pb2_grpc.py +0 -0
  258. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/checkpointer_pb2_grpc.pyi +0 -0
  259. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/encryption_pb2.py +0 -0
  260. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/encryption_pb2.pyi +0 -0
  261. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/encryption_pb2_grpc.py +0 -0
  262. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/encryption_pb2_grpc.pyi +0 -0
  263. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/engine_common_pb2.py +0 -0
  264. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/engine_common_pb2.pyi +0 -0
  265. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/engine_common_pb2_grpc.py +0 -0
  266. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/engine_common_pb2_grpc.pyi +0 -0
  267. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2.py +0 -0
  268. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2.pyi +0 -0
  269. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2_grpc.py +0 -0
  270. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cancel_run_action_pb2_grpc.pyi +0 -0
  271. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_control_signal_pb2.py +0 -0
  272. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_control_signal_pb2.pyi +0 -0
  273. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_control_signal_pb2_grpc.py +0 -0
  274. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_control_signal_pb2_grpc.pyi +0 -0
  275. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2.py +0 -0
  276. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2.pyi +0 -0
  277. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2_grpc.py +0 -0
  278. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_cron_on_run_completed_pb2_grpc.pyi +0 -0
  279. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_durability_pb2.py +0 -0
  280. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_durability_pb2.pyi +0 -0
  281. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_durability_pb2_grpc.py +0 -0
  282. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_durability_pb2_grpc.pyi +0 -0
  283. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2.py +0 -0
  284. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2.pyi +0 -0
  285. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2_grpc.py +0 -0
  286. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_multitask_strategy_pb2_grpc.pyi +0 -0
  287. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_run_status_pb2.py +0 -0
  288. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_run_status_pb2.pyi +0 -0
  289. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_run_status_pb2_grpc.py +0 -0
  290. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_run_status_pb2_grpc.pyi +0 -0
  291. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2.py +0 -0
  292. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2.pyi +0 -0
  293. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2_grpc.py +0 -0
  294. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_stream_mode_pb2_grpc.pyi +0 -0
  295. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_status_pb2.py +0 -0
  296. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_status_pb2.pyi +0 -0
  297. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_status_pb2_grpc.py +0 -0
  298. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_status_pb2_grpc.pyi +0 -0
  299. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2.py +0 -0
  300. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2.pyi +0 -0
  301. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2_grpc.py +0 -0
  302. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/enum_thread_stream_mode_pb2_grpc.pyi +0 -0
  303. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/errors_pb2.py +0 -0
  304. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/errors_pb2.pyi +0 -0
  305. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/errors_pb2_grpc.py +0 -0
  306. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/errors_pb2_grpc.pyi +0 -0
  307. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/store_pb2.py +0 -0
  308. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/store_pb2.pyi +0 -0
  309. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/store_pb2_grpc.py +0 -0
  310. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/proto/store_pb2_grpc.pyi +0 -0
  311. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/serde.py +0 -0
  312. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_grpc_common/store.py +0 -0
  313. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_license/__init__.py +0 -0
  314. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_license/validation.py +0 -0
  315. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/__init__.py +0 -0
  316. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/checkpoint.py +0 -0
  317. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/database.py +0 -0
  318. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/lifespan.py +0 -0
  319. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/metrics.py +0 -0
  320. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/ops.py +0 -0
  321. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/queue.py +0 -0
  322. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/retry.py +0 -0
  323. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/routes.py +0 -0
  324. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/langgraph_runtime/store.py +0 -0
  325. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/logging.json +0 -0
  326. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/scripts/build_wheel.py +0 -0
  327. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/scripts/create_license.py +0 -0
  328. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/scripts/run_a2a_tck.py +0 -0
  329. {langgraph_api-0.15.4 → langgraph_api-0.16.0}/uv.lock +0 -0
@@ -317,6 +317,10 @@ VERSION_KIND ?= patch
317
317
  bump-version:
318
318
  uv run --with hatch hatch version $(VERSION_KIND)
319
319
 
320
+ sync-runtime-inmem-dep:
321
+ @python3 -c 'import pathlib,re; v=pathlib.Path("../runtime_inmem/langgraph_runtime_inmem/__init__.py").read_text(); m=re.search(r"__version__\s*=\s*\"(\d+)\.(\d+)", v); major,minor=map(int,m.groups()); p=pathlib.Path("pyproject.toml"); text,n=re.subn(r"\"langgraph-runtime-inmem>=[^\"]+\",", f"\"langgraph-runtime-inmem>={major}.{minor}.0.dev0,<{major}.{minor + 1}.0.dev0\",", p.read_text(), count=1); \
322
+ assert n==1, "langgraph-runtime-inmem dep not found in pyproject.toml"; p.write_text(text)'
323
+
320
324
  # A2A TCK (Test Compatibility Kit) targets
321
325
  # Requires a running server: make start (in another terminal)
322
326
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-api
3
- Version: 0.15.4
3
+ Version: 0.16.0
4
4
  Author-email: Will Fu-Hinthorn <will@langchain.dev>, Josh Rogers <josh@langchain.dev>, Parker Rule <parker@langchain.dev>
5
5
  License: Elastic-2.0
6
6
  License-File: LICENSE
@@ -17,7 +17,7 @@ Requires-Dist: jsonschema-rs<0.45,>=0.20.0
17
17
  Requires-Dist: langchain-core>=0.3.64
18
18
  Requires-Dist: langchain-protocol<0.1,>=0.0.18
19
19
  Requires-Dist: langgraph-checkpoint<5,>=3.0.1
20
- Requires-Dist: langgraph-runtime-inmem<0.36.0.dev0,>=0.35.0.dev0
20
+ Requires-Dist: langgraph-runtime-inmem<0.37.0.dev0,>=0.36.0.dev0
21
21
  Requires-Dist: langgraph-sdk>=0.4.4
22
22
  Requires-Dist: langgraph<2,>=0.4.10
23
23
  Requires-Dist: langsmith[otel]>=0.6.3
@@ -7,7 +7,7 @@ This directory contains k6 + Python/Node benchmark tooling for LangGraph API.
7
7
  - `make benchmark-ramp` - Ramps up to a set number of concurrent virtual users, holds plateau load, then ramps down.
8
8
  - **Environment variables:**
9
9
  - `LOAD_SIZE`, `LEVELS`, `PLATEAU_DURATION`
10
- - `P95_RUN_DURATION`, `AVERAGE_RUN_DURATION`
10
+ - `P95_RUN_DURATION`
11
11
  - **Example:**
12
12
  - `BENCHMARK_PROFILE=streaming-long BENCHMARK_TYPE=stream_write LOAD_SIZE=300 LEVELS=3 make benchmark-ramp`
13
13
 
@@ -25,7 +25,7 @@ This directory contains k6 + Python/Node benchmark tooling for LangGraph API.
25
25
  - `make benchmark-reset` - Cleans benchmark server state via `clean-cli.js`.
26
26
  - `make benchmark-new-revision` - Triggers a new benchmark deployment revision.
27
27
 
28
- - `make benchmark-capacity-ci` - Runs the daily capacity matrix for one deployment (`DEPLOYMENT` env). Uses `staircase.py` per cell (default 200–2000 VUs, step 100). Config lives in `capacity-ci.js`.
28
+ - `make benchmark-capacity-ci` - Runs the daily capacity matrix for one deployment (`DEPLOYMENT` env). Uses `staircase.py` per cell with k6's `constant-arrival-rate` executor (open model: a fixed request rate per step, so capacity reads as the service rate; requests k6 has to drop count as failures). Geometry is per deployment in `capacity-ci.js` (dedicated 50 to 600 req/s in steps of 50, serverless 10 to 70 in steps of 5). `K6_EXECUTOR=constant-vus` restores the closed model. A cell that never meets SLO still reports `throughputCeiling`, the best runs/sec any step served.
29
29
  - **Example:**
30
30
  - `DEPLOYMENT=daily-cap-benchmark-dedicated make benchmark-capacity-ci`
31
31
 
@@ -48,6 +48,7 @@ The runner contains the actual logic each virtual user will execute during the b
48
48
  - `store` - Writes, reads, validates, and deletes a Store item with a configurable payload size.
49
49
  - `meta_workload` (randomly delegates to one of the others)
50
50
  - `delta_turns` - Keeps one thread per VU and adds a turn to it every iteration, so latency can be read against thread depth. Built for the delta-channel cells; see below.
51
+ - `interrupt_resume` - Starts a run that pauses on an `interrupt()` before step `interrupt_at`, then resumes it at once. Measures the paused checkpoint write and the full state read on resume. Use with the `interrupt` profile.
51
52
 
52
53
  ## Profiles (`BENCHMARK_PROFILE`)
53
54
 
@@ -60,6 +61,9 @@ Profiles are quick ways to configure the benchmark graph for any run depending o
60
61
  - `parallel-small` - Stateful high-fanout profile with small checkpoint payloads.
61
62
  - `parallel-medium` - Stateful high-fanout profile with medium checkpoint payloads.
62
63
  - `delta-turns` - Cheap turns for the delta-channel cells, so a VU reaches the depth where a `snapshot_frequency` of 1000 fires.
64
+ - `wide-deep` - 50 parallel branches held for 20 supersteps: concurrent checkpoint writes and worker spread at the same time.
65
+ - `messages` - State grows as a chat message list (2 KB per step, 10 steps), so each checkpoint serializes structured objects rather than appending bytes.
66
+ - `interrupt` - 5 steps, pauses before step 3. For the `interrupt_resume` runner.
63
67
 
64
68
  Each profile defines:
65
69
 
@@ -85,6 +89,9 @@ Context overrides (merged on top of profile):
85
89
  - `EXPAND` - Fanout width (number of parallel branches at start).
86
90
  - `STEPS` - Number of graph turns/iterations to execute.
87
91
  - `CHECKPOINT_SIZE` - Bytes written per checkpoint operation.
92
+ - `PAYLOAD` - `text` (default; compresses like real state), `random` (incompressible worst case) or `messages` (a chat message of `CHECKPOINT_SIZE` chars per step).
93
+ - `CHANNELS` - Spread the bytes payload across this many channels (1 to 8). Postgres stores one row per changed channel per version, so this costs rows, not only bytes.
94
+ - `INTERRUPT_AT` - Pause the run before this step (1-based) until resumed. 0 never pauses.
88
95
  - `STORE_VALUE_SIZE` - Store runner payload size in bytes (defaults to 1 MiB).
89
96
  - `LLM_ENABLED` - Enables fake LLM streaming inside the benchmark graph.
90
97
  - `STREAM_SIZE` - Normal total streamed payload size (bytes).
@@ -122,7 +129,11 @@ by replaying them. That moves cost from write to read, and the read cost grows w
122
129
  thread depth. `delta_turns` measures it: one thread per VU, one turn per iteration,
123
130
  `turn_duration` tagged by depth bucket.
124
131
 
125
- Run the cells one at a time and compare `turn_duration` by depth. Both sides get slower
132
+ `benchmark-delta.yml` runs four cells nightly, 20 users for 25 minutes each, 1 KB appended per turn (`CHECKPOINT_SIZE`): plain channel, then
133
+ delta snapshotting every 50 (`benchmark_delta_freq`, the deepagents setting), 500
134
+ (`benchmark_delta_500`) and 1000 (`benchmark_delta`, the library default). `delta-report.js` posts
135
+ a headline table (non-delta vs delta at 50: read, write, storage by depth) and one table per metric
136
+ across the three frequencies. The messages and migration cells stay manual. Run the cells one at a time and compare `turn_duration` by depth. Both sides get slower
126
137
  with depth for different reasons, the control by re-writing a growing value and delta by
127
138
  replaying more writes, so the deliverable is which curve is worse and where they cross.
128
139
 
@@ -131,12 +142,14 @@ replaying more writes, so the deliverable is which curve is worse and where they
131
142
  | control | `GRAPH_ID=benchmark EXPECT_DELTA=false` |
132
143
  | delta, snapshot every 1000 | `GRAPH_ID=benchmark_delta` |
133
144
  | delta, snapshot every 50 | `GRAPH_ID=benchmark_delta_freq` |
145
+ | delta, snapshot every 500 | `GRAPH_ID=benchmark_delta_500` |
146
+ | delta on a message list | `GRAPH_ID=benchmark_delta_messages PAYLOAD=messages` |
134
147
  | migration | `GRAPH_ID=benchmark_delta MIGRATE_FROM_GRAPH_ID=benchmark MIGRATE_AFTER=200` |
135
148
 
136
149
  ```bash
137
150
  BASE_URL=... BENCHMARK_TYPE=delta_turns BENCHMARK_PROFILE=delta-turns \
138
151
  GRAPH_ID=benchmark_delta LOAD_SIZE=25 LEVELS=1 PLATEAU_DURATION=300 \
139
- P95_RUN_DURATION=... AVERAGE_RUN_DURATION=... k6 run --system-tags='[]' ramp.js
152
+ P95_RUN_DURATION=... k6 run --system-tags='[]' ramp.js
140
153
  ```
141
154
 
142
155
  Knobs beyond the common ones:
@@ -146,21 +159,23 @@ Knobs beyond the common ones:
146
159
  - `MIGRATE_FROM_GRAPH_ID` / `MIGRATE_AFTER` - run the first N turns on another graph, then
147
160
  switch on the same thread.
148
161
  - `VERIFY_EVERY` - cold re-read cadence in turns. Default 50, `0` disables.
149
- - `DEPTH_BUCKET` - width of a depth bucket. Default 100.
162
+ - `DEPTH_BUCKET` - width of a depth bucket. Defaults to `VERIFY_EVERY`, so each bucket holds one cold read per thread.
150
163
 
151
164
  Two things to know before reading any result:
152
165
 
153
166
  - **Run at low `LOAD_SIZE`.** Depth and load fight each other: saturate the deployment and
154
167
  turn time measures queueing, not reconstruction. Verify the control curve is flat before
155
168
  trusting the delta one.
156
- - **`successful_runs` will fail its threshold.** The count is derived from a duration table
157
- that assumes the existing workload. Pass `P95_RUN_DURATION` and `AVERAGE_RUN_DURATION`,
158
- and treat these cells as report-only until the numbers settle.
169
+ - **The ramp gates on p95 and error rate only.** Pass a `P95_RUN_DURATION` that fits the
170
+ cell, and treat these cells as report-only until the numbers settle.
159
171
 
160
172
  The runner fails a turn rather than reporting a false pass when delta is not actually on:
161
173
  the `DeltaChannel` import is version-guarded, and its stub degrades to a plain channel.
162
- Storage savings, the reason the feature exists, are not measured here; that needs a query
163
- against the deployment database, which CI cannot reach.
174
+ At every `VERIFY_EVERY` turn it also times the cold `GET /threads/{thread_id}/state` as
175
+ `state_read_duration` (the read path alone; a turn is one save plus the read before it), and
176
+ reads `GET /threads/{thread_id}/storage` and records
177
+ `thread_storage_bytes` tagged by depth bucket and graph, so the control and delta cells can
178
+ be compared on the bytes they occupy, which is what the feature exists to reduce.
164
179
 
165
180
  ## Outputs
166
181
 
@@ -177,6 +192,12 @@ against the deployment database, which CI cannot reach.
177
192
 
178
193
  - Daily ramp benchmark: `.github/workflows/benchmark.yml`
179
194
  - Daily capacity matrix: `.github/workflows/benchmark-capacity-matrix.yml`
195
+ - Daily delta-channel cells: `.github/workflows/benchmark-delta.yml`, reported by `delta-report.js`
196
+
197
+ The staging deployments build their graphs from
198
+ [langgraph-server-benchmarks](https://github.com/langchain-ai/langgraph-server-benchmarks), not
199
+ from this repo. Its `src/agent/graph.py` is a copy of `api/tests/graphs/benchmark.py`; a graph
200
+ change here reaches the nightly only once that copy is updated.
180
201
 
181
202
  ### Capacity matrix setup
182
203
 
@@ -184,6 +205,19 @@ against the deployment database, which CI cannot reach.
184
205
  2. Fill in `deploymentId` and `baseUrl` in `CAPACITY_DEPLOYMENTS` at the top of [`capacity-ci.js`](capacity-ci.js).
185
206
  3. Scheduled run: two parallel CI jobs (one per deployment), aggregate job posts Slack summary.
186
207
 
208
+ Each cell also records the deployment's own view of its window (queue and API pod counts, CPU peaks) from the control plane's deployment metrics route. The posted message names any component at or over 95% of its CPU request as that tier's bottleneck, and the full per-cell table goes to the message's thread. Every tier starts at one queue pod and autoscales during a cell, so a capacity number is partly a pod-count number; pinning pods at the tier maximum would need the benchmark deployments recreated as `external_docker` sources, the only kind whose `resource_spec` the API accepts.
209
+
210
+ ### Slack reports
211
+
212
+ Every report follows one shape so the channel reads the same way each morning, on a phone as well as a desktop:
213
+
214
+ - The first line is the verdict: a dot (green quiet, yellow something regressed or a bottleneck or errors, red something failed), the report name and date, `manual run` when dispatched by hand, the API version, and a clause saying what needs attention. The second line says what the benchmark is, for a reader outside the team.
215
+ - Then `Failed`, `Regressed`, `Improved` and `Bottlenecks` or `Errors` lines, worst first. `Regressed` prints `none` rather than going quiet.
216
+ - One table per deployment, about 40 characters wide so it fits a phone, with `▲ better` / `▼ worse` markers on cells that left their usual range.
217
+ - One legend line (pass rule, arrows, dot colors), then a link to the thread, which holds the per-cell or per-run numbers and the longer *How to read*, and to the run's artifacts.
218
+
219
+ Both reports compare tonight with the previous scheduled nights (`history.js`, artifacts fetched with `gh`). A figure is flagged only when it leaves that cell's own observed range, and only once five same-kind nights exist; before that the table says how many nights it has. The daily benchmark posts one digest per workflow once its last runner finishes (`daily-report.js`, fed by each runner's artifact), not one message per runner. The delta-channel report (`delta-report.js`) keeps the plain-vs-delta table in the channel and puts the snapshot-frequency sweep in the thread.
220
+
187
221
  Local commands:
188
222
 
189
223
  ```bash
@@ -201,3 +235,12 @@ node capacity-ci.js --aggregate --summaries-dir ./artifacts --slack
201
235
 
202
236
  - [k6 docs](https://k6.io/docs/)
203
237
  - [k6 JavaScript API](https://k6.io/docs/javascript-api/)
238
+
239
+ ## What a run returns
240
+
241
+ The graph's output schema is `counter`, `start_time`, `end_time`, `payload_bytes` and
242
+ `payload_sha256`. The payload channels themselves stay out of `/runs/wait` bodies and
243
+ `values` events, so `CHECKPOINT_SIZE` drives database writes only; before this, every
244
+ response carried the whole payload base64-encoded and write cost could not be told from
245
+ egress. `GET /threads/{thread_id}/state` still returns every channel. The delta cells
246
+ verify a cold re-read by hashing it and comparing with `payload_sha256`.
@@ -13,6 +13,9 @@ export interface BenchmarkContext {
13
13
  burst_mode: boolean;
14
14
  burst_probability: number;
15
15
  burst_size: number;
16
+ payload?: 'text' | 'random' | 'messages';
17
+ channels?: number;
18
+ interrupt_at?: number;
16
19
  }
17
20
 
18
21
  export interface BenchmarkProfileEnv {
@@ -32,6 +35,9 @@ export interface BenchmarkProfileEnv {
32
35
  BURST_MODE?: string;
33
36
  BURST_PROBABILITY?: string;
34
37
  BURST_SIZE?: string;
38
+ PAYLOAD?: string;
39
+ CHANNELS?: string;
40
+ INTERRUPT_AT?: string;
35
41
  }
36
42
 
37
43
  export interface BenchmarkProfile {
@@ -173,6 +179,64 @@ export const BENCHMARK_PROFILES: Record<string, BenchmarkProfile> = {
173
179
  burst_size: 0,
174
180
  },
175
181
  },
182
+ // Width held for many supersteps: concurrent checkpoint writes and worker
183
+ // spread at the same time, which the wide-but-one-step profiles never do.
184
+ 'wide-deep': {
185
+ resumable: false,
186
+ runMode: 'stateless',
187
+ context: {
188
+ delay: 0,
189
+ delay_jitter_ratio: 0.2,
190
+ expand: 50,
191
+ steps: 20,
192
+ checkpoint_size: 1_000,
193
+ llm_enabled: false,
194
+ stream_size: 0,
195
+ chunk_size: 1,
196
+ burst_mode: false,
197
+ burst_probability: 0,
198
+ burst_size: 0,
199
+ },
200
+ },
201
+ // State grows as a message list, so each checkpoint serializes structured
202
+ // objects rather than appending bytes.
203
+ messages: {
204
+ resumable: false,
205
+ runMode: 'stateful',
206
+ context: {
207
+ delay: 0,
208
+ delay_jitter_ratio: 0.2,
209
+ expand: 1,
210
+ steps: 10,
211
+ checkpoint_size: 2_000,
212
+ payload: 'messages',
213
+ llm_enabled: false,
214
+ stream_size: 0,
215
+ chunk_size: 1,
216
+ burst_mode: false,
217
+ burst_probability: 0,
218
+ burst_size: 0,
219
+ },
220
+ },
221
+ // For the interrupt_resume runner.
222
+ interrupt: {
223
+ resumable: false,
224
+ runMode: 'stateful',
225
+ context: {
226
+ delay: 0,
227
+ delay_jitter_ratio: 0.2,
228
+ expand: 1,
229
+ steps: 5,
230
+ checkpoint_size: 1_000,
231
+ interrupt_at: 3,
232
+ llm_enabled: false,
233
+ stream_size: 0,
234
+ chunk_size: 1,
235
+ burst_mode: false,
236
+ burst_probability: 0,
237
+ burst_size: 0,
238
+ },
239
+ },
176
240
  };
177
241
 
178
242
  export function getBenchmarkProfile(profileName?: string): { name: string; profile: BenchmarkProfile } {
@@ -241,6 +305,9 @@ export function get_profile(env: BenchmarkProfileEnv): ParsedBenchmarkProfile {
241
305
  ? { burst_probability: parseOptionalFloat(env.BURST_PROBABILITY)! }
242
306
  : {}),
243
307
  ...(parseOptionalInt(env.BURST_SIZE) !== undefined ? { burst_size: parseOptionalInt(env.BURST_SIZE)! } : {}),
308
+ ...(env.PAYLOAD ? { payload: env.PAYLOAD as 'text' | 'random' | 'messages' } : {}),
309
+ ...(parseOptionalInt(env.CHANNELS) !== undefined ? { channels: parseOptionalInt(env.CHANNELS)! } : {}),
310
+ ...(parseOptionalInt(env.INTERRUPT_AT) !== undefined ? { interrupt_at: parseOptionalInt(env.INTERRUPT_AT)! } : {}),
244
311
  });
245
312
 
246
313
  return {
@@ -9,6 +9,7 @@ import { ThreadsSearchMetadata } from './threads_search_metadata.js';
9
9
  import { Store } from './store.js';
10
10
  import { MetaWorkload } from './meta_workload.js';
11
11
  import { DeltaTurns } from './delta_turns.js';
12
+ import { InterruptResume } from './interrupt_resume.js';
12
13
  import type { BenchmarkRunner } from './benchmark-runner.js';
13
14
 
14
15
  export class Benchmarks {
@@ -36,6 +37,8 @@ export class Benchmarks {
36
37
  return MetaWorkload;
37
38
  case DeltaTurns.toString():
38
39
  return DeltaTurns;
40
+ case InterruptResume.toString():
41
+ return InterruptResume;
39
42
  default:
40
43
  throw new Error(`Unknown benchmark type: ${type}`);
41
44
  }
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
3
3
  import {
4
4
  planTurn,
5
5
  deltaGuardFailure,
6
+ digestDrift,
6
7
  stateDrift,
7
8
  unusableCheckpointSize,
8
9
  } from './dist/delta-turns-lib.js';
@@ -167,3 +168,15 @@ test('the delta-turns profile is wired and writes a growing channel', () => {
167
168
  assert.equal(profile.context.delay, 0);
168
169
  assert.equal(profile.context.steps, 1);
169
170
  });
171
+
172
+ test('matching digests are no drift, and a changed byte anywhere is', () => {
173
+ const live = 'a'.repeat(64);
174
+ assert.equal(digestDrift(live, live), null);
175
+ assert.equal(digestDrift(live, 'b'.repeat(64)), `digest_drift:${'a'.repeat(12)}!=${'b'.repeat(12)}`);
176
+ });
177
+
178
+ test('a missing digest on either side is reported, not passed', () => {
179
+ assert.equal(digestDrift(undefined, 'a'.repeat(64)), 'run_digest_missing');
180
+ assert.equal(digestDrift('', 'a'.repeat(64)), 'run_digest_missing');
181
+ assert.equal(digestDrift('a'.repeat(64), null), 'cold_digest_missing');
182
+ });
@@ -100,3 +100,13 @@ function encodeChannel(value: unknown): string {
100
100
  if (value == null) return '';
101
101
  return typeof value === 'string' ? value : JSON.stringify(value);
102
102
  }
103
+
104
+ /**
105
+ * The run reports the SHA-256 of its stored payload; the cold read is hashed
106
+ * the same way. A rebuild that loses or corrupts a write changes the hash.
107
+ */
108
+ export function digestDrift(liveDigest: unknown, coldDigest: unknown): string | null {
109
+ if (typeof liveDigest !== 'string' || liveDigest === '') return 'run_digest_missing';
110
+ if (typeof coldDigest !== 'string' || coldDigest === '') return 'cold_digest_missing';
111
+ return liveDigest === coldDigest ? null : `digest_drift:${liveDigest.slice(0, 12)}!=${coldDigest.slice(0, 12)}`;
112
+ }
@@ -1,6 +1,8 @@
1
1
  import { BenchmarkRunner } from './benchmark-runner.js';
2
2
  import type { ErrorMetrics } from './benchmark-runner.js';
3
3
  import { check } from 'k6';
4
+ import crypto from 'k6/crypto';
5
+ import encoding from 'k6/encoding';
4
6
  import http from 'k6/http';
5
7
  import { Trend } from 'k6/metrics';
6
8
  import type { BenchmarkResult, BenchmarkGraphOptions, HttpResponse } from './types.js';
@@ -10,22 +12,36 @@ import { logFailure } from './log-failure.js';
10
12
  import {
11
13
  planTurn,
12
14
  deltaGuardFailure,
13
- stateDrift,
15
+ digestDrift,
14
16
  unusableCheckpointSize,
15
17
  } from './delta-turns-lib.js';
16
18
  import type { DeltaTurnsOptions, TurnPlan } from './delta-turns-lib.js';
17
19
 
18
- const CHANNEL = 'cumulative_checkpoint';
20
+ const PAYLOAD_CHANNELS = [
21
+ 'cumulative_checkpoint',
22
+ 'payload_2',
23
+ 'payload_3',
24
+ 'payload_4',
25
+ 'payload_5',
26
+ 'payload_6',
27
+ 'payload_7',
28
+ 'payload_8',
29
+ ] as const;
19
30
 
20
31
  /** Tagged by depth: VUs ramp in, so clock time is not thread length. */
21
32
  const turnDuration = new Trend('turn_duration', true);
33
+ /** The read path alone: a cold GET /state replays the channel without writing. */
34
+ const stateReadDuration = new Trend('state_read_duration', true);
35
+ const checkpointSaveDuration = new Trend('checkpoint_save', true);
36
+ const threadStorageBytes = new Trend('thread_storage_bytes', false);
22
37
 
23
38
  const TURN_CONFIG: Omit<DeltaTurnsOptions, 'graphId'> = {
24
39
  migrateFromGraphId: __ENV.MIGRATE_FROM_GRAPH_ID || '',
25
40
  migrateAfter: parseInt(__ENV.MIGRATE_AFTER || '0', 10),
26
41
  expectDelta: (__ENV.EXPECT_DELTA || 'true') === 'true',
27
42
  verifyEvery: parseInt(__ENV.VERIFY_EVERY || '50', 10),
28
- bucketSize: parseInt(__ENV.DEPTH_BUCKET || '100', 10),
43
+ // One bucket per cold read, so every row of the report is one sample per thread.
44
+ bucketSize: parseInt(__ENV.DEPTH_BUCKET || __ENV.VERIFY_EVERY || '50', 10),
29
45
  };
30
46
 
31
47
  // Each k6 VU runs this module in its own context, so these are per VU.
@@ -37,6 +53,8 @@ interface DeltaTurnsData {
37
53
  plan: TurnPlan;
38
54
  guardFailure: string | null;
39
55
  drift: string | null;
56
+ /** Non-200 status of the cold state read, which is a failed request, not a correctness failure. */
57
+ stateStatus: number | null;
40
58
  }
41
59
 
42
60
  export class DeltaTurns extends BenchmarkRunner {
@@ -68,6 +86,9 @@ export class DeltaTurns extends BenchmarkRunner {
68
86
  input: {},
69
87
  context: { ...benchmarkGraphOptions.context, steps: plan.steps },
70
88
  config: { recursion_limit: plan.steps + 2 },
89
+ // The graph times its own checkpoint save as the gap between its two
90
+ // steps; a background save would overlap the second step and hide in it.
91
+ durability: 'sync',
71
92
  });
72
93
 
73
94
  const startedAt = Date.now();
@@ -78,16 +99,34 @@ export class DeltaTurns extends BenchmarkRunner {
78
99
  );
79
100
  turnDuration.add(Date.now() - startedAt, { depth: plan.depthBucket });
80
101
  addResponse(responses, 'wait', response);
102
+ const saveMs = response.status === 200 ? safeJson(response.body)?.save_ms : null;
103
+ if (typeof saveMs === 'number' && saveMs > 0) {
104
+ // Snapshots land on multiples of the bucket width, so the bucket's last
105
+ // turn is where a snapshot save shows; a percentile over the bucket hides it.
106
+ const rowTurn = plan.steps % TURN_CONFIG.bucketSize === 0 ? '1' : '0';
107
+ checkpointSaveDuration.add(saveMs, { depth: plan.depthBucket, row_turn: rowTurn });
108
+ }
81
109
 
82
110
  let guardFailure: string | null = null;
83
111
  let drift: string | null = null;
112
+ let stateStatus: number | null = null;
84
113
  // A failed body is not state, and comparing against it would report drift
85
114
  // on top of the failure that caused it.
86
115
  if (response.status === 200 && (plan.guard || plan.verify)) {
87
116
  const state = http.get(`${baseUrl}/threads/${threadId}/state`, requestParams);
117
+ // Time to first byte: the server cannot start answering before it has
118
+ // rebuilt the value, and the body that follows is the whole state, which
119
+ // at depth is hundreds of KB the turn itself never sends. Wall time made
120
+ // a plain-channel read cost as much as a whole turn. The turn-2 guard
121
+ // read is at depth 2, not the bucket's end, so it is not timed.
122
+ if (state.status === 200 && plan.verify) {
123
+ stateReadDuration.add(state.timings.waiting, { depth: plan.depthBucket });
124
+ }
88
125
  addResponse(responses, 'state', state);
89
126
  const body = state.status === 200 ? safeJson(state.body) : null;
90
- if (body === null) {
127
+ if (state.status !== 200) {
128
+ stateStatus = state.status;
129
+ } else if (body === null) {
91
130
  guardFailure = plan.guard ? 'state_unreadable' : guardFailure;
92
131
  drift = plan.verify ? 'state_unreadable' : drift;
93
132
  } else {
@@ -95,13 +134,26 @@ export class DeltaTurns extends BenchmarkRunner {
95
134
  guardFailure = deltaGuardFailure(body.metadata, plan.expectDelta);
96
135
  }
97
136
  if (plan.verify) {
98
- // `/runs/wait` returns the state values themselves; `/state` nests them.
99
- drift = stateDrift(safeJson(response.body), body.values, CHANNEL);
137
+ drift = digestDrift(safeJson(response.body)?.payload_sha256, coldDigest(body.values));
138
+ }
139
+ }
140
+ if (plan.verify) {
141
+ // Servers before 0.16 have no storage endpoint. A 404 there is a missing
142
+ // sample, not a failed request: without this, one 404 per 50 turns is
143
+ // about 2% and trips the 1% error budget on every cell.
144
+ const storage = http.get(`${baseUrl}/threads/${threadId}/storage`, {
145
+ ...requestParams,
146
+ responseCallback: http.expectedStatuses(200, 404),
147
+ });
148
+ addResponse(responses, 'storage', storage);
149
+ const total = storage.status === 200 ? safeJson(storage.body)?.total_bytes : null;
150
+ if (typeof total === 'number') {
151
+ threadStorageBytes.add(total, { depth: plan.depthBucket, graph: plan.graphId });
100
152
  }
101
153
  }
102
154
  }
103
155
 
104
- return okResult(responses, { rawResponse: response, plan, guardFailure, drift });
156
+ return okResult(responses, { rawResponse: response, plan, guardFailure, drift, stateStatus });
105
157
  }
106
158
 
107
159
  static validate(
@@ -120,6 +172,7 @@ export class DeltaTurns extends BenchmarkRunner {
120
172
  'Response does not contain __error__': () => json?.__error__ === undefined,
121
173
  'Run advanced the thread by one turn': () =>
122
174
  json == null || plan == null || json.counter === plan.steps,
175
+ 'Cold state read succeeded': () => result.data?.stateStatus == null,
123
176
  'Delta storage is as expected': () => result.data?.guardFailure == null,
124
177
  'Cold re-read matches the run': () => result.data?.drift == null,
125
178
  });
@@ -131,6 +184,7 @@ export class DeltaTurns extends BenchmarkRunner {
131
184
  const detail = [
132
185
  plan ? `turn=${plan.steps} graph=${plan.graphId}` : '',
133
186
  res ? `status=${res.status}` : '',
187
+ result.data?.stateStatus ? `state_status=${result.data.stateStatus}` : '',
134
188
  result.data?.guardFailure ? `guard=${result.data.guardFailure}` : '',
135
189
  result.data?.drift ? `drift=${result.data.drift}` : '',
136
190
  ]
@@ -139,7 +193,9 @@ export class DeltaTurns extends BenchmarkRunner {
139
193
  logFailure(DeltaTurns.toString(), result, { extra: detail });
140
194
  recordError(
141
195
  classifyError({
142
- response: res ?? result.responses?.create_thread ?? null,
196
+ response: result.data?.stateStatus
197
+ ? result.responses?.state ?? null
198
+ : res ?? result.responses?.create_thread ?? null,
143
199
  hasApiError: json?.__error__ !== undefined,
144
200
  missingMessage: result.data?.drift != null || result.data?.guardFailure != null,
145
201
  }),
@@ -167,3 +223,22 @@ function safeJson(body: string | null | ArrayBuffer): Record<string, any> | null
167
223
  return null;
168
224
  }
169
225
  }
226
+
227
+ function coldDigest(values: Record<string, unknown> | undefined): string | null {
228
+ if (values == null || typeof values !== 'object') return null;
229
+ // Reproduce the graph's payload_digest: every bytes channel in order, then
230
+ // the concatenated message contents. Absent channels default to empty, so
231
+ // they add nothing, exactly as the unwritten bytes channels do server-side.
232
+ const hasher = crypto.createHash('sha256');
233
+ for (const name of PAYLOAD_CHANNELS) {
234
+ const stored = values[name];
235
+ if (typeof stored === 'string' && stored.length > 0) {
236
+ hasher.update(encoding.b64decode(stored));
237
+ }
238
+ }
239
+ const messages = values.messages;
240
+ if (Array.isArray(messages) && messages.length > 0) {
241
+ hasher.update(messages.map((m) => String((m as { content?: unknown })?.content ?? '')).join(''));
242
+ }
243
+ return hasher.digest('hex');
244
+ }
@@ -0,0 +1,119 @@
1
+ import { BenchmarkRunner } from './benchmark-runner.js';
2
+ import type { ErrorMetrics } from './benchmark-runner.js';
3
+ import { check } from 'k6';
4
+ import http from 'k6/http';
5
+ import type { BenchmarkResult, BenchmarkGraphOptions, HttpResponse } from './types.js';
6
+ import { addResponse, okResult, failResult } from './types.js';
7
+ import { classifyError, recordError } from './classify-error.js';
8
+ import { logFailure } from './log-failure.js';
9
+
10
+ interface InterruptResumeData {
11
+ paused: ReturnType<typeof http.post>;
12
+ resumed: ReturnType<typeof http.post>;
13
+ expectedSteps: number;
14
+ }
15
+
16
+ /**
17
+ * Pause a run mid-way and resume it. The virtual user is the human: it answers
18
+ * at once. What is measured is the server's work around the pause: writing the
19
+ * paused checkpoint, and on resume reading the whole thread state back before
20
+ * continuing, which is the most expensive read a delta channel can face.
21
+ */
22
+ export class InterruptResume extends BenchmarkRunner {
23
+ static run(
24
+ baseUrl: string,
25
+ requestParams: Record<string, unknown>,
26
+ benchmarkGraphOptions: BenchmarkGraphOptions
27
+ ): BenchmarkResult<InterruptResumeData> {
28
+ const base = benchmarkGraphOptions.context;
29
+ const context = {
30
+ ...base,
31
+ interrupt_at: base.interrupt_at || Math.max(1, Math.ceil(base.steps / 2)),
32
+ };
33
+ const config = { recursion_limit: Math.max(context.expand, context.steps) + 4 };
34
+ const responses: Record<string, HttpResponse> = {};
35
+
36
+ const thread = http.post(`${baseUrl}/threads`, '{}', requestParams);
37
+ addResponse(responses, 'create_thread', thread);
38
+ if (thread.status !== 200) {
39
+ return failResult('create_thread', responses) as BenchmarkResult<InterruptResumeData>;
40
+ }
41
+ const threadId = (thread.json() as { thread_id: string }).thread_id;
42
+ const url = `${baseUrl}/threads/${threadId}/runs/wait`;
43
+
44
+ const paused = http.post(
45
+ url,
46
+ JSON.stringify({ assistant_id: benchmarkGraphOptions.graph_id, input: {}, context, config }),
47
+ requestParams
48
+ );
49
+ addResponse(responses, 'pause', paused);
50
+ if (paused.status !== 200) {
51
+ return failResult('pause', responses) as BenchmarkResult<InterruptResumeData>;
52
+ }
53
+
54
+ const resumed = http.post(
55
+ url,
56
+ JSON.stringify({
57
+ assistant_id: benchmarkGraphOptions.graph_id,
58
+ command: { resume: true },
59
+ context,
60
+ config,
61
+ }),
62
+ requestParams
63
+ );
64
+ addResponse(responses, 'resume', resumed);
65
+ return okResult(responses, { paused, resumed, expectedSteps: context.steps });
66
+ }
67
+
68
+ static validate(
69
+ result: BenchmarkResult<InterruptResumeData>,
70
+ errorMetrics: ErrorMetrics
71
+ ): boolean {
72
+ const data = result.data;
73
+ const pausedJson = safeJson(data?.paused?.body);
74
+ const resumedJson = safeJson(data?.resumed?.body);
75
+ let success = false;
76
+ try {
77
+ success = check(result, {
78
+ 'Pause request succeeded': () => (data?.paused?.status ?? 0) === 200,
79
+ 'Run paused on an interrupt': () => Array.isArray(pausedJson?.__interrupt__) && pausedJson.__interrupt__.length > 0,
80
+ 'Resume request succeeded': () => (data?.resumed?.status ?? 0) === 200,
81
+ 'Resumed run finished every step': () =>
82
+ resumedJson != null &&
83
+ resumedJson.__interrupt__ === undefined &&
84
+ resumedJson.__error__ === undefined &&
85
+ resumedJson.counter === data?.expectedSteps,
86
+ });
87
+ } catch (error) {
88
+ console.log(`Unknown error checking result: ${(error as Error).message}`);
89
+ }
90
+ if (!success) {
91
+ logFailure(InterruptResume.toString(), result, {
92
+ extra: `pause=${data?.paused?.status} resume=${data?.resumed?.status} counter=${resumedJson?.counter} expected=${data?.expectedSteps}`,
93
+ });
94
+ recordError(
95
+ classifyError({
96
+ response: data?.resumed ?? data?.paused ?? result.responses?.create_thread ?? null,
97
+ hasApiError: resumedJson?.__error__ !== undefined || pausedJson?.__error__ !== undefined,
98
+ missingMessage: resumedJson != null && resumedJson.counter !== data?.expectedSteps,
99
+ }),
100
+ errorMetrics
101
+ );
102
+ }
103
+ return success;
104
+ }
105
+
106
+ static toString(): string {
107
+ return 'interrupt_resume';
108
+ }
109
+ }
110
+
111
+ function safeJson(body: unknown): Record<string, any> | null {
112
+ if (typeof body !== 'string') return null;
113
+ try {
114
+ const parsed = JSON.parse(body);
115
+ return parsed != null && typeof parsed === 'object' ? parsed : null;
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
@@ -44,6 +44,9 @@ export interface BenchmarkContext {
44
44
  burst_mode: boolean;
45
45
  burst_probability: number;
46
46
  burst_size: number;
47
+ payload?: 'text' | 'random' | 'messages';
48
+ channels?: number;
49
+ interrupt_at?: number;
47
50
  }
48
51
 
49
52
  /**