Skip to content

feat(openai): integrate exact lifecycle and trusted sessions - #1258

Merged
ndizazzo merged 1 commit into
logging-ui-v3from
codex/openai-observability-stacked-1176
Aug 12, 2026
Merged

feat(openai): integrate exact lifecycle and trusted sessions#1258
ndizazzo merged 1 commit into
logging-ui-v3from
codex/openai-observability-stacked-1176

Conversation

@ndizazzo

@ndizazzo ndizazzo commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #1176 (logging-ui-v3). Recreates the functional behavior from #1234 using the typed lifecycle, durable logging, API, TUI, and console constructs introduced by #1174#1176.

  • add trusted-header provenance and stable native KV sessions only for trusted agent-session identities
  • serialize generations within one trusted session while allowing unrelated sessions to proceed
  • use one cancellation-aware absolute deadline across session and global-lane admission
  • capture stream usage internally without changing client include_usage wire behavior
  • publish typed backend terminal, first-backend-item, usage, and request-cancellation lifecycle events
  • persist and render backend-first-item plus prompt/cached/completion/total token evidence

Deliberate differences from #1234

  • retain the stack's canonical UUID RequestId and typed lifecycle observer; do not add a parallel free-form tracing lifecycle
  • acquire the session semaphore before reserving global queue capacity, fixing the open starvation review finding in Add exact OpenAI request lifecycle observability #1234
  • classify cancellation as 499/cancelled rather than a backend failure
  • mark protocol completion before yielding [DONE], so drop-after-DONE remains successful
  • use a stable local pseudonym for trusted session labels/lock keys rather than the raw trusted header value
  • keep lifecycle metadata bounded: no raw errors, session IDs, prompts, completions, URLs, or token counts are added to OTLP attributes
  • extract backend, request, and stream lifecycle ownership from the already-large router

Validation

  • cargo fmt --all -- --check
  • cargo test -p openai-frontend (172 unit + 10 integration/contract tests)
  • cargo test -p skippy-server --lib (414 tests)
  • cargo test -p mesh-llm-events (81 tests)
  • cargo test -p mesh-llm-log-store (114 tests)
  • cargo test -p mesh-llm-host-runtime --lib logging::openai_lifecycle (8 tests)
  • cargo test -p mesh-llm-host-runtime --lib api::routes::logs::dto::usage_tests (1 test)
  • cargo clippy -p openai-frontend --all-targets -- -D warnings
  • cargo clippy -p skippy-server --all-targets -- -D warnings
  • cargo clippy -p mesh-llm-host-runtime -p mesh-llm-events -p mesh-llm-log-store -p mesh-llm-tui --all-targets -- -D warnings
  • just ui-test (1,301 passed, 3 skipped)
  • UI lint/Prettier checks
  • just build (backend-neutral debug host + packaged Metal runtime)
  • ./target/debug/mesh-llm --version

Hardware-backed two-node/public-mesh/agent-loop validation remains appropriate before merge; this draft intentionally leaves those deployment gates to the stacked review/CI phase.

Supersedes the implementation approach in #1234; references its behavior and review findings without cherry-picking its commits.

Summary by CodeRabbit

  • New Features
    • Log timelines now show backend stream starts and recorded usage events.
    • Token usage details include prompt, cached prompt, completion, and total tokens.
    • Streaming responses report usage and completion status more consistently.
  • Bug Fixes
    • Improved handling of cancellations, client disconnects, timeouts, and failed streams.
    • Prevented duplicate lifecycle events and request identifiers across concurrent or replicated instances.
  • Performance
    • Added session-aware generation limits and improved queue cancellation and deadline handling.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ab70fc8-c98b-4441-b1d6-f0580b152d2f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/openai-observability-stacked-1176

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request is currently a draft. Reviews will not take place until the PR is marked as ready for review.

@ndizazzo
ndizazzo force-pushed the codex/openai-observability-stacked-1176 branch from 9c71109 to ce58b04 Compare August 12, 2026 06:59
@ndizazzo
ndizazzo marked this pull request as ready for review August 12, 2026 07:07
@github-actions
github-actions Bot requested a review from michaelneale August 12, 2026 07:07
@ndizazzo
ndizazzo requested a review from i386 August 12, 2026 07:50

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (6)
crates/mesh-llm-host-runtime/src/logging/openai_lifecycle.rs (1)

355-381: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Narrow the tracked lock scope in backend_dispatched.

backend_dispatched holds the self.tracked mutex across merge_request_metadata, enqueue_event, and start_attempt. The sibling handlers added in this change (backend_terminal, backend_stream_first_item, response_completed, stream_terminal) release the lock before they call self.service. This method is the only one that does not.

LifecycleGuard is cloneable; line 538 already clones it. Clone the guard, release the lock, then perform the service calls and store the attempt under a second short lock.

♻️ Suggested restructure
     fn backend_dispatched(&self, request_id: RequestId, operation: OpenAiBackendOperation) {
-        let mut tracked = lock_recover(&self.tracked);
-        let Some(TrackedRequest::Active(active)) = tracked.requests.get_mut(&request_id) else {
-            return;
-        };
+        let guard = {
+            let tracked = lock_recover(&self.tracked);
+            let Some(TrackedRequest::Active(active)) = tracked.requests.get(&request_id) else {
+                return;
+            };
+            active.guard.clone()
+        };
 
         let metadata = RequestSummaryMetadata::from_parts(
             None,
             None,
             Some("openai_frontend"),
             Some(operation_label(operation)),
         );
         self.service
             .merge_request_metadata(request_id, metadata.clone());
         let event = LifecycleEvent::RouteSelected {
             model: None,
             provider: metadata.provider().map(str::to_owned),
             engine: metadata.engine().map(str::to_owned),
         };
-        if let Ok(payload) = serde_json::to_string(&event) {
-            let _ = self
-                .service
-                .enqueue_event(request_id, ReplayChannel::Operations, payload);
-        }
-        let attempt_id = self.service.start_attempt(request_id, &active.guard);
-        active.backend_attempt = Some((operation, attempt_id));
+        self.enqueue_operation_event(request_id, event);
+        let attempt_id = self.service.start_attempt(request_id, &guard);
+        let mut tracked = lock_recover(&self.tracked);
+        if let Some(TrackedRequest::Active(active)) = tracked.requests.get_mut(&request_id) {
+            active.backend_attempt = Some((operation, attempt_id));
+        }
     }

The change also reuses the new enqueue_operation_event helper instead of repeating the serialize-and-enqueue block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/logging/openai_lifecycle.rs` around lines
355 - 381, Update backend_dispatched to clone the active LifecycleGuard while
holding the tracked lock, then release that lock before calling
merge_request_metadata, enqueue_operation_event, and start_attempt. After
obtaining the attempt ID, reacquire self.tracked briefly and update the matching
active request’s backend_attempt, preserving the existing request lookup
behavior. Reuse enqueue_operation_event for the RouteSelected event.
crates/mesh-llm-host-runtime/src/api/routes/logs/dto.rs (1)

342-362: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract one shared event_kind mapping.

crates/mesh-llm-host-runtime/src/api/routes/logs/dto.rs and crates/mesh-llm-host-runtime/src/api/routes/logs/events/protocol.rs contain identical event_kind functions. This PR had to add backend_stream_first_item and usage_recorded to both. If a future variant is added to only one map, the REST DTO and the SSE protocol emit different kind strings for the same event, and the UI eventKindSchema picklist rejects the unknown value.

Move the mapping into one module in crates/mesh-llm-host-runtime/src/api/routes/logs/ and let both call sites use it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/api/routes/logs/dto.rs` around lines 342 -
362, The duplicate event_kind mappings in dto.rs and events/protocol.rs can
diverge; extract one shared event_kind function into a common logs module, then
update both DTO and SSE protocol call sites to reuse it. Preserve all existing
variant-to-string mappings, including backend_stream_first_item and
usage_recorded, and remove the duplicate local definitions.
crates/mesh-llm-ui/src/features/logs/components/LogRequestEvidenceTimeline.test.tsx (1)

91-114: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a partial-usage case.

This test covers only the all-counters-present path. tokenUsageEntries in crates/mesh-llm-ui/src/features/logs/lib/log-token-usage.ts omits each entry when the counter is undefined, and falls back from completionTokens to the legacy tokens field. Neither branch is exercised.

Add one case with a subset of counters, for example promptTokens set and cachedPromptTokens omitted, and one case where tokens supplies the completion count.

Based on learnings, tests in crates/mesh-llm-ui/**/*.test.{ts,tsx} should "Cover edge cases in tests: missing status, empty peers, client nodes, warm/cold models, malformed attachment data, and localStorage failures."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@crates/mesh-llm-ui/src/features/logs/components/LogRequestEvidenceTimeline.test.tsx`
around lines 91 - 114, Extend the usage evidence tests around
LogRequestEvidenceTimeline with one partial-usage case that omits
cachedPromptTokens and verifies tokenUsageEntries excludes the missing counter,
plus one case where the legacy tokens field supplies the completion count when
completionTokens is absent. Keep the existing all-counters-present assertion
unchanged.

Source: Learnings

crates/openai-frontend/src/errors.rs (1)

133-139: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the existing 499 constant instead of the inline literal.

The crate already defines CLIENT_CLOSED_REQUEST_STATUS in lifecycle.rs; backend_lifecycle.rs imports it at line 9 and request_lifecycle.rs compares against it. This constructor re-declares 499 as a literal and unwraps it. Two definitions of the same status can drift.

StatusCode::from_u16(499) never fails, so the expect is safe. The concern is duplication, not a panic.

♻️ Proposed change to reuse the shared constant
     pub fn cancelled(message: impl Into<String>) -> Self {
         Self::from_kind(
-            StatusCode::from_u16(499).expect("499 is a valid HTTP status"),
+            StatusCode::from_u16(crate::lifecycle::CLIENT_CLOSED_REQUEST_STATUS)
+                .expect("the client-closed status is a valid HTTP status"),
             OpenAiErrorKind::Cancelled,
             message,
         )
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/openai-frontend/src/errors.rs` around lines 133 - 139, Update the
cancelled constructor to use the existing CLIENT_CLOSED_REQUEST_STATUS constant
from lifecycle.rs instead of constructing status 499 inline. Preserve the
current OpenAiErrorKind::Cancelled and message handling while removing the
duplicate conversion and expect.
crates/openai-frontend/src/router.rs (1)

339-344: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid cloning the full response to read usage.

response.clone() duplicates the entire response body on every non-streaming request. The clone exists only because &response.usage is borrowed in the same call expression. Chat completion bodies can be large.

Extract the usage first, then move the response. The same pattern appears at lines 675-680 in completions and at lines 599-601 in responses.

♻️ Proposed change to remove the clone
         state.response_completed(
             &context,
             OpenAiBackendOperation::ChatCompletion,
             &response.usage,
         );
-        Ok(json_response_with_usage(response.clone(), &response.usage))
+        let usage = response.usage.clone();
+        Ok(json_response_with_usage(response, &usage))
     }

If Usage is Copy, drop the .clone() on the extracted value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/openai-frontend/src/router.rs` around lines 339 - 344, In the
non-streaming chat completion flow, extract `response.usage` into a local before
calling `json_response_with_usage`, then move `response` into that function
instead of cloning it. Apply the same ownership pattern in the `completions` and
`responses` flows, and omit the usage clone if `Usage` implements `Copy`.
crates/mesh-llm-host-runtime/src/api/routes/logs/events/protocol.rs (1)

154-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share the event_kind mapping between protocol.rs and dto.rs.

Both files contain identical 17-arm mappings. Move the mapping into one shared function to prevent future divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/api/routes/logs/events/protocol.rs` around
lines 154 - 158, Extract the duplicated 17-arm LifecycleEvent-to-event_kind
mapping from protocol.rs and dto.rs into one shared function, then update both
callers to reuse it. Preserve every existing string mapping, including the
StreamStarted, StreamChunk, StreamCompleted, and UsageRecorded variants.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/openai-frontend/src/errors.rs`:
- Line 214: Update the OpenAiErrorKind::Cancelled mapping to use
invalid_request_error as the error type while retaining request_cancelled as the
error code.

In `@crates/openai-frontend/src/router.rs`:
- Around line 369-388: Refactor the responses handler into semantic helper
functions for its streaming and non-streaming branches, keeping each branch’s
existing behavior and error propagation unchanged. Move the logic currently
represented by the ResponsesStream flow, including call_backend_with_context and
observe_backend_stream, into the streaming helper, and place the regular
response path in a corresponding non-streaming helper so responses is below the
configured line threshold.

In `@crates/skippy-server/src/frontend/generation/cache_hints.rs`:
- Around line 62-71: Update the untrusted-session branch in the session label
construction to include process_nonce() alongside the timestamp and sequence,
ensuring session_id differs across processes. Keep the trusted agent-session
path based solely on stable_wire_id and preserve its existing stable identity
behavior.

---

Nitpick comments:
In `@crates/mesh-llm-host-runtime/src/api/routes/logs/dto.rs`:
- Around line 342-362: The duplicate event_kind mappings in dto.rs and
events/protocol.rs can diverge; extract one shared event_kind function into a
common logs module, then update both DTO and SSE protocol call sites to reuse
it. Preserve all existing variant-to-string mappings, including
backend_stream_first_item and usage_recorded, and remove the duplicate local
definitions.

In `@crates/mesh-llm-host-runtime/src/api/routes/logs/events/protocol.rs`:
- Around line 154-158: Extract the duplicated 17-arm
LifecycleEvent-to-event_kind mapping from protocol.rs and dto.rs into one shared
function, then update both callers to reuse it. Preserve every existing string
mapping, including the StreamStarted, StreamChunk, StreamCompleted, and
UsageRecorded variants.

In `@crates/mesh-llm-host-runtime/src/logging/openai_lifecycle.rs`:
- Around line 355-381: Update backend_dispatched to clone the active
LifecycleGuard while holding the tracked lock, then release that lock before
calling merge_request_metadata, enqueue_operation_event, and start_attempt.
After obtaining the attempt ID, reacquire self.tracked briefly and update the
matching active request’s backend_attempt, preserving the existing request
lookup behavior. Reuse enqueue_operation_event for the RouteSelected event.

In
`@crates/mesh-llm-ui/src/features/logs/components/LogRequestEvidenceTimeline.test.tsx`:
- Around line 91-114: Extend the usage evidence tests around
LogRequestEvidenceTimeline with one partial-usage case that omits
cachedPromptTokens and verifies tokenUsageEntries excludes the missing counter,
plus one case where the legacy tokens field supplies the completion count when
completionTokens is absent. Keep the existing all-counters-present assertion
unchanged.

In `@crates/openai-frontend/src/errors.rs`:
- Around line 133-139: Update the cancelled constructor to use the existing
CLIENT_CLOSED_REQUEST_STATUS constant from lifecycle.rs instead of constructing
status 499 inline. Preserve the current OpenAiErrorKind::Cancelled and message
handling while removing the duplicate conversion and expect.

In `@crates/openai-frontend/src/router.rs`:
- Around line 339-344: In the non-streaming chat completion flow, extract
`response.usage` into a local before calling `json_response_with_usage`, then
move `response` into that function instead of cloning it. Apply the same
ownership pattern in the `completions` and `responses` flows, and omit the usage
clone if `Usage` implements `Copy`.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37f32e22-4be1-48a3-9c00-c1563147dfd9

📥 Commits

Reviewing files that changed from the base of the PR and between a04274b and ce58b04.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (39)
  • crates/mesh-llm-events/src/logging/events.rs
  • crates/mesh-llm-events/src/logging/presentation.rs
  • crates/mesh-llm-events/src/logging/tests.rs
  • crates/mesh-llm-host-runtime/src/api/routes/logs/dto.rs
  • crates/mesh-llm-host-runtime/src/api/routes/logs/events/protocol.rs
  • crates/mesh-llm-host-runtime/src/logging/openai_lifecycle.rs
  • crates/mesh-llm-log-store/src/repositories.rs
  • crates/mesh-llm-tui/src/output/logging_projection.rs
  • crates/mesh-llm-ui/src/features/logs/api/schemas.test.ts
  • crates/mesh-llm-ui/src/features/logs/api/schemas.ts
  • crates/mesh-llm-ui/src/features/logs/components/LogRequestEvidenceTimeline.test.tsx
  • crates/mesh-llm-ui/src/features/logs/lib/log-fixtures.test.ts
  • crates/mesh-llm-ui/src/features/logs/lib/log-fixtures/lifecycle-events.ts
  • crates/mesh-llm-ui/src/features/logs/lib/log-timeline.test.ts
  • crates/mesh-llm-ui/src/features/logs/lib/log-timeline.ts
  • crates/mesh-llm-ui/src/features/logs/lib/log-token-usage.ts
  • crates/openai-frontend/src/backend.rs
  • crates/openai-frontend/src/backend_lifecycle.rs
  • crates/openai-frontend/src/common.rs
  • crates/openai-frontend/src/errors.rs
  • crates/openai-frontend/src/lib.rs
  • crates/openai-frontend/src/lifecycle.rs
  • crates/openai-frontend/src/request_lifecycle.rs
  • crates/openai-frontend/src/router.rs
  • crates/openai-frontend/src/router_tests.rs
  • crates/openai-frontend/src/stream_lifecycle.rs
  • crates/openai-frontend/tests/lifecycle_observer.rs
  • crates/openai-frontend/tests/request_context_wrappers.rs
  • crates/skippy-server/Cargo.toml
  • crates/skippy-server/src/frontend/backend.rs
  • crates/skippy-server/src/frontend/backend/tests.rs
  • crates/skippy-server/src/frontend/generation/cache_hints.rs
  • crates/skippy-server/src/frontend/generation/queue.rs
  • crates/skippy-server/src/frontend/generation/server.rs
  • crates/skippy-server/src/frontend/generation/types.rs
  • crates/skippy-server/src/frontend/local_generation/linear_decode.rs
  • crates/skippy-server/src/frontend/local_generation/tests.rs
  • crates/skippy-server/src/frontend/tests/generation.rs
  • crates/skippy-server/src/frontend/tests/multimodal.rs

Comment thread crates/openai-frontend/src/errors.rs Outdated
Comment thread crates/openai-frontend/src/router.rs Outdated
Comment thread crates/skippy-server/src/frontend/generation/cache_hints.rs Outdated
@ndizazzo
ndizazzo force-pushed the codex/openai-observability-stacked-1176 branch from 4239da0 to dfa78b4 Compare August 12, 2026 15:48
@ndizazzo
ndizazzo merged commit 5997c12 into main Aug 12, 2026
48 checks passed
@ndizazzo
ndizazzo deleted the codex/openai-observability-stacked-1176 branch August 12, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant