File size: 3,047 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { NextResponse } from "next/server";
import { z } from "zod";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { judgeFidelityBatch } from "@omniroute/open-sse/services/compression/eval/fidelityCheck";
import { createPricedJudgeClient } from "@/lib/compression/judgeModelClient";
import type { ProviderCredentials } from "@omniroute/open-sse/executors/base";
import { getProviderCredentials } from "@/sse/services/auth";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";

export const dynamic = "force-dynamic";

const VerifyRequestSchema = z.object({
  items: z
    .array(z.object({ id: z.string(), original: z.string(), compressed: z.string() }))
    .min(1)
    .max(20),
  provider: z.string().min(1),
  judgeModel: z.string().min(1),
  costCapUsd: z.number().positive().max(5).default(0.1),
});

export async function POST(req: Request) {
  const authError = await requireManagementAuth(req);
  if (authError) return authError;
  let body: unknown;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
  }
  const parsed = VerifyRequestSchema.safeParse(body);
  if (!parsed.success) {
    return NextResponse.json(
      { error: "Invalid request", details: parsed.error.issues },
      { status: 400 }
    );
  }
  const { items, provider, judgeModel, costCapUsd } = parsed.data;
  try {
    const rawCredentials = await getProviderCredentials(provider);
    if (!rawCredentials) {
      return NextResponse.json(
        { error: `No credentials configured for provider "${provider}"` },
        { status: 400 }
      );
    }
    // Positively require a credential-shaped object before casting. This rejects the
    // current non-credential return shapes ({allRateLimited}, {allExpired}) AND any
    // future error shape, instead of denylisting known ones. The success return from
    // getProviderCredentials is a structural superset of ProviderCredentials; the extra
    // fields (id, provider, email, etc.) are ignored by the executor adapter.
    const looksLikeCredentials =
      typeof rawCredentials === "object" &&
      rawCredentials !== null &&
      "connectionId" in rawCredentials &&
      ("apiKey" in rawCredentials || "accessToken" in rawCredentials);
    if (!looksLikeCredentials) {
      return NextResponse.json(
        { error: `Provider "${provider}" credentials are unavailable` },
        { status: 503 }
      );
    }
    const credentials = rawCredentials as unknown as ProviderCredentials;
    const client = createPricedJudgeClient(provider, credentials);
    const result = await judgeFidelityBatch(client, judgeModel, items, costCapUsd);
    return NextResponse.json(result);
  } catch (err: unknown) {
    const msg = err instanceof Error ? err.message : String(err);
    console.error("[/api/compression/compare/verify]", msg);
    return NextResponse.json(
      { error: "Verify failed", details: sanitizeErrorMessage(msg) },
      { status: 500 }
    );
  }
}