feat(job): Implement asynchronous prompt iteration#250
Conversation
📝 WalkthroughWalkthroughPrompt iteration now runs as an asynchronous job workflow. The API enqueues jobs with signed callback URLs, webhook routes persist status snapshots, an SSE endpoint streams updates, and a reusable hook handles SSE, polling fallback, completion, errors, cache invalidation, and navigation. ChangesPrompt improvement jobs
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant EvaluationPage
participant UsePromptImprovement
participant ImprovePromptRoute
participant PromptImprovementStream
participant PromptImprovementStore
EvaluationPage->>UsePromptImprovement: handleIteratePrompt()
UsePromptImprovement->>ImprovePromptRoute: POST improvement request
ImprovePromptRoute->>PromptImprovementStore: markJobPending(job_id)
ImprovePromptRoute-->>UsePromptImprovement: 202 job_id
UsePromptImprovement->>PromptImprovementStream: GET stream with job_id
PromptImprovementStream->>PromptImprovementStore: subscribeJobSnapshot(job_id)
PromptImprovementStore-->>PromptImprovementStream: Terminal snapshot
PromptImprovementStream-->>UsePromptImprovement: snapshot event
UsePromptImprovement-->>EvaluationPage: Success or failure result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
app/api/webhooks/prompt-improvement/route.ts (2)
9-14: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low valueUse a constant-time comparison for the webhook secret.
To prevent timing attacks that could theoretically allow an attacker to guess the webhook secret character by character, use
crypto.timingSafeEqualinstead of a standard string comparison.🛡️ Proposed refactor for secure comparison
+import { timingSafeEqual } from "crypto"; + function isAuthorized(request: NextRequest): boolean { const secretResult = readWebhookSecret(); if (!secretResult.ok || !secretResult.secret) return false; const provided = request.nextUrl.searchParams.get("secret_value"); - return provided === secretResult.secret; + if (!provided || provided.length !== secretResult.secret.length) return false; + + return timingSafeEqual( + Buffer.from(provided), + Buffer.from(secretResult.secret) + ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/webhooks/prompt-improvement/route.ts` around lines 9 - 14, Update isAuthorized to compare the provided webhook secret with secretResult.secret using crypto.timingSafeEqual rather than string equality; convert both values to equivalent byte buffers, handle differing lengths without throwing, and preserve the existing false result for missing or invalid secrets.
50-66: 🔒 Security & Privacy | 🔵 TrivialEnsure job snapshots are protected against unauthorized access.
The GET endpoint retrieves the job snapshot from the in-memory store using only the
job_id, bypassing session or API key validation.
Ifjob_idvalues generated by the backend are enumerable (e.g., sequential integers), this endpoint exposes an Insecure Direct Object Reference (IDOR) vulnerability, allowing attackers to scrape sensitive prompt configurations.Ensure that the backend uses a secure UUIDv4 for
job_idso it functions safely as an unguessable capability token. For defense-in-depth, consider storing anorg_idoruser_idalongside the snapshot in the future to enforce strict ownership checks before returning the data.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/webhooks/prompt-improvement/route.ts` around lines 50 - 66, Update the job creation flow that generates the identifiers consumed by GET and getJobSnapshot so every job_id uses a cryptographically secure UUIDv4 rather than sequential or otherwise enumerable values. Preserve the existing snapshot lookup and response behavior, and leave ownership fields such as org_id or user_id for a separate future authorization change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/api/evaluations/`[id]/improve-prompt/route.ts:
- Around line 14-22: Update the callback URL base construction in the
improve-prompt route to use forwarded/host header fallback only in development
environments. When running in production without NEXT_PUBLIC_APP_URL or APP_URL,
throw an error instead of constructing a URL from request headers; preserve the
configured application URL path when available.
In `@app/api/webhooks/prompt-improvement/stream/route.ts`:
- Around line 32-74: The stream setup invokes push(current) before unsubscribe
and heartbeat are initialized, causing terminal snapshots to fail in finish. In
the stream start flow, initialize the subscription cleanup handle and heartbeat
before priming with getJobSnapshot(jobId), while preserving terminal-state
cleanup and synthetic PENDING behavior.
In `@app/hooks/usePromptImprovement.ts`:
- Around line 85-151: Add a shared timeout deadline for the job lifecycle in
pollJob and watchJob, tracking the start time or deadline via a ref and checking
it on every poll tick and SSE snapshot/event. When the deadline is exceeded,
stop the watcher, settle with the established timeout error result so the caller
shows an error toast and clears isImprovingPrompt, and prevent fallback or
further polling from continuing after settlement.
- Line 111: Update the polling interval assigned to pollRef.current in the
usePromptImprovement polling flow to run every two seconds, replacing the
current 3000ms delay while preserving the existing tick callback and interval
management.
---
Nitpick comments:
In `@app/api/webhooks/prompt-improvement/route.ts`:
- Around line 9-14: Update isAuthorized to compare the provided webhook secret
with secretResult.secret using crypto.timingSafeEqual rather than string
equality; convert both values to equivalent byte buffers, handle differing
lengths without throwing, and preserve the existing false result for missing or
invalid secrets.
- Around line 50-66: Update the job creation flow that generates the identifiers
consumed by GET and getJobSnapshot so every job_id uses a cryptographically
secure UUIDv4 rather than sequential or otherwise enumerable values. Preserve
the existing snapshot lookup and response behavior, and leave ownership fields
such as org_id or user_id for a separate future authorization change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 296e254b-d0b1-4271-9e07-d5c4148528ef
📒 Files selected for processing (8)
app/(main)/evaluations/[id]/page.tsxapp/api/evaluations/[id]/improve-prompt/route.tsapp/api/webhooks/prompt-improvement/route.tsapp/api/webhooks/prompt-improvement/stream/route.tsapp/hooks/usePromptImprovement.tsapp/lib/store/promptImprovementStore.tsapp/lib/types/promptImprovement.tsapp/lib/webhookSecret.ts
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/lib/types/webhookSecret.ts (1)
6-10: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winMake
WebhookSecretResulta strict discriminated union.The current interface permits invalid states such as
{ ok: true }or{ ok: false, secret: "..." }, weakening the compile-time guarantees around webhook authorization. Model success and failure as separateok: true/ok: falsebranches so callers can safely narrow the fields.Proposed fix
-export interface WebhookSecretResult { - ok: boolean; - secret?: string; - reason?: WebhookSecretFailureReason; -} +export type WebhookSecretResult = + | { + ok: true; + secret: string; + reason?: never; + } + | { + ok: false; + secret?: never; + reason: WebhookSecretFailureReason; + };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/lib/types/webhookSecret.ts` around lines 6 - 10, Replace the WebhookSecretResult interface with a strict discriminated union containing separate success and failure branches: the ok: true branch must require secret and exclude reason, while the ok: false branch must require reason and exclude secret. Preserve the existing WebhookSecretFailureReason type and ensure callers can narrow fields by checking ok.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@app/lib/types/webhookSecret.ts`:
- Around line 6-10: Replace the WebhookSecretResult interface with a strict
discriminated union containing separate success and failure branches: the ok:
true branch must require secret and exclude reason, while the ok: false branch
must require reason and exclude secret. Preserve the existing
WebhookSecretFailureReason type and ensure callers can narrow fields by checking
ok.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: da162137-0898-4764-a7f8-c3c62daef601
📒 Files selected for processing (3)
app/(main)/evaluations/[id]/page.tsxapp/lib/types/webhookSecret.tsapp/lib/webhookSecret.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- app/lib/webhookSecret.ts
- app/(main)/evaluations/[id]/page.tsx
|
🎉 This PR is included in version 0.4.0-main.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Issue
Closes #248
Summary
In this PR, end-to-end async prompt-improvement + judge config + fast-eval polish in the Text Evaluations flow.
PROMPT_IMPROVEMENT_WEBHOOK_SECRET,strict/hex-only).Checklist
Before submitting a pull request, please ensure that you mark these task.
npm run devandnpm run buildin the repository root and test.Notes
NEXT_PUBLIC_APP_URLandPROMPT_IMPROVEMENT_WEBHOOK_SECRETvalues in the environment file.