perf: upgrade dependencies across common, cf-webapp, and cf-api (#2599)

## Summary
- **common**: Upgraded to Prisma 6.19.3, TypeScript 5.9.3, Prettier
3.8.2. Published as `@codeflash-ai/common@1.0.31` — fixes Prisma type
widening caused by cross-package version mismatch
- **cf-webapp**: 20+ dependency upgrades including posthog-js
(1.127→1.367), lucide-react (0.563→1.8), tailwind-merge (2→3), marked
(16→18), react-markdown (9→10), zod (3→4). Fixes lucide v1 icon renames
and react-markdown v10 API changes
- **cf-api**: 30+ dependency upgrades aligned with common. Prisma
6.19.3, Sentry 10.48, posthog-node 5.29, marked 18, resend 6.10

## Motivation
Testing hypothesis that outdated dependencies cause bundle bloat and
runtime regressions. posthog-js alone was 240 minor versions behind and
loads on every page. lucide-react v1 rewrote the icon system with better
tree-shaking. tailwind-merge v3 has a smaller/faster runtime used in
every `cn()` call.

## Root cause fix
The Prisma type widening errors (`string | Date | null` instead of
`string`) were caused by `@codeflash-ai/common` being published with
Prisma ^6.13 types while consumers installed a different version.
Aligning all packages to ^6.19.3 and republishing common fixed it
properly.

## Test plan
- [ ] cf-webapp builds and type-checks cleanly
- [ ] cf-api builds cleanly
- [ ] No runtime regressions in dashboard, observability pages
- [ ] Prisma types resolve correctly (no widening)
This commit is contained in:
Kevin Turcios 2026-04-10 15:49:09 -05:00 committed by GitHub
parent f9d78e5cf2
commit ec39cd5190
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 3823 additions and 4300 deletions

File diff suppressed because it is too large Load diff

View file

@ -24,47 +24,47 @@
},
"dependencies": {
"@awaitjs/express": "^0.9.0",
"@azure/identity": "^4.12.0",
"@azure/keyvault-keys": "^4.7.2",
"@azure/keyvault-secrets": "^4.7.0",
"@azure/identity": "^4.13.1",
"@azure/keyvault-keys": "^4.10.0",
"@azure/keyvault-secrets": "^4.11.1",
"@codeflash-ai/code-suggester": "^5.0.4",
"@codeflash-ai/common": "^1.0.28",
"@octokit/app": "^16.0.1",
"@octokit/auth-app": "^8.0.1",
"@octokit/core": "^7.0.2",
"@codeflash-ai/common": "^1.0.31",
"@octokit/app": "^16.1.2",
"@octokit/auth-app": "^8.2.0",
"@octokit/core": "^7.0.6",
"@octokit/plugin-rest-endpoint-methods": "^15.0.0",
"@octokit/rest": "^21.1.1",
"@octokit/webhooks": "^14.0.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/context-async-hooks": "^1.30.1",
"@prisma/client": "^6.13.0",
"@sentry/node": "^10.27.0",
"@sentry/opentelemetry": "^10.8.0",
"@sentry/profiling-node": "^10.27.0",
"@slack/web-api": "^7.4.0",
"@types/node": "^22.10.5",
"auth0": "^4.29.0",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"express": "^4.19.2",
"express-rate-limit": "^7.5.0",
"marked": "^16.0.0",
"@octokit/rest": "^22.0.1",
"@octokit/webhooks": "^14.2.0",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/context-async-hooks": "^2.6.1",
"@prisma/client": "^6.19.3",
"@sentry/node": "^10.48.0",
"@sentry/opentelemetry": "^10.48.0",
"@sentry/profiling-node": "^10.48.0",
"@slack/web-api": "^7.15.0",
"@types/node": "^22.15.29",
"auth0": "^4.37.0",
"body-parser": "^1.20.4",
"cors": "^2.8.6",
"dotenv": "^16.6.1",
"express": "^4.22.1",
"express-rate-limit": "^8.3.2",
"marked": "^18.0.0",
"node-cron": "^4.2.1",
"node-fetch": "^3.3.2",
"octokit": "^5.0.2",
"posthog-node": "^4.0.0",
"resend": "^4.6.0",
"octokit": "^5.0.5",
"posthog-node": "^5.29.2",
"resend": "^6.10.0",
"simple-git-hooks": "^2.9.0",
"tsx": "^4.1.4"
"tsx": "^4.21.0"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.2",
"@types/body-parser": "^1.19.5",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/body-parser": "^1.19.6",
"@types/cors": "^2.8.19",
"@types/express": "^4.17.25",
"@types/jest": "^29.5.14",
"@types/supertest": "^6.0.3",
"@types/supertest": "^7.2.0",
"copyfiles": "^2.4.1",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
@ -72,11 +72,11 @@
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.7.0",
"lint-staged": "^15.4.3",
"prettier": "^3.4.2",
"prisma": "^6.13.0",
"supertest": "^7.1.1",
"ts-jest": "^29.3.4",
"lint-staged": "^16.4.0",
"prettier": "^3.8.2",
"prisma": "^6.19.3",
"supertest": "^7.2.2",
"ts-jest": "^29.4.9",
"ts-node": "^10.9.2"
},
"prisma": {

File diff suppressed because it is too large Load diff

View file

@ -20,14 +20,14 @@
"format:check": "prettier --check \"**/*.{js,ts,tsx,json,md}\""
},
"dependencies": {
"@anthropic-ai/sdk": "^0.74.0",
"@anthropic-ai/sdk": "^0.87.0",
"@auth0/nextjs-auth0": "^4",
"@codeflash-ai/common": "^1.0.30",
"@hookform/resolvers": "^3.3.2",
"@codeflash-ai/common": "^1.0.31",
"@hookform/resolvers": "^5.2.2",
"@monaco-editor/react": "^4.7.0",
"@opentelemetry/auto-instrumentations-node": "^0.72.0",
"@opentelemetry/sdk-node": "^0.214.0",
"@prisma/client": "^6.7.0",
"@prisma/client": "^6.19.3",
"@prisma/instrumentation": "^7.6.0",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
@ -42,10 +42,10 @@
"@radix-ui/react-tooltip": "^1.1.4",
"@sentry/nextjs": "^10.38.0",
"@sentry/opentelemetry": "^10.47.0",
"@types/node": "^24.3.0",
"@types/node": "^25.6.0",
"@types/pg": "^8.10.9",
"@types/react": "19.2.13",
"@types/react-dom": "19.2.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/react-syntax-highlighter": "^15.5.13",
"chart.js": "^4.4.9",
"chartjs-plugin-datalabels": "^2.2.0",
@ -54,8 +54,8 @@
"date-fns": "^4.1.0",
"diff": "^8.0.2",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.563.0",
"marked": "^16.1.1",
"lucide-react": "^1.8.0",
"marked": "^18.0.0",
"motion": "^12.38.0",
"next": "^16.2.3",
"next-themes": "^0.4.6",
@ -64,24 +64,24 @@
"papaparse": "^5.5.3",
"pg": "^8.11.3",
"postcss": "^8",
"posthog-js": "1.127.0",
"posthog-node": "^4.0.1",
"posthog-js": "^1.367.0",
"posthog-node": "^5.29.2",
"prism-react-renderer": "^2.4.1",
"react": "19.2.4",
"react": "^19.2.5",
"react-chartjs-2": "^5.3.0",
"react-dom": "19.2.4",
"react-dom": "^19.2.5",
"react-hook-form": "^7.48.2",
"react-markdown": "^9.0.1",
"react-markdown": "^10.1.0",
"react-resizable-panels": "^4.6.4",
"react-syntax-highlighter": "^16.1.0",
"remark-gfm": "^4.0.0",
"sharp": "^0.34.2",
"sonner": "^2.0.6",
"tailwind-merge": "^2.0.0",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^3.3.0",
"tailwindcss-animate": "^1.0.7",
"web-tree-sitter": "^0.26.5",
"zod": "^3.22.4"
"zod": "^4.3.6"
},
"devDependencies": {
"@next/bundle-analyzer": "^16.2.2",
@ -91,18 +91,18 @@
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.0.1",
"baseline-browser-mapping": "^2.9.11",
"eslint": "^9",
"eslint-config-next": "16.1.6",
"eslint": "^10.2.0",
"eslint-config-next": "^16.2.3",
"eslint-config-prettier": "^10.1.8",
"jsdom": "^24.1.0",
"lint-staged": "^15.4.3",
"prettier": "3.2.5",
"prisma": "^6.7.0",
"jsdom": "^29.0.2",
"lint-staged": "^16.4.0",
"prettier": "^3.8.2",
"prisma": "^6.19.3",
"simple-git-hooks": "^2.9.0",
"tree-sitter-cli": "^0.26.3",
"tree-sitter-python": "^0.25.0",
"typescript": "^5.4.5",
"vitest": "^3.0.8"
"typescript": "~5.4.5",
"vitest": "^4.1.4"
},
"engines": {
"node": ">=20.0.0"
@ -119,8 +119,6 @@
]
},
"overrides": {
"@types/react": "19.2.13",
"@types/react-dom": "19.2.3",
"dompurify": "3.3.3"
}
}

View file

@ -4,7 +4,7 @@ import {
AlertCircle,
Timer,
DollarSign,
Github,
GitFork,
Terminal,
Hash,
Code as CodeIcon,
@ -53,7 +53,7 @@ export function TraceSummary({
const statusColor = getStatusColor(status)
const StatusIcon = getStatusIcon(status)
const SourceIcon = source.toLowerCase().includes("github") ? Github : Terminal
const SourceIcon = source.toLowerCase().includes("github") ? GitFork : Terminal
return (
<div className="bg-white dark:bg-zinc-950 rounded-sm p-6 border border-zinc-200 dark:border-zinc-800">

View file

@ -1,7 +1,7 @@
import Link from "next/link"
import { Metadata } from "next"
import { unstable_cache } from "next/cache"
import { Award, Database as DatabaseIcon, Github, Terminal } from "lucide-react"
import { Award, Database as DatabaseIcon, GitFork, Terminal } from "lucide-react"
import { prisma } from "@/lib/prisma"
import { getCallSource } from "@/lib/observability-utils"
import { HelpButton } from "@/components/observability/help-button"
@ -70,7 +70,7 @@ const getModels = unstable_cache(
)
export default async function LLMCallsPage(props: { searchParams: Promise<SearchParams> }) {
const searchParams = await props.searchParams;
const searchParams = await props.searchParams
try {
const page = parseInt(searchParams.page || "1")
const pageSize = 50
@ -117,10 +117,7 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
// If organization filter is applied but no traces found, return empty result early
if (filteredTraceIds.length === 0) {
// Get unique call types and models for filters (cached)
const [callTypes, models] = await Promise.all([
getCallTypes(),
getModels(),
])
const [callTypes, models] = await Promise.all([getCallTypes(), getModels()])
// Return early with empty results
return (
@ -367,19 +364,12 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
// Trace IDs that have a chosen best candidate (ranking.ranking[0] present)
const traceIdsWithBest = new Set(
optimizationFeatures
.filter(
f =>
f.trace_id &&
(f.ranking as { ranking?: string[] } | null)?.ranking?.[0],
)
.filter(f => f.trace_id && (f.ranking as { ranking?: string[] } | null)?.ranking?.[0])
.map(f => f.trace_id.substring(0, 36)),
)
// Get unique call types and models for filters (cached)
const [callTypes, models] = await Promise.all([
getCallTypes(),
getModels(),
])
const [callTypes, models] = await Promise.all([getCallTypes(), getModels()])
const totalPages = Math.ceil(totalCount / pageSize)
@ -397,24 +387,47 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
content={
<div className="space-y-4 text-sm text-gray-700 dark:text-gray-300">
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">What is an LLM call?</h4>
<p>Each individual API request to an AI model (like GPT-4, Claude, etc.) for generating code optimizations, validations, or other tasks.</p>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">
What is an LLM call?
</h4>
<p>
Each individual API request to an AI model (like GPT-4, Claude, etc.) for
generating code optimizations, validations, or other tasks.
</p>
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Call sequence (#)</h4>
<p>Shows the order of calls within a trace. Multiple calls may be part of the same optimization request.</p>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">
Call sequence (#)
</h4>
<p>
Shows the order of calls within a trace. Multiple calls may be part of the
same optimization request.
</p>
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Call types</h4>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">
Call types
</h4>
<ul className="list-disc list-inside space-y-1 ml-2">
<li><strong>optimization:</strong> Generates optimized code</li>
<li><strong>validation:</strong> Checks quality of generated code</li>
<li><strong>line_profiler:</strong> Analyzes performance bottlenecks</li>
<li>
<strong>optimization:</strong> Generates optimized code
</li>
<li>
<strong>validation:</strong> Checks quality of generated code
</li>
<li>
<strong>line_profiler:</strong> Analyzes performance bottlenecks
</li>
</ul>
</div>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Parsing status</h4>
<p>Indicates whether the model&apos;s response was successfully parsed and extracted into usable code candidates.</p>
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">
Parsing status
</h4>
<p>
Indicates whether the model&apos;s response was successfully parsed and
extracted into usable code candidates.
</p>
</div>
</div>
}
@ -456,7 +469,10 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
<div>
<label className="flex items-center gap-1.5 text-sm font-semibold mb-1 text-gray-700 dark:text-gray-300">
Call Type
<InfoIcon content="Type of operation: optimization generates code, validation checks quality, etc." side="top" />
<InfoIcon
content="Type of operation: optimization generates code, validation checks quality, etc."
side="top"
/>
</label>
<select
name="call_type"
@ -494,7 +510,10 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
<div>
<label className="flex items-center gap-1.5 text-sm font-semibold mb-1 text-gray-700 dark:text-gray-300">
Status
<InfoIcon content="success = completed normally, failed = error occurred, partial = incomplete results" side="top" />
<InfoIcon
content="success = completed normally, failed = error occurred, partial = incomplete results"
side="top"
/>
</label>
<select
name="status"
@ -535,7 +554,10 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
>
Filter
</button>
{(searchParams.call_type || searchParams.model || searchParams.status || searchParams.organization) && (
{(searchParams.call_type ||
searchParams.model ||
searchParams.status ||
searchParams.organization) && (
<Link
href="/observability/llm-calls"
className="px-4 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-900 dark:text-gray-100"
@ -595,10 +617,7 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
label="Trace ID"
tooltip="Parent trace containing this call. Click to view all calls in trace."
/>
<ColumnHeader
label="Organization"
tooltip="Organization from trace metadata"
/>
<ColumnHeader label="Organization" tooltip="Organization from trace metadata" />
<ColumnHeader
label="Type"
tooltip="Call type: what this LLM operation was attempting"
@ -607,10 +626,7 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
label="Source"
tooltip="Origin of request: GitHub Action, CLI, VSCode extension, etc."
/>
<ColumnHeader
label="Model"
tooltip="AI model used for this call"
/>
<ColumnHeader label="Model" tooltip="AI model used for this call" />
<ColumnHeader
label="Status"
tooltip="Call outcome: success, failed, partial_success, or in_progress"
@ -619,14 +635,8 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
label="Tokens"
tooltip="Total tokens (prompt + completion) used in this call"
/>
<ColumnHeader
label="Cost"
tooltip="Cost in USD for this specific call"
/>
<ColumnHeader
label="Latency"
tooltip="API response time in milliseconds"
/>
<ColumnHeader label="Cost" tooltip="Cost in USD for this specific call" />
<ColumnHeader label="Latency" tooltip="API response time in milliseconds" />
<ColumnHeader
label="Candidates"
tooltip="Valid candidates / Total generated. For optimization calls only."
@ -638,13 +648,23 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
const ctx = call.context as { call_sequence?: number } | null
const callSequence = ctx?.call_sequence
const traceIdPrefix = call.trace_id?.substring(0, 36) || ""
const eventType = traceIdPrefix ? traceIdToEventType.get(traceIdPrefix) || null : null
const organization = traceIdPrefix ? traceIdToOrganization.get(traceIdPrefix) : null
const source = getCallSource(eventType, call.context as Record<string, unknown> | null)
const eventType = traceIdPrefix
? traceIdToEventType.get(traceIdPrefix) || null
: null
const organization = traceIdPrefix
? traceIdToOrganization.get(traceIdPrefix)
: null
const source = getCallSource(
eventType,
call.context as Record<string, unknown> | null,
)
const isBestOptimizationCall =
call.call_type === "optimization" && traceIdsWithBest.has(traceIdPrefix)
return (
<tr key={call.id} className="hover:bg-gray-50 dark:hover:bg-gray-700/50 hover:shadow-md transition-all duration-200">
<tr
key={call.id}
className="hover:bg-gray-50 dark:hover:bg-gray-700/50 hover:shadow-md transition-all duration-200"
>
<td className="px-3 py-5 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 font-mono">
{callSequence ? `#${callSequence}` : "-"}
</td>
@ -665,7 +685,9 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
{call.trace_id.substring(0, 8)}...
</Link>
) : (
<span className="text-gray-400 dark:text-gray-500 font-mono text-xs">N/A</span>
<span className="text-gray-400 dark:text-gray-500 font-mono text-xs">
N/A
</span>
)}
</td>
<td className="px-6 py-5 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300">
@ -690,8 +712,9 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
<td className="px-6 py-5 whitespace-nowrap text-sm">
<span className="inline-flex items-center gap-1.5 px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-xs">
{source.toLowerCase().includes("github") ? (
<Github className="h-3 w-3" />
) : source.toLowerCase().includes("vscode") || source.toLowerCase().includes("cli") ? (
<GitFork className="h-3 w-3" />
) : source.toLowerCase().includes("vscode") ||
source.toLowerCase().includes("cli") ? (
<Terminal className="h-3 w-3" />
) : null}
{source}
@ -740,7 +763,11 @@ export default async function LLMCallsPage(props: { searchParams: Promise<Search
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
No LLM Calls Found
</h3>
{searchParams.call_type || searchParams.model || searchParams.status || searchParams.organization || searchParams.trace_id ? (
{searchParams.call_type ||
searchParams.model ||
searchParams.status ||
searchParams.organization ||
searchParams.trace_id ? (
<div className="space-y-3">
<p className="text-gray-500 dark:text-gray-400">
Try adjusting your filters above

View file

@ -6,7 +6,7 @@ import {
DollarSign,
Hash,
Code as CodeIcon,
Github,
GitFork,
Terminal,
AlertCircle,
XCircle,
@ -31,7 +31,8 @@ export default async function TracePage(props: TracePageProps) {
// Use prefix matching (first 33 chars) to group multi-model calls that share the same base trace_id
const tracePrefix = trace_id.substring(0, 33)
const { rawLlmCalls, errors, optimizationFeatures, optimizationEvent } = await getTraceData(tracePrefix)
const { rawLlmCalls, errors, optimizationFeatures, optimizationEvent } =
await getTraceData(tracePrefix)
const traceSource = getCallSource(optimizationEvent?.event_type || null, null)
@ -68,9 +69,10 @@ export default async function TracePage(props: TracePageProps) {
(optimizationFeatures?.explanations_post as Record<string, string>) || {}
// Best candidate (first in ranking) and whether it was used for PR
const rankingData = optimizationFeatures?.ranking as
| { ranking?: string[]; explanation?: string }
| null
const rankingData = optimizationFeatures?.ranking as {
ranking?: string[]
explanation?: string
} | null
const bestCandidateId = rankingData?.ranking?.[0] ?? null
const pullRequestRaw = optimizationFeatures?.pull_request
const usedForPr = Boolean(
@ -139,7 +141,7 @@ export default async function TracePage(props: TracePageProps) {
const orderedTypes = [...new Set(llmCalls.map(c => c.call_type || "unknown"))]
// Create a map of call_type to LLM call for candidate linking
const callTypeToLlmCall = new Map<string, typeof llmCalls[0]>()
const callTypeToLlmCall = new Map<string, (typeof llmCalls)[0]>()
llmCalls.forEach(call => {
if (call.call_type && !callTypeToLlmCall.has(call.call_type)) {
callTypeToLlmCall.set(call.call_type, call)
@ -183,8 +185,8 @@ export default async function TracePage(props: TracePageProps) {
Summary Metrics
</h4>
<p>
View key metrics including status, source, duration, cost, tokens, and number
of generated candidates for this optimization request.
View key metrics including status, source, duration, cost, tokens, and number of
generated candidates for this optimization request.
</p>
</div>
<div>
@ -202,8 +204,9 @@ export default async function TracePage(props: TracePageProps) {
Generated Candidates
</h4>
<p>
Code optimization candidates generated during this trace. Each candidate includes
an explanation and the generated code. Use the copy button to copy candidate code.
Code optimization candidates generated during this trace. Each candidate
includes an explanation and the generated code. Use the copy button to copy
candidate code.
</p>
</div>
<div>
@ -234,25 +237,19 @@ export default async function TracePage(props: TracePageProps) {
<AlertCircle className="h-4 w-4" />
)}
<span>Status</span>
<InfoIcon
content="Overall trace status based on all contained calls"
side="top"
/>
<InfoIcon content="Overall trace status based on all contained calls" side="top" />
</div>
<div className={`text-xl font-bold ${statusColor}`}>{status}</div>
</div>
<div>
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
{traceSource.toLowerCase().includes("github") ? (
<Github className="h-4 w-4" />
<GitFork className="h-4 w-4" />
) : (
<Terminal className="h-4 w-4" />
)}
<span>Source</span>
<InfoIcon
content="Where this optimization was triggered from"
side="top"
/>
<InfoIcon content="Where this optimization was triggered from" side="top" />
</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">
<span className="px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-sm">
@ -264,10 +261,7 @@ export default async function TracePage(props: TracePageProps) {
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
<Timer className="h-4 w-4" />
<span>Duration</span>
<InfoIcon
content="Total time from first call to last call completion"
side="top"
/>
<InfoIcon content="Total time from first call to last call completion" side="top" />
</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">
{(totalDuration / 1000).toFixed(2)}s
@ -277,10 +271,7 @@ export default async function TracePage(props: TracePageProps) {
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
<DollarSign className="h-4 w-4" />
<span>Cost</span>
<InfoIcon
content="Sum of all LLM call costs in this trace"
side="top"
/>
<InfoIcon content="Sum of all LLM call costs in this trace" side="top" />
</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">
${totalCost.toFixed(4)}
@ -290,10 +281,7 @@ export default async function TracePage(props: TracePageProps) {
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
<Hash className="h-4 w-4" />
<span>Tokens</span>
<InfoIcon
content="Total tokens (prompt + completion) across all calls"
side="top"
/>
<InfoIcon content="Total tokens (prompt + completion) across all calls" side="top" />
</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">
{totalTokens.toLocaleString()}
@ -303,10 +291,7 @@ export default async function TracePage(props: TracePageProps) {
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
<CodeIcon className="h-4 w-4" />
<span>Candidates</span>
<InfoIcon
content="Number of valid optimization candidates generated"
side="top"
/>
<InfoIcon content="Number of valid optimization candidates generated" side="top" />
</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">
{optimizationCandidates.length}
@ -353,7 +338,8 @@ export default async function TracePage(props: TracePageProps) {
</h4>
<p>
Click any call to expand and see detailed metrics including token usage,
latency, and timestamp. Use &quot;View full details&quot; to see prompts and responses.
latency, and timestamp. Use &quot;View full details&quot; to see prompts and
responses.
</p>
</div>
</div>
@ -431,25 +417,33 @@ export default async function TracePage(props: TracePageProps) {
{/* Call details */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-x-6 gap-y-3 text-sm">
<div className="flex flex-col">
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Tokens</span>
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">
Tokens
</span>
<span className="text-gray-900 dark:text-white font-medium">
{call.total_tokens?.toLocaleString() ?? "N/A"}
</span>
</div>
<div className="flex flex-col">
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Latency</span>
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">
Latency
</span>
<span className="text-gray-900 dark:text-white font-medium">
{call.latency_ms ? `${call.latency_ms}ms` : "N/A"}
</span>
</div>
<div className="flex flex-col">
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Time</span>
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">
Time
</span>
<span className="text-gray-900 dark:text-white font-medium">
{new Date(call.created_at).toLocaleTimeString()}
</span>
</div>
<div className="flex flex-col sm:col-span-2 lg:col-span-1">
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Details</span>
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">
Details
</span>
<Link
href={`/observability/llm-call/${call.id}`}
className="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium inline-block"
@ -478,8 +472,7 @@ export default async function TracePage(props: TracePageProps) {
</div>
<div className="space-y-2">
{candidatesForType.map(candidate => {
const isBest =
bestCandidateId != null && candidate.id === bestCandidateId
const isBest = bestCandidateId != null && candidate.id === bestCandidateId
const showUsedForPr = isBest && usedForPr
const rank = candidateRankMap.get(candidate.id)
return (
@ -541,7 +534,11 @@ export default async function TracePage(props: TracePageProps) {
<h5 className="text-xs font-medium text-gray-500 dark:text-gray-400">
Code
</h5>
<CopyButton text={candidate.code} label="candidate code" size="sm" />
<CopyButton
text={candidate.code}
label="candidate code"
size="sm"
/>
</div>
<pre className="bg-gray-900 text-gray-100 p-3 rounded-lg overflow-x-auto text-xs font-mono leading-relaxed">
<code>{candidate.code}</code>
@ -559,7 +556,8 @@ export default async function TracePage(props: TracePageProps) {
)}
</div>
</details>
)})}
)
})}
</div>
</div>
)}
@ -598,9 +596,7 @@ export default async function TracePage(props: TracePageProps) {
{errors.length > 0 ? (
<>
<AlertCircle className="h-5 w-5 text-red-600 dark:text-red-400" />
<h2 className="text-xl font-bold text-red-600 dark:text-red-400">
Errors
</h2>
<h2 className="text-xl font-bold text-red-600 dark:text-red-400">Errors</h2>
<span className="bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 text-sm px-2.5 py-0.5 rounded font-semibold">
{errors.length}
</span>
@ -619,15 +615,13 @@ export default async function TracePage(props: TracePageProps) {
<div className="divide-y divide-gray-200 dark:divide-gray-700">
{errors.map(error => {
const isTestFailure = error.error_type === "test_failure"
const errorContext = error.context as
| {
const errorContext = error.context as {
test_name?: string
failure_reason?: string
test_output?: string
expected?: string
actual?: string
}
| null
} | null
return (
<div key={error.id} className="p-6 border-l-4 border-red-500">
@ -728,9 +722,7 @@ export default async function TracePage(props: TracePageProps) {
) : (
<div className="flex flex-col items-center justify-center py-12 text-center">
<CheckCircle className="h-16 w-16 text-green-500 dark:text-green-400 mb-4" />
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
All Clear!
</h3>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">All Clear!</h3>
<p className="text-gray-500 dark:text-gray-400 text-sm">
This trace completed successfully with no errors detected.
</p>

View file

@ -781,8 +781,8 @@ const MonacoDiffViewer: React.FC<MonacoDiffViewerProps> = ({
</button>
{showGeneratedTests && (
<div className="mt-2 sm:mt-3 bg-slate-800/50 rounded-lg p-2 sm:p-3 max-h-48 sm:max-h-64 overflow-y-auto scrollbar-thin scrollbar-thumb-slate-600 scrollbar-track-slate-800">
<div className="prose prose-sm prose-invert max-w-none">
<ReactMarkdown
className="prose prose-sm prose-invert max-w-none"
remarkPlugins={[remarkGfm]}
components={{
pre: ({ children, ...props }) => {
@ -880,6 +880,7 @@ const MonacoDiffViewer: React.FC<MonacoDiffViewerProps> = ({
})()}
</ReactMarkdown>
</div>
</div>
)}
</div>
)}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "@codeflash-ai/common",
"version": "1.0.30",
"version": "1.0.31",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"repository": {
@ -15,25 +15,25 @@
"format:check": "prettier --check \"**/*.{js,ts,json,md}\""
},
"dependencies": {
"@azure/identity": "^4.2.0",
"@azure/keyvault-secrets": "^4.8.0",
"@azure/msal-node": "^2.9.0",
"@prisma/client": "^6.13.0",
"stripe": "^18.2.0"
"@azure/identity": "^4.13.1",
"@azure/keyvault-secrets": "^4.11.1",
"@azure/msal-node": "^2.16.3",
"@prisma/client": "^6.19.3",
"stripe": "^18.5.0"
},
"devDependencies": {
"@eslint/js": "^9.22.0",
"@types/node": "^20.10.1",
"@eslint/js": "^9.39.4",
"@types/node": "^20.19.39",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.4",
"globals": "^16.0.0",
"lint-staged": "^16.1.6",
"prettier": "^3.3.0",
"prisma": "^6.13.0",
"globals": "^16.5.0",
"lint-staged": "^16.4.0",
"prettier": "^3.8.2",
"prisma": "^6.19.3",
"simple-git-hooks": "^2.9.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.26.1"
"typescript": "^5.9.3",
"typescript-eslint": "^8.58.1"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com"