Skip to content

Conversation

@rossmanko
Copy link
Contributor

@rossmanko rossmanko commented Jan 28, 2026

Summary by CodeRabbit

  • New Features

    • Integrated OpenRouter as an AI provider option for model access.
  • Documentation

    • Added OpenRouter to the list of required accounts in setup prerequisites.
  • Chores

    • Added OpenRouter provider dependency and configuration support to the project.

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

rossmanko and others added 3 commits January 28, 2026 16:46
Remove city, country, and region fields from user context to reduce
sensitive data in logs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, when users aborted a request, usage was not being saved because:
1. The streamText.onFinish callback (which sets streamUsage) hadn't fired yet
2. The backend skipped saving when message already existed from frontend
3. The Convex saveMessage mutation ignored usage when updating existing messages

This fix:
- Awaits result.usage before deciding to skip backend save
- Adds hasUsageToRecord condition to proceed with save if usage available
- Updates Convex saveMessage to patch usage/model/metrics on existing messages

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Switch ask-model, ask-vision-model, ask-vision-model-for-pdfs, and
summarization-model from AI Gateway to OpenRouter with google/gemini-3-flash-preview.

- Add @openrouter/ai-sdk-provider dependency
- Add OPENROUTER_API_KEY to env example
- Update README with OpenRouter requirement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
hackerai Ready Ready Preview, Comment Jan 28, 2026 10:11pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

The PR integrates OpenRouter as an AI provider by adding environment variables and dependencies, switching model providers from gateway to OpenRouter endpoints, refactoring message update logic to conditionally patch fields, enhancing usage data capture during abort scenarios, and removing location tracking from user event logging.

Changes

Cohort / File(s) Summary
OpenRouter Provider Integration
.env.local.example, README.md, package.json, lib/ai/providers.ts
Added OPENROUTER_API_KEY environment variable and prerequisite documentation; added @openrouter/ai-sdk-provider dependency; switched ask-model, ask-vision-model, ask-vision-model-for-pdfs, and summarization-model from gateway to openrouter provider endpoints
Message Update Handling
convex/messages.ts
Refactored message patching to build patch objects incrementally with conditional field updates; added batch file attachment with per-file error handling; extends coverage to optional fields (usage, model, generation_time_ms, finish_reason)
Usage Data Capture & Abort Handling
lib/api/chat-handler.ts
Enhanced abort handling to robustly capture usage data with fallback logic; introduced hasUsageToRecord flag to determine message save eligibility; ensures resolvedUsage is used when available during streaming completion
Event Logging
lib/logger.ts
Removed optional location field (city, country, region) from ChatWideEvent.user; narrowed WideEventBuilder.setUser parameter to contain only id and subscription

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through provider gates,
OpenRouter now calibrates,
With usage tracked through abort's haste,
And patches built—no time to waste!
Location gone, but messages stay,
Better flows brighten the day! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Daily branch 2026 01 28' is a generic date-based label that does not describe any meaningful aspect of the changeset, which includes OpenRouter integration, API improvements, and logging refactoring. Provide a descriptive title summarizing the main changes, such as 'Integrate OpenRouter AI provider and improve usage tracking' or similar that captures the primary objectives.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

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

Caution

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

⚠️ Outside diff range comments (1)
convex/messages.ts (1)

126-149: Verify file ownership before attaching to existing messages.

Line 136–149 marks new files as attached without checking that the file belongs to args.userId. This can let callers associate and mutate other users’ file records. Filter newFileIds by ownership before patching file_ids and is_attached.

🔧 Suggested fix (validate ownership before attaching)
-          if (newFileIds.length > 0) {
-            patch.file_ids = [...currentFileIds, ...newFileIds];
-
-            // Mark new files as linked
-            for (const fileId of newFileIds) {
+          if (newFileIds.length > 0) {
+            const verifiedNewFileIds: Id<"files">[] = [];
+            // Mark new files as linked (only if owned by the user)
+            for (const fileId of newFileIds) {
               try {
                 const file = await ctx.db.get(fileId);
-                if (file && !file.is_attached) {
+                if (!file) continue;
+                if (file.user_id !== args.userId) {
+                  console.warn("Skipping file not owned by user:", fileId);
+                  continue;
+                }
+                verifiedNewFileIds.push(fileId);
+                if (!file.is_attached) {
                   await ctx.db.patch(file._id, { is_attached: true });
                 }
               } catch (error) {
                 console.error(
                   `Failed to mark file ${fileId} as attached:`,
                   error,
                 );
               }
             }
+            if (verifiedNewFileIds.length > 0) {
+              patch.file_ids = [...currentFileIds, ...verifiedNewFileIds];
+            }
           }
🤖 Fix all issues with AI agents
In `@convex/messages.ts`:
- Around line 153-167: The current updates use truthy checks that will skip
valid falsy values (e.g., 0 or empty string); change the presence checks for
args.generationTimeMs, args.model, args.finishReason (and any similar checks
like args.usage if needed) from truthy checks to explicit !== undefined checks
so values like 0 or "" are persisted; keep the existingMessage checks as-is
(e.g., !existingMessage.generation_time_ms) or adjust them to === undefined if
you want symmetric semantics, and update the conditions around
patch.generation_time_ms, patch.model, and patch.finish_reason accordingly.

In `@lib/logger.ts`:
- Around line 158-161: WideEventBuilder.setUser currently types its parameter as
{ id: string; subscription: string } causing TypeScript errors when chat-logger
(lib/api/chat-logger.ts) and chat-handler (lib/api/chat-handler.ts) pass a
UserContext that includes an optional location; update the
WideEventBuilder.setUser signature to accept an optional location property
(e.g., location?: { city?: string; country?: string; region?: string }) so the
method accepts the full UserContext object passed by chatLogger.setUser and
chatLogger callers, and ensure the implementation stores the location on
this.event.user without changing other behavior.

In `@package.json`:
- Line 62: The package version for `@openrouter/ai-sdk-provider` in package.json
is incorrect and causes install failures; update the dependency entry
"@openrouter/ai-sdk-provider": "^2.1.1" to use the published version "^1.2.0" so
npm can resolve and install the package successfully.

Comment on lines +153 to +167
// Update usage if provided and not already set (e.g., on abort)
if (args.usage && !existingMessage.usage) {
patch.usage = args.usage;
}

// Update metrics if provided and not already set
if (args.model && !existingMessage.model) {
patch.model = args.model;
}
if (args.generationTimeMs && !existingMessage.generation_time_ms) {
patch.generation_time_ms = args.generationTimeMs;
}
if (args.finishReason && !existingMessage.finish_reason) {
patch.finish_reason = args.finishReason;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid truthy checks that drop valid zero/empty values.

Line 153–167 uses truthy checks (args.generationTimeMs, args.model, args.finishReason). If values are valid but falsy (e.g., 0), they won’t be persisted. Use !== undefined checks instead.

✅ Safer conditional checks
-        if (args.usage && !existingMessage.usage) {
+        if (args.usage !== undefined && !existingMessage.usage) {
           patch.usage = args.usage;
         }

-        if (args.model && !existingMessage.model) {
+        if (args.model !== undefined && !existingMessage.model) {
           patch.model = args.model;
         }
-        if (args.generationTimeMs && !existingMessage.generation_time_ms) {
+        if (
+          args.generationTimeMs !== undefined &&
+          !existingMessage.generation_time_ms
+        ) {
           patch.generation_time_ms = args.generationTimeMs;
         }
-        if (args.finishReason && !existingMessage.finish_reason) {
+        if (args.finishReason !== undefined && !existingMessage.finish_reason) {
           patch.finish_reason = args.finishReason;
         }
🤖 Prompt for AI Agents
In `@convex/messages.ts` around lines 153 - 167, The current updates use truthy
checks that will skip valid falsy values (e.g., 0 or empty string); change the
presence checks for args.generationTimeMs, args.model, args.finishReason (and
any similar checks like args.usage if needed) from truthy checks to explicit !==
undefined checks so values like 0 or "" are persisted; keep the existingMessage
checks as-is (e.g., !existingMessage.generation_time_ms) or adjust them to ===
undefined if you want symmetric semantics, and update the conditions around
patch.generation_time_ms, patch.model, and patch.finish_reason accordingly.

Comment on lines +158 to 161
setUser(user: { id: string; subscription: string }): this {
this.event.user = user;
return this;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check the chat-handler.ts file to see if it passes location to setUser
rg -n "setUser" lib/api/chat-handler.ts -A 10

Repository: hackerai-tech/hackerai

Length of output: 551


🏁 Script executed:

# Also check the ChatWideEvent interface definition
rg -n "ChatWideEvent\|interface.*Event" lib/logger.ts -A 5

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Check the actual setUser method in logger.ts around lines 158-161
sed -n '155,165p' lib/logger.ts

Repository: hackerai-tech/hackerai

Length of output: 245


🏁 Script executed:

# Search for all setUser calls in the codebase
rg -n "\.setUser\(" --type ts --type tsx

Repository: hackerai-tech/hackerai

Length of output: 93


🏁 Script executed:

# Check if there are any overloads or other definitions of setUser in logger.ts
rg -n "setUser" lib/logger.ts

Repository: hackerai-tech/hackerai

Length of output: 131


🏁 Script executed:

# Search for all setUser calls in the codebase
rg -n "\.setUser\(" --type ts

Repository: hackerai-tech/hackerai

Length of output: 688


🏁 Script executed:

# Check the chat-logger.ts to see what builder.setUser is doing
sed -n '75,85p' lib/api/chat-logger.ts

Repository: hackerai-tech/hackerai

Length of output: 225


🏁 Script executed:

# Also check the context around line 81 to understand what builder is
sed -n '70,90p' lib/api/chat-logger.ts

Repository: hackerai-tech/hackerai

Length of output: 490


🏁 Script executed:

# Check what UserContext is defined as in chat-logger.ts
rg -n "UserContext" lib/api/chat-logger.ts -B 2 -A 2

Repository: hackerai-tech/hackerai

Length of output: 271


🏁 Script executed:

# Check what builder is in chat-logger.ts
rg -n "builder.*=" lib/api/chat-logger.ts | head -5

Repository: hackerai-tech/hackerai

Length of output: 142


🏁 Script executed:

# Look at the full context of where builder is initialized in chat-logger.ts
sed -n '1,50p' lib/api/chat-logger.ts

Repository: hackerai-tech/hackerai

Length of output: 1175


🏁 Script executed:

# Let's check the WideEventBuilder type definition to confirm
rg -n "WideEventBuilder\|createWideEventBuilder" lib/logger.ts -A 10 | head -40

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Search for WideEventBuilder definition across the codebase
rg -n "WideEventBuilder\|createWideEventBuilder" lib/

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Also check what's exported from lib/logger.ts
rg -n "export.*WideEventBuilder\|export.*createWideEventBuilder\|class.*EventBuilder\|function.*createWideEventBuilder" lib/logger.ts

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Let's look at the full logger.ts file to find where WideEventBuilder is defined
wc -l lib/logger.ts

Repository: hackerai-tech/hackerai

Length of output: 83


🏁 Script executed:

# Search for class definitions or function definitions that might be WideEventBuilder
rg -n "class\|function\|export" lib/logger.ts | head -30

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Let's look at the structure of logger.ts
sed -n '100,200p' lib/logger.ts

Repository: hackerai-tech/hackerai

Length of output: 2491


🏁 Script executed:

# Also search for the word "class" or "export" differently
grep -n "^class\|^export\|^const.*EventBuilder" lib/logger.ts

Repository: hackerai-tech/hackerai

Length of output: 209


Type mismatch: WideEventBuilder.setUser() rejects the location field passed by chat-logger.

The WideEventBuilder.setUser() method (lib/logger.ts, line 158) accepts only { id: string; subscription: string }. However, lib/api/chat-logger.ts wraps this with a UserContext interface that includes an optional location field (lines 27-33). When chat-logger calls builder.setUser(user) at line 81, it passes the entire UserContext object, which TypeScript will reject because location is not in the accepted type signature.

Additionally, lib/api/chat-handler.ts (lines 137-147) passes a location object to chatLogger.setUser(), which compounds the issue downstream.

Fix by updating WideEventBuilder.setUser() to accept the location field:

setUser(user: { id: string; subscription: string; location?: { city?: string; country?: string; region?: string } }): this {
🤖 Prompt for AI Agents
In `@lib/logger.ts` around lines 158 - 161, WideEventBuilder.setUser currently
types its parameter as { id: string; subscription: string } causing TypeScript
errors when chat-logger (lib/api/chat-logger.ts) and chat-handler
(lib/api/chat-handler.ts) pass a UserContext that includes an optional location;
update the WideEventBuilder.setUser signature to accept an optional location
property (e.g., location?: { city?: string; country?: string; region?: string })
so the method accepts the full UserContext object passed by chatLogger.setUser
and chatLogger callers, and ensure the implementation stores the location on
this.event.user without changing other behavior.

"@e2b/code-interpreter": "2.3.3",
"@langchain/community": "^1.1.7",
"@monaco-editor/react": "^4.7.0",
"@openrouter/ai-sdk-provider": "^2.1.1",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@openrouter/ai-sdk-provider npm package latest version

💡 Result:

The latest published version of @openrouter/ai-sdk-provider on npm is 1.2.0. [1]


🏁 Script executed:

# Check if this dependency is used in lib/ai/providers.ts
fd providers.ts

Repository: hackerai-tech/hackerai

Length of output: 103


🏁 Script executed:

# Check lib/ai/providers.ts to see the import
head -50 lib/ai/providers.ts

# Get context around line 62 in package.json
sed -n '55,70p' package.json

Repository: hackerai-tech/hackerai

Length of output: 2523


Incorrect dependency version—npm install will fail.

The specified version ^2.1.1 does not exist. The latest published version of @openrouter/ai-sdk-provider on npm is 1.2.0. Update to ^1.2.0 to match the actual package.

🤖 Prompt for AI Agents
In `@package.json` at line 62, The package version for `@openrouter/ai-sdk-provider`
in package.json is incorrect and causes install failures; update the dependency
entry "@openrouter/ai-sdk-provider": "^2.1.1" to use the published version
"^1.2.0" so npm can resolve and install the package successfully.

@rossmanko rossmanko merged commit 9f58bb5 into main Jan 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants