Skip to content

feat: enhance dashboard, log detail & costs UX - #1623

Merged
smakosh merged 2 commits into
mainfrom
feat/dashboard-improvements
Feb 9, 2026
Merged

smakosh merged 2 commits into
mainfrom
feat/dashboard-improvements

Conversation

@smakosh

@smakosh smakosh commented Feb 9, 2026

Copy link
Copy Markdown
Member

Summary

  • Add cached tokens and cached input cost tracking to the activity API
  • Add GET /logs/:id endpoint and a dedicated log detail page (/activity/[logId])
  • Add new dashboard metric cards: Input Tokens, Cached Tokens, Most Used Model
  • Break the cost overview chart into input/output/cached input cost lines
  • Clarify that inference costs are provider reference pricing (not deducted from balance)
  • Separate provider pricing from organization-billed costs in log cards
  • Auto-prompt the top-up credits dialog when playground credits reach zero
  • Make TopUpCreditsDialog externally controllable via open/onOpenChange props
  • Fix discount price alignment in the models list

Test plan

  • Verify activity API returns cachedTokens and cachedInputCost fields
  • Verify GET /logs/:id returns a single log with proper auth checks
  • Navigate to /activity/[logId] and confirm the log detail page renders correctly
  • Check dashboard cards show Input Tokens, Cached Tokens, and Most Used Model
  • Confirm cost chart renders three lines (input, output, cached input)
  • Verify cost tooltips and sections distinguish provider vs billed costs
  • In playground, set credits to 0 and confirm top-up dialog appears on send
  • Check models list discount prices are right-aligned

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • View detailed log pages with comprehensive request, cost, token, metadata, and error sections (new log detail view and API).
    • Fetch individual logs by ID via the API.
    • Top-up credits dialog available from the chat UI when balance is insufficient.
  • Improvements

    • Track cached token usage and cached input cost in activity and charts.
    • Dashboard adds Input Tokens, Cached Tokens, and Most Used Model/Provider metrics; inference costs shown as provider reference.
    • Minor UI tweaks to log cards and recent activity presentation.

- Add cached tokens and cached input cost to activity API
- Add GET /logs/:id endpoint for individual log retrieval
- Add dedicated log detail page (/activity/[logId])
- Add Input Tokens, Cached Tokens, Most Used Model cards
- Break cost chart into input/output/cached input lines
- Clarify inference costs as reference (not deducted)
- Separate provider pricing from billed costs in log cards
- Prompt top-up dialog when playground credits are zero
- Make TopUpCreditsDialog controllable (open/onOpenChange)
- Fix discount price alignment in models list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds cachedTokens and cachedInputCost to daily activity data; introduces GET /logs/{id} API and a server+client log detail page; updates dashboard and recent-activity flows to surface cached metrics and provider pricing; makes TopUpCreditsDialog controllable and integrates it into the chat UI.

Changes

Cohort / File(s) Summary
Activity Metrics Data
apps/api/src/routes/activity.ts, apps/ui/src/types/activity.ts
Added cachedTokens and cachedInputCost to daily activity schema, aggregation query, and UI types; propagated fields through API response and UI data shapes.
New Logs Endpoint
apps/api/src/routes/logs.ts
Added GET /logs/{id} route with auth and org-access validation; returns single log or 404.
Log Detail Page & Client
apps/ui/src/app/dashboard/[orgId]/[projectId]/activity/[logId]/page.tsx, apps/ui/src/app/dashboard/[orgId]/[projectId]/activity/[logId]/log-detail-client.tsx
Added server page fetching /logs/{id} and a comprehensive LogDetailClient component rendering request, routing, cost, tokens, metadata, plugins, tools, messages, and error sections.
Activity Listing Context
apps/ui/src/app/dashboard/[orgId]/[projectId]/activity/page.tsx, apps/ui/src/components/activity/recent-logs.tsx
Propagated orgId through activity page and RecentLogs; RecentLogs accepts optional orgId and forwards to LogCard.
Log Card Enhancement
apps/ui/src/components/dashboard/log-card.tsx
Extended LogCard props to accept orgId/projectId; added "View details" link to log detail page, reworked cost/billing display and provider-pricing tooltip.
Dashboard Metrics & Charts
apps/ui/src/components/dashboard/dashboard-client.tsx, apps/ui/src/components/dashboard/overview.tsx, apps/ui/src/components/dashboard/recent-activity-card.tsx
Added Input Tokens, Cached Tokens, Most Used Model metrics; introduced cachedInputCost in chart data and tooltip; relabeled inference cost as provider reference pricing.
Credits Dialog & Chat Integration
apps/playground/src/components/credits/top-up-credits-dialog.tsx, apps/playground/src/components/playground/chat-page-client.tsx
Made TopUpCreditsDialog controllable (open, onOpenChange) with internal fallback; added chat gating and TopUp dialog trigger when org credits are insufficient.
Chat UI Update
apps/playground/src/components/playground/chat-ui.tsx
Removed the error alert rendering from the chat input area.
Minor Layout Fix
apps/ui/src/components/models/all-models.tsx
Adjusted discounted price alignment CSS classes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser as Client (LogDetailClient)
    participant Server as NextJS Server (LogDetailPage)
    participant API as API Server
    participant DB as Database

    User->>Browser: Navigate to /dashboard/{orgId}/{projectId}/activity/{logId}
    Browser->>Server: Request page
    Server->>API: GET /logs/{id}
    API->>API: Validate auth & org access
    API->>DB: Query log by id
    DB-->>API: Return log record
    API-->>Server: Return { log }
    Server-->>Browser: Render LogDetailClient with initialData
    Browser->>Browser: Render sections (Request, Cost, Tokens, Messages, etc.)
    Browser-->>User: Display log details
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • steebchen
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: enhance dashboard, log detail & costs UX' accurately describes the main changes: adding dashboard enhancements, a new log detail page, and UX improvements related to costs display and tracking.

✏️ 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 feat/dashboard-improvements

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.

@smakosh smakosh self-assigned this Feb 9, 2026
@smakosh
smakosh enabled auto-merge February 9, 2026 20:37
@smakosh
smakosh disabled auto-merge February 9, 2026 20:42

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

Caution

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

⚠️ Outside diff range comments (2)
apps/api/src/routes/logs.ts (1)

33-103: ⚠️ Potential issue | 🟠 Major

Add missing fields to logSchema that are used by the log detail page and defined in the database schema.

The schema is missing cachedTokens, cachedInputCost, timeToFirstToken, dataStorageCost, usedModelMapping, and other fields from the DB schema (see packages/db/src/schema.ts). The log-detail client actively uses these fields—for example, cachedInputCost is displayed in the cost breakdown, and cachedTokens determines the cache status. Without these fields in the schema, Zod validation could strip them from responses, breaking the UI.

At minimum, add:

 responseSize: z.number(),
+cachedInputCost: z.number().nullable(),
+cachedTokens: z.string().nullable(),
+timeToFirstToken: z.number().nullable(),
+dataStorageCost: z.string().nullable(),
+usedModelMapping: z.string().nullable(),
 content: z.string().nullable(),

Consider adding remaining fields (discount, serviceFee, pricingTier, traceId, effort, webSearchCost, etc.) for completeness.

apps/ui/src/components/dashboard/log-card.tsx (1)

755-761: ⚠️ Potential issue | 🟡 Minor

Same as any usage for responseFormat as in log-detail-client.tsx.

This is the same coding guidelines violation flagged in the detail client. Apply the same type-guard approach here for consistency.

As per coding guidelines, **/*.{ts,tsx}: "Never use any or as any type assertions in TypeScript code unless absolutely necessary."

🤖 Fix all issues with AI agents
In `@apps/api/src/routes/logs.ts`:
- Around line 105-127: The OpenAPI spec for the getById route (created via
createRoute) only declares 200 and 404 but the handler can throw
HTTPException(401) and HTTPException(403); update the responses object for
getById to include 401 and 403 entries (with appropriate descriptions and, if
your project uses a shared error schema, reference that schema under
"application/json") so the generated docs include those auth/permission error
cases and match the handler behavior.
- Around line 138-140: The query using db.query.log.findFirst currently uses the
shorthand where: { id } but the repository prefers explicit operators; update
the where clause in the log retrieval to use the imported eq operator (e.g.,
where: { id: { eq: id } }) so it matches the style used elsewhere in logs.ts and
authorization.ts.

In `@apps/playground/src/components/playground/chat-page-client.tsx`:
- Around line 623-626: The credit check inside handleUserMessage currently
returns early but does not prevent handlePromptSubmit from clearing the input
(setText("")) or calling sendMessage, so the LLM still receives the prompt and
the input is lost; modify the flow so the credit guard runs before clearing
input/sending: either (A) change onUserMessage/handleUserMessage to return a
boolean (e.g., true=continue, false=abort) and update handlePromptSubmit to
await onUserMessage and only call setText("") and sendMessage(...) when the
returned value is true, or (B) move the credits check out of handleUserMessage
into handlePromptSubmit and perform it there before setText/sendMessage; ensure
handleUserMessage returns an appropriate value when using option A and that
setShowTopUp(true) still runs when aborting.

In
`@apps/ui/src/app/dashboard/`[orgId]/[projectId]/activity/[logId]/log-detail-client.tsx:
- Around line 599-607: The JSX uses (log.responseFormat as any).type; replace
this unsafe assertion by introducing and using a proper type guard such as
isResponseFormat(value): value is { type?: string } (or your existing
ResponseFormat interface) and then render the Field value by checking
log.responseFormat with that guard (e.g., isResponseFormat(log.responseFormat) ?
log.responseFormat.type || "-" : "-"). Update the component around the Field
render to call the guard instead of using as any so the code conforms to the
no-any rule while preserving the same fallback logic.

In `@apps/ui/src/components/dashboard/dashboard-client.tsx`:
- Around line 158-181: The code computes the top model by aggregated cost
(modelCostMap built from activityData → modelBreakdown) but exposes it as
mostUsedModel/mostUsedProvider which is misleading; either change the metric
calculation to rank by request count (use the count/request field in each
modelBreakdown entry and sum it into a map) or rename the outputs to reflect
cost ranking (e.g., topModelByCost/topProviderByCost) and update all usages and
the UI label accordingly; update the anonymous IIFE return keys
(mostUsedModel/mostUsedProvider) and any components that consume them (and the
display card title) to match the chosen semantic change so behavior and label
are consistent.

In `@apps/ui/src/components/dashboard/log-card.tsx`:
- Around line 200-219: The nested ternary inside the cost display is redundant:
replace the expression "{log.cost ? `$${log.cost.toFixed(6)}` : log.cached ?
"$0" : "$0"}" with a single fallback value so it reads "{log.cost ?
`$${log.cost.toFixed(6)}` : "$0"}"; update the JSX in the component that renders
the cost (the block using Tooltip, TooltipTrigger and the "log" variable) and
remove the unnecessary "log.cached ? "$0" : "$0"" branch, keeping any use of
log.cached only if you intend different behavior.
🧹 Nitpick comments (8)
apps/playground/src/components/playground/chat-ui.tsx (1)

133-134: error prop is accepted but never used in the component's JSX.

The error alert UI was removed, but error remains declared in ChatUIProps (Line 133) and destructured at Line 407. Since no JSX references it anymore, this is dead code. Consider removing it from the interface and the destructuring to avoid confusion — callers like chat-page-client.tsx still pass error={error} to no effect.

♻️ Suggested cleanup

In the ChatUIProps interface:

-	error?: string | null;

In the destructured props:

-	error = null,

Also applies to: 407-407

apps/playground/src/components/credits/top-up-credits-dialog.tsx (1)

90-96: Minor: handleClose setTimeout can race with a quick reopen in controlled mode.

When the parent controls open, calling setOpen(false) at Line 91 and then scheduling the step/loading reset at Line 92–95 creates a window where re-opening the dialog within 300ms would have its state reset underneath it. This is very unlikely in practice with a payment dialog, so flagging as a nit.

A safer alternative is to reset state when open transitions from false → true:

♻️ Optional: reset on open instead of delayed reset on close
+	useEffect(() => {
+		if (open) {
+			setStep("amount");
+			setLoading(false);
+		}
+	}, [open]);
+
 	const handleClose = () => {
 		setOpen(false);
-		setTimeout(() => {
-			setStep("amount");
-			setLoading(false);
-		}, 300);
 	};

Also applies to: 106-108

apps/ui/src/components/dashboard/recent-activity-card.tsx (1)

83-88: The abbreviation "ref." may be unclear to users.

Consider using a more descriptive label like "inference (reference)" or adding a tooltip. "ref." is ambiguous — users might not understand it means "reference pricing" without additional context.

apps/ui/src/types/activity.ts (1)

40-65: ActivitT duplicates DailyActivity inline — consider reusing the interface.

This is a pre-existing pattern, but now that both types need to stay in sync (with cachedTokens, cachedInputCost, etc.), consider defining ActivitT in terms of DailyActivity:

export type ActivitT = { activity: DailyActivity[] } | undefined;

This eliminates the risk of the two definitions drifting apart.

apps/ui/src/components/dashboard/overview.tsx (1)

41-57: Tooltip color mapping relies on a catch-all fallback.

The ternary chain maps inputCost → blue, outputCost → amber, and falls through to green for anything else. If a fourth line is added later, it would incorrectly render green. Consider using a lookup map for maintainability.

♻️ Optional: use a color map for clarity
+const COST_LINE_COLORS: Record<string, string> = {
+	inputCost: "#3b82f6",
+	outputCost: "#f59e0b",
+	cachedInputCost: "#10b981",
+};
+
 {payload.map((entry) => (
 	<p key={entry.dataKey} className="text-sm">
 		<span
 			className="inline-block w-2 h-2 rounded-full mr-1.5"
 			style={{
-				backgroundColor:
-					entry.dataKey === "inputCost"
-						? "#3b82f6"
-						: entry.dataKey === "outputCost"
-							? "#f59e0b"
-							: "#10b981",
+				backgroundColor: COST_LINE_COLORS[entry.dataKey ?? ""] ?? "#888",
 			}}
 		/>

This would also deduplicate the color strings shared between the tooltip and the <Line> elements below.

apps/ui/src/components/dashboard/dashboard-client.tsx (1)

422-442: Long subtitle string could overflow on narrow screens.

The subtitle concatenates multiple cost segments into one line. On smaller cards this will wrap aggressively and may be hard to scan. The MetricCard renders this as a single <p> with text-xs. Consider truncating or showing fewer details inline (e.g., just "Not deducted from your balance") and revealing the full breakdown on hover or in the chart tooltip.

apps/ui/src/app/dashboard/[orgId]/[projectId]/activity/[logId]/log-detail-client.tsx (2)

46-61: navigator.clipboard.writeText can reject — consider adding error handling.

In non-secure contexts (HTTP) or when the document lacks focus, this API throws. A .catch(() => {}) or try-catch would prevent an unhandled promise rejection.

🛡️ Proposed fix
 onClick={() => {
-	navigator.clipboard.writeText(value);
-	setCopied(true);
-	setTimeout(() => setCopied(false), 1500);
+	navigator.clipboard.writeText(value).then(() => {
+		setCopied(true);
+		setTimeout(() => setCopied(false), 1500);
+	}).catch(() => {
+		// Silently fail — clipboard API may not be available
+	});
 }}

204-208: as Log type assertion after date conversion — acceptable but slightly fragile.

The spread + date conversion produces a shape that may not exactly match Log (e.g., if the API response omits optional fields or serializes differently). This works for now but could silently mask mismatches if the API schema evolves.

Comment thread apps/api/src/routes/logs.ts
Comment on lines +138 to +140
const log = await db.query.log.findFirst({
where: { id },
});

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for findFirst and findMany with their where clauses
rg -n 'findFirst|findMany' --type=ts -A5 -g '!node_modules/**' | head -150

Repository: theopenco/llmgateway

Length of output: 8556


🏁 Script executed:

#!/bin/bash
# Check the exact code in logs.ts around lines 138-140
sed -n '130,150p' apps/api/src/routes/logs.ts

# Search for any findFirst/findMany with bare value where clauses (pattern: where: { [a-zA-Z_]: [^{] )
echo "---"
echo "Searching for any bare value where clauses in findFirst/findMany:"
rg 'findFirst|findMany' --type=ts -A3 | rg -B2 'where:\s*\{\s*[a-zA-Z_]+\s*\}' || echo "No bare value where clauses found"

Repository: theopenco/llmgateway

Length of output: 759


🏁 Script executed:

#!/bin/bash
# Search for the logSchema definition in logs.ts
rg -n 'logSchema' apps/api/src/routes/logs.ts -B5 -A15 | head -50

# Also check the database schema for log table
echo "---"
echo "Checking database schema for log table:"
find . -path ./node_modules -prune -o -name "*.ts" -type f -exec rg -l "export const log" {} \; | grep -i schema

Repository: theopenco/llmgateway

Length of output: 1507


🏁 Script executed:

#!/bin/bash
# Check the database schema for log table
rg -n "export const log" packages/db/src/schema.ts -A100 | head -120

# Also verify if there are any instances where bare value where clause works
echo "---"
echo "Checking if Drizzle ORM version supports bare value where syntax:"
cat packages/db/package.json | rg -A2 'drizzle-orm'

Repository: theopenco/llmgateway

Length of output: 3462


🏁 Script executed:

#!/bin/bash
# Check if the bare value where syntax is valid in Drizzle 1.0.0-beta.1
# Search for any documentation or examples in the codebase about where clause syntax
rg -n 'where:' packages/db/src -B2 -A2 | head -80

# Also check if there's a types file that might show the expected where clause structure
echo "---"
echo "Looking for Drizzle type definitions or examples:"
find . -path ./node_modules -prune -o -name "*.ts" -type f -exec rg -l "type.*where" {} \; | head -5

Repository: theopenco/llmgateway

Length of output: 1274


🏁 Script executed:

#!/bin/bash
# Check if the logs.ts file has any tests or if this endpoint is tested
find . -path ./node_modules -prune -o -name "*.spec.ts" -o -name "*.test.ts" | xargs rg -l "logs" 2>/dev/null | head -5

# Check the GET /logs/:id endpoint more thoroughly
echo "---"
sed -n '100,160p' apps/api/src/routes/logs.ts

# Verify Drizzle 1.0.0-beta.1 where clause syntax by checking their examples
echo "---"
echo "Checking if bare value syntax appears anywhere else in codebase:"
rg 'findFirst|findMany' --type=ts -A2 | rg -B2 'where:\s*{\s*[a-zA-Z_]+\s*:' | head -30

Repository: theopenco/llmgateway

Length of output: 1690


🏁 Script executed:

#!/bin/bash
# Fix the regex and search for where clauses more carefully
echo "Looking for all where clauses in findFirst/findMany calls:"
rg 'findFirst|findMany' --type=ts -A3 | grep -A2 'where:' | head -60

# Check if there are any tests for the logs.ts endpoint
echo "---"
echo "Checking logs.spec.ts for getById test:"
rg -n 'getById|/logs.*id' apps/api/src/routes/logs.ts.spec.ts 2>/dev/null || echo "No logs.spec.ts file found for routes"

# Check if there's a test file for the API routes
ls -la apps/api/src/routes/*.spec.ts 2>/dev/null || echo "No spec files in routes directory"

Repository: theopenco/llmgateway

Length of output: 2806


🏁 Script executed:

#!/bin/bash
# Check the logs.spec.ts file for tests on the getById endpoint
rg -n 'getById|/:id|findFirst' apps/api/src/routes/logs.spec.ts -B3 -A5 | head -80

# Also check the exact line where the issue occurs to see context
echo "---"
echo "Checking the findFirst call with bare value syntax in logs.ts:"
sed -n '135,145p' apps/api/src/routes/logs.ts

Repository: theopenco/llmgateway

Length of output: 316


🏁 Script executed:

#!/bin/bash
# Check the beacon.ts and stripe.ts files to see if they're using a different syntax
echo "Checking beacon.ts where clause:"
rg -n 'where.*type.*self-host' apps/api/src/lib/beacon.ts -B2 -A2

echo "---"
echo "Checking stripe.ts where clauses:"
rg -n 'where.*id:.*organizationId' apps/api/src/stripe.ts -B2 -A2

echo "---"
echo "Checking if these are different query methods (not findFirst/findMany):"
rg -n 'where:' apps/api/src/stripe.ts -B5 | head -40

Repository: theopenco/llmgateway

Length of output: 1413


🏁 Script executed:

#!/bin/bash
# Get the full where clauses from stripe.ts
rg -n 'findFirst' apps/api/src/stripe.ts -A5 | head -100

Repository: theopenco/llmgateway

Length of output: 2032


🏁 Script executed:

#!/bin/bash
# Check authorization.ts to see the pattern mentioned in the review
rg -n 'getActiveUserOrganizationIds' apps/api/src -A15 | head -50

echo "---"
echo "Check if there's a pattern in what files use bare values vs explicit operators:"
echo "Bare values (stripe.ts lines 27, 116, 141):"
sed -n '25,30p;114,119p;139,144p' apps/api/src/stripe.ts

echo "---"
echo "Files using explicit operators:"
rg -n 'findFirst|findMany' apps/api/src/lib/beacon.ts -A3 | head -20

Repository: theopenco/llmgateway

Length of output: 3264


🏁 Script executed:

#!/bin/bash
# Check if there are ANY instances of the shorthand { id } syntax vs { id: id }
echo "Searching for shorthand syntax like { id } or { userId }:"
rg -n 'where:\s*{\s*[a-zA-Z_]+\s*}' apps/api/src --type=ts

echo "---"
echo "Checking if all bare values use the full form { field: value }:"
rg -n 'where:' apps/api/src/stripe.ts -A1 | grep -v 'eq:' | head -30

Repository: theopenco/llmgateway

Length of output: 736


🏁 Script executed:

#!/bin/bash
# Check the context around stripe.ts line 1041
sed -n '1035,1050p' apps/api/src/stripe.ts

Repository: theopenco/llmgateway

Length of output: 454


🏁 Script executed:

#!/bin/bash
# Check if stripe.ts tests pass or if there are runtime errors
echo "Looking for tests of the findMany with bare value syntax in stripe.ts:"
rg -n 'stripe' apps/api/src/routes --type=ts -l

echo "---"
echo "Check stripe.ts to see if it's a working, tested file:"
head -5 apps/api/src/stripe.ts

echo "---"
echo "Summary: Count usage patterns in the codebase"
echo "Pattern 1: { field: { eq: value } } - explicit operator:"
rg 'where:' apps/api/src --type=ts -A2 | grep -c 'eq:'

echo "Pattern 2: { field: value } - bare value/shorthand (not wrapped in eq):"
rg 'where:' apps/api/src --type=ts -A2 | grep -E 'field|organizationId|userId|id' | grep -v 'eq:' | grep -v '{' | wc -l

echo "---"
echo "Checking beacon.ts where clause pattern more carefully:"
sed -n '78,85p' apps/api/src/lib/beacon.ts

Repository: theopenco/llmgateway

Length of output: 961


🏁 Script executed:

#!/bin/bash
# Verify that stripe.ts with bare value syntax is production code and not just tests
echo "Check git log to see if stripe.ts is actively used:"
git log --oneline apps/api/src/stripe.ts 2>/dev/null | head -5 || echo "Git not available"

echo "---"
echo "Check if stripe.ts is imported and used in production routes:"
rg 'from.*stripe' apps/api/src/routes --type=ts

echo "---"
echo "Verify Drizzle documentation comment in the codebase about object syntax:"
rg -n 'object query\|relational query API' packages/db/src -B2 -A2 | head -30

Repository: theopenco/llmgateway

Length of output: 639


🏁 Script executed:

#!/bin/bash
# Final verification: Check if logs.ts imports `eq` but doesn't use it in the findFirst call
echo "logs.ts imports (lines 7-20):"
sed -n '7,20p' apps/api/src/routes/logs.ts

echo "---"
echo "Does logs.ts use eq elsewhere in the file?"
rg -n '\beq\(' apps/api/src/routes/logs.ts | head -10

Repository: theopenco/llmgateway

Length of output: 850


Align the where clause with the predominant codebase pattern for consistency.

While the bare value syntax where: { id } is functional and used in other files (e.g., stripe.ts), most database queries in this codebase use explicit operators like { id: { eq: id } }. For consistency with files like authorization.ts and the rest of logs.ts itself (which imports eq and uses it throughout), consider adopting the explicit operator form:

Suggested change
 const log = await db.query.log.findFirst({
-	where: { id },
+	where: { id: { eq: id } },
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const log = await db.query.log.findFirst({
where: { id },
});
const log = await db.query.log.findFirst({
where: { id: { eq: id } },
});
🤖 Prompt for AI Agents
In `@apps/api/src/routes/logs.ts` around lines 138 - 140, The query using
db.query.log.findFirst currently uses the shorthand where: { id } but the
repository prefers explicit operators; update the where clause in the log
retrieval to use the imported eq operator (e.g., where: { id: { eq: id } }) so
it matches the style used elsewhere in logs.ts and authorization.ts.

Comment thread apps/playground/src/components/playground/chat-page-client.tsx
Comment on lines +599 to +607
<Field
label="Response Format"
value={
log.responseFormat
? typeof log.responseFormat === "object"
? (log.responseFormat as any).type || "-"
: "-"
: "-"
}

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.

⚠️ Potential issue | 🟡 Minor

Avoid as any for responseFormat — violates coding guidelines.

Line 604 uses (log.responseFormat as any).type. Since responseFormat is a JSON column, prefer a type guard or a narrower assertion.

🔧 Suggested fix
 <Field
 	label="Response Format"
 	value={
 		log.responseFormat
-			? typeof log.responseFormat === "object"
-				? (log.responseFormat as any).type || "-"
+			? typeof log.responseFormat === "object" &&
+				log.responseFormat !== null &&
+				"type" in log.responseFormat
+				? String(
+						(log.responseFormat as Record<string, unknown>).type,
+					) || "-"
 				: "-"
 			: "-"
 	}
 />

As per coding guidelines, **/*.{ts,tsx}: "Never use any or as any type assertions in TypeScript code unless absolutely necessary."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Field
label="Response Format"
value={
log.responseFormat
? typeof log.responseFormat === "object"
? (log.responseFormat as any).type || "-"
: "-"
: "-"
}
<Field
label="Response Format"
value={
log.responseFormat
? typeof log.responseFormat === "object" &&
log.responseFormat !== null &&
"type" in log.responseFormat
? String(
(log.responseFormat as Record<string, unknown>).type,
) || "-"
: "-"
: "-"
}
🤖 Prompt for AI Agents
In
`@apps/ui/src/app/dashboard/`[orgId]/[projectId]/activity/[logId]/log-detail-client.tsx
around lines 599 - 607, The JSX uses (log.responseFormat as any).type; replace
this unsafe assertion by introducing and using a proper type guard such as
isResponseFormat(value): value is { type?: string } (or your existing
ResponseFormat interface) and then render the Field value by checking
log.responseFormat with that guard (e.g., isResponseFormat(log.responseFormat) ?
log.responseFormat.type || "-" : "-"). Update the component around the Field
render to call the guard instead of using as any so the code conforms to the
no-any rule while preserving the same fallback logic.

Comment thread apps/ui/src/components/dashboard/dashboard-client.tsx
Comment thread apps/ui/src/components/dashboard/log-card.tsx
## Summary
Fixed a potential issue with Map iteration in the dashboard cost
calculation logic by explicitly converting the Map to an array before
iterating.

## Changes
- Wrapped `modelCostMap` with `Array.from()` when iterating in the
for...of loop to ensure proper iteration behavior across different
JavaScript environments

## Details
The change ensures that the Map iteration is more explicit and
compatible with various runtime environments. Using `Array.from()`
converts the Map entries to an array before iteration, which can prevent
potential issues with certain TypeScript/JavaScript configurations or
bundlers that may not properly handle direct Map iteration in for...of
loops.

https://claude.ai/code/session_01WXgGhPNd1LjfxF9CiS8Aqy

Co-authored-by: Claude <noreply@anthropic.com>
@smakosh
smakosh enabled auto-merge February 9, 2026 20:59

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

🤖 Fix all issues with AI agents
In `@apps/ui/src/components/dashboard/dashboard-client.tsx`:
- Around line 162-167: The code stores provider only on first encounter in
modelCostMap (modelCostMap.set(m.id, { cost: m.cost, provider: m.provider }))
which leads to stale provider attribution when a model appears with different
providers; modify the update logic inside the loop where modelCostMap is updated
(the block referencing modelCostMap, existing, m.id, m.provider) to either (A)
overwrite existing.provider with m.provider when you add to existing.cost (i.e.,
existing.cost += m.cost; existing.provider = m.provider;) so the latest provider
is shown, or (B) change the map value shape to track per-provider costs (e.g.,
store a provider->cost map for each m.id and aggregate/display the provider with
highest cost) depending on intended semantics — implement one of these fixes and
ensure all code reading provider from modelCostMap handles the new shape if you
choose option B.

Comment thread apps/ui/src/components/dashboard/dashboard-client.tsx
@smakosh
smakosh added this pull request to the merge queue Feb 9, 2026
Merged via the queue into main with commit e948869 Feb 9, 2026
8 checks passed
@smakosh
smakosh deleted the feat/dashboard-improvements branch February 9, 2026 21:08
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