@ggui-ai/ui-gen 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/base.d.ts +7 -0
- package/dist/adapters/base.js +1 -0
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/generation-dispatch.d.ts +17 -3
- package/dist/adapters/generation-dispatch.js +959 -3484
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +35 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +4 -4
- package/dist/advanced/index.js +1039 -3573
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +10 -3
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js +2 -2
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-CxPvudbs.d.ts → create-ui-generator-D7kOXNjJ.d.ts} +2 -2
- package/dist/{dispatch-N8wdNOX-.d.ts → dispatch-DGvhmARp.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +3 -3
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +1 -1
- package/dist/evaluation/index.d.ts +38 -6
- package/dist/evaluation/index.js +649 -3370
- package/dist/evaluation/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +2 -2
- package/dist/harness/check/runtime-render/index.js +10 -3
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +5 -3
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +2 -2
- package/dist/harness/index.js +562 -30
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.d.ts +7 -1
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/result-types.d.ts +1 -1
- package/dist/harness/runtime.d.ts +11 -2
- package/dist/harness/runtime.js +3 -3
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +1 -1
- package/dist/harness/types-public.js +2 -2
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1039 -3573
- package/dist/index.js.map +1 -1
- package/dist/{llm-router-D7fjBhtr.d.ts → llm-router-DwwUK1bz.d.ts} +16 -3
- package/dist/patch.d.ts +1 -1
- package/dist/providers/index.js +5 -4
- package/dist/providers/index.js.map +1 -1
- package/dist/{render-check-WfOVO9kU.d.ts → render-check-C0CqVceF.d.ts} +17 -0
- package/dist/{types-public-DKR_h70H.d.ts → types-public-CzYqZclY.d.ts} +44 -1
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +16 -12
- package/src/adapters/base.ts +8 -0
- package/src/adapters/claude/claude-code-login.ts +73 -0
- package/src/adapters/claude/sdk.ts +8 -1
- package/src/adapters/generation-dispatch.ts +19 -2
- package/src/adapters/openai/tokens.ts +18 -0
- package/src/adapters/provider-router.ts +28 -0
- package/src/boilerplate/generate.ts +19 -4
- package/src/boilerplate/hard-sections.ts +1 -1
- package/src/create-ui-generator.ts +19 -10
- package/src/evaluation/design-identity.ts +16 -5
- package/src/evaluation/types-public.ts +42 -0
- package/src/evaluation/visual-evaluator.ts +231 -24
- package/src/harness/check/runtime-render/adapter.ts +9 -0
- package/src/harness/check/runtime-render/render-check.ts +26 -5
- package/src/harness/claude-code-login-query.ts +528 -0
- package/src/harness/coding/assemble-result.ts +10 -1
- package/src/harness/coding/run-eval-round.ts +179 -34
- package/src/harness/enforced-coding.ts +20 -0
- package/src/harness/llm-router.ts +390 -55
- package/src/harness/llm-trace-sink.ts +7 -1
- package/src/harness/runtime.ts +6 -0
- package/src/providers/openai.ts +31 -19
- package/src/validation/primitives.ts +1 -1
|
@@ -22,13 +22,20 @@ import { listContractGadgets } from "@ggui-ai/protocol";
|
|
|
22
22
|
import type { Classification } from "../../classifier/index.js";
|
|
23
23
|
import type { AgentWorkspace } from "../../coding-agent/workspace.js";
|
|
24
24
|
import type { CostTracker } from "../../evaluation/cost-tracker.js";
|
|
25
|
-
import type { EvalIssue, EvalResult, RuntimeProbeMeta, VisualEvalSummary } from "../../evaluation/types-public.js";
|
|
25
|
+
import type { EvalIssue, EvalResult, RuntimeProbeMeta, VisualEvalSummary, VisualCoverage } from "../../evaluation/types-public.js";
|
|
26
26
|
import { notApplicableCoverage } from "../../evaluation/types-public.js";
|
|
27
|
-
import { mapProviderForEvaluator } from "../enforced-coding.js";
|
|
27
|
+
import { mapProviderForEvaluator, visionJudgeProvider } from "../enforced-coding.js";
|
|
28
28
|
import { runCheck } from "../index.js";
|
|
29
29
|
import { isRecoverableRenderCrash } from "../check/runtime-render/index.js";
|
|
30
30
|
import type { Harness } from "../types-public.js";
|
|
31
31
|
import type { AgentSpec, SingleComponentParams } from "../runtime.js";
|
|
32
|
+
import type {
|
|
33
|
+
CanvasFitReading,
|
|
34
|
+
VisualEvalContext,
|
|
35
|
+
VisualEvalOutcome,
|
|
36
|
+
VisualFitConfig,
|
|
37
|
+
VisualFitOutcome,
|
|
38
|
+
} from "../../evaluation/visual-evaluator.js";
|
|
32
39
|
|
|
33
40
|
type PreWarmedEvalContext =
|
|
34
41
|
import("../../evaluation/llm-evaluator.js").PreWarmedEvalContext;
|
|
@@ -122,6 +129,23 @@ export interface EvalRoundResult {
|
|
|
122
129
|
/** Cap feedback to N issues per eval-fix turn so the LLM stays surgical. */
|
|
123
130
|
const MAX_FEEDBACK_ISSUES = 3;
|
|
124
131
|
|
|
132
|
+
/**
|
|
133
|
+
* ggui#1195 — the visual leg's deterministic fit verdict: the inline card's
|
|
134
|
+
* content is taller than the box it was judged at (`canvas-overflow`,
|
|
135
|
+
* critical ⇒ fail). Recognised by the marker the evaluator emits, never by
|
|
136
|
+
* the description's wording.
|
|
137
|
+
*/
|
|
138
|
+
function isFitFail(issue: EvalIssue): boolean {
|
|
139
|
+
return issue.result === "fail" && issue.category === "visual" && issue.subcategory === "canvas-overflow";
|
|
140
|
+
}
|
|
141
|
+
/** The same marker as {@link isFitFail}, on the fingerprint `fingerprintFail` builds (`category|subcategory|…`). */
|
|
142
|
+
const FIT_FINGERPRINT_PREFIX = "visual|canvas-overflow|";
|
|
143
|
+
|
|
144
|
+
/** `[fit]`-tagged so the LLM can see the violation is the box, not the judge's taste. */
|
|
145
|
+
function formatFitFeedback(issue: EvalIssue): string {
|
|
146
|
+
return `[fit] ${issue.category}/${issue.subcategory ?? ""}: ${issue.description}\n Fix: ${issue.fix ?? ""}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
125
149
|
/**
|
|
126
150
|
* `criteriaCoverage` reason stamped on the same-image low-risk bypass
|
|
127
151
|
* exits: tier-1/2 evaluation is never invoked there BY DESIGN, so every
|
|
@@ -131,6 +155,65 @@ const MAX_FEEDBACK_ISSUES = 3;
|
|
|
131
155
|
/** Why an eval result carries no criteria: the round itself threw (ggui#1110). */
|
|
132
156
|
const EVAL_ROUND_THREW_REASON = "eval round threw";
|
|
133
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The in-loop visual leg, made total (ggui#1248). The judge runs on a VISION
|
|
160
|
+
* provider: the agent's provider narrowed by `visionJudgeProvider`, never
|
|
161
|
+
* asserted into the vision union — a lane whose generation provider has no
|
|
162
|
+
* vision path (OpenAI, OpenRouter) gets a leg recorded as SKIPPED with the
|
|
163
|
+
* reason, and the judge is not called. And whatever the leg does, it resolves:
|
|
164
|
+
* a rejection becomes the same skipped coverage with the error, so the round
|
|
165
|
+
* never takes its thrown path on account of the visual leg and the runtime
|
|
166
|
+
* probe still runs — a bar that requires the probe decides on the probe, not
|
|
167
|
+
* on a judge that could not be reached.
|
|
168
|
+
*
|
|
169
|
+
* The leg's FIT half does not need the judge (ggui#1195): the fit verdict is a
|
|
170
|
+
* frame at the declared box and a scroll height, so a no-vision lane with
|
|
171
|
+
* canvases runs `measureFit` alone. Its canvas-overflow fail rides the round's
|
|
172
|
+
* issues like the judge path's would — so the `[fit]` round the cap grants
|
|
173
|
+
* can fire on that lane — and the coverage still reads `skipped` for the judge,
|
|
174
|
+
* with what the fit half measured (or why it could not) appended. Without
|
|
175
|
+
* that half, a card taller than its declared chat box on such a lane was
|
|
176
|
+
* never told so in the loop, so the composition fix was never asked for.
|
|
177
|
+
*/
|
|
178
|
+
function runVisualLeg(
|
|
179
|
+
visualEvalAgent: AgentSpec,
|
|
180
|
+
run: (provider: "claude" | "google") => Promise<VisualEvalOutcome>,
|
|
181
|
+
measureFit: (() => Promise<VisualFitOutcome>) | undefined,
|
|
182
|
+
): Promise<VisualEvalOutcome> {
|
|
183
|
+
const provider = visionJudgeProvider(visualEvalAgent.provider);
|
|
184
|
+
if (provider === undefined) {
|
|
185
|
+
const noJudge = `no vision judge for provider '${visualEvalAgent.provider}' (model ${visualEvalAgent.model}) — the in-loop visual judge runs on a vision provider (anthropic, google)`;
|
|
186
|
+
if (measureFit === undefined) {
|
|
187
|
+
return Promise.resolve({ issues: [], coverage: { status: "skipped", reason: noJudge } });
|
|
188
|
+
}
|
|
189
|
+
const notMeasured = (why: string): VisualEvalOutcome => ({
|
|
190
|
+
issues: [],
|
|
191
|
+
coverage: { status: "skipped", reason: `${noJudge}; fit not measured: ${why}` },
|
|
192
|
+
});
|
|
193
|
+
return measureFit().then(
|
|
194
|
+
(fit): VisualEvalOutcome =>
|
|
195
|
+
fit.status === "measured"
|
|
196
|
+
? {
|
|
197
|
+
issues: fit.issues,
|
|
198
|
+
coverage: { status: "skipped", reason: `${noJudge}; fit measured without the judge: ${describeFitReadings(fit.readings)}` },
|
|
199
|
+
}
|
|
200
|
+
: notMeasured(fit.reason),
|
|
201
|
+
(e: unknown): VisualEvalOutcome => notMeasured(e instanceof Error ? e.message : String(e)),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return run(provider).catch((e: unknown): VisualEvalOutcome => ({
|
|
205
|
+
issues: [],
|
|
206
|
+
coverage: { status: "skipped", reason: `visual leg failed: ${e instanceof Error ? e.message : String(e)}` },
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The fit half's receipt on the coverage: `xs-chat-card 384×516 content 535px (overflow)`, one per canvas. */
|
|
211
|
+
function describeFitReadings(readings: readonly CanvasFitReading[]): string {
|
|
212
|
+
return readings
|
|
213
|
+
.map((r) => `${r.canvas} ${r.viewport.width}×${r.viewport.height} content ${r.contentHeight ?? "?"}px${r.overflow ? " (overflow)" : ""}`)
|
|
214
|
+
.join(", ");
|
|
215
|
+
}
|
|
216
|
+
|
|
134
217
|
const LOW_RISK_BYPASS_REASON =
|
|
135
218
|
"same-image low-risk bypass: axis checks clean, tier-1/2 evaluation not invoked";
|
|
136
219
|
|
|
@@ -448,6 +531,30 @@ export async function runEvalRound(
|
|
|
448
531
|
let llmResult: (EvalResult & { inputTokens: number; outputTokens: number }) | null = null;
|
|
449
532
|
let visualIssues: EvalIssue[] | null = null;
|
|
450
533
|
|
|
534
|
+
// The visual leg's page and frame — ONE value each, read by the judge and
|
|
535
|
+
// by its fit half alike, so both frame the same component the same way.
|
|
536
|
+
const visualContext: VisualEvalContext = {
|
|
537
|
+
compiledCode,
|
|
538
|
+
originalPrompt: userPrompt,
|
|
539
|
+
profile: harness.profile,
|
|
540
|
+
// The caller's theme, when named — the round paints what the app paints.
|
|
541
|
+
...(visualEvaluation?.cssTokens !== undefined ? { cssTokens: visualEvaluation.cssTokens } : {}),
|
|
542
|
+
};
|
|
543
|
+
const visualFrame: VisualFitConfig = {
|
|
544
|
+
// The in-loop visual round renders the same sample the runtime probe
|
|
545
|
+
// does (the caller's fixture) unless the config names its own.
|
|
546
|
+
sampleProps: visualEvaluation?.sampleProps ?? fixtureProps,
|
|
547
|
+
// Per-canvas judging (arm-neutral) — absent = single shot.
|
|
548
|
+
...(visualEvaluation?.canvases !== undefined ? { canvases: visualEvaluation.canvases } : {}),
|
|
549
|
+
// ggui#1195 — the declared box per canvas (same-target with the composer).
|
|
550
|
+
...(visualEvaluation?.canvasViewports !== undefined ? { canvasViewports: visualEvaluation.canvasViewports } : {}),
|
|
551
|
+
};
|
|
552
|
+
// The fit verdict is per canvas: a single-shot leg has no box to hold content against.
|
|
553
|
+
const measureFit =
|
|
554
|
+
visualMod && visualFrame.canvases !== undefined && visualFrame.canvases.length > 0
|
|
555
|
+
? () => visualMod.runVisualFit(visualContext, visualFrame)
|
|
556
|
+
: undefined;
|
|
557
|
+
|
|
451
558
|
const evalLlmStart = Date.now();
|
|
452
559
|
const [llm, visual] = await Promise.all([
|
|
453
560
|
llmEvalMod
|
|
@@ -483,35 +590,25 @@ export async function runEvalRound(
|
|
|
483
590
|
)
|
|
484
591
|
: null,
|
|
485
592
|
visualMod
|
|
486
|
-
?
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
: {}),
|
|
506
|
-
// #484/#489 — same threading as the LLM-eval leg above:
|
|
507
|
-
// the visual eval's multimodal call runs inside the same
|
|
508
|
-
// concurrent generation, so it needs the same routing
|
|
509
|
-
// override (never fall back to `process.env`) and the
|
|
510
|
-
// same provider-429-retry observer (retries reach the
|
|
511
|
-
// host application's structured log).
|
|
512
|
-
routeOverride: visualEvalAgent.routeOverride,
|
|
513
|
-
onRetry: visualEvalAgent.onRetry,
|
|
514
|
-
},
|
|
593
|
+
? runVisualLeg(
|
|
594
|
+
visualEvalAgent,
|
|
595
|
+
(provider) =>
|
|
596
|
+
visualMod.runVisualEval(visualContext, {
|
|
597
|
+
provider,
|
|
598
|
+
model: visualEvalAgent.model,
|
|
599
|
+
passThreshold: visualThreshold,
|
|
600
|
+
viewport: visualEvaluation?.viewport,
|
|
601
|
+
...visualFrame,
|
|
602
|
+
// #484/#489 — same threading as the LLM-eval leg above:
|
|
603
|
+
// the visual eval's multimodal call runs inside the same
|
|
604
|
+
// concurrent generation, so it needs the same routing
|
|
605
|
+
// override (never fall back to `process.env`) and the
|
|
606
|
+
// same provider-429-retry observer (retries reach the
|
|
607
|
+
// host application's structured log).
|
|
608
|
+
routeOverride: visualEvalAgent.routeOverride,
|
|
609
|
+
onRetry: visualEvalAgent.onRetry,
|
|
610
|
+
}),
|
|
611
|
+
measureFit,
|
|
515
612
|
)
|
|
516
613
|
: null,
|
|
517
614
|
]);
|
|
@@ -519,6 +616,12 @@ export async function runEvalRound(
|
|
|
519
616
|
llmResult = llm;
|
|
520
617
|
visualIssues = visual?.issues ?? null;
|
|
521
618
|
const visualSummary: VisualEvalSummary | undefined = visual?.summary;
|
|
619
|
+
// ggui#1221 — the judge leg's coverage is ALWAYS stamped: ran / skipped (with the
|
|
620
|
+
// reason) / not-applicable when no visual leg was configured for this round.
|
|
621
|
+
const visualCoverage: VisualCoverage = visual?.coverage ?? { status: 'not-applicable', reason: 'visual leg not configured' };
|
|
622
|
+
if (visualCoverage.status === 'skipped') {
|
|
623
|
+
console.log(`[eval] visual-judge: SKIPPED (${visualCoverage.reason ?? 'no reason recorded'})`);
|
|
624
|
+
}
|
|
522
625
|
|
|
523
626
|
// Track costs from LLM eval calls
|
|
524
627
|
if (llmResult) {
|
|
@@ -549,6 +652,7 @@ export async function runEvalRound(
|
|
|
549
652
|
// requested and the leg ran; every later re-stamp spreads this
|
|
550
653
|
// base so it survives to the assembled result.
|
|
551
654
|
...(visualSummary !== undefined ? { visual: visualSummary } : {}),
|
|
655
|
+
visualCoverage,
|
|
552
656
|
};
|
|
553
657
|
|
|
554
658
|
// ── Log merged results, with mode-check subcategory breakdown ──
|
|
@@ -683,13 +787,19 @@ export async function runEvalRound(
|
|
|
683
787
|
// to localize them; the +2 max-rounds extension is the right escape
|
|
684
788
|
// hatch, not stuck-loop exit. Keep stuck-loop active for non-runtime
|
|
685
789
|
// fail-sets (the original tier-1 nitpick case it was built for).
|
|
686
|
-
|
|
790
|
+
// ggui#1195 follow-up: a FIT fail (`visual|canvas-overflow|…`) is exempt
|
|
791
|
+
// the same way — its escape hatch is the one `[fit]` round at the cap, and
|
|
792
|
+
// a card whose measured height did not move between rounds would otherwise
|
|
793
|
+
// exit here as "stuck" before that round is ever reached (the adversarial
|
|
794
|
+
// review ran the commit's own test and found round 2's break WAS the stuck
|
|
795
|
+
// exit). Bounded: the cap block grants the fit round exactly once.
|
|
796
|
+
const onlyExtensionExemptFails =
|
|
687
797
|
currFailFingerprints.size > 0 &&
|
|
688
|
-
[...currFailFingerprints].every((fp) => fp.includes("|runtime:"));
|
|
798
|
+
[...currFailFingerprints].every((fp) => fp.includes("|runtime:") || fp.startsWith(FIT_FINGERPRINT_PREFIX));
|
|
689
799
|
if (
|
|
690
800
|
evalRoundsUsed > 1 &&
|
|
691
801
|
currFailFingerprints.size > 0 &&
|
|
692
|
-
!
|
|
802
|
+
!onlyExtensionExemptFails &&
|
|
693
803
|
[...currFailFingerprints].every((fp) => prevFailFingerprints.has(fp))
|
|
694
804
|
) {
|
|
695
805
|
console.log(
|
|
@@ -747,6 +857,11 @@ export async function runEvalRound(
|
|
|
747
857
|
// stuck-loop detector won't fire (some fails change), but the cap
|
|
748
858
|
// hits with the runtime fail unresolved.
|
|
749
859
|
const RUNTIME_EXTENSION_BONUS = 2;
|
|
860
|
+
// ggui#1195 — a fit fail (the hello does not fit its declared first
|
|
861
|
+
// screen — the founder's ruling is FAIL, not warn) active at the cap is
|
|
862
|
+
// worth ONE more round: the fix is a composition change the LLM can make
|
|
863
|
+
// in a turn, and without the round the card ships cut off.
|
|
864
|
+
const FIT_EXTENSION_BONUS = 1;
|
|
750
865
|
if (evalRoundsUsed >= maxEvalRounds) {
|
|
751
866
|
// 2026-04-27 (in-loop probe trigger): probe is now off the per-turn
|
|
752
867
|
// hot path; `blocking` from runCheck won't contain `runtime:*` fails
|
|
@@ -764,6 +879,36 @@ export async function runEvalRound(
|
|
|
764
879
|
const allowRuntimeExtension =
|
|
765
880
|
hasRuntimeFail &&
|
|
766
881
|
evalRoundsUsed < maxEvalRounds + RUNTIME_EXTENSION_BONUS;
|
|
882
|
+
const fitFails = evalResult.issues.filter(isFitFail);
|
|
883
|
+
const allowFitExtension =
|
|
884
|
+
!allowRuntimeExtension &&
|
|
885
|
+
fitFails.length > 0 &&
|
|
886
|
+
evalRoundsUsed < maxEvalRounds + FIT_EXTENSION_BONUS;
|
|
887
|
+
if (allowFitExtension) {
|
|
888
|
+
const fitLines = fitFails.slice(0, MAX_FEEDBACK_ISSUES).map(formatFitFeedback);
|
|
889
|
+
evalResult = {
|
|
890
|
+
...evalResult,
|
|
891
|
+
issues: [...evalResult.issues, ...exitProbe.probeIssues],
|
|
892
|
+
runtimeProbe: exitProbe.meta,
|
|
893
|
+
};
|
|
894
|
+
console.log(
|
|
895
|
+
`[simple] eval round ${evalRoundsUsed}: fit fail active at cap — granting +1 retry (ggui#1195; ${fitFails.length} canvas-overflow)`,
|
|
896
|
+
);
|
|
897
|
+
return {
|
|
898
|
+
control: "feedback",
|
|
899
|
+
evalDone: false,
|
|
900
|
+
evalResult,
|
|
901
|
+
evalRoundsUsed,
|
|
902
|
+
prevModeSubcats: updatedPrevModeSubcats,
|
|
903
|
+
prevFailFingerprints: new Set([...currFailFingerprints, ...fitFails.map(fingerprintFail)]),
|
|
904
|
+
preWarmedContext,
|
|
905
|
+
evalTokens,
|
|
906
|
+
evalLlmMs,
|
|
907
|
+
lastResultText: fitLines.join("\n\n"),
|
|
908
|
+
isEvalFeedback: true,
|
|
909
|
+
lastDiffFailed: false,
|
|
910
|
+
};
|
|
911
|
+
}
|
|
767
912
|
if (!allowRuntimeExtension) {
|
|
768
913
|
// Fold probe issues (if any) into evalResult for telemetry and
|
|
769
914
|
// ALWAYS stamp the probe's execution meta, even though we're not
|
|
@@ -18,3 +18,23 @@
|
|
|
18
18
|
export function mapProviderForEvaluator(provider: 'anthropic' | 'openai' | 'google' | 'openrouter'): 'claude' | 'openai' | 'google' | 'openrouter' {
|
|
19
19
|
return provider === 'anthropic' ? 'claude' : provider;
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The visual judge's provider for an agent's provider (ggui#1248) — only the
|
|
24
|
+
* providers whose SDK path takes an image (`createVisionAgent`: Anthropic,
|
|
25
|
+
* Google). `undefined` for a provider with no vision path, so a caller must
|
|
26
|
+
* decide what the visual leg does there instead of asserting it away: the
|
|
27
|
+
* in-loop judge inherits the generation's agent by default, and an OpenAI or
|
|
28
|
+
* OpenRouter lane reaching `createVisionAgent` got `undefined` back and threw.
|
|
29
|
+
*/
|
|
30
|
+
export function visionJudgeProvider(provider: 'anthropic' | 'openai' | 'google' | 'openrouter'): 'claude' | 'google' | undefined {
|
|
31
|
+
switch (provider) {
|
|
32
|
+
case 'anthropic':
|
|
33
|
+
return 'claude';
|
|
34
|
+
case 'google':
|
|
35
|
+
return 'google';
|
|
36
|
+
case 'openai':
|
|
37
|
+
case 'openrouter':
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|