Skip to content

🔭 feat: Conversation Trace Viewer - #15869

Merged
danny-avila merged 21 commits into
devfrom
danny-avila/librechat-deepseek-trace-viewer-ca115a
Sep 13, 2026
Merged

danny-avila merged 21 commits into
devfrom
danny-avila/librechat-deepseek-trace-viewer-ca115a

Conversation

@danny-avila

@danny-avila danny-avila commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

LibreChat exports agent runs to Langfuse, but a user has no way to see what a response actually did (which model calls ran, how long each took to first token, which tool failed) without a Langfuse account and the right project. This adds a Trace view for the user's own conversations. It opens from a new header button next to Export/Share (or View trace in the mobile overflow menu) and covers the chat surface. It shows a summary row, a pinned waterfall overview with drag-to-focus intervals, a hierarchical ledger grouped by response, and a record inspector.

The feature is off by default (interface.traceViewer.enabled). The control appears only on a persisted conversation the user owns that has at least one sampled trace in a readable destination. The client talks to a provider-neutral contract, and Langfuse is the first reader behind a TraceReader seam.

A response's trace sampling fields (langfuseSampled, langfuseDestinationIds, langfuseRunId) decide which traces it owns, so client-authored writes (the message-create route and imports) now strip them through one helper, withoutTraceRefs. Forked conversations used to copy them onto messages with new ids. Those copies claimed traces they never produced, which would have shown a trace button over an empty trace and pointed feedback scores at nonexistent traces. Forks now drop them.

A failed turn's error row (<userMessageId>_) never recorded sampling, and its id is not the id of the run that failed, so failed runs were missing from the trace. The error row now stores the run's sampling fields plus langfuseRunId when the run was created (getFailedTurnTraceFields in packages/api; the controller only passes whether the run exists). The trace viewer and feedback scores follow that run's trace, and langfuseRunId gets the same ingress stripping.

How it works

sequenceDiagram
    participant Header
    participant Routes as /api/traces
    participant Reader as Langfuse reader
    participant Mongo
    participant Langfuse
    Header->>Routes: GET :conversationId/availability
    par ownership
        Routes->>Mongo: getConvoOwnership (owner, not a child thread)
    and existence
        Routes->>Reader: isAvailable
        Reader->>Mongo: hasSampledTraceMessage(destinationIds)
    end
    Routes-->>Header: available (gated on ownership, no Langfuse call)
    Header->>Routes: GET :conversationId/records[?cursor]
    Routes->>Mongo: getConvoOwnership
    Routes->>Reader: listRecords
    Reader->>Mongo: getConversationTraceRefs
    opt a turn more than one project could hold
        Reader->>Langfuse: GET v2/observations?filter=[sessionId, traceId any of, parentObservationId is null] (which traces this project holds)
    end
    Reader->>Langfuse: GET /api/public/v2/observations?filter=[sessionId, traceId any of (one segment's turns), startTime]
    Reader-->>Routes: normalized TTraceRecord[] (owned traces only) + sourceId + nextCursor (turn, project, Langfuse cursor)
Loading
packages/data-provider/src/types/traces.ts   # TTraceRecord, TTracePage, TTraceRecordDetail, error codes
packages/api/src/traces/                     # TraceReader seam, handlers (ownership, cost policy), per-user limiter
packages/api/src/langfuse/reader.ts          # Langfuse v2 Observations adapter
packages/data-schemas/.../message.ts         # getConversationTraceRefs: plain refs, no FilterQuery in the signature
client/src/components/Chat/Trace/            # Surface, Viewer, Timeline, Ledger, Inspector, Summary, model.ts
  • Authorization. Every read first proves conversation ownership within the requester's tenant; the ownership lookup and both message queries carry tenantId, and tenantless is its own scope. The reader then narrows the Langfuse session to traces derived from the user's own sampled response ids: the run trace sha256(messageId) and its title run sha256("title-" + messageId). Conversation ids are unique per user, not globally, so the session filter alone is not the boundary. Message ids can also be influenced by a request, so trace ids alone aren't a boundary either: every Langfuse read also requires the requester's internal user id as the trace userId. The server stamps that id at export (verified on Langfuse Cloud: the owner's filter returns every observation, anyone else's none), so a colliding trace id never returns another user's observations. A deployment that exports another allowed user field via langfuse.trace.userIdField has no immutable principal on its traces (a field the exporter ignores still exports the internal id and keeps working), since names and usernames repeat and emails and provider ids repeat across tenants. For such a deployment the viewer reports no trace and logs why once. Only server-authored responses count (isCreatedByUser: false, not isUserSubmitted). The message-create route, conversation imports and forks replace langfuseSampled/langfuseDestinationIds/langfuseRunId with an explicit langfuseSampled: false, so a client can't forge a claim to someone else's trace, and a feedback score on a copied row never recomputes sampling for a trace that was never made. Credentials and Langfuse URLs never reach the browser.
  • Destinations. Reads reuse getScoreDestinations, which covers central env credentials, fanout tenant, and the admin connection. Destination resolution never waits on the central project lookup. Availability is a single existence query across every sampled response, and it runs alongside the ownership check. While central's project id is still resolving, it answers with retryAfterMs rather than a definitive no. A list read loads sampled responses a segment at a time: at most 50, ending at the turn the page starts from, plus one older response that marks where the next page begins. A long conversation's pages therefore never reload the responses before them. The cursor carries the anchor's position (creation time, then _id), so that read and the first-message lookup run together in one round trip. It pages by turn segments, newest first, and each segment is read from one project shown to hold it. Recorded destination ids only say where a trace was eligible to go, so a trace that more than one readable project could hold is looked up first. A turn's run and its title run are exported separately, so each trace id gets its own evidence. Each project is asked which of the window's traces it holds a root observation for (parentObservationId is null: one small row per trace), preferred project first and later ones only for traces still unaccounted for. A turn only one project could hold needs no lookup, so single-project deployments make no extra calls. Each trace is read from the preferred project that holds it. A trace no project shows joins an adjacent segment the project could hold it in: a title run the turn never had, or a run still in progress whose root isn't exported yet. When a turn's run and title run sit in different projects, the probes' root start times decide which is read first. Pages are ordered by turn: a turn's records may continue on the next page, and the client groups them by turn and orders them by time. A segment is a stretch of consecutive traces (up to 50 turns' worth) read from the same project. It asks Langfuse only for those turns' run and title traces (traceId any of, verified on Langfuse Cloud with 100 ids and cursor paging), so pages stay in turn order whichever projects held which turns. Paging within a segment uses that project's own Langfuse cursor, which handles records sharing a start time, and the page's record budget bounds that one read. A project that fails on a fresh read is dropped for the request, and its traces move to another project that holds them. A run that only a failed project could hold, or a title run a probe found in a failed project, raises that failure instead of an empty or partial trace, and it raises it at its own place in the page order. A cursor names the segment's first trace, the project, its Langfuse cursor and a hash of the segment's trace set. A continuation never moves to another project or replays pages: its failure is returned as is, and a cursor the segment no longer matches (or Langfuse rejects) returns invalid_request. The viewer answers that by reloading from the newest page. Sampled responses are ordered by createdAt then _id, so every request rebuilds the same turn order a cursor was positioned in. Detail reads carry the turn the list attributed the record to (?message=), load only that sampled response, and ask Langfuse only within that turn's traces. They start with the listing page's project, then ask the remaining ones until the record turns up, reporting a failure rather than a 404 when a failed project could hold it. The route injects the destination resolver, the HTTP client and the rate-limit store (resolveLangfuseReadDestinations, fetch, limiterCache).
  • Reads. core,basic,time,model,usage for the list, paged by Langfuse cursors up to maxRecords per request, each round trip bounded by requestTimeoutMs. Each request is bounded by the conversation's message times. The detail read is an id + sessionId filter, and it asks for io,metadata only when showInputOutput is on, truncated to maxContentLength. Upstream 401/403, 404 (no v2 API, e.g. self-hosted v3), 429, timeouts, malformed bodies and pages whose rows all fail the schema map to typed error codes the UI explains. Input and output that are literally the strings {} or null are shown as written. Closing the viewer or dropping the connection aborts the Langfuse request (client query signal → server disconnect → AbortSignal.any with the timeout). Costs are stripped server-side unless interface.contextCost is on, and the summary omits a cost total when any model call has no price. Open in Langfuse only appears when the linked project (the session link now reports its destinationId) served every loaded page.
  • Client. model.ts builds the tree from flat, possibly partial pages. Records whose parent is missing, in another turn, or part of a cycle become roots instead of disappearing. Bars scale to their own response until an interval is focused, and generations split TTFT from decoding. Running records show a start marker, not an invented duration. Rows are windowed, and the ledger follows the ARIA tree pattern via aria-activedescendant, keeping the active row mounted while it is scrolled out of the window. Times and durations format in the app language and the user's 12/24-hour setting. A settled run invalidates cached pages, so reopening shows the new turn. Refresh (and a settled run) keeps only the newest page, so a focused interval or selection that pointed at older pages is fitted to what remains or cleared. The chat stays mounted and inert underneath, so closing restores scroll, draft and focus.

Change Type

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Testing

Focused suites, all passing locally:

  • packages/api: src/langfuse/reader.spec.ts (60), src/langfuse/feedback.spec.ts (70), src/traces/handlers.spec.ts (22), src/admin/langfuse.handler.spec.ts, src/langfuse + src/app/permissions.spec.ts (205)
  • packages/data-schemas: src/methods/message.traces.spec.ts (10, mongodb-memory-server, includes tenant isolation and same-millisecond ordering), src/methods/message.spec.ts (136), src/app/interface.spec.ts (20)
  • packages/data-provider: specs/config-schemas.spec.ts -t traceViewer (3)
  • api: server/routes/__tests__/traces.spec.js (real @librechat/api handlers + reader, fetch stubbed at the boundary), server/utils/import/fork.spec.js, server/utils/import/importers.spec.js, server/routes/__tests__/messages-{get,feedback}.spec.js, server/controllers/agents/__tests__/request.resumeMetadata.spec.js (139)
  • client: Chat/Trace/__tests__/{model,format,Viewer,Surface} (64), Menus/__tests__/HeaderMenu.spec.tsx, __tests__/ChatView*.spec.tsx

Regression checks: I removed the trace-ownership filter, the fork fix, the parallel availability lookup, the non-blocking destination resolution, the disconnect guard, failure-behind-empty reporting, detail probing, the trim reset, segment boundaries, the segment size bound, run-id ownership, all-malformed pages, the user-id read filter, the configured-user-field refusal (and the ignored-field fallback), bounded response windows, the repeated probe and list cursors, single-turn detail authorization, detail refresh, the focus fallback, failed title reads, evidence over eligibility, per-trace evidence, split title ordering, unverifiable-turn reporting, continuations that neither fail over nor replay, the viewer's reload on a changed trace, same-millisecond ordering, the running-turn fallback, the unpriced-generation rule, literal content strings, the active-row mount and the error row's run fields one at a time, and confirmed their tests fail without them.

npx tsc --noEmit passes in packages/api, packages/data-schemas and client, and packages/data-provider builds with tsc. node scripts/static-checks.mts --full --against origin/dev passes, with the unused-npm-packages check skipped because depcheck is not installed.

Manual run against real data. I ran the backend from this branch on a local mongod with the deployment's Langfuse Cloud credentials. I seeded a user who owns two conversations whose message ids match existing Langfuse traces: 59 responses / 421 observations, and one response plus its title run.

In Playwright:

  • desktop light and dark themes
  • header button → viewer
  • selecting a generation loads input and output
  • drag-focusing an interval
  • closing restores the chat
  • mobile overflow item → viewer → bottom-sheet inspector

Confirmed against the live API:

  • trace ids equal sha256(messageId) and sha256("title-" + messageId)
  • Cloud serves the model field as model (not the SDK's providedModelName); the reader accepts both
  • the filter-JSON detail read works

Test Configuration:

interface:
  traceViewer:
    enabled: true
    showInputOutput: true   # optional
    requestTimeoutMs: 10000 # optional
  contextCost: true         # optional, shows cost

Plus Langfuse tracing configured (central LANGFUSE_* credentials, fanout tenant, or the admin connection) on Langfuse Cloud or a self-hosted release that serves /api/public/v2/observations.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • I have made pertinent documentation changes
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

Adds a trace view for a user's own conversations: a summary, a pinned
waterfall overview with interval focus, a hierarchical record ledger and
a record inspector, opened from the chat header (or the mobile overflow
menu) and read through a provider-neutral seam with Langfuse as the
first reader.

- interface.traceViewer (enabled, showInputOutput, maxRecords,
  maxContentLength, requestsPerMinute), off by default
- /api/traces/:conversationId availability, records and record routes,
  re-proving conversation ownership and narrowing the Langfuse session
  to traces the user's own sampled responses produced
- Langfuse reader over the v2 Observations API with cursor paging, time
  bounds, typed failures and server-side input/output withholding
- forks no longer copy the source messages' trace sampling record
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 12, 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-13T04:35:54.515793Z d6cc751 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: b921060d93

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/traces/handlers.ts Outdated
Comment thread client/src/components/Chat/Trace/Ledger.tsx Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
- interface.traceViewer.requestTimeoutMs bounds each Langfuse round trip
- availability is one existence query across every sampled response
  instead of the oldest one, run alongside the ownership check
- tree items report their position among visible siblings
- point-in-time Langfuse events read as completed, not running
- reads prefer the project holding the most responses when a tenant
  connection arrived mid-conversation
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: f9a478e354

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread client/src/data-provider/Traces/queries.ts Outdated
Comment thread client/src/components/Chat/Trace/model.ts
Comment thread client/src/components/Chat/Trace/Viewer.tsx Outdated
- page cursors and record details stay on the project that served the
  first page; pages report an opaque sourceId
- Open in Langfuse only appears when the linked project served every
  loaded page (the session link now reports its destinationId)
- client and server abort Langfuse reads when the viewer closes or the
  client disconnects
- reads never wait on the central project lookup, which the request
  timeout does not govern
- the cost total is withheld when any model call with usage is unpriced
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 546f696d09

ℹ️ 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 client/src/data-provider/Traces/queries.ts
Comment thread client/src/components/Chat/Trace/model.ts Outdated
Comment thread client/src/components/Chat/Trace/Viewer.tsx Outdated
…Refresh Failures

- a settled run invalidates the cached trace pages, so reopening shows
  the new turn instead of pages fresh for another 30 seconds
- trace durations and clock times format in the app language and the
  user's 12/24-hour setting, as message timestamps do
- a failed refresh with every page loaded surfaces an alert and a retry
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 793f137d04

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread client/src/components/Chat/Trace/useTraceControl.ts
Comment thread client/src/components/Chat/Trace/Ledger.tsx Outdated
Comment thread packages/data-schemas/src/methods/message.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/data-schemas/src/methods/message.ts
- the message-create route and imports strip langfuseSampled and
  langfuseDestinationIds, and trace reference queries ignore client-authored
  rows (isUserSubmitted) and user turns, so a forged row cannot claim a trace
- the Langfuse reader receives its destination resolver and HTTP client
  from the route instead of defaulting to process-global ones
- availability asks the client to check again while central's project id
  is still resolving, instead of caching a false answer
- listRecords tries the next readable project when legacy responses left
  the first one empty
- a settled run also invalidates cached record details
- running is read from a record's status, not a missing end time
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 1565792b55

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/traces/limiter.ts Outdated
Comment thread client/src/data-provider/Traces/queries.ts Outdated
Comment thread packages/api/src/traces/handlers.ts Outdated
…g a Turn

- ownership and both trace reference queries carry the requester's tenant
  (tenantless is its own scope), so a duplicate user and conversation id in
  another tenant cannot grant or supply trace references
- a read visits, page by page, every project that could hold a turn no
  earlier project provably holds, so legacy turns in another project stay
  reachable after a newer destination ranks first
- the trace read limiter takes its store from the route
- transient availability failures retry instead of hiding the control
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 6b8b98af94

ℹ️ 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 packages/api/src/traces/limiter.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread client/src/components/Chat/Trace/Viewer.tsx Outdated
Comment thread client/src/components/Chat/Trace/model.ts Outdated
…abels

- a fresh read fails over to another project holding the same turns when the
  preferred one fails; the cursor carries the failed sources so later pages
  keep the rebuilt plan, and detail reads fail over the same way
- destinations for one project collapse into one source
- the trace read limiter keys each user within their tenant
- refresh and a settled run keep only the newest page before refetching, so
  older pages are not replayed through the limiter; retry repeats the read
  that failed
- search matches the localized kind and status labels a row shows
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 490faa8487

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread client/src/components/Chat/Trace/Viewer.tsx
Comment thread packages/api/src/langfuse/reader.ts Outdated
…cts, Fit Trimmed Views

Rank trace sources by the newest sampled response they could hold before coverage and preference, so a conversation that moved projects shows its latest turn first.

Throw a list failure when a failed project could hold a turn that no answering project provably holds, instead of rendering an empty trace. Detail reads keep probing projects after an empty answer, skip ones whose turns an answering project provably holds, and surface a failure that could hide the record.

Refresh and settled runs trim the cache to the newest page; the viewer now fits a focused interval to the remaining records or clears it, and drops a selection whose record left with the older pages.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head 3a3ceaa.

@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: 3a3ceaa037

ℹ️ 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 client/src/components/Chat/Trace/Ledger.tsx
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/data-schemas/src/methods/message.ts
Comment thread packages/api/src/langfuse/reader.ts
…Mounted

List reads now page by turn segments instead of whole projects. A segment is the newest unread turn and up to 49 older consecutive turns one project could hold. The request filters Langfuse to those turns' run and title traces, so pages stay newest first when a conversation's responses alternate between projects. Legacy turns without destination ids are asked of the other projects in the segment. A project that failed for newer turns is retried when it alone holds older turns. A page whose rows all fail the observation schema is an upstream failure, not an empty trace.

A failed turn's error row now stores the sampling fields of the run that failed, plus langfuseRunId when that run was created, so the trace viewer and feedback scores follow the run's trace. langfuseRunId is stripped from client message writes and imports, dropped by forks, and hidden from client message projections.

The ledger keeps its active tree item mounted while virtualization scrolls it out of the window, so aria-activedescendant always names a rendered row.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head c9f6d8c.

@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

https://github.com/danny-avila/LibreChat/blob/c9f6d8c64cd9926156b99bdf403837959f8055ac/api/server/controllers/agents/responses.js#L422
P1 Badge Record trace provenance on successful chat turns

The only successful-response call to getLangfuseTraceMessageFields is here in the OpenAI Responses API controller. In the production web-chat path checked in ResumableAgentController, the terminal response is saved from response without this helper, and a repository-wide search finds no other success-path writer for langfuseSampled; therefore ordinary successful Agent chats never satisfy hasSampledTraceMessage, so the new header control remains hidden unless the conversation also contains a marked failed or Responses-API turn. Add the sampling fields to the standard Agent response persistence paths as well.

AGENTS.md reference: AGENTS.md:L31-L34

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts
…ontent

A segment that reads more than one project now runs every read before returning. A read that fills the record budget cuts the page at the oldest start time all reads loaded, and the continuation carries that start time as an inclusive bound instead of a Langfuse cursor. Every project continues from the same point, so a newer turn held by another project is never left behind an older one, and failover works on any page. A page drawn from several projects no longer names a single source. A continuation that would repeat its own bound fails instead of looping.

Input and output that are literally the strings {} or null are kept; only an empty object or JSON null value is dropped.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head a846006.

@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: a8460061df

ℹ️ 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 packages/api/src/langfuse/reader.ts
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread client/src/components/Chat/Header.tsx
Comment thread api/server/controllers/agents/request.js Outdated
Trace ids derive from message ids, which a request can influence, so a colliding id must not return someone else's trace. Every Langfuse list, probe and detail read now also filters by userId: the requester's internal id, plus the configured langfuse.trace.userIdField value when set. The server stamps that id on the trace at export. The handler passes the requesting user into the trace query.

When a turn's run and title run sit in different projects, the probes' root start times decide which is read first, so a title generated after the response is not left behind Load older. Cursors name the trace a segment starts at within its turn.

The failed-turn trace lookup moves into packages/api as getFailedTurnTraceFields. The agent controller only passes the error row id, the run id and whether the run was created.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head c035b50.

@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: c035b5053a

ℹ️ 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 packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts
Comment thread packages/api/src/langfuse/reader.ts Outdated
A configured langfuse.trace.userIdField can export a non-unique value (name, username) or one that repeats across tenants (email, provider ids) as the trace userId, so it cannot prove who a trace belongs to. Reads now require the internal user id alone. A deployment that exports another field reports no trace and logs why once, instead of trusting that value.

A failed read of a title run that a probe found in that project now raises its failure instead of dropping the title. The page contract now states that pages are ordered by turn and a turn's records may continue on the next page.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head 86b3058.

@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: 86b30588b3

ℹ️ 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 api/server/routes/messages.js Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/data-schemas/src/methods/message.ts Outdated
Comment thread client/src/components/Chat/Trace/Viewer.tsx
Comment thread packages/api/src/langfuse/reader.ts
Comment thread client/src/components/Chat/Trace/Viewer.tsx Outdated
List reads load sampled response references a segment at a time: up to 50 ending at the cursor's turn, plus one older response that names the next page. Pages of a long conversation no longer rematerialize every sampled response. getConversationTraceRefs gains through and limit, anchored by createdAt and _id.

The message-create route and conversation imports strip trace sampling fields through withoutTraceRefs instead of their own field lists. A userIdField the exporter ignores counts as the internal id. A root lookup whose cursor repeats fails that project instead of paging forever.

In the viewer, Refresh also rereads the open record's detail, and closing the inspector after a filter removed every row returns focus to the search field.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head c4dd606.

@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: c4dd606429

ℹ️ 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 packages/data-schemas/src/methods/message.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts Outdated
Comment thread packages/api/src/langfuse/reader.ts
…to Their Turn

Cursors carry the anchor response's order key (creation time, then _id). The bounded sampled-response read then runs alongside the first-message lookup, with no anchor lookup ahead of it. A key that no longer names its response returns nothing, and the reader answers the cursor as changed.

Detail reads take the turn the list attributed the record to (?message=). They load only that sampled response instead of the conversation's whole history, and ask Langfuse within that turn's traces. The route rejects a detail read without it, and the client sends it from the listed record.

A list read whose Langfuse cursor repeats now fails instead of handing the next page the same records.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head c5175da.

…deepseek-trace-viewer-ca115a

# Conflicts:
#	api/server/controllers/agents/request.js

@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: c5175da16a

ℹ️ 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 packages/api/src/langfuse/reader.ts
Comment thread packages/api/src/traces/handlers.ts Outdated
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head 2c825ae (this branch merged with the latest dev).

@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: 2c825ae7b5

ℹ️ 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 packages/data-schemas/src/methods/message.ts Outdated
Comment thread packages/api/src/traces/handlers.ts Outdated
Comment thread api/server/utils/import/fork.js
…Copies Unsampled

A page cursor whose base-36 time parses to a number outside the Date range reached Mongoose as an Invalid Date and surfaced as a 500 the viewer retried; it now resumes nothing, which the reader reports as invalid_request so the viewer reloads its newest page.

The detail read capped the message id at the Langfuse record-id bound while the app stores response ids at any length, so a listed turn could refuse to open; message ids only name a stored row and are accepted at any length. Ids that reach a Langfuse filter keep their caps.

Copied and client-authored rows now carry langfuseSampled: false instead of no record, so a feedback score on a fork, import or posted message never recomputes sampling for a trace that was never made.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head 0568bc6.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 0568bc6b62

ℹ️ 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".

An object literal with none of the trace fields fails TypeScript's weak-type check against the helper's all-optional constraint; the spec now passes a typed row that declares the optional field, as every stored message type does.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head e5bcb88 (differs from the clean 0568bc6 only by a type annotation in packages/api/src/langfuse/trace.spec.ts).

@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: e5bcb8830b

ℹ️ 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 packages/api/src/traces/handlers.ts
Comment thread packages/api/src/langfuse/reader.ts
Comment thread packages/api/src/langfuse/reader.ts
Comment thread packages/api/src/langfuse/reader.ts
A non-success Langfuse response was turned into a TraceReadError without consuming or cancelling its body, and Node's fetch returns a connection to its pool only once the body is released, so a sustained upstream failure left sockets checked out until garbage collection. The body is now cancelled before the status becomes an error.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review head d6cc751.

@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: d6cc751738

ℹ️ 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 packages/api/src/langfuse/reader.ts
Comment thread packages/api/src/langfuse/reader.ts
@danny-avila
danny-avila merged commit f43301f into dev Sep 13, 2026
40 checks passed
@danny-avila
danny-avila deleted the danny-avila/librechat-deepseek-trace-viewer-ca115a branch September 13, 2026 04:56
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.

1 participant