Skip to content

feat(job): Implement asynchronous prompt iteration#250

Merged
Ayush8923 merged 7 commits into
mainfrom
feat/prompt-iteration-async-job
Jul 21, 2026
Merged

feat(job): Implement asynchronous prompt iteration#250
Ayush8923 merged 7 commits into
mainfrom
feat/prompt-iteration-async-job

Conversation

@Ayush8923

@Ayush8923 Ayush8923 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Issue

Closes #248

Summary

In this PR, end-to-end async prompt-improvement + judge config + fast-eval polish in the Text Evaluations flow.

  • Enqueues LLM prompt-improvement jobs via Kaapi's new async endpoint.
  • Hosts a webhook receiver + SSE stream so browsers see terminal state in ms, with polling as fallback.
  • Exposes a Judge Config panel for Fast-mode evaluations (ad-hoc prompt/model/temperature OR saved config ref).
  • Renders AI-generated commit messages as structured badges instead of raw dumps.
  • Verifies webhook callbacks via a shared secret (PROMPT_IMPROVEMENT_WEBHOOK_SECRET, strict/hex-only).

Checklist

Before submitting a pull request, please ensure that you mark these task.

  • Ran npm run dev and npm run build in the repository root and test.
  • If you've fixed a bug or added code that is tested

Notes

  • In the staging and production, need to add the NEXT_PUBLIC_APP_URL and PROMPT_IMPROVEMENT_WEBHOOK_SECRET values in the environment file.

@Ayush8923 Ayush8923 self-assigned this Jul 20, 2026
@github-actions github-actions Bot changed the title feat(evals): prompt iteration async job feat(job): Implement asynchronous prompt iteration Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Prompt 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.

Changes

Prompt improvement jobs

Layer / File(s) Summary
Job contracts, secret validation, and snapshot storage
app/lib/types/promptImprovement.ts, app/lib/types/webhookSecret.ts, app/lib/webhookSecret.ts, app/lib/store/promptImprovementStore.ts
Defines job statuses and payload types, validates webhook secrets, and stores snapshots with subscriptions and retention limits.
Job enqueue and webhook persistence
app/api/evaluations/[id]/improve-prompt/route.ts, app/api/webhooks/prompt-improvement/route.ts
Adds callback URL resolution, registers pending jobs, authorizes webhook callbacks, persists updates, and exposes snapshot retrieval.
Snapshot SSE stream
app/api/webhooks/prompt-improvement/stream/route.ts
Streams initial and updated snapshots, supports keep-alives, closes on terminal statuses, and handles request aborts.
Client iteration orchestration and page integration
app/hooks/usePromptImprovement.ts, app/(main)/evaluations/[id]/page.tsx
Moves iteration handling into a hook with SSE and polling fallback, terminal-state handling, UI feedback, cache invalidation, navigation, and the updated results layout class.

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
Loading

Possibly related PRs

Suggested reviewers: vprashrex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR shifts to SSE/webhook plumbing, but #248 requires a GET poll route and 2s polling loop in handleIteratePrompt, which aren't shown. Add app/api/evaluations/[id]/improve-prompt/[jobId]/route.ts and keep handleIteratePrompt polling every 2s until terminal status, then handle success, failure, and timeout per #248.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The added webhook, store, hook, and secret-validation code all support asynchronous prompt iteration and are not clearly unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding asynchronous prompt iteration/job handling.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/prompt-iteration-async-job

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
app/api/webhooks/prompt-improvement/route.ts (2)

9-14: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Use 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.timingSafeEqual instead 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 | 🔵 Trivial

Ensure 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.
If job_id values 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_id so it functions safely as an unguessable capability token. For defense-in-depth, consider storing an org_id or user_id alongside 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

📥 Commits

Reviewing files that changed from the base of the PR and between d030bff and b26cb77.

📒 Files selected for processing (8)
  • app/(main)/evaluations/[id]/page.tsx
  • app/api/evaluations/[id]/improve-prompt/route.ts
  • app/api/webhooks/prompt-improvement/route.ts
  • app/api/webhooks/prompt-improvement/stream/route.ts
  • app/hooks/usePromptImprovement.ts
  • app/lib/store/promptImprovementStore.ts
  • app/lib/types/promptImprovement.ts
  • app/lib/webhookSecret.ts

Comment thread app/api/evaluations/[id]/improve-prompt/route.ts
Comment thread app/api/webhooks/prompt-improvement/stream/route.ts
Comment thread app/hooks/usePromptImprovement.ts
Comment thread app/hooks/usePromptImprovement.ts Outdated
@Ayush8923
Ayush8923 requested a review from vprashrex July 20, 2026 18:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/lib/types/webhookSecret.ts (1)

6-10: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Make WebhookSecretResult a 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 separate ok: true / ok: false branches 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

📥 Commits

Reviewing files that changed from the base of the PR and between 265400c and 181fdf3.

📒 Files selected for processing (3)
  • app/(main)/evaluations/[id]/page.tsx
  • app/lib/types/webhookSecret.ts
  • app/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

@Ayush8923
Ayush8923 merged commit 3ac0f20 into main Jul 21, 2026
2 checks passed
@Ayush8923
Ayush8923 deleted the feat/prompt-iteration-async-job branch July 21, 2026 09:37
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.4.0-main.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Evals: Migrate prompt iteration to async job polling

2 participants