Skip to content

⏱️ feat: Expose Tool Preparation and Dispatch Timing - #577

Merged
danny-avila merged 4 commits into
mainfrom
lia/tool-call-progress-plan
Sep 28, 2026
Merged

danny-avila merged 4 commits into
mainfrom
lia/tool-call-progress-plan

Conversation

@lia-by-librechat

Copy link
Copy Markdown
Contributor

Why

The current tool-step elapsed time can include minutes of model-generated arguments and post-dispatch tool work. Displaying it as tool runtime misattributes the wait.

What

  • Stamp on_run_step_delta.observed_at when an argument fragment first reaches the SDK, before any awaited step handling. Consumers can keep the first timestamp per call/index as the start of preparation.
  • Emit a small on_tool_calls_dispatched event immediately before direct, eager, or host tool dispatch. Only the timestamp and call identity travel, never the arguments. A denied/aborted tool does not emit a dispatch event.
  • Preserve existing run-step timestamps and tool completion timestamps. The interval between dispatch and completion is SDK-to-result time, not an MCP or ClickHouse query duration. Stream events can arrive out of order, so hosts should correlate by call ID and timestamps, and omit a preparation duration if no argument chunks were streamed.

Verification

  • Focused Jest: 200 passed across 8 suites; final changed specs 77 passed across 2 suites.
  • tsc --noEmit, tsdown, touched-file ESLint, import sort check, and git diff --check: passed.
  • Prettier on new test passed; several existing large files are already unformatted in the base commit.

Follow-up

LibreChat needs a companion consumer/UI change to present the new phases. This PR does not alter ClickHouse-specific metrics or claim database execution time.

@danny-avila

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T17:34:35.297182Z b98de52 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b98de52969

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/tools/ToolNode.ts
};

void safeDispatchCustomEvent(
GraphEvents.ON_TOOL_CALLS_DISPATCHED,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward dispatch timing from subagent workflows

When this ToolNode runs inside an attached or detached subagent, SubagentExecutor replaces the inherited callback chain with createForwarderCallback (src/tools/subagent/SubagentExecutor.ts:2753-2764), whose custom-event switch handles ON_TOOL_EXECUTE and run-step events but silently falls through for ON_TOOL_CALLS_DISPATCHED (:3444-3475). Consequently, host handlers never receive this handoff event for tools executed by child agents, so the newly exposed preparation/runtime split is unavailable precisely for multi-agent runs; add this event to the subagent forwarding path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Child dispatch now forwards as a sanitized, scoped subagent timing update with no arguments; the forwarder regression passed. Fixed in 31f7ce3. Focused SDK tests: 274 passed; typecheck, tsdown, touched lint and import order passed.

Comment thread src/types/stream.ts
Comment on lines +272 to +274
* it does not claim the host has begun its own work. Paired with the tool
* completion `completed_at`, the stamp measures SDK handoff-to-result time,
* not MCP or database execution time. The earliest `observed_at` tool-call

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stamp completion when the host result arrives

For a host that reports results only through the required resolve callback, this documented interval does not end when the SDK receives the result: dispatchPromise waits for both resultSettled and the entire ON_TOOL_EXECUTE handler to return, and only afterward does dispatchStepCompleted call Date.now(). The wrapper in safeDispatchCustomEvent can further delay request.resolve until Langfuse metadata tracing finishes. A handler that calls resolve(results) and then performs slow cleanup therefore inflates dispatched_at→completed_at even though the result was already received; capture the receipt timestamp in resolve (and propagate it to completion) rather than stamping after dispatch settlement.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host results are stamped when resolve/onResult is called, before tracing and handler cleanup; the slow-cleanup regression passed. Fixed in 31f7ce3. Focused SDK tests: 274 passed; typecheck, tsdown, touched lint and import order passed.

Comment thread src/tools/handlers.ts Outdated
toolCallChunks: ToolCallChunk[];
metadata?: Record<string, unknown>;
}): Promise<void> {
const observedAt = Date.now();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Capture fragment time before eager dispatch

On a coalesced model chunk containing both complete tool_calls and tool_call_chunks, this timestamp is captured too late: ChatModelStreamHandler.handle first awaits handleToolCalls and can then call startEagerToolExecutions (src/stream.ts:1835-1880) before it reaches handleToolCallChunks at :1928. Thus a slow run-step handler skews observed_at, and an eager call can even emit dispatched_at before the first fragment's observed_at, yielding a negative preparation duration. Capture the receipt time at handler entry and pass it through, or process/stamp the chunks before any complete-call dispatch.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The model-chunk receipt timestamp is captured before awaited step dispatch and propagated to the delta even on coalesced eager calls; its regression passed. Fixed in 31f7ce3. Focused SDK tests: 274 passed; typecheck, tsdown, touched lint and import order passed.

@lia-by-librechat

Copy link
Copy Markdown
Contributor Author

Updated review head a81d1c0740933ef80cb1bd5d6715c7e5c82f812e: addressed the three inline SDK findings and corrected the receipt-time fixture that had failed CI shard 4. Focused SDK specs, typecheck, build, lint and import-order checks passed. This comment supersedes the earlier review head.

@danny-avila
danny-avila merged commit 05fbb63 into main Sep 28, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants