Skip to content

Implement token-level probability tracking and reroll UI#23

Merged
CoolSpring8 merged 22 commits intomainfrom
token-probability
Dec 5, 2025
Merged

Implement token-level probability tracking and reroll UI#23
CoolSpring8 merged 22 commits intomainfrom
token-probability

Conversation

@CoolSpring8
Copy link
Copy Markdown
Owner

@CoolSpring8 CoolSpring8 commented Dec 4, 2025

Summary by CodeRabbit

  • New Features

    • Inline token UI with per-token probabilities, hoverable alternatives menu, and select-to-replace tokens.
    • Token reroll: regenerate from a specific token and continue the response.
    • Token overlay for chat and text completion inputs with interactive token controls.
    • Settings: new Display tab to enable a token probability heatmap and choose themes.
  • Other

    • Token probability data is persisted and appears in message history and live streams.

✏️ Tip: You can customize this high-level summary in your review settings.

…xpose seed text for completion visualization
…and disable token reroll for reasoning content
…gprobStream.ts` with a new `openaiLogprobs.ts` module and updating its usage
…over state management with delayed opening and separate highlight/tooltip indices
…ges and fix `isGenerating` state reset race condition
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds token-level logprob capture, parsing, streaming, UI heatmaps, and interactive token rerolling; extends provider readiness with OpenAI-compatible baseURL/apiKey; propagates tokenLogprobs through AI streaming, tree/state, hooks, and components; introduces a token inline renderer and heatmap settings.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Added @floating-ui/react (^0.27.16) for token-alternatives menu positioning.
Types
src/types.ts
Added TokenLogprob and TokenAlternative; added tokenLogprobs?: TokenLogprob[] to message metadata; expanded openai-compatible ChatProviderReady with baseURL/apiKey.
OpenAI logprob utilities
src/ai/openaiLogprobs.ts
New module to normalize top_logprobs, parse chat/completion logprob chunks into StreamChunk, build provider-specific logprob options, and map internal messages to model messages.
Provider name constant
src/ai/openaiCompatible.ts
Added and exported OPENAI_COMPATIBLE_PROVIDER_NAME and replaced literal usage.
Streaming helpers
src/ai/streamUtils.ts
New processFullStream utility to iterate async stream parts and append content/reasoning/tokenLogprobs via provided appender and optional raw-chunk parser.
Message streaming
src/ai/sendMessage.ts
Dual-path streaming (built-in vs OpenAI-compatible) with logprob parsing; append deltas extended to include tokenLogprobs; adjusted stream lifecycle and finalization/error handling.
Conversation tree & types
src/tree/types.ts, src/tree/useConversationTree.ts
TreeNode gains tokenLogprobs; append/clone/replace/setNodeText signatures accept/propagate tokenLogprobs; snapshots serialize/deserialize token data; toMessage includes token metadata.
Settings state
src/state/useSettingsStore.ts
Added HEATMAP_THEMES, HeatmapTheme type, enableTokenHeatmap and heatmapTheme state, persistence, and setters.
Settings modal
src/components/SettingsModal.tsx
Added "Display" tab with Token Probability Heatmap toggle and heatmap theme selector wired to persisted settings.
Provider readiness hook
src/hooks/useProviderReadiness.ts
UseProviderReadinessOptions now includes baseURL and apiKey; openai-compatible readiness returns them; dependency arrays updated.
Conversation controller hook
src/hooks/useConversationController.ts
Added rerollFromToken API and token-reroll streaming flow; tokenLogprobs-aware equality/comparison and seed/abort handling; exposes rerollFromToken.
Text completion hook
src/hooks/useTextCompletion.ts
Tracks tokenLogprobs and seedText; exposes rerollFromToken, overwriteTextContent; handles logprob parsing/continuation and OpenAI-compatible options.
Token renderer component
src/components/TokenInlineRenderer.tsx
New component rendering tokens with heatmap themes and a floating token-alternatives menu (uses @floating-ui/react), hover/tooltip timers, and selection callback.
Message item component
src/components/MessageItem.tsx
New props: showTokens, tokenLogprobs, onRerollToken, disableReroll, onShowTokensChange; conditional rendering to use TokenInlineRenderer when token view is active.
Chat view component
src/components/ChatView.tsx
Added onTokenReroll prop; per-message token visibility state; forwards token props to MessageItem and handles reroll events.
Text completion view
src/components/TextCompletionView.tsx
New props: tokenLogprobs, onTokenReroll, showTokenOverlay, generatedPrefix; layered overlay + textarea with hover mapping and token interactions.
Token-aware tree/state
src/tree/useConversationTree.ts
Threaded tokenLogprobs through node creation, append, clone, replace, export/import snapshot, and toMessage serialisation.
Main App wiring
src/App.tsx
Passes baseURL/apiKey into useProviderReadiness; adapts to updated hooks and supplies token-related props (tokenLogprobs, reroll callbacks, seedText, overwriteTextContent) to views.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant UI as User/UI
    participant Msg as MessageItem
    participant Chat as ChatView
    participant Controller as useConversationController
    participant Provider as OpenAI-compatible Provider
    participant Tree as Conversation Tree

    UI->>Msg: select token alternative
    Msg->>Chat: onRerollToken(tokenIdx, alternative)
    Chat->>Controller: rerollFromToken(messageId, tokenIdx, alternative)
    activate Controller
    Controller->>Provider: stream with seed (replacement) + logprob options
    Provider-->>Controller: streamed chunks (text + logprobs)
    Controller->>Controller: parseChatLogprobsChunk -> StreamChunk
    Controller->>Tree: appendToNode(delta {content/reasoning, tokenLogprobs})
    Tree-->>Controller: updated node
    Controller-->>Chat: updated message + tokenLogprobs
    deactivate Controller
    Chat->>Msg: re-render with new tokenLogprobs
Loading
sequenceDiagram
    autonumber
    participant User as User/UI
    participant App as App.tsx
    participant TextHook as useTextCompletion
    participant Provider as OpenAI-compatible Provider
    participant Parser as openaiLogprobs

    User->>App: request text completion
    App->>TextHook: predict/runGeneration()
    activate TextHook
    TextHook->>Provider: streamText(messages, {logprobs: true})
    Provider-->>TextHook: chunks (raw + logprobs)
    TextHook->>Parser: parseCompletionLogprobsChunk(rawChunk)
    Parser-->>TextHook: StreamChunk {content, tokenLogprobs}
    TextHook->>TextHook: update tokenLogprobs state and seedText
    deactivate TextHook
    TextHook-->>App: {textContent, tokenLogprobs, seedText}
    App->>TextCompletionView: render overlay with TokenInlineRenderer
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

  • Areas needing focused review:
    • src/ai/openaiLogprobs.ts — parsing, normalization, and edge-case handling for logprobs
    • src/hooks/useConversationController.ts — reroll flow, concurrency/abort handling, and equality changes with tokenLogprobs
    • src/hooks/useTextCompletion.ts — seed alignment, continuation vs restart logic, and state transitions
    • src/ai/sendMessage.ts — dual-path streaming, raw-chunk parsing, and error/finalization handling
    • UI components (TokenInlineRenderer, TextCompletionView, MessageItem) — overlay/textarea coordination, floating-ui timers/positioning, accessibility, and interaction disabling

Possibly related PRs

Suggested labels

codex

🐰
I nibble on tokens, small and bright,
Heatmaps glow beneath the night,
A reroll hop, a fresh-up spin—
New tokens dance and stories begin!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: implementing token-level probability tracking and a reroll UI, which aligns with the extensive additions of tokenLogprobs, token reroll functionality, and new UI components throughout the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch token-probability

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 and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @CoolSpring8, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a powerful new feature set centered around token probabilities, significantly enhancing user interaction and control over AI model outputs. Users can now visually inspect the probability of each generated token and, for OpenAI-compatible models, actively reroll responses from any point, offering a deeper understanding and more granular influence on the generation process. This is complemented by new display settings for customization.

Highlights

  • Token Probability Visualization: Introduced the ability to visualize token probabilities within generated responses, including configurable heatmap coloring themes (traffic-light, monochrome-red, monochrome-blue) to highlight low-probability tokens.
  • Token Reroll Capability: Added functionality to 'reroll' responses from a specific token, allowing users to explore alternative continuations based on probability data. This includes an inline token alternatives menu for selection.
  • New Display Settings: A new 'Display' tab has been added to the settings modal, providing options to toggle the token probability heatmap and select a preferred theme.
  • Core Logic Integration: Extensive updates to the core message streaming and conversation management logic to capture, parse, and store token log probabilities from OpenAI-compatible providers, enabling the new visualization and reroll features.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant and well-implemented feature for token probability visualization and interaction. The code is generally of high quality, with good attention to detail, especially in the complex UI components for displaying token information. I've provided a few suggestions to improve type safety, address potential edge cases with token parsing, and refactor some duplicated logic for better maintainability. Overall, this is an excellent addition to the project.

Comment thread src/ai/openaiLogprobs.ts
Comment thread src/ai/openaiLogprobs.ts Outdated
Comment thread src/ai/openaiLogprobs.ts Outdated
Comment thread src/components/SettingsModal.tsx
Comment thread src/hooks/useTextCompletion.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useConversationController.ts (1)

163-190: Missing dependency in areMessagesEqual.

areMessagesEqual now calls areTokenLogprobsEqual (line 179), but areTokenLogprobsEqual is not included in the dependency array (line 189). While areTokenLogprobsEqual has an empty dependency array and is stable, it should still be listed for correctness and to avoid future issues if its dependencies change.

 	},
-	[areContentsEqual],
+	[areContentsEqual, areTokenLogprobsEqual],
 );
🧹 Nitpick comments (4)
src/state/useSettingsStore.ts (1)

11-20: Token heatmap settings wiring looks consistent

State, persistence, and hydration for enableTokenHeatmap / heatmapTheme are coherent and backward compatible; defaults are applied correctly when older stored settings are missing these fields. As an optional cleanup, you could centralize the heatmap theme union into a shared HeatmapTheme type to avoid repeating the string literals across files.

Also applies to: 22-35, 52-60, 71-77, 111-122, 135-145

src/components/SettingsModal.tsx (1)

38-38: Display tab and heatmap controls are wired correctly

The new “Display” tab correctly binds to enableTokenHeatmap / heatmapTheme and uses reasonable copy and defaults. As an optional improvement, consider centralizing the heatmap theme values (label/value pairs) in a shared config or type to keep Settings, the store, and TokenInlineRenderer in sync if you add themes later.

Also applies to: 40-58, 97-101, 370-395, 397-449, 760-777, 790-795

src/hooks/useConversationController.ts (1)

363-500: Token reroll implementation is well-structured.

The handleRerollFromToken function:

  • Properly validates readiness and provider compatibility
  • Correctly aborts active streams before starting a new one
  • Builds seed tokens with the replacement injected at the correct position
  • Separates reasoning vs content tokens for proper prefix construction
  • Handles errors gracefully with appropriate status transitions

One observation: the temperature: 0.3 at line 435 is hardcoded. Consider exposing this as a configurable option if users need different creativity levels for rerolls.

src/ai/openaiLogprobs.ts (1)

92-127: Consider type refinement for ModelMessage casting.

The as unknown as ModelMessage casts work but are somewhat brittle. If the ai library's ModelMessage type evolves, these casts could silently break.

Consider creating a local type or using a more specific type assertion if the ai library provides better typing options:

// Alternative: explicit type with the properties you need
type ChatMessage = { role: string; content: MessageContent };

However, this is low priority if the current approach is working reliably with the ai library version in use.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 954b09e and 3d86ac7.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • package.json (1 hunks)
  • src/App.tsx (5 hunks)
  • src/ai/openaiCompatible.ts (2 hunks)
  • src/ai/openaiLogprobs.ts (1 hunks)
  • src/ai/sendMessage.ts (4 hunks)
  • src/components/ChatView.tsx (6 hunks)
  • src/components/MessageItem.tsx (3 hunks)
  • src/components/SettingsModal.tsx (6 hunks)
  • src/components/TextCompletionView.tsx (3 hunks)
  • src/components/TokenInlineRenderer.tsx (1 hunks)
  • src/hooks/useConversationController.ts (6 hunks)
  • src/hooks/useProviderReadiness.ts (5 hunks)
  • src/hooks/useTextCompletion.ts (3 hunks)
  • src/state/useSettingsStore.ts (6 hunks)
  • src/tree/types.ts (2 hunks)
  • src/tree/useConversationTree.ts (10 hunks)
  • src/types.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
!(**/*.md)

📄 CodeRabbit inference engine (AGENTS.md)

Indentation: use one tab (Biome default) except for Markdown files where space indentation is used

Files:

  • package.json
  • src/tree/types.ts
  • src/state/useSettingsStore.ts
  • src/types.ts
  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/tree/useConversationTree.ts
  • src/components/TokenInlineRenderer.tsx
  • src/ai/openaiCompatible.ts
  • src/components/TextCompletionView.tsx
  • src/ai/sendMessage.ts
  • src/hooks/useProviderReadiness.ts
  • src/components/MessageItem.tsx
  • src/ai/openaiLogprobs.ts
  • src/hooks/useTextCompletion.ts
  • src/App.tsx
  • src/hooks/useConversationController.ts
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting. Run bunx biome check --write . to format and organize imports

Files:

  • package.json
  • src/tree/types.ts
  • src/state/useSettingsStore.ts
  • src/types.ts
  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/tree/useConversationTree.ts
  • src/components/TokenInlineRenderer.tsx
  • src/ai/openaiCompatible.ts
  • src/components/TextCompletionView.tsx
  • src/ai/sendMessage.ts
  • src/hooks/useProviderReadiness.ts
  • src/components/MessageItem.tsx
  • src/ai/openaiLogprobs.ts
  • src/hooks/useTextCompletion.ts
  • src/App.tsx
  • src/hooks/useConversationController.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript and React 18 for development
Use tsc for type-checking. Run bunx tsc --noEmit to check for type errors

Files:

  • src/tree/types.ts
  • src/state/useSettingsStore.ts
  • src/types.ts
  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/tree/useConversationTree.ts
  • src/components/TokenInlineRenderer.tsx
  • src/ai/openaiCompatible.ts
  • src/components/TextCompletionView.tsx
  • src/ai/sendMessage.ts
  • src/hooks/useProviderReadiness.ts
  • src/components/MessageItem.tsx
  • src/ai/openaiLogprobs.ts
  • src/hooks/useTextCompletion.ts
  • src/App.tsx
  • src/hooks/useConversationController.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Never commit API keys. Keys are user-provided and stored client-side (IndexedDB). Avoid server code or bundling secrets into dist/
Keep code concise and self-documenting

Files:

  • src/tree/types.ts
  • src/state/useSettingsStore.ts
  • src/types.ts
  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/tree/useConversationTree.ts
  • src/components/TokenInlineRenderer.tsx
  • src/ai/openaiCompatible.ts
  • src/components/TextCompletionView.tsx
  • src/ai/sendMessage.ts
  • src/hooks/useProviderReadiness.ts
  • src/components/MessageItem.tsx
  • src/ai/openaiLogprobs.ts
  • src/hooks/useTextCompletion.ts
  • src/App.tsx
  • src/hooks/useConversationController.ts
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Components under src/components/ should include their own UI behavior (hover/edit states, popovers, menus) rather than being purely presentational

Files:

  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/components/TokenInlineRenderer.tsx
  • src/components/TextCompletionView.tsx
  • src/components/MessageItem.tsx
src/**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer Tailwind utilities for CSS; add global styles in src/index.css only when necessary

Files:

  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/components/TokenInlineRenderer.tsx
  • src/components/TextCompletionView.tsx
  • src/components/MessageItem.tsx
  • src/App.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

When using border in Tailwind, also include border-solid (and set a color) to avoid browser defaults

Files:

  • src/components/ChatView.tsx
  • src/components/SettingsModal.tsx
  • src/components/TokenInlineRenderer.tsx
  • src/components/TextCompletionView.tsx
  • src/components/MessageItem.tsx
  • src/App.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Providers only in `index.tsx` (e.g., `MantineProvider`). Settings state lives in `src/state/useSettingsStore.ts`, conversation state in `src/tree/useConversationTree.ts` and `src/tree/types.ts`, and AI provider helpers in `src/ai/openaiCompatible.ts`

Applied to files:

  • src/state/useSettingsStore.ts
  • src/components/SettingsModal.tsx
  • src/tree/useConversationTree.ts
  • src/ai/sendMessage.ts
  • src/hooks/useProviderReadiness.ts
  • src/ai/openaiLogprobs.ts
  • src/hooks/useTextCompletion.ts
  • src/App.tsx
  • src/hooks/useConversationController.ts
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Applies to src/components/**/*.{ts,tsx} : Components under `src/components/` should include their own UI behavior (hover/edit states, popovers, menus) rather than being purely presentational

Applied to files:

  • src/components/SettingsModal.tsx
  • src/components/TokenInlineRenderer.tsx
  • src/components/TextCompletionView.tsx
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Shared conversation/tree logic belongs in the store (`src/tree/` and `src/state/`), not in individual components

Applied to files:

  • src/tree/useConversationTree.ts
🧬 Code graph analysis (11)
src/tree/types.ts (1)
src/types.ts (1)
  • TokenLogprob (23-28)
src/state/useSettingsStore.ts (2)
src/types.ts (1)
  • BuiltInAvailability (56-61)
src/constants/storageKeys.ts (1)
  • settingsKey (1-1)
src/components/ChatView.tsx (1)
src/types.ts (2)
  • TokenAlternative (30-33)
  • MessageContentPart (10-12)
src/tree/useConversationTree.ts (2)
src/types.ts (2)
  • TokenLogprob (23-28)
  • MessageContent (14-14)
src/tree/types.ts (1)
  • NodeID (3-3)
src/components/TokenInlineRenderer.tsx (2)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/state/useSettingsStore.ts (1)
  • useSettingsStore (52-275)
src/components/TextCompletionView.tsx (1)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/ai/sendMessage.ts (3)
src/types.ts (1)
  • TokenLogprob (23-28)
src/ai/openaiLogprobs.ts (3)
  • toModelMessages (92-127)
  • buildChatLogprobOptions (297-302)
  • parseChatLogprobsChunk (129-226)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
src/ai/openaiLogprobs.ts (1)
src/types.ts (2)
  • TokenLogprob (23-28)
  • Message (16-21)
src/hooks/useTextCompletion.ts (3)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/ai/openaiLogprobs.ts (2)
  • buildCompletionLogprobOptions (304-309)
  • parseCompletionLogprobsChunk (228-295)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
src/App.tsx (1)
src/hooks/useConversationController.ts (1)
  • useConversationController (28-529)
src/hooks/useConversationController.ts (4)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/tree/useConversationTree.ts (1)
  • useConversationTree (224-592)
src/ai/openaiLogprobs.ts (3)
  • toModelMessages (92-127)
  • buildChatLogprobOptions (297-302)
  • parseChatLogprobsChunk (129-226)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
🔇 Additional comments (34)
src/ai/sendMessage.ts (6)

1-14: LGTM!

The import reorganization properly brings in the necessary types and utilities for the dual-path streaming logic with token logprobs support.


28-35: LGTM!

The appendToNode delta signature cleanly extends to support tokenLogprobs alongside existing content and reasoning fields, maintaining consistent optional semantics.


74-100: LGTM!

The branching logic for determining whether to append to an existing assistant message or create a new one is well-structured. The optional chaining on lastMessage?.role properly guards against undefined, ensuring the shouldAppendToAssistant condition safely short-circuits before accessing lastMessage._metadata.uuid inside the conditional block.


115-149: LGTM!

The built-in provider path correctly handles text and reasoning deltas without token logprobs (as expected for browser-based AI). The conditional prefix injection for browser-ai is properly scoped.


150-192: LGTM!

The OpenAI-compatible streaming path properly:

  • Enables raw chunk access with includeRawChunks: true
  • Uses buildChatLogprobOptions for provider-specific logprob configuration
  • Safely guards against undefined/empty logprobs with optional chaining
  • Handles stream errors comprehensively (string, Error instance, and fallback)

200-205: LGTM!

The conditional cleanup pattern correctly prevents race conditions by only clearing the generating state if this stream is still the latest one. This ensures that a newer stream won't have its state inadvertently cleared by an older finishing stream.

src/tree/types.ts (2)

1-1: LGTM!

Import correctly brings in TokenLogprob alongside the existing MessageContent import.


15-15: LGTM!

The optional tokenLogprobs field cleanly extends TreeNode to carry token-level probability data, aligning with the broader token tracking feature without breaking existing node handling.

src/ai/openaiCompatible.ts (2)

4-5: LGTM!

Extracting the provider name into a constant establishes a single source of truth, which is now correctly consumed by other modules (sendMessage.ts, hooks) per the AI summary. This aligns with the codebase convention of placing AI provider helpers in this file. Based on learnings.


22-22: LGTM!

Using the constant here ensures consistency with all other usages of the provider name across the codebase.

src/types.ts (3)

7-7: LGTM!

Adding tokenLogprobs to MessageMetadata enables token-level data to flow through the message layer, complementing the TreeNode extension in src/tree/types.ts.


23-33: LGTM!

The TokenLogprob and TokenAlternative interfaces are well-designed:

  • TokenLogprob.probability is correctly optional (some providers may not include probability for the selected token)
  • TokenAlternative.probability is required (alternatives always have probabilities for ranking)
  • The segment field enables distinguishing content vs reasoning tokens for extended thinking models

68-79: LGTM!

Extending ChatProviderReady for openai-compatible with baseURL and apiKey enables downstream features (like token reroll) to reconstruct API calls without additional lookups.

package.json (1)

16-16: @floating-ui/react version is current. The specified version ^0.27.16 matches the latest stable release on npm, making this dependency addition appropriate.

src/hooks/useProviderReadiness.ts (1)

12-20: Base URL and API key propagation is correct

baseURL/apiKey are correctly added to the readiness options, included in memo deps, and exposed only on the "openai-compatible" branches for chat and completion, which matches the intended usage.

Also applies to: 22-30, 41-47, 57-65, 67-89

src/components/ChatView.tsx (1)

7-12: Per-message token visibility and reroll wiring look solid

The tokenViewStates map, cleanup effect, and handleTokenViewChange/handleTokenReroll callbacks are all correctly keyed on message._metadata.uuid and integrate cleanly with MessageItem props. The new onTokenReroll surface is well-shaped.

Also applies to: 33-37, 53-59, 104-113, 129-152, 269-295

src/App.tsx (1)

56-66: End-to-end token probability / reroll wiring in App looks correct

The additional baseURL/apiKey threading into useProviderReadiness, the new rerollFromToken and text-completion fields from the hooks, and their propagation into ChatView and TextCompletionView are all coherently wired and respect existing provider-kind constraints.

Also applies to: 84-92, 94-120, 124-135, 173-178, 211-227, 238-256

src/hooks/useTextCompletion.ts (1)

5-15: Token logprob streaming and reroll logic in useTextCompletion looks solid

The new tokenLogprobs/seedRef flow, including runGeneration, predict’s alignment+pruning logic, rerollFromToken, and overwriteTextContent, is internally consistent and should keep the overlay in sync with the underlying text while handling edits, aborts, and rerolls correctly.

Also applies to: 23-28, 29-94, 96-175, 188-244

src/components/MessageItem.tsx (4)

46-55: Well-implemented controlled/uncontrolled pattern.

The token view state correctly supports both controlled (via showTokens + onShowTokensChange) and uncontrolled (via internal state) modes, following React best practices.


63-84: Token mapping logic is sound.

The buildSelectHandler correctly maps the local token index (within the filtered contentTokenEntries or reasoningTokenEntries) back to the global index in tokenLogprobs. The guard at line 79 handles edge cases appropriately.


157-163: Token view toggle button correctly gated.

The toggle only renders when token data exists, and the icon/title appropriately reflects the current state.


184-191: Token rendering integration looks correct.

TokenInlineRenderer is correctly used for both reasoning (with disabled to prevent rerolls) and content (with onSelectAlternative for rerolls). The fallback to Markdown rendering when not in token view maintains backward compatibility.

Also applies to: 196-203

src/hooks/useConversationController.ts (2)

117-161: Token comparison logic is thorough.

The areTokenLogprobsEqual helper correctly handles identity checks, null/undefined cases, length mismatches, and deep comparison of token properties including alternatives.


329-334: Correctly clears tokenLogprobs on edit.

Setting tokenLogprobs: undefined when replacing an edited node ensures stale token data doesn't persist after content changes.

src/tree/useConversationTree.ts (5)

35-47: Type definitions correctly extended for tokenLogprobs.

The delta and function signatures are properly updated to support optional tokenLogprobs throughout the tree operations.


166-178: Conditional tokenLogprobs serialization is correct.

The toMessage function properly includes tokenLogprobs in _metadata only when present, avoiding unnecessary property pollution.


361-389: Token append logic is correct.

appendToNode properly spreads existing tokens with new ones, preserving order and handling the empty/undefined cases appropriately.


449-472: Clone preserves token data correctly.

cloneNode now copies tokenLogprobs from the source node, maintaining data integrity during duplication.


560-590: Snapshot import handles tokenLogprobs.

The importSnapshot function correctly preserves tokenLogprobs when restoring conversation state from a snapshot.

src/ai/openaiLogprobs.ts (5)

10-33: Logprob to probability conversion is mathematically correct.

Math.exp(logprob) correctly converts log-probabilities to probabilities. The Number.isFinite filter appropriately handles edge cases like -Infinity logprobs.


35-44: Fallback token handling is sound.

Ensuring the chosen token always appears in alternatives (even with probability 0 if not returned by the API) prevents UI issues when displaying token choices.


129-226: Chat logprobs parsing handles multiple API formats.

The function correctly handles:

  • Both array and record formats for top_logprobs
  • String and array formats for reasoning_content
  • Fallback paths when primary logprobs data is unavailable

The complexity is warranted given the API format variations.


228-295: Completion logprobs parsing is consistent with chat parsing.

The structure mirrors parseChatLogprobsChunk appropriately, with the key difference being the text field instead of delta.content and hardcoded segment: "content" which is correct for text completions.


297-309: Provider options builders are clean and reusable.

The functions correctly construct provider-specific options for enabling logprobs in API requests.

Comment thread src/components/TextCompletionView.tsx
Comment thread src/components/TokenInlineRenderer.tsx
@CoolSpring8 CoolSpring8 changed the title Token probability Implement token-level probability tracking and reroll UI' Dec 4, 2025
@CoolSpring8 CoolSpring8 changed the title Implement token-level probability tracking and reroll UI' Implement token-level probability tracking and reroll UI Dec 4, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d86ac7 and 97ac30d.

📒 Files selected for processing (7)
  • src/ai/openaiLogprobs.ts (1 hunks)
  • src/components/SettingsModal.tsx (7 hunks)
  • src/components/TextCompletionView.tsx (3 hunks)
  • src/components/TokenInlineRenderer.tsx (1 hunks)
  • src/hooks/useConversationController.ts (6 hunks)
  • src/hooks/useTextCompletion.ts (3 hunks)
  • src/state/useSettingsStore.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/TokenInlineRenderer.tsx
  • src/components/SettingsModal.tsx
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript and React 18 for development
Use tsc for type-checking. Run bunx tsc --noEmit to check for type errors

Files:

  • src/state/useSettingsStore.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/openaiLogprobs.ts
  • src/components/TextCompletionView.tsx
!(**/*.md)

📄 CodeRabbit inference engine (AGENTS.md)

Indentation: use one tab (Biome default) except for Markdown files where space indentation is used

Files:

  • src/state/useSettingsStore.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/openaiLogprobs.ts
  • src/components/TextCompletionView.tsx
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting. Run bunx biome check --write . to format and organize imports

Files:

  • src/state/useSettingsStore.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/openaiLogprobs.ts
  • src/components/TextCompletionView.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Never commit API keys. Keys are user-provided and stored client-side (IndexedDB). Avoid server code or bundling secrets into dist/
Keep code concise and self-documenting

Files:

  • src/state/useSettingsStore.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/openaiLogprobs.ts
  • src/components/TextCompletionView.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Components under src/components/ should include their own UI behavior (hover/edit states, popovers, menus) rather than being purely presentational

Files:

  • src/components/TextCompletionView.tsx
src/**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer Tailwind utilities for CSS; add global styles in src/index.css only when necessary

Files:

  • src/components/TextCompletionView.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

When using border in Tailwind, also include border-solid (and set a color) to avoid browser defaults

Files:

  • src/components/TextCompletionView.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Providers only in `index.tsx` (e.g., `MantineProvider`). Settings state lives in `src/state/useSettingsStore.ts`, conversation state in `src/tree/useConversationTree.ts` and `src/tree/types.ts`, and AI provider helpers in `src/ai/openaiCompatible.ts`

Applied to files:

  • src/state/useSettingsStore.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/openaiLogprobs.ts
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Applies to src/components/**/*.{ts,tsx} : Components under `src/components/` should include their own UI behavior (hover/edit states, popovers, menus) rather than being purely presentational

Applied to files:

  • src/components/TextCompletionView.tsx
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript and React 18 for development

Applied to files:

  • src/components/TextCompletionView.tsx
🧬 Code graph analysis (5)
src/state/useSettingsStore.ts (2)
src/types.ts (2)
  • ProviderEntry (44-54)
  • BuiltInAvailability (56-61)
src/constants/storageKeys.ts (1)
  • settingsKey (1-1)
src/hooks/useTextCompletion.ts (3)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/ai/openaiLogprobs.ts (2)
  • buildCompletionLogprobOptions (325-330)
  • parseCompletionLogprobsChunk (254-316)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
src/hooks/useConversationController.ts (3)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/ai/openaiLogprobs.ts (3)
  • toModelMessages (123-158)
  • buildChatLogprobOptions (318-323)
  • parseChatLogprobsChunk (160-252)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
src/ai/openaiLogprobs.ts (1)
src/types.ts (2)
  • TokenLogprob (23-28)
  • Message (16-21)
src/components/TextCompletionView.tsx (1)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
🔇 Additional comments (24)
src/state/useSettingsStore.ts (7)

11-17: LGTM! Clean type definition.

The heatmap theme constant and type are well-structured. Using as const ensures literal types for the theme options, and deriving the type from the constant maintains a single source of truth.


23-24: LGTM! Proper storage schema extension.

The new heatmap settings are correctly added to the persisted settings interface with appropriate types.


34-35: LGTM! Consistent state API extension.

The new heatmap fields and setter methods follow the established pattern for settings state. The async signatures align with other persistence operations.

Also applies to: 40-41


60-75: LGTM! Proper persistence implementation.

The new heatmap settings are correctly extracted from state and included in the persisted object, following the same pattern as existing settings.


81-82: LGTM! Sensible default values.

The defaults (enableTokenHeatmap: false, heatmapTheme: "traffic-light") are appropriate. Making the heatmap opt-in is a good UX choice.


121-128: LGTM! Correct setter implementation.

Both setters follow the established pattern: update in-memory state, then persist. The implementation is consistent with other settings like setEnableBeforeUnloadWarning.


149-150: LGTM! Backward-compatible hydration.

The hydration logic correctly handles existing users who don't have these settings stored yet, falling back to sensible defaults that match the initial state.

src/ai/openaiLogprobs.ts (6)

16-39: LGTM!

The normalization logic correctly handles both array and object formats, properly converts log probabilities to probabilities using Math.exp, and safely filters out non-finite values.


41-50: LGTM!

The fallback logic correctly ensures that the selected token always appears in the alternatives list, using probability 0 when it's not already present.


123-158: LGTM!

The function now properly handles content type conversions without unsafe type assertions. System and assistant messages are correctly converted to text-only content via helper functions, while user messages preserve multipart structure including images. The past review concern has been addressed.


160-252: LGTM!

The parsing logic correctly handles both content and reasoning segments, properly allows empty string tokens (as some models may produce them), and includes appropriate fallback logic to reconstruct text from tokens when delta content is missing.


254-316: LGTM!

The completion logprob parsing correctly handles token extraction and includes proper fallback logic to reconstruct text from tokens when choice.text is unavailable.


318-330: LGTM!

The option builder functions correctly construct provider-specific logprob configurations with sensible defaults.

src/hooks/useTextCompletion.ts (4)

29-94: LGTM!

The generation function properly manages streaming, abort controllers, and token logprob updates. Error handling correctly distinguishes between aborts and actual errors.


96-142: LGTM!

The predict function cleanly validates token reusability in a single pass and branches appropriately. The duplicate logic mentioned in past reviews has been consolidated.


144-153: LGTM!

The cancel function properly aborts the active controller and resets generation state.


192-199: LGTM!

The overwrite function correctly resets the seed and clears token state when manually replacing content.

src/components/TextCompletionView.tsx (3)

1-32: LGTM!

Imports and props are correctly defined. The past review concerns about React type imports and unused props have been resolved.


33-92: LGTM!

Mouse handling and RAF cleanup are properly implemented. The requestAnimationFrame is correctly cancelled on mousemove, mouse leave, and component unmount. Past review concerns have been addressed.


94-178: LGTM!

The two-layer rendering approach correctly overlays tokens while keeping the textarea interactive. The showTokenOverlay prop is properly wired into the rendering logic.

src/hooks/useConversationController.ts (4)

117-161: LGTM!

The token logprob equality checker correctly performs deep comparison of tokens, probabilities, segments, and alternatives arrays.


329-349: LGTM!

Correctly clears tokenLogprobs when finishing an edit, as the original token metadata is no longer valid after content modification.


363-500: LGTM!

The token reroll implementation is comprehensive and correct. It properly validates provider compatibility, constructs seed tokens with correct segment metadata, separates reasoning and content segments, and streams the regenerated response with logprob tracking. Error handling and cleanup are properly implemented.


527-527: LGTM!

The rerollFromToken function is correctly exposed in the public API.

Comment thread src/hooks/useTextCompletion.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/hooks/useTextCompletion.ts (1)

100-142: Token alignment / reuse logic in predict looks correct; consider extracting if reused elsewhere

The canReuseTokens + currentCheck walk correctly ensures:

  • Only prefixes where seedRef.current + concatenated tokens remain a prefix of textContent are reused.
  • Pure tail deletions truncate tokenLogprobs (via validTokenCount) and continue generation.
  • Any mid-span edit or seed misalignment resets tokens and treats textContent as a fresh seed.

If you have similar alignment logic in the chat completion hook or other flows, consider extracting this into a shared helper to avoid future drift and keep the behavior consistent across modes.

🧹 Nitpick comments (1)
src/hooks/useTextCompletion.ts (1)

155-191: Reroll behavior and segment preservation look good; verify intended handling of suffix/user edits

The rerollFromToken implementation is solid:

  • Safely early-returns on out-of-range tokenIndex.
  • Cancels in‑flight generation when isGenerating is true.
  • Rebuilds alternatives with the new choice first and removes duplicate tokens.
  • Preserves segment via segment: target.segment, resolving the previous missing-segment issue.
  • Recomputes newSeed from seedRef.current + prefixTokens + replacement and updates tokenLogprobs accordingly before regenerating.

One behavior to double‑check: by rebuilding newSeed from seedRef.current and the prefix tokens, then calling setTextContent(newSeed), any manual user edits after the rerolled token (or outside the tracked token region) are discarded. If the UI only exposes reroll for purely model-generated spans this is fine; otherwise, you may want to confirm this is the desired UX, or consider rebasing rerolls relative to the current textContent instead of seedRef.current.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97ac30d and a765053.

📒 Files selected for processing (1)
  • src/hooks/useTextCompletion.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript and React 18 for development
Use tsc for type-checking. Run bunx tsc --noEmit to check for type errors

Files:

  • src/hooks/useTextCompletion.ts
!(**/*.md)

📄 CodeRabbit inference engine (AGENTS.md)

Indentation: use one tab (Biome default) except for Markdown files where space indentation is used

Files:

  • src/hooks/useTextCompletion.ts
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting. Run bunx biome check --write . to format and organize imports

Files:

  • src/hooks/useTextCompletion.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Never commit API keys. Keys are user-provided and stored client-side (IndexedDB). Avoid server code or bundling secrets into dist/
Keep code concise and self-documenting

Files:

  • src/hooks/useTextCompletion.ts
🧠 Learnings (1)
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Providers only in `index.tsx` (e.g., `MantineProvider`). Settings state lives in `src/state/useSettingsStore.ts`, conversation state in `src/tree/useConversationTree.ts` and `src/tree/types.ts`, and AI provider helpers in `src/ai/openaiCompatible.ts`

Applied to files:

  • src/hooks/useTextCompletion.ts
🔇 Additional comments (2)
src/hooks/useTextCompletion.ts (2)

5-28: Imports and new token/seed state wiring look consistent

The added imports for OpenAI-compatible logprob handling and the new tokenLogprobs/seedRef state are coherently used below; types line up with later usage and there are no apparent typing or lifecycle issues here.


29-94: Streaming + logprob aggregation and abort handling are robust

The runGeneration implementation correctly:

  • Guards against concurrent runs (hasActiveGeneration + abortControllerRef).
  • Updates seedRef only when requested via updateSeed.
  • Streams text deltas into textContent and pushes parsed tokenLogprobs incrementally.
  • Distinguishes between user-initiated aborts (no toast) and real errors (logs + toast) while ensuring isGenerating and abortControllerRef are reset in finally.

No changes requested here.

Comment thread src/hooks/useTextCompletion.ts
@CoolSpring8
Copy link
Copy Markdown
Owner Author

@codex review
/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that introduces a sophisticated set of features for token-level interaction, including probability visualization and rerolling. The implementation is well-thought-out, especially the complex UI for text completion and the intelligent logic in the useTextCompletion hook. The code is generally of high quality.

My review includes a few suggestions to improve robustness and maintainability:

  • Increasing type safety when parsing raw API stream chunks to prevent potential runtime errors.
  • Refactoring duplicated stream-processing logic to improve maintainability.
  • Replacing a hardcoded color value with a theme-based Tailwind class for better consistency.
  • Adding comments to a particularly complex function to improve its readability.

Overall, this is a fantastic contribution.

Comment thread src/ai/openaiLogprobs.ts Outdated
Comment thread src/components/TextCompletionView.tsx
Comment thread src/hooks/useConversationController.ts Outdated
Comment thread src/hooks/useTextCompletion.ts
Copy link
Copy Markdown

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

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.

ℹ️ 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/ai/sendMessage.ts Outdated
…ntroducing utility functions for improved readability and maintainability
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/ai/streamUtils.ts (1)

34-50: The if-if pattern allows processing multiple part types in a single iteration.

The use of sequential if statements rather than if-else-if means all conditions are evaluated for each part. This appears intentional to allow a raw part to potentially also be an error part, but in practice the ai library's StreamPart types are mutually exclusive. Consider using else if or a switch statement for clarity and marginal performance benefit.

 	for await (const part of fullStream) {
-		if (part.type === "text-delta" && part.text) {
+		switch (part.type) {
+		case "text-delta":
+			if (part.text) append({ content: part.text });
+			break;
+		case "reasoning-delta":
+			if (part.text) append({ reasoning: part.text });
+			break;
+		case "raw":
+			if (parseRawChunk) {
+				const chunk = parseRawChunk(part.rawValue);
+				if (chunk?.tokenLogprobs?.length) {
+					append({ tokenLogprobs: chunk.tokenLogprobs });
+				}
+			}
+			break;
+		case "error":
+			throw new Error(toErrorMessage(part.error));
+		}
-			append({ content: part.text });
-		}
-		if (part.type === "reasoning-delta" && part.text) {
-			append({ reasoning: part.text });
-		}
-		if (part.type === "raw" && parseRawChunk) {
-			const chunk = parseRawChunk(part.rawValue);
-			if (chunk?.tokenLogprobs?.length) {
-				append({ tokenLogprobs: chunk.tokenLogprobs });
-			}
-		}
-		if (part.type === "error") {
-			throw new Error(toErrorMessage(part.error));
-		}
 	}
src/hooks/useConversationController.ts (2)

427-462: Consider avoiding the detached async IIFE pattern.

The void (async () => { ... })(); pattern fires off an async operation without awaiting it, which means handleRerollFromToken returns the assistantId before the streaming completes. While this may be intentional for UI responsiveness, it makes error handling less predictable for callers. The error is caught internally and shown via toast, but the caller has no way to know if the operation succeeded.

If this is intentional, consider adding a comment explaining why the fire-and-forget pattern is used here.


436-436: Hardcoded temperature value.

The temperature 0.3 is hardcoded here and in sendMessage.ts. Consider extracting this to a shared constant or making it configurable to maintain consistency and ease future adjustments.

src/hooks/useTextCompletion.ts (1)

58-79: Stream processing is inlined here but uses processFullStream elsewhere.

Unlike sendMessage.ts and useConversationController.ts which use the shared processFullStream utility, this file manually iterates over the stream. Consider using processFullStream for consistency, though the inline approach here allows direct access to setTextContent and setTokenLogprobs via Immer drafts.

src/components/TextCompletionView.tsx (1)

147-147: Minor: Consider adding focus ring styles for accessibility.

The textarea has ring-0 which removes the focus ring. While the caret provides some focus indication, a subtle focus ring improves accessibility for keyboard users. As per coding guidelines, prefer Tailwind utilities for CSS.

-							className="absolute inset-0 h-full w-full resize-none overflow-hidden bg-transparent p-3 font-sans text-base leading-8 text-transparent caret-slate-900 focus:outline-none dark:text-transparent dark:caret-slate-100 border-none outline-none ring-0"
+							className="absolute inset-0 h-full w-full resize-none overflow-hidden bg-transparent p-3 font-sans text-base leading-8 text-transparent caret-slate-900 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:text-transparent dark:caret-slate-100 border-none outline-none"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c42956 and 1969585.

📒 Files selected for processing (6)
  • src/ai/openaiLogprobs.ts (1 hunks)
  • src/ai/sendMessage.ts (4 hunks)
  • src/ai/streamUtils.ts (1 hunks)
  • src/components/TextCompletionView.tsx (3 hunks)
  • src/hooks/useConversationController.ts (6 hunks)
  • src/hooks/useTextCompletion.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ai/openaiLogprobs.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript and React 18 for development
Use tsc for type-checking. Run bunx tsc --noEmit to check for type errors

Files:

  • src/ai/streamUtils.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/sendMessage.ts
  • src/components/TextCompletionView.tsx
!(**/*.md)

📄 CodeRabbit inference engine (AGENTS.md)

Indentation: use one tab (Biome default) except for Markdown files where space indentation is used

Files:

  • src/ai/streamUtils.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/sendMessage.ts
  • src/components/TextCompletionView.tsx
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting. Run bunx biome check --write . to format and organize imports

Files:

  • src/ai/streamUtils.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/sendMessage.ts
  • src/components/TextCompletionView.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Never commit API keys. Keys are user-provided and stored client-side (IndexedDB). Avoid server code or bundling secrets into dist/
Keep code concise and self-documenting

Files:

  • src/ai/streamUtils.ts
  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/sendMessage.ts
  • src/components/TextCompletionView.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Components under src/components/ should include their own UI behavior (hover/edit states, popovers, menus) rather than being purely presentational

Files:

  • src/components/TextCompletionView.tsx
src/**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer Tailwind utilities for CSS; add global styles in src/index.css only when necessary

Files:

  • src/components/TextCompletionView.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

When using border in Tailwind, also include border-solid (and set a color) to avoid browser defaults

Files:

  • src/components/TextCompletionView.tsx
🧠 Learnings (2)
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Providers only in `index.tsx` (e.g., `MantineProvider`). Settings state lives in `src/state/useSettingsStore.ts`, conversation state in `src/tree/useConversationTree.ts` and `src/tree/types.ts`, and AI provider helpers in `src/ai/openaiCompatible.ts`

Applied to files:

  • src/hooks/useTextCompletion.ts
  • src/hooks/useConversationController.ts
  • src/ai/sendMessage.ts
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Applies to src/components/**/*.{ts,tsx} : Components under `src/components/` should include their own UI behavior (hover/edit states, popovers, menus) rather than being purely presentational

Applied to files:

  • src/components/TextCompletionView.tsx
🧬 Code graph analysis (4)
src/ai/streamUtils.ts (2)
src/types.ts (1)
  • TokenLogprob (23-28)
src/ai/openaiLogprobs.ts (1)
  • StreamChunk (4-8)
src/hooks/useTextCompletion.ts (3)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
src/ai/openaiLogprobs.ts (2)
  • buildCompletionLogprobOptions (442-447)
  • parseCompletionLogprobsChunk (371-433)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
src/ai/sendMessage.ts (4)
src/types.ts (1)
  • TokenLogprob (23-28)
src/ai/streamUtils.ts (1)
  • processFullStream (24-51)
src/ai/openaiLogprobs.ts (3)
  • toModelMessages (123-158)
  • buildChatLogprobOptions (435-440)
  • parseChatLogprobsChunk (295-369)
src/ai/openaiCompatible.ts (1)
  • OPENAI_COMPATIBLE_PROVIDER_NAME (4-4)
src/components/TextCompletionView.tsx (1)
src/types.ts (2)
  • TokenLogprob (23-28)
  • TokenAlternative (30-33)
🔇 Additional comments (21)
src/ai/streamUtils.ts (2)

1-15: LGTM!

The type definitions for StreamPart and StreamAppender are well-structured and align with the StreamChunk and TokenLogprob types defined in the related modules.


17-22: LGTM!

The toErrorMessage helper provides sensible fallback error messaging with proper type narrowing.

src/hooks/useConversationController.ts (4)

118-162: LGTM!

The areTokenLogprobsEqual comparison function is thorough, correctly handling identity checks, nullability, length mismatches, and deep comparison of token properties and alternatives.


178-191: LGTM!

The areMessagesEqual function is correctly extended to include tokenLogprobs comparison, and the dependency array is updated accordingly.


330-350: LGTM!

Clearing tokenLogprobs when editing a message is correct behavior since the edited content invalidates the original token probability data.


364-478: LGTM overall for the reroll implementation.

The handleRerollFromToken function correctly:

  • Validates provider compatibility (OpenAI-compatible only)
  • Aborts active streams before starting new generation
  • Reconstructs seed tokens with the replacement
  • Separates reasoning vs content segments
  • Handles abort and error states appropriately

The logic for building seedTokens, seedText, seedReasoning, and seedContent is sound.

src/ai/sendMessage.ts (6)

29-36: LGTM!

The appendToNode delta type is correctly extended to include tokenLogprobs for token-level probability tracking.


79-95: Verify the "continue generation" logic is intentional.

When hasContent is false and the last message is from the assistant, the code reuses the existing assistant node (line 85). This enables a "continue generation" flow where the user can extend the assistant's response without adding a new user message.

Ensure this behavior is documented and that callers understand that sending an empty prompt after an assistant message will continue generating into that same assistant node rather than creating a new conversation turn.


100-102: LGTM!

Filtering out tool messages before streaming is correct since the model providers don't need tool messages in this context.


105-130: LGTM!

The built-in provider streaming path correctly uses processFullStream without the raw chunk parser since the built-in model doesn't provide logprobs.


131-148: LGTM!

The OpenAI-compatible streaming path correctly enables raw chunks and uses parseChatLogprobsChunk to extract token logprobs. The use of processFullStream consolidates the stream processing logic as intended.


156-161: LGTM!

The finalization logic correctly only clears isGenerating when this stream is the latest, preventing race conditions with concurrent streams.

src/hooks/useTextCompletion.ts (5)

29-94: LGTM!

The runGeneration function correctly manages the abort controller lifecycle, handles streaming with token logprobs, and ensures proper cleanup in the finally block.


96-144: LGTM!

The predict function logic for determining whether to reuse existing token probabilities is well-documented with inline comments explaining the two paths: continuing generation with existing tokens vs. treating current text as a new seed.


157-193: LGTM!

The rerollFromToken function correctly preserves the segment property (line 175) as flagged in a past review. The alternatives array is properly reordered to place the replacement first.


195-205: LGTM!

The overwriteTextContent function correctly cancels any in-flight generation before resetting state, addressing the past review concern.


216-216: Returning seedRef.current yields a snapshot, not reactive state.

seedText: seedRef.current returns the ref's value at the time of render, but since it's not React state, changes to seedRef.current won't trigger re-renders. Consumers may see stale values. If reactivity is needed, consider using state instead of a ref, or document that seedText is a snapshot.

src/components/TextCompletionView.tsx (4)

1-32: LGTM!

The imports are correct with MouseEvent properly imported from React. The new props (tokenLogprobs, onTokenReroll, showTokenOverlay, generatedPrefix) are well-typed and destructured with sensible defaults.


44-76: LGTM!

The RAF-based hit-testing for hover detection is well-implemented:

  • Properly cancels previous animation frames before scheduling new ones
  • Temporarily disables pointer events on textarea to hit-test the overlay
  • Correctly handles the token menu case to avoid changing selection
  • Restores pointer events after hit-testing

78-92: LGTM!

The RAF cleanup is properly handled in both handleMouseLeave (for immediate cleanup) and the useEffect cleanup function (for unmount), addressing the past review concern about stale callbacks firing after unmount.


106-151: LGTM!

The layered UI approach with a relative overlay div and absolute textarea is a clever solution for combining token visualization with text editing. The overlay dictates height while the transparent textarea captures input.

…f streaming text and token log probabilities
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/hooks/useTextCompletion.ts (1)

88-136: Consider adding explanatory comments for the token reuse strategy.

The predict function implements sophisticated token reuse logic that correctly handles various editing scenarios. However, as noted in previous reviews, the strategy isn't immediately obvious. Adding brief comments explaining the high-level approach (what canReuseTokens means, why there are two generation paths) would improve maintainability.

Example:

 const predict = useCallback(async () => {
 	if (isGenerating) {
 		return;
 	}
+	// Strategy: Reuse existing token probabilities if the user only appended text.
+	// This allows continuing generation without restarting. If the user edited
+	// the generated portion, treat the entire current text as a new seed.
 	const canReuseTokens =
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1969585 and 788ad13.

📒 Files selected for processing (1)
  • src/hooks/useTextCompletion.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript and React 18 for development
Use tsc for type-checking. Run bunx tsc --noEmit to check for type errors

Files:

  • src/hooks/useTextCompletion.ts
!(**/*.md)

📄 CodeRabbit inference engine (AGENTS.md)

Indentation: use one tab (Biome default) except for Markdown files where space indentation is used

Files:

  • src/hooks/useTextCompletion.ts
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting. Run bunx biome check --write . to format and organize imports

Files:

  • src/hooks/useTextCompletion.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Never commit API keys. Keys are user-provided and stored client-side (IndexedDB). Avoid server code or bundling secrets into dist/
Keep code concise and self-documenting

Files:

  • src/hooks/useTextCompletion.ts
🧠 Learnings (1)
📚 Learning: 2025-11-26T05:58:11.231Z
Learnt from: CR
Repo: CoolSpring8/iaslate PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-26T05:58:11.231Z
Learning: Providers only in `index.tsx` (e.g., `MantineProvider`). Settings state lives in `src/state/useSettingsStore.ts`, conversation state in `src/tree/useConversationTree.ts` and `src/tree/types.ts`, and AI provider helpers in `src/ai/openaiCompatible.ts`

Applied to files:

  • src/hooks/useTextCompletion.ts
🔇 Additional comments (6)
src/hooks/useTextCompletion.ts (6)

5-15: LGTM: Imports align with token tracking requirements.

All new imports are necessary for the token-level probability tracking and logprob parsing functionality.


26-28: LGTM: State initialization supports token tracking.

The tokenLogprobs state and seedRef are appropriately initialized for tracking per-token probabilities and managing the regeneration seed.


30-86: Well-structured generation orchestration.

The runGeneration function properly manages abort controllers, error handling, and cleanup. The early return on line 36 when a generation is already active prevents race conditions but silently ignores subsequent calls - this appears intentional and reasonable for preventing concurrent generations.


149-185: LGTM: Token reroll logic is correct.

The rerollFromToken function properly handles token replacement, including the segment preservation that was flagged in previous reviews (line 167). The function correctly cancels active generations, updates alternatives, and restarts generation from the modified point.


187-197: LGTM: Overwrite handles cancellation correctly.

The overwriteTextContent function properly cancels any in-flight generation before resetting state (lines 189-191), addressing the race condition concern from previous reviews. This ensures old streamed tokens don't mix with the new content.


199-209: LGTM: Expanded API supports token tracking and reroll UI.

The return value appropriately exposes all functionality required for token-level probability tracking and interactive rerolling, aligning with the PR objectives.

@CoolSpring8 CoolSpring8 merged commit 55e8a2f into main Dec 5, 2025
1 check passed
@CoolSpring8 CoolSpring8 deleted the token-probability branch December 5, 2025 04:26
@coderabbitai coderabbitai Bot mentioned this pull request Dec 18, 2025
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