visual-ai-assertions 0.4.0 → 0.6.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/README.md CHANGED
@@ -98,9 +98,8 @@ const ai = visualAI();
98
98
 
99
99
  // Explicit configuration
100
100
  const ai = visualAI({
101
- provider: "anthropic", // optional — auto-inferred from model or API key
102
- apiKey: "sk-...", // optional, defaults to provider env var
103
101
  model: "claude-sonnet-4-6", // optional, sensible defaults per provider
102
+ apiKey: "sk-...", // optional, defaults to provider env var
104
103
  debug: true, // optional, logs prompts/responses to stderr
105
104
  maxTokens: 4096, // optional, default 4096
106
105
  reasoningEffort: "high", // optional, "low" | "medium" | "high" | "xhigh"
@@ -109,7 +108,6 @@ const ai = visualAI({
109
108
 
110
109
  // Use constants for IDE autocomplete
111
110
  const ai = visualAI({
112
- provider: Provider.ANTHROPIC,
113
111
  model: Model.Anthropic.SONNET_4_6,
114
112
  });
115
113
  ```
@@ -389,21 +387,24 @@ The `VisualAIKnownError` union and `isVisualAIKnownError()` helper are useful wh
389
387
 
390
388
  ### Optional Configuration
391
389
 
392
- | Variable | Description |
393
- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
394
- | `VISUAL_AI_PROVIDER` | Default provider when `provider` is not set in config. Must be `"anthropic"`, `"openai"`, or `"google"`. Falls back to auto-detecting from which API key env var is set. |
395
- | `VISUAL_AI_MODEL` | Default model when `model` is not set in config. Overrides the provider's default model. |
396
- | `VISUAL_AI_DEBUG` | Enable debug logging when `debug` is not set in config. Use `"true"` or `"1"` to enable. |
397
- | `VISUAL_AI_TRACK_USAGE` | Enable usage tracking when `trackUsage` is not set in config. Use `"true"` or `"1"` to enable. |
390
+ | Variable | Description |
391
+ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
392
+ | `VISUAL_AI_MODEL` | Default model when `model` is not set in config. Overrides the provider's default model. |
393
+ | `VISUAL_AI_DEBUG` | Enable error diagnostic logging to stderr. Does **not** enable prompt/response logging. Use `"true"` or `"1"`. |
394
+ | `VISUAL_AI_DEBUG_PROMPT` | Enable prompt-only debug logging to stderr. Use `"true"` or `"1"`. |
395
+ | `VISUAL_AI_DEBUG_RESPONSE` | Enable response-only debug logging to stderr. Use `"true"` or `"1"`. |
396
+ | `VISUAL_AI_REASONING_EFFORT` | Default reasoning effort when `reasoningEffort` is not set in config. Use `"low"`, `"medium"`, `"high"`, or `"xhigh"`. |
397
+ | `VISUAL_AI_TRACK_USAGE` | Enable usage tracking (token counts and cost) to stderr. Use `"true"` or `"1"`. |
398
398
 
399
399
  ## Configuration
400
400
 
401
401
  | Option | Type | Default | Description |
402
402
  | ----------------- | ------- | ---------------- | ----------------------------------------------------------------------------- |
403
- | `provider` | string | auto-inferred | `"anthropic"` `"openai"` `"google"` — inferred from model name or API key |
404
403
  | `apiKey` | string | env var | API key for the provider |
405
404
  | `model` | string | provider default | Model to use |
406
- | `debug` | boolean | `false` | Log prompts/responses to stderr |
405
+ | `debug` | boolean | `false` | Enable error diagnostic logging to stderr |
406
+ | `debugPrompt` | boolean | `false` | Log prompts to stderr |
407
+ | `debugResponse` | boolean | `false` | Log responses to stderr |
407
408
  | `maxTokens` | number | `4096` | Max tokens for AI response |
408
409
  | `reasoningEffort` | string | `undefined` | `"low"` `"medium"` `"high"` `"xhigh"` — controls how deeply the model reasons |
409
410
  | `trackUsage` | boolean | `false` | Log token usage and estimated cost to stderr |
package/dist/index.cjs CHANGED
@@ -46,7 +46,6 @@ __export(index_exports, {
46
46
  Provider: () => Provider,
47
47
  StatementResultSchema: () => StatementResultSchema,
48
48
  UsageInfoSchema: () => UsageInfoSchema,
49
- VALID_PROVIDERS: () => VALID_PROVIDERS,
50
49
  VisualAIAssertionError: () => VisualAIAssertionError,
51
50
  VisualAIAuthError: () => VisualAIAuthError,
52
51
  VisualAIConfigError: () => VisualAIConfigError,
@@ -79,6 +78,8 @@ var Model = {
79
78
  OpenAI: {
80
79
  GPT_5_4: "gpt-5.4",
81
80
  GPT_5_4_PRO: "gpt-5.4-pro",
81
+ GPT_5_4_MINI: "gpt-5.4-mini",
82
+ GPT_5_4_NANO: "gpt-5.4-nano",
82
83
  GPT_5_2: "gpt-5.2",
83
84
  GPT_5_MINI: "gpt-5-mini"
84
85
  },
@@ -89,7 +90,7 @@ var Model = {
89
90
  };
90
91
  var DEFAULT_MODELS = {
91
92
  [Provider.ANTHROPIC]: Model.Anthropic.SONNET_4_6,
92
- [Provider.OPENAI]: Model.OpenAI.GPT_5_MINI,
93
+ [Provider.OPENAI]: Model.OpenAI.GPT_5_4_MINI,
93
94
  [Provider.GOOGLE]: Model.Google.GEMINI_3_FLASH_PREVIEW
94
95
  };
95
96
  var DEFAULT_MAX_TOKENS = 4096;
@@ -99,6 +100,11 @@ var MODEL_TO_PROVIDER = new Map([
99
100
  ...Object.values(Model.Google).map((m) => [m, Provider.GOOGLE])
100
101
  ]);
101
102
  var VALID_PROVIDERS = Object.values(Provider);
103
+ var PROVIDER_DEFAULT_REASONING = {
104
+ openai: "medium",
105
+ anthropic: "off",
106
+ google: "off"
107
+ };
102
108
  var Content = {
103
109
  /** Detects Lorem ipsum, TODO, TBD, and similar placeholder text */
104
110
  PLACEHOLDER_TEXT: "placeholder-text",
@@ -726,23 +732,6 @@ function inferProviderFromModel(model) {
726
732
  return prefixMatch?.[1];
727
733
  }
728
734
  function resolveProvider(config) {
729
- if (config.provider) {
730
- if (!VALID_PROVIDERS.includes(config.provider)) {
731
- throw new VisualAIConfigError(
732
- `Unknown provider: "${config.provider}". Supported: ${VALID_PROVIDERS.join(", ")}`
733
- );
734
- }
735
- return config.provider;
736
- }
737
- const envProvider = process.env.VISUAL_AI_PROVIDER;
738
- if (envProvider) {
739
- if (VALID_PROVIDERS.includes(envProvider)) {
740
- return envProvider;
741
- }
742
- throw new VisualAIConfigError(
743
- `Invalid VISUAL_AI_PROVIDER: "${envProvider}". Supported: ${VALID_PROVIDERS.join(", ")}`
744
- );
745
- }
746
735
  const model = config.model ?? process.env.VISUAL_AI_MODEL;
747
736
  if (model) {
748
737
  const inferred = inferProviderFromModel(model);
@@ -756,7 +745,7 @@ function resolveProvider(config) {
756
745
  const detected = apiKeyProviderMap.find(([key]) => process.env[key]);
757
746
  if (detected) return detected[1];
758
747
  throw new VisualAIConfigError(
759
- "No provider specified. Set `provider` in config, `VISUAL_AI_PROVIDER` env variable, or an API key env variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY)."
748
+ "Cannot determine provider. Set a model name (config or VISUAL_AI_MODEL) or an API key env variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY)."
760
749
  );
761
750
  }
762
751
  function parseBooleanEnv(envName, value) {
@@ -768,13 +757,27 @@ function parseBooleanEnv(envName, value) {
768
757
  `Invalid ${envName} value: "${value}". Use "true", "1", "false", or "0".`
769
758
  );
770
759
  }
760
+ var VALID_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
761
+ function parseReasoningEffortEnv(envName, value) {
762
+ if (value === void 0 || value === "") return void 0;
763
+ const lower = value.toLowerCase();
764
+ if (VALID_REASONING_EFFORTS.includes(lower)) return lower;
765
+ throw new VisualAIConfigError(
766
+ `Invalid ${envName} value: "${value}". Use "low", "medium", "high", or "xhigh".`
767
+ );
768
+ }
769
+ var debugDeprecationWarned = false;
771
770
  function resolveConfig(config) {
772
771
  const provider = resolveProvider(config);
773
772
  const model = config.model ?? process.env.VISUAL_AI_MODEL ?? DEFAULT_MODELS[provider];
774
- const modelProvider = inferProviderFromModel(model);
775
- if (modelProvider && modelProvider !== provider) {
776
- throw new VisualAIConfigError(
777
- `Model "${model}" appears to be a ${modelProvider} model, but provider is "${provider}". Either change the provider or use a ${provider}-compatible model.`
773
+ const debug = config.debug ?? parseBooleanEnv("VISUAL_AI_DEBUG", process.env.VISUAL_AI_DEBUG) ?? false;
774
+ const debugPrompt = config.debugPrompt ?? parseBooleanEnv("VISUAL_AI_DEBUG_PROMPT", process.env.VISUAL_AI_DEBUG_PROMPT) ?? false;
775
+ const debugResponse = config.debugResponse ?? parseBooleanEnv("VISUAL_AI_DEBUG_RESPONSE", process.env.VISUAL_AI_DEBUG_RESPONSE) ?? false;
776
+ if (debug && !debugPrompt && !debugResponse && !debugDeprecationWarned) {
777
+ debugDeprecationWarned = true;
778
+ process.stderr.write(
779
+ `[visual-ai-assertions] Warning: VISUAL_AI_DEBUG no longer enables prompt/response logging. Use VISUAL_AI_DEBUG_PROMPT=true and/or VISUAL_AI_DEBUG_RESPONSE=true instead.
780
+ `
778
781
  );
779
782
  }
780
783
  return {
@@ -782,8 +785,10 @@ function resolveConfig(config) {
782
785
  apiKey: config.apiKey,
783
786
  model,
784
787
  maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
785
- reasoningEffort: config.reasoningEffort,
786
- debug: config.debug ?? parseBooleanEnv("VISUAL_AI_DEBUG", process.env.VISUAL_AI_DEBUG) ?? false,
788
+ reasoningEffort: config.reasoningEffort ?? parseReasoningEffortEnv("VISUAL_AI_REASONING_EFFORT", process.env.VISUAL_AI_REASONING_EFFORT),
789
+ debug,
790
+ debugPrompt,
791
+ debugResponse,
787
792
  trackUsage: config.trackUsage ?? parseBooleanEnv("VISUAL_AI_TRACK_USAGE", process.env.VISUAL_AI_TRACK_USAGE) ?? false
788
793
  };
789
794
  }
@@ -815,6 +820,14 @@ var PRICING_TABLE = {
815
820
  inputPricePerToken: 1.75 / PER_MILLION,
816
821
  outputPricePerToken: 14 / PER_MILLION
817
822
  },
823
+ [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_MINI}`]: {
824
+ inputPricePerToken: 0.75 / PER_MILLION,
825
+ outputPricePerToken: 4.5 / PER_MILLION
826
+ },
827
+ [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_NANO}`]: {
828
+ inputPricePerToken: 0.2 / PER_MILLION,
829
+ outputPricePerToken: 1.25 / PER_MILLION
830
+ },
818
831
  [`${Provider.OPENAI}:${Model.OpenAI.GPT_5_MINI}`]: {
819
832
  inputPricePerToken: 0.25 / PER_MILLION,
820
833
  outputPricePerToken: 2 / PER_MILLION
@@ -836,8 +849,9 @@ function calculateCost(provider, model, inputTokens, outputTokens) {
836
849
  }
837
850
 
838
851
  // src/core/debug.ts
839
- function debugLog(config, label, data) {
840
- if (config.debug) {
852
+ function debugLog(config, label, data, kind = "error") {
853
+ const enabled = kind === "prompt" ? config.debugPrompt : kind === "response" ? config.debugResponse : config.debug;
854
+ if (enabled) {
841
855
  process.stderr.write(`[visual-ai-assertions] ${label}: ${data}
842
856
  `);
843
857
  }
@@ -845,8 +859,9 @@ function debugLog(config, label, data) {
845
859
  function usageLog(config, method, usage) {
846
860
  if (!config.trackUsage) return;
847
861
  const costStr = usage.estimatedCost !== void 0 ? `$${usage.estimatedCost.toFixed(6)}` : "unknown";
862
+ const reasoningStr = config.reasoningEffort ? `reasoning: ${config.reasoningEffort}` : `reasoning: ${PROVIDER_DEFAULT_REASONING[config.provider]} (provider default)`;
848
863
  process.stderr.write(
849
- `[visual-ai-assertions] ${method} usage: ${usage.inputTokens} input + ${usage.outputTokens} output tokens (${costStr}) in ${usage.durationSeconds?.toFixed(3) ?? "0.000"}s [${config.model}]
864
+ `[visual-ai-assertions] ${method} usage: ${usage.inputTokens} input + ${usage.outputTokens} output tokens (${costStr}) in ${usage.durationSeconds?.toFixed(3) ?? "0.000"}s [${config.model}, ${reasoningStr}]
850
865
  `
851
866
  );
852
867
  }
@@ -862,6 +877,28 @@ function processUsage(method, rawUsage, durationSeconds, config) {
862
877
  usageLog(config, method, usage);
863
878
  return usage;
864
879
  }
880
+ var MAX_RAW_RESPONSE_PREVIEW = 500;
881
+ function formatError(error) {
882
+ if (error instanceof VisualAIResponseParseError) {
883
+ const truncated = error.rawResponse.length > MAX_RAW_RESPONSE_PREVIEW ? error.rawResponse.slice(0, MAX_RAW_RESPONSE_PREVIEW) + "..." : error.rawResponse;
884
+ return `${error.name} (${error.code}): ${error.message}. Raw (truncated): ${truncated}`;
885
+ }
886
+ if (error instanceof VisualAIError) {
887
+ return `${error.name} (${error.code}): ${error.message}`;
888
+ }
889
+ if (error instanceof Error) {
890
+ return `${error.name}: ${error.message}`;
891
+ }
892
+ return String(error);
893
+ }
894
+ async function withErrorDebug(config, method, fn) {
895
+ try {
896
+ return await fn();
897
+ } catch (error) {
898
+ debugLog(config, `${method} error`, formatError(error), "error");
899
+ throw error;
900
+ }
901
+ }
865
902
  async function timedSendMessage(driver, images, prompt) {
866
903
  const start = performance.now();
867
904
  const response = await driver.sendMessage(images, prompt);
@@ -1190,16 +1227,18 @@ function visualAI(config = {}) {
1190
1227
  if (elements.length === 0) {
1191
1228
  throw new VisualAIConfigError(`At least one element is required for ${methodName}()`);
1192
1229
  }
1193
- const img = await normalizeImage(image);
1194
- const prompt = buildElementsVisibilityPrompt(elements, visible, options);
1195
- debugLog(resolvedConfig, `${methodName} prompt`, prompt);
1196
- const response = await timedSendMessage(driver, [img], prompt);
1197
- debugLog(resolvedConfig, `${methodName} response`, response.text);
1198
- const result = parseCheckResponse(response.text);
1199
- return {
1200
- ...result,
1201
- usage: processUsage(methodName, response.usage, response.durationSeconds, resolvedConfig)
1202
- };
1230
+ return withErrorDebug(resolvedConfig, methodName, async () => {
1231
+ const img = await normalizeImage(image);
1232
+ const prompt = buildElementsVisibilityPrompt(elements, visible, options);
1233
+ debugLog(resolvedConfig, `${methodName} prompt`, prompt, "prompt");
1234
+ const response = await timedSendMessage(driver, [img], prompt);
1235
+ debugLog(resolvedConfig, `${methodName} response`, response.text, "response");
1236
+ const result = parseCheckResponse(response.text);
1237
+ return {
1238
+ ...result,
1239
+ usage: processUsage(methodName, response.usage, response.durationSeconds, resolvedConfig)
1240
+ };
1241
+ });
1203
1242
  }
1204
1243
  return {
1205
1244
  async check(image, statements, options) {
@@ -1207,61 +1246,64 @@ function visualAI(config = {}) {
1207
1246
  if (stmts.length === 0) {
1208
1247
  throw new VisualAIConfigError("At least one statement is required for check()");
1209
1248
  }
1210
- const img = await normalizeImage(image);
1211
- const prompt = buildCheckPrompt(stmts, { instructions: options?.instructions });
1212
- debugLog(resolvedConfig, "check prompt", prompt);
1213
- const response = await timedSendMessage(driver, [img], prompt);
1214
- debugLog(resolvedConfig, "check response", response.text);
1215
- const result = parseCheckResponse(response.text);
1216
- return {
1217
- ...result,
1218
- usage: processUsage("check", response.usage, response.durationSeconds, resolvedConfig)
1219
- };
1249
+ return withErrorDebug(resolvedConfig, "check", async () => {
1250
+ const img = await normalizeImage(image);
1251
+ const prompt = buildCheckPrompt(stmts, { instructions: options?.instructions });
1252
+ debugLog(resolvedConfig, "check prompt", prompt, "prompt");
1253
+ const response = await timedSendMessage(driver, [img], prompt);
1254
+ debugLog(resolvedConfig, "check response", response.text, "response");
1255
+ const result = parseCheckResponse(response.text);
1256
+ return {
1257
+ ...result,
1258
+ usage: processUsage("check", response.usage, response.durationSeconds, resolvedConfig)
1259
+ };
1260
+ });
1220
1261
  },
1221
1262
  async ask(image, userPrompt, options) {
1222
- const img = await normalizeImage(image);
1223
- const prompt = buildAskPrompt(userPrompt, { instructions: options?.instructions });
1224
- debugLog(resolvedConfig, "ask prompt", prompt);
1225
- const response = await timedSendMessage(driver, [img], prompt);
1226
- debugLog(resolvedConfig, "ask response", response.text);
1227
- const result = parseAskResponse(response.text);
1228
- return {
1229
- ...result,
1230
- usage: processUsage("ask", response.usage, response.durationSeconds, resolvedConfig)
1231
- };
1263
+ return withErrorDebug(resolvedConfig, "ask", async () => {
1264
+ const img = await normalizeImage(image);
1265
+ const prompt = buildAskPrompt(userPrompt, { instructions: options?.instructions });
1266
+ debugLog(resolvedConfig, "ask prompt", prompt, "prompt");
1267
+ const response = await timedSendMessage(driver, [img], prompt);
1268
+ debugLog(resolvedConfig, "ask response", response.text, "response");
1269
+ const result = parseAskResponse(response.text);
1270
+ return {
1271
+ ...result,
1272
+ usage: processUsage("ask", response.usage, response.durationSeconds, resolvedConfig)
1273
+ };
1274
+ });
1232
1275
  },
1233
1276
  async compare(imageA, imageB, options) {
1234
- const [imgA, imgB] = await Promise.all([normalizeImage(imageA), normalizeImage(imageB)]);
1235
- const prompt = buildComparePrompt({
1236
- userPrompt: options?.prompt,
1237
- instructions: options?.instructions
1238
- });
1239
- debugLog(resolvedConfig, "compare prompt", prompt);
1240
- const response = await timedSendMessage(driver, [imgA, imgB], prompt);
1241
- debugLog(resolvedConfig, "compare response", response.text);
1242
- const supportsAnnotatedDiff = resolvedConfig.provider === "google" && resolvedConfig.model === Model.Google.GEMINI_3_FLASH_PREVIEW;
1243
- const effectiveDiffImage = options?.diffImage ?? (supportsAnnotatedDiff ? true : false);
1244
- let diffImage;
1245
- if (effectiveDiffImage) {
1246
- try {
1247
- diffImage = await generateAiDiff(imgA, imgB, resolvedConfig.model, driver);
1248
- } catch (err) {
1249
- const msg = err instanceof Error ? err.message : String(err);
1250
- debugLog(resolvedConfig, "ai diff error", msg);
1251
- if (!resolvedConfig.debug) {
1277
+ return withErrorDebug(resolvedConfig, "compare", async () => {
1278
+ const [imgA, imgB] = await Promise.all([normalizeImage(imageA), normalizeImage(imageB)]);
1279
+ const prompt = buildComparePrompt({
1280
+ userPrompt: options?.prompt,
1281
+ instructions: options?.instructions
1282
+ });
1283
+ debugLog(resolvedConfig, "compare prompt", prompt, "prompt");
1284
+ const response = await timedSendMessage(driver, [imgA, imgB], prompt);
1285
+ debugLog(resolvedConfig, "compare response", response.text, "response");
1286
+ const supportsAnnotatedDiff = resolvedConfig.provider === "google" && resolvedConfig.model === Model.Google.GEMINI_3_FLASH_PREVIEW;
1287
+ const effectiveDiffImage = options?.diffImage ?? (supportsAnnotatedDiff ? true : false);
1288
+ let diffImage;
1289
+ if (effectiveDiffImage) {
1290
+ try {
1291
+ diffImage = await generateAiDiff(imgA, imgB, resolvedConfig.model, driver);
1292
+ } catch (err) {
1293
+ const msg = err instanceof Error ? err.message : String(err);
1252
1294
  process.stderr.write(
1253
1295
  `[visual-ai-assertions] warning: diff generation failed: ${msg}
1254
1296
  `
1255
1297
  );
1256
1298
  }
1257
1299
  }
1258
- }
1259
- const result = parseCompareResponse(response.text);
1260
- return {
1261
- ...result,
1262
- ...diffImage ? { diffImage } : {},
1263
- usage: processUsage("compare", response.usage, response.durationSeconds, resolvedConfig)
1264
- };
1300
+ const result = parseCompareResponse(response.text);
1301
+ return {
1302
+ ...result,
1303
+ ...diffImage ? { diffImage } : {},
1304
+ usage: processUsage("compare", response.usage, response.durationSeconds, resolvedConfig)
1305
+ };
1306
+ });
1265
1307
  },
1266
1308
  elementsVisible(image, elements, options) {
1267
1309
  return checkElementsVisibility(image, elements, true, options);
@@ -1270,57 +1312,65 @@ function visualAI(config = {}) {
1270
1312
  return checkElementsVisibility(image, elements, false, options);
1271
1313
  },
1272
1314
  async accessibility(image, options) {
1273
- const img = await normalizeImage(image);
1274
- const prompt = buildAccessibilityPrompt(options);
1275
- debugLog(resolvedConfig, "accessibility prompt", prompt);
1276
- const response = await timedSendMessage(driver, [img], prompt);
1277
- debugLog(resolvedConfig, "accessibility response", response.text);
1278
- const result = parseCheckResponse(response.text);
1279
- return {
1280
- ...result,
1281
- usage: processUsage(
1282
- "accessibility",
1283
- response.usage,
1284
- response.durationSeconds,
1285
- resolvedConfig
1286
- )
1287
- };
1315
+ return withErrorDebug(resolvedConfig, "accessibility", async () => {
1316
+ const img = await normalizeImage(image);
1317
+ const prompt = buildAccessibilityPrompt(options);
1318
+ debugLog(resolvedConfig, "accessibility prompt", prompt, "prompt");
1319
+ const response = await timedSendMessage(driver, [img], prompt);
1320
+ debugLog(resolvedConfig, "accessibility response", response.text, "response");
1321
+ const result = parseCheckResponse(response.text);
1322
+ return {
1323
+ ...result,
1324
+ usage: processUsage(
1325
+ "accessibility",
1326
+ response.usage,
1327
+ response.durationSeconds,
1328
+ resolvedConfig
1329
+ )
1330
+ };
1331
+ });
1288
1332
  },
1289
1333
  async layout(image, options) {
1290
- const img = await normalizeImage(image);
1291
- const prompt = buildLayoutPrompt(options);
1292
- debugLog(resolvedConfig, "layout prompt", prompt);
1293
- const response = await timedSendMessage(driver, [img], prompt);
1294
- debugLog(resolvedConfig, "layout response", response.text);
1295
- const result = parseCheckResponse(response.text);
1296
- return {
1297
- ...result,
1298
- usage: processUsage("layout", response.usage, response.durationSeconds, resolvedConfig)
1299
- };
1334
+ return withErrorDebug(resolvedConfig, "layout", async () => {
1335
+ const img = await normalizeImage(image);
1336
+ const prompt = buildLayoutPrompt(options);
1337
+ debugLog(resolvedConfig, "layout prompt", prompt, "prompt");
1338
+ const response = await timedSendMessage(driver, [img], prompt);
1339
+ debugLog(resolvedConfig, "layout response", response.text, "response");
1340
+ const result = parseCheckResponse(response.text);
1341
+ return {
1342
+ ...result,
1343
+ usage: processUsage("layout", response.usage, response.durationSeconds, resolvedConfig)
1344
+ };
1345
+ });
1300
1346
  },
1301
1347
  async pageLoad(image, options) {
1302
- const img = await normalizeImage(image);
1303
- const prompt = buildPageLoadPrompt(options);
1304
- debugLog(resolvedConfig, "pageLoad prompt", prompt);
1305
- const response = await timedSendMessage(driver, [img], prompt);
1306
- debugLog(resolvedConfig, "pageLoad response", response.text);
1307
- const result = parseCheckResponse(response.text);
1308
- return {
1309
- ...result,
1310
- usage: processUsage("pageLoad", response.usage, response.durationSeconds, resolvedConfig)
1311
- };
1348
+ return withErrorDebug(resolvedConfig, "pageLoad", async () => {
1349
+ const img = await normalizeImage(image);
1350
+ const prompt = buildPageLoadPrompt(options);
1351
+ debugLog(resolvedConfig, "pageLoad prompt", prompt, "prompt");
1352
+ const response = await timedSendMessage(driver, [img], prompt);
1353
+ debugLog(resolvedConfig, "pageLoad response", response.text, "response");
1354
+ const result = parseCheckResponse(response.text);
1355
+ return {
1356
+ ...result,
1357
+ usage: processUsage("pageLoad", response.usage, response.durationSeconds, resolvedConfig)
1358
+ };
1359
+ });
1312
1360
  },
1313
1361
  async content(image, options) {
1314
- const img = await normalizeImage(image);
1315
- const prompt = buildContentPrompt(options);
1316
- debugLog(resolvedConfig, "content prompt", prompt);
1317
- const response = await timedSendMessage(driver, [img], prompt);
1318
- debugLog(resolvedConfig, "content response", response.text);
1319
- const result = parseCheckResponse(response.text);
1320
- return {
1321
- ...result,
1322
- usage: processUsage("content", response.usage, response.durationSeconds, resolvedConfig)
1323
- };
1362
+ return withErrorDebug(resolvedConfig, "content", async () => {
1363
+ const img = await normalizeImage(image);
1364
+ const prompt = buildContentPrompt(options);
1365
+ debugLog(resolvedConfig, "content prompt", prompt, "prompt");
1366
+ const response = await timedSendMessage(driver, [img], prompt);
1367
+ debugLog(resolvedConfig, "content response", response.text, "response");
1368
+ const result = parseCheckResponse(response.text);
1369
+ return {
1370
+ ...result,
1371
+ usage: processUsage("content", response.usage, response.durationSeconds, resolvedConfig)
1372
+ };
1373
+ });
1324
1374
  }
1325
1375
  };
1326
1376
  }
@@ -1397,7 +1447,6 @@ function assertVisualCompareResult(result, label) {
1397
1447
  Provider,
1398
1448
  StatementResultSchema,
1399
1449
  UsageInfoSchema,
1400
- VALID_PROVIDERS,
1401
1450
  VisualAIAssertionError,
1402
1451
  VisualAIAuthError,
1403
1452
  VisualAIConfigError,