Skip to content

refactor: DRY model tier detection, tool merging, and log cleanup - #685

Merged
2witstudios merged 2 commits into
masterfrom
refactor/ai-chat-dry-cleanup
Feb 15, 2026
Merged

2witstudios merged 2 commits into
masterfrom
refactor/ai-chat-dry-cleanup

Conversation

@2witstudios

@2witstudios 2witstudios commented Feb 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Centralize all hardcoded model ID checks (=== 'glm-4.7', === 'glm-5') behind getPageSpaceModelTier() across chat route, global assistant route, SidebarSettingsTab, and getUserFacingModelName
  • Extract mergeToolSets() utility to eliminate eslint-disable / as any casts for integration tool merging
  • Unify duplicated message reconstruction logic via shared reconstructFromStructuredContent helper (−70 lines)
  • Replace console.error with structured loggers calls in message-utils
  • Strip emoji prefixes from 18 global route log messages to match chat route conventions
  • Remove deprecated StreamingMarkdown id prop, unused _isPulling var, and backward-compat test
  • Switch VirtualizedMessageList measurement from setTimeout(50) to requestAnimationFrame

Test plan

  • ai-providers-config.test.ts — 24/24 pass
  • StreamingMarkdown.test.tsx — 14/14 pass
  • Full pnpm typecheck build — 10/10 tasks pass
  • Verify AI chat rate limiting works correctly for standard and pro tiers
  • Verify SidebarSettingsTab subscription gating behaves unchanged
  • Verify integration tool merging in page and global assistant chat

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance Improvements

    • Improved message list measurement timing for smoother rendering.
  • Refactor

    • Centralized AI model tier handling and tool merging utilities.
    • Removed deprecated component props and unused variables.
    • Consolidated internal message reconstruction for reliability.
    • Cleaned up several log and status messages for consistency.
  • Tests

    • Removed a deprecated backward-compatibility test.

Centralize hardcoded model ID checks (`=== 'glm-4.7'`) behind
`getPageSpaceModelTier()` across chat route, global assistant route,
and SidebarSettingsTab. Extract `mergeToolSets()` utility to eliminate
`eslint-disable` / `as any` casts for tool merging. Unify duplicated
message reconstruction logic via shared `reconstructFromStructuredContent`
helper (-70 lines). Replace `console.error` with structured `loggers`
calls, strip emoji prefixes from global route log messages to match
chat route conventions, and remove deprecated `StreamingMarkdown` id
prop plus unused variables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Feb 15, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Centralizes PageSpace model-tier determination and tool merging (getPageSpaceModelTier, mergeToolSets), removes deprecated id/messageId props from streaming/rendering components, refactors message reconstruction, and switches virtualized list measurement to requestAnimationFrame.

Changes

Cohort / File(s) Summary
API Route Updates
apps/web/src/app/api/ai/chat/route.ts, apps/web/src/app/api/ai/global/[id]/messages/route.ts
Replaced ad-hoc model/provider checks with getPageSpaceModelTier(currentModel) (defaulting to 'standard'); centralized tool merging via mergeToolSets; cleaned up log messages (removed emoji prefixes).
Message Reconstruction & Utilities
apps/web/src/lib/ai/core/message-utils.ts, apps/web/src/lib/ai/core/tool-utils.ts
Added mergeToolSets helper; introduced internal reconstructFromStructuredContent to centralize message reconstruction and switched error logging to loggers.ai.error.
Provider Configuration & Sidebar
apps/web/src/lib/ai/core/ai-providers-config.ts, apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarSettingsTab.tsx
Switched PageSpace naming and access checks to use getPageSpaceModelTier; replaced hard-coded model name checks with tier/alias-based logic.
Message Rendering Components
apps/web/src/components/ai/shared/chat/StreamingMarkdown.tsx, apps/web/src/components/ai/shared/chat/MessageRenderer.tsx, apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx
Removed deprecated id/messageId props and propagation; inlined mention regex inside preprocessMentions; adjusted prop interfaces accordingly.
Chat UI & Virtualization
apps/web/src/components/ai/shared/chat/ChatMessagesArea.tsx, apps/web/src/components/ai/shared/chat/VirtualizedMessageList.tsx
Dropped unused aliased isPulling variable; replaced setTimeout measurement with requestAnimationFrame and cancelled via cancelAnimationFrame.
Tests
apps/web/src/components/ai/shared/__tests__/StreamingMarkdown.test.tsx
Removed test validating backward compatibility for deprecated id prop.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant API as Chat API Route
  participant Utils as Tier/Tool Utils
  participant Provider as AI Provider
  participant DB as Database

  Client->>API: Send chat request (model, tools, message)
  API->>Utils: getPageSpaceModelTier(currentModel)
  API->>Utils: mergeToolSets(filteredTools, integrationTools)
  API->>Provider: Forward request (providerType based on tier, merged tools)
  Provider-->>API: Streaming/response (tool calls/results)
  API->>DB: Save message (use reconstructFromStructuredContent)
  DB-->>API: Ack saved
  API-->>Client: Streamed response / final message
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through tiers and knitted tools with care,
No more stray ids bobbing in the air,
Regex tucked in, messages rebuilt tight,
Frames now timed to the animation light,
A little rabbit cheers—code in bloom tonight! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 refactoring changes: DRY principle applied to model tier detection, tool merging consolidation, and log cleanup across multiple files.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 refactor/ai-chat-dry-cleanup

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarSettingsTab.tsx`:
- Around line 647-649: The Upgrade CTA condition in SidebarSettingsTab.tsx
currently checks hasModelAccess('pagespace', PAGESPACE_MODEL_ALIASES.standard)
which depends on the earlier inverted requiresSubscription logic; update this
conditional to check the correct gating model (use PAGESPACE_MODEL_ALIASES.pro
instead of .standard) so the CTA shows only when the user truly lacks pro
access, and verify the hasModelAccess('pagespace', ...) call and any related
requiresSubscription inversion are consistent across the component.
- Around line 264-266: The subscription gating is inverted: update the
requiresSubscription function to return true only for the pro tier (change the
check in requiresSubscription(provider, model) from getPageSpaceModelTier(model)
=== 'standard' to === 'pro') and then update the related logic that resets or
blocks free users (the code at the places that handle selected model for free
users—the reset logic around the selection at the earlier block that currently
targets 'standard'—to target 'pro' instead) and any CTA/visibility checks (the
check that shows the upgrade CTA which currently treats 'standard' as
restricted) so they consistently treat 'pro' as the restricted tier.
🧹 Nitpick comments (3)
apps/web/src/components/ai/shared/chat/CompactMessageRenderer.tsx (1)

129-404: Significant duplication with MessageRenderer.tsx.

CompactMessageRenderer and MessageRenderer share nearly identical logic for todo list state management, socket handling, task loading, editing, deletion, and retry — differing only in styling/layout. The PR title mentions DRY cleanup; this is a prime candidate for extracting shared logic into a custom hook (e.g., useMessageState) to centralize the duplicated state, effects, and handlers across both renderers.

apps/web/src/lib/ai/core/message-utils.ts (1)

348-356: Consider inlining the trivial wrapper functions.

Both reconstructMessageFromStructuredContent and reconstructGlobalAssistantMessageFromStructuredContent are now single-line delegates to reconstructFromStructuredContent. Since they're private (not exported) and each has exactly one call site (lines 247 and 490 respectively), they could be inlined to reduce indirection.

♻️ Suggested simplification

In convertDbMessageToUIMessage (line 247):

-        return reconstructMessageFromStructuredContent(dbMessage, parsed);
+        return reconstructFromStructuredContent(dbMessage, parsed);

In convertGlobalAssistantMessageToUIMessage (line 490):

-        return reconstructGlobalAssistantMessageFromStructuredContent(dbMessage, parsed);
+        return reconstructFromStructuredContent(dbMessage, parsed);

Then remove both wrapper functions (lines 348–356 and 512–517).

Also applies to: 512-517

apps/web/src/lib/ai/core/tool-utils.ts (1)

1-5: Centralizing the cast is a good DRY improvement.

One consideration: the additional: Record<string, unknown> parameter accepts anything, so the as ToolSet cast silently trusts the caller. If integration tool shapes ever diverge from what ToolSet expects, errors will surface at runtime rather than compile time.

If the Vercel AI SDK's ToolSet definition allows it, consider tightening additional to something like Record<string, ToolSet[string]> (or a partial tool shape) so the compiler can catch mismatches. If the current type is intentional because integration tool shapes don't conform, this is fine as-is.

Comment thread apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarSettingsTab.tsx Outdated
The requiresSubscription check was gating 'standard' tier instead of
'pro', blocking free users from the standard model and showing the
upgrade CTA for the wrong tier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit ac11ede into master Feb 15, 2026
3 checks passed
@2witstudios
2witstudios deleted the refactor/ai-chat-dry-cleanup branch March 11, 2026 03:12
@coderabbitai coderabbitai Bot mentioned this pull request Apr 6, 2026
2 of 6 tasks
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