feat: enhance tool call lifecycle management with call_id - #42
Merged
Conversation
- Introduced a stable `call_id` for `tool_call.started` and `tool_call.completed` events to associate parallel or same-name invocations within a step. - Updated the `emit_tool_call_started` and `emit_tool_call_completed` methods to accept and return `call_id`. - Modified adapters to utilize `call_id` for better tracking of tool call results and errors. - Enhanced documentation to reflect changes in the tool call lifecycle and the usage of `call_id`.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a stable call_id (persisted as ToolCall.id) to reliably correlate tool_call.started and tool_call.completed events, especially when tools run in parallel or when the same tool name is invoked multiple times in the same step. It updates both frontend event application logic and backend persistence/emission paths, and adds tests + documentation to lock in the new lifecycle contract.
Changes:
- Add stable
call_idgeneration/propagation through adapter emit APIs, DB persistence, and SSE payloads (with legacy fallback behavior whencall_idis omitted). - Update frontend SSE reducer logic to associate tool-call started/completed events by
call_id(or oldest pending same-name call when missing). - Add regression tests covering out-of-order completion and parallel same-name tool calls; update docs describing the contract.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/lib/run-events.ts | Track tool-call lifecycle by call_id, with fallback to oldest pending same-name call. |
| docs/data-model.md | Document ToolCall.id as the lifecycle association key for SSE events. |
| docs/api-contract.md | Document call_id usage and adapter expectations for started/completed pairing. |
| backend/tests/test_tool_call_lifecycle.py | New tests for call_id matching, fallback behavior, and parallel emissions via executor. |
| backend/tests/test_adapter_tools.py | Assert call_id presence and propagation plus latency_ms typing. |
| backend/app/worker/executor.py | Serialize event handling from concurrent adapter tasks to protect shared AsyncSession. |
| backend/app/services/run_service.py | Persist tool calls using provided call_id, resolve completion by call_id with legacy fallback, and rebroadcast persisted call_id. |
| backend/app/adapters/langgraph_adapter.py | Execute tool calls in parallel within a step to exercise/benefit from call_id association. |
| backend/app/adapters/echo_adapter.py | Pass call_id from started → completed to ensure correct association. |
| backend/app/adapters/base.py | Add ULID-based new_tool_call_id() and return call_id from emit_tool_call_started. |
| backend/app/adapters/adapter_tools.py | Use call_id end-to-end and emit latency_ms based on monotonic timing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+565
to
567
| for tc, result in pairs: | ||
| name = str(tc.get("name") or "") | ||
| tool_results[name] = result |
Comment on lines
+94
to
+101
| # Serialize DB writes: adapters may emit from concurrent tasks | ||
| # (e.g. parallel tool calls) while sharing one AsyncSession. | ||
| emit_lock = asyncio.Lock() | ||
|
|
||
| async def _emit(event_type: EventType, data: dict[str, Any]) -> None: | ||
| async with emit_lock: | ||
| await service._handle_event(run.id, event_type, data) | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
call_idfortool_call.startedandtool_call.completedevents to associate parallel or same-name invocations within a step.emit_tool_call_startedandemit_tool_call_completedmethods to accept and returncall_id.call_idfor better tracking of tool call results and errors.call_id.