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 +12 -11
- package/dist/index.cjs +179 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -10
- package/dist/index.d.ts +13 -10
- package/dist/index.js +179 -129
- package/dist/index.js.map +1 -1
- package/package.json +20 -12
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
/** Supported provider identifiers for
|
|
3
|
+
/** Supported provider identifiers used internally for pricing and provider selection. */
|
|
4
4
|
declare const Provider: {
|
|
5
5
|
readonly ANTHROPIC: "anthropic";
|
|
6
6
|
readonly OPENAI: "openai";
|
|
@@ -16,6 +16,8 @@ declare const Model: {
|
|
|
16
16
|
readonly OpenAI: {
|
|
17
17
|
readonly GPT_5_4: "gpt-5.4";
|
|
18
18
|
readonly GPT_5_4_PRO: "gpt-5.4-pro";
|
|
19
|
+
readonly GPT_5_4_MINI: "gpt-5.4-mini";
|
|
20
|
+
readonly GPT_5_4_NANO: "gpt-5.4-nano";
|
|
19
21
|
readonly GPT_5_2: "gpt-5.2";
|
|
20
22
|
readonly GPT_5_MINI: "gpt-5-mini";
|
|
21
23
|
};
|
|
@@ -29,11 +31,9 @@ type KnownModelName = (typeof Model.Anthropic)[keyof typeof Model.Anthropic] | (
|
|
|
29
31
|
/** Default model selection used when a caller omits `config.model`. */
|
|
30
32
|
declare const DEFAULT_MODELS: {
|
|
31
33
|
readonly anthropic: "claude-sonnet-4-6";
|
|
32
|
-
readonly openai: "gpt-5-mini";
|
|
34
|
+
readonly openai: "gpt-5.4-mini";
|
|
33
35
|
readonly google: "gemini-3-flash-preview";
|
|
34
36
|
};
|
|
35
|
-
/** List of accepted provider names for validation and public consumption. */
|
|
36
|
-
declare const VALID_PROVIDERS: readonly ProviderName[];
|
|
37
37
|
/** Built-in content checks available through `client.content()`. */
|
|
38
38
|
declare const Content: {
|
|
39
39
|
/** Detects Lorem ipsum, TODO, TBD, and similar placeholder text */
|
|
@@ -392,17 +392,20 @@ type ReasoningEffort = "low" | "medium" | "high" | "xhigh";
|
|
|
392
392
|
* @example
|
|
393
393
|
* ```ts
|
|
394
394
|
* const client = visualAI({
|
|
395
|
-
* provider: "openai",
|
|
396
395
|
* model: "gpt-5-mini",
|
|
397
396
|
* apiKey: process.env.OPENAI_API_KEY,
|
|
398
397
|
* });
|
|
399
398
|
* ```
|
|
400
399
|
*/
|
|
401
400
|
interface VisualAIConfig {
|
|
402
|
-
provider?: ProviderName;
|
|
403
401
|
apiKey?: string;
|
|
404
402
|
model?: string;
|
|
403
|
+
/** Enable error diagnostic logging to stderr. Does not enable prompt/response logging — use `debugPrompt` and `debugResponse` for that. */
|
|
405
404
|
debug?: boolean;
|
|
405
|
+
/** Log prompts to stderr. */
|
|
406
|
+
debugPrompt?: boolean;
|
|
407
|
+
/** Log responses to stderr. */
|
|
408
|
+
debugResponse?: boolean;
|
|
406
409
|
maxTokens?: number;
|
|
407
410
|
reasoningEffort?: ReasoningEffort;
|
|
408
411
|
trackUsage?: boolean;
|
|
@@ -458,7 +461,7 @@ interface ContentOptions {
|
|
|
458
461
|
*
|
|
459
462
|
* @example
|
|
460
463
|
* ```ts
|
|
461
|
-
* const client = visualAI({
|
|
464
|
+
* const client = visualAI({ model: "gpt-5-mini" });
|
|
462
465
|
* const result = await client.check("./tests/fixtures/small.png", "The button is visible");
|
|
463
466
|
* ```
|
|
464
467
|
*/
|
|
@@ -608,7 +611,7 @@ interface VisualAIClient {
|
|
|
608
611
|
/**
|
|
609
612
|
* Creates a configured visual AI client.
|
|
610
613
|
*
|
|
611
|
-
* @param config
|
|
614
|
+
* @param config Model selection and runtime options for subsequent requests.
|
|
612
615
|
* @returns A `VisualAIClient` instance with check, compare, ask, and template helpers.
|
|
613
616
|
* @throws {VisualAIConfigError} When the provider or model configuration is invalid.
|
|
614
617
|
* @throws {VisualAIAuthError} When required API credentials are missing.
|
|
@@ -619,7 +622,7 @@ interface VisualAIClient {
|
|
|
619
622
|
*
|
|
620
623
|
* test("hero loads correctly", async ({ page }) => {
|
|
621
624
|
* const client = visualAI({
|
|
622
|
-
*
|
|
625
|
+
* model: "gpt-5-mini",
|
|
623
626
|
* apiKey: process.env.OPENAI_API_KEY,
|
|
624
627
|
* });
|
|
625
628
|
*
|
|
@@ -836,4 +839,4 @@ declare function assertVisualResult(result: CheckResult, label?: string): void;
|
|
|
836
839
|
*/
|
|
837
840
|
declare function assertVisualCompareResult(result: CompareResult, label?: string): void;
|
|
838
841
|
|
|
839
|
-
export { Accessibility, type AccessibilityCheckName, type AccessibilityOptions, type AskOptions, type AskResult, AskResultSchema, type ChangeEntry, ChangeEntrySchema, type CheckOptions, type CheckResult, CheckResultSchema, type CompareOptions, type CompareResult, CompareResultSchema, type Confidence, ConfidenceSchema, Content, type ContentCheckName, type ContentOptions, DEFAULT_MODELS, type DiffImageResult, type ElementsVisibilityOptions, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, Model, type PageLoadOptions, Provider, type ProviderName, type ReasoningEffort, type StatementResult, StatementResultSchema, type SupportedMimeType, type UsageInfo, UsageInfoSchema,
|
|
842
|
+
export { Accessibility, type AccessibilityCheckName, type AccessibilityOptions, type AskOptions, type AskResult, AskResultSchema, type ChangeEntry, ChangeEntrySchema, type CheckOptions, type CheckResult, CheckResultSchema, type CompareOptions, type CompareResult, CompareResultSchema, type Confidence, ConfidenceSchema, Content, type ContentCheckName, type ContentOptions, DEFAULT_MODELS, type DiffImageResult, type ElementsVisibilityOptions, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, Model, type PageLoadOptions, Provider, type ProviderName, type ReasoningEffort, type StatementResult, StatementResultSchema, type SupportedMimeType, type UsageInfo, UsageInfoSchema, VisualAIAssertionError, VisualAIAuthError, type VisualAIClient, type VisualAIConfig, VisualAIConfigError, VisualAIError, type VisualAIErrorCode, VisualAIImageError, type VisualAIKnownError, VisualAIProviderError, VisualAIRateLimitError, VisualAIResponseParseError, assertVisualCompareResult, assertVisualResult, formatCheckResult, formatCompareResult, isVisualAIKnownError, visualAI };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
/** Supported provider identifiers for
|
|
3
|
+
/** Supported provider identifiers used internally for pricing and provider selection. */
|
|
4
4
|
declare const Provider: {
|
|
5
5
|
readonly ANTHROPIC: "anthropic";
|
|
6
6
|
readonly OPENAI: "openai";
|
|
@@ -16,6 +16,8 @@ declare const Model: {
|
|
|
16
16
|
readonly OpenAI: {
|
|
17
17
|
readonly GPT_5_4: "gpt-5.4";
|
|
18
18
|
readonly GPT_5_4_PRO: "gpt-5.4-pro";
|
|
19
|
+
readonly GPT_5_4_MINI: "gpt-5.4-mini";
|
|
20
|
+
readonly GPT_5_4_NANO: "gpt-5.4-nano";
|
|
19
21
|
readonly GPT_5_2: "gpt-5.2";
|
|
20
22
|
readonly GPT_5_MINI: "gpt-5-mini";
|
|
21
23
|
};
|
|
@@ -29,11 +31,9 @@ type KnownModelName = (typeof Model.Anthropic)[keyof typeof Model.Anthropic] | (
|
|
|
29
31
|
/** Default model selection used when a caller omits `config.model`. */
|
|
30
32
|
declare const DEFAULT_MODELS: {
|
|
31
33
|
readonly anthropic: "claude-sonnet-4-6";
|
|
32
|
-
readonly openai: "gpt-5-mini";
|
|
34
|
+
readonly openai: "gpt-5.4-mini";
|
|
33
35
|
readonly google: "gemini-3-flash-preview";
|
|
34
36
|
};
|
|
35
|
-
/** List of accepted provider names for validation and public consumption. */
|
|
36
|
-
declare const VALID_PROVIDERS: readonly ProviderName[];
|
|
37
37
|
/** Built-in content checks available through `client.content()`. */
|
|
38
38
|
declare const Content: {
|
|
39
39
|
/** Detects Lorem ipsum, TODO, TBD, and similar placeholder text */
|
|
@@ -392,17 +392,20 @@ type ReasoningEffort = "low" | "medium" | "high" | "xhigh";
|
|
|
392
392
|
* @example
|
|
393
393
|
* ```ts
|
|
394
394
|
* const client = visualAI({
|
|
395
|
-
* provider: "openai",
|
|
396
395
|
* model: "gpt-5-mini",
|
|
397
396
|
* apiKey: process.env.OPENAI_API_KEY,
|
|
398
397
|
* });
|
|
399
398
|
* ```
|
|
400
399
|
*/
|
|
401
400
|
interface VisualAIConfig {
|
|
402
|
-
provider?: ProviderName;
|
|
403
401
|
apiKey?: string;
|
|
404
402
|
model?: string;
|
|
403
|
+
/** Enable error diagnostic logging to stderr. Does not enable prompt/response logging — use `debugPrompt` and `debugResponse` for that. */
|
|
405
404
|
debug?: boolean;
|
|
405
|
+
/** Log prompts to stderr. */
|
|
406
|
+
debugPrompt?: boolean;
|
|
407
|
+
/** Log responses to stderr. */
|
|
408
|
+
debugResponse?: boolean;
|
|
406
409
|
maxTokens?: number;
|
|
407
410
|
reasoningEffort?: ReasoningEffort;
|
|
408
411
|
trackUsage?: boolean;
|
|
@@ -458,7 +461,7 @@ interface ContentOptions {
|
|
|
458
461
|
*
|
|
459
462
|
* @example
|
|
460
463
|
* ```ts
|
|
461
|
-
* const client = visualAI({
|
|
464
|
+
* const client = visualAI({ model: "gpt-5-mini" });
|
|
462
465
|
* const result = await client.check("./tests/fixtures/small.png", "The button is visible");
|
|
463
466
|
* ```
|
|
464
467
|
*/
|
|
@@ -608,7 +611,7 @@ interface VisualAIClient {
|
|
|
608
611
|
/**
|
|
609
612
|
* Creates a configured visual AI client.
|
|
610
613
|
*
|
|
611
|
-
* @param config
|
|
614
|
+
* @param config Model selection and runtime options for subsequent requests.
|
|
612
615
|
* @returns A `VisualAIClient` instance with check, compare, ask, and template helpers.
|
|
613
616
|
* @throws {VisualAIConfigError} When the provider or model configuration is invalid.
|
|
614
617
|
* @throws {VisualAIAuthError} When required API credentials are missing.
|
|
@@ -619,7 +622,7 @@ interface VisualAIClient {
|
|
|
619
622
|
*
|
|
620
623
|
* test("hero loads correctly", async ({ page }) => {
|
|
621
624
|
* const client = visualAI({
|
|
622
|
-
*
|
|
625
|
+
* model: "gpt-5-mini",
|
|
623
626
|
* apiKey: process.env.OPENAI_API_KEY,
|
|
624
627
|
* });
|
|
625
628
|
*
|
|
@@ -836,4 +839,4 @@ declare function assertVisualResult(result: CheckResult, label?: string): void;
|
|
|
836
839
|
*/
|
|
837
840
|
declare function assertVisualCompareResult(result: CompareResult, label?: string): void;
|
|
838
841
|
|
|
839
|
-
export { Accessibility, type AccessibilityCheckName, type AccessibilityOptions, type AskOptions, type AskResult, AskResultSchema, type ChangeEntry, ChangeEntrySchema, type CheckOptions, type CheckResult, CheckResultSchema, type CompareOptions, type CompareResult, CompareResultSchema, type Confidence, ConfidenceSchema, Content, type ContentCheckName, type ContentOptions, DEFAULT_MODELS, type DiffImageResult, type ElementsVisibilityOptions, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, Model, type PageLoadOptions, Provider, type ProviderName, type ReasoningEffort, type StatementResult, StatementResultSchema, type SupportedMimeType, type UsageInfo, UsageInfoSchema,
|
|
842
|
+
export { Accessibility, type AccessibilityCheckName, type AccessibilityOptions, type AskOptions, type AskResult, AskResultSchema, type ChangeEntry, ChangeEntrySchema, type CheckOptions, type CheckResult, CheckResultSchema, type CompareOptions, type CompareResult, CompareResultSchema, type Confidence, ConfidenceSchema, Content, type ContentCheckName, type ContentOptions, DEFAULT_MODELS, type DiffImageResult, type ElementsVisibilityOptions, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, Model, type PageLoadOptions, Provider, type ProviderName, type ReasoningEffort, type StatementResult, StatementResultSchema, type SupportedMimeType, type UsageInfo, UsageInfoSchema, VisualAIAssertionError, VisualAIAuthError, type VisualAIClient, type VisualAIConfig, VisualAIConfigError, VisualAIError, type VisualAIErrorCode, VisualAIImageError, type VisualAIKnownError, VisualAIProviderError, VisualAIRateLimitError, VisualAIResponseParseError, assertVisualCompareResult, assertVisualResult, formatCheckResult, formatCompareResult, isVisualAIKnownError, visualAI };
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ var Model = {
|
|
|
13
13
|
OpenAI: {
|
|
14
14
|
GPT_5_4: "gpt-5.4",
|
|
15
15
|
GPT_5_4_PRO: "gpt-5.4-pro",
|
|
16
|
+
GPT_5_4_MINI: "gpt-5.4-mini",
|
|
17
|
+
GPT_5_4_NANO: "gpt-5.4-nano",
|
|
16
18
|
GPT_5_2: "gpt-5.2",
|
|
17
19
|
GPT_5_MINI: "gpt-5-mini"
|
|
18
20
|
},
|
|
@@ -23,7 +25,7 @@ var Model = {
|
|
|
23
25
|
};
|
|
24
26
|
var DEFAULT_MODELS = {
|
|
25
27
|
[Provider.ANTHROPIC]: Model.Anthropic.SONNET_4_6,
|
|
26
|
-
[Provider.OPENAI]: Model.OpenAI.
|
|
28
|
+
[Provider.OPENAI]: Model.OpenAI.GPT_5_4_MINI,
|
|
27
29
|
[Provider.GOOGLE]: Model.Google.GEMINI_3_FLASH_PREVIEW
|
|
28
30
|
};
|
|
29
31
|
var DEFAULT_MAX_TOKENS = 4096;
|
|
@@ -33,6 +35,11 @@ var MODEL_TO_PROVIDER = new Map([
|
|
|
33
35
|
...Object.values(Model.Google).map((m) => [m, Provider.GOOGLE])
|
|
34
36
|
]);
|
|
35
37
|
var VALID_PROVIDERS = Object.values(Provider);
|
|
38
|
+
var PROVIDER_DEFAULT_REASONING = {
|
|
39
|
+
openai: "medium",
|
|
40
|
+
anthropic: "off",
|
|
41
|
+
google: "off"
|
|
42
|
+
};
|
|
36
43
|
var Content = {
|
|
37
44
|
/** Detects Lorem ipsum, TODO, TBD, and similar placeholder text */
|
|
38
45
|
PLACEHOLDER_TEXT: "placeholder-text",
|
|
@@ -660,23 +667,6 @@ function inferProviderFromModel(model) {
|
|
|
660
667
|
return prefixMatch?.[1];
|
|
661
668
|
}
|
|
662
669
|
function resolveProvider(config) {
|
|
663
|
-
if (config.provider) {
|
|
664
|
-
if (!VALID_PROVIDERS.includes(config.provider)) {
|
|
665
|
-
throw new VisualAIConfigError(
|
|
666
|
-
`Unknown provider: "${config.provider}". Supported: ${VALID_PROVIDERS.join(", ")}`
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
return config.provider;
|
|
670
|
-
}
|
|
671
|
-
const envProvider = process.env.VISUAL_AI_PROVIDER;
|
|
672
|
-
if (envProvider) {
|
|
673
|
-
if (VALID_PROVIDERS.includes(envProvider)) {
|
|
674
|
-
return envProvider;
|
|
675
|
-
}
|
|
676
|
-
throw new VisualAIConfigError(
|
|
677
|
-
`Invalid VISUAL_AI_PROVIDER: "${envProvider}". Supported: ${VALID_PROVIDERS.join(", ")}`
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
670
|
const model = config.model ?? process.env.VISUAL_AI_MODEL;
|
|
681
671
|
if (model) {
|
|
682
672
|
const inferred = inferProviderFromModel(model);
|
|
@@ -690,7 +680,7 @@ function resolveProvider(config) {
|
|
|
690
680
|
const detected = apiKeyProviderMap.find(([key]) => process.env[key]);
|
|
691
681
|
if (detected) return detected[1];
|
|
692
682
|
throw new VisualAIConfigError(
|
|
693
|
-
"
|
|
683
|
+
"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)."
|
|
694
684
|
);
|
|
695
685
|
}
|
|
696
686
|
function parseBooleanEnv(envName, value) {
|
|
@@ -702,13 +692,27 @@ function parseBooleanEnv(envName, value) {
|
|
|
702
692
|
`Invalid ${envName} value: "${value}". Use "true", "1", "false", or "0".`
|
|
703
693
|
);
|
|
704
694
|
}
|
|
695
|
+
var VALID_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
696
|
+
function parseReasoningEffortEnv(envName, value) {
|
|
697
|
+
if (value === void 0 || value === "") return void 0;
|
|
698
|
+
const lower = value.toLowerCase();
|
|
699
|
+
if (VALID_REASONING_EFFORTS.includes(lower)) return lower;
|
|
700
|
+
throw new VisualAIConfigError(
|
|
701
|
+
`Invalid ${envName} value: "${value}". Use "low", "medium", "high", or "xhigh".`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
var debugDeprecationWarned = false;
|
|
705
705
|
function resolveConfig(config) {
|
|
706
706
|
const provider = resolveProvider(config);
|
|
707
707
|
const model = config.model ?? process.env.VISUAL_AI_MODEL ?? DEFAULT_MODELS[provider];
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
708
|
+
const debug = config.debug ?? parseBooleanEnv("VISUAL_AI_DEBUG", process.env.VISUAL_AI_DEBUG) ?? false;
|
|
709
|
+
const debugPrompt = config.debugPrompt ?? parseBooleanEnv("VISUAL_AI_DEBUG_PROMPT", process.env.VISUAL_AI_DEBUG_PROMPT) ?? false;
|
|
710
|
+
const debugResponse = config.debugResponse ?? parseBooleanEnv("VISUAL_AI_DEBUG_RESPONSE", process.env.VISUAL_AI_DEBUG_RESPONSE) ?? false;
|
|
711
|
+
if (debug && !debugPrompt && !debugResponse && !debugDeprecationWarned) {
|
|
712
|
+
debugDeprecationWarned = true;
|
|
713
|
+
process.stderr.write(
|
|
714
|
+
`[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.
|
|
715
|
+
`
|
|
712
716
|
);
|
|
713
717
|
}
|
|
714
718
|
return {
|
|
@@ -716,8 +720,10 @@ function resolveConfig(config) {
|
|
|
716
720
|
apiKey: config.apiKey,
|
|
717
721
|
model,
|
|
718
722
|
maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
719
|
-
reasoningEffort: config.reasoningEffort,
|
|
720
|
-
debug
|
|
723
|
+
reasoningEffort: config.reasoningEffort ?? parseReasoningEffortEnv("VISUAL_AI_REASONING_EFFORT", process.env.VISUAL_AI_REASONING_EFFORT),
|
|
724
|
+
debug,
|
|
725
|
+
debugPrompt,
|
|
726
|
+
debugResponse,
|
|
721
727
|
trackUsage: config.trackUsage ?? parseBooleanEnv("VISUAL_AI_TRACK_USAGE", process.env.VISUAL_AI_TRACK_USAGE) ?? false
|
|
722
728
|
};
|
|
723
729
|
}
|
|
@@ -749,6 +755,14 @@ var PRICING_TABLE = {
|
|
|
749
755
|
inputPricePerToken: 1.75 / PER_MILLION,
|
|
750
756
|
outputPricePerToken: 14 / PER_MILLION
|
|
751
757
|
},
|
|
758
|
+
[`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_MINI}`]: {
|
|
759
|
+
inputPricePerToken: 0.75 / PER_MILLION,
|
|
760
|
+
outputPricePerToken: 4.5 / PER_MILLION
|
|
761
|
+
},
|
|
762
|
+
[`${Provider.OPENAI}:${Model.OpenAI.GPT_5_4_NANO}`]: {
|
|
763
|
+
inputPricePerToken: 0.2 / PER_MILLION,
|
|
764
|
+
outputPricePerToken: 1.25 / PER_MILLION
|
|
765
|
+
},
|
|
752
766
|
[`${Provider.OPENAI}:${Model.OpenAI.GPT_5_MINI}`]: {
|
|
753
767
|
inputPricePerToken: 0.25 / PER_MILLION,
|
|
754
768
|
outputPricePerToken: 2 / PER_MILLION
|
|
@@ -770,8 +784,9 @@ function calculateCost(provider, model, inputTokens, outputTokens) {
|
|
|
770
784
|
}
|
|
771
785
|
|
|
772
786
|
// src/core/debug.ts
|
|
773
|
-
function debugLog(config, label, data) {
|
|
774
|
-
|
|
787
|
+
function debugLog(config, label, data, kind = "error") {
|
|
788
|
+
const enabled = kind === "prompt" ? config.debugPrompt : kind === "response" ? config.debugResponse : config.debug;
|
|
789
|
+
if (enabled) {
|
|
775
790
|
process.stderr.write(`[visual-ai-assertions] ${label}: ${data}
|
|
776
791
|
`);
|
|
777
792
|
}
|
|
@@ -779,8 +794,9 @@ function debugLog(config, label, data) {
|
|
|
779
794
|
function usageLog(config, method, usage) {
|
|
780
795
|
if (!config.trackUsage) return;
|
|
781
796
|
const costStr = usage.estimatedCost !== void 0 ? `$${usage.estimatedCost.toFixed(6)}` : "unknown";
|
|
797
|
+
const reasoningStr = config.reasoningEffort ? `reasoning: ${config.reasoningEffort}` : `reasoning: ${PROVIDER_DEFAULT_REASONING[config.provider]} (provider default)`;
|
|
782
798
|
process.stderr.write(
|
|
783
|
-
`[visual-ai-assertions] ${method} usage: ${usage.inputTokens} input + ${usage.outputTokens} output tokens (${costStr}) in ${usage.durationSeconds?.toFixed(3) ?? "0.000"}s [${config.model}]
|
|
799
|
+
`[visual-ai-assertions] ${method} usage: ${usage.inputTokens} input + ${usage.outputTokens} output tokens (${costStr}) in ${usage.durationSeconds?.toFixed(3) ?? "0.000"}s [${config.model}, ${reasoningStr}]
|
|
784
800
|
`
|
|
785
801
|
);
|
|
786
802
|
}
|
|
@@ -796,6 +812,28 @@ function processUsage(method, rawUsage, durationSeconds, config) {
|
|
|
796
812
|
usageLog(config, method, usage);
|
|
797
813
|
return usage;
|
|
798
814
|
}
|
|
815
|
+
var MAX_RAW_RESPONSE_PREVIEW = 500;
|
|
816
|
+
function formatError(error) {
|
|
817
|
+
if (error instanceof VisualAIResponseParseError) {
|
|
818
|
+
const truncated = error.rawResponse.length > MAX_RAW_RESPONSE_PREVIEW ? error.rawResponse.slice(0, MAX_RAW_RESPONSE_PREVIEW) + "..." : error.rawResponse;
|
|
819
|
+
return `${error.name} (${error.code}): ${error.message}. Raw (truncated): ${truncated}`;
|
|
820
|
+
}
|
|
821
|
+
if (error instanceof VisualAIError) {
|
|
822
|
+
return `${error.name} (${error.code}): ${error.message}`;
|
|
823
|
+
}
|
|
824
|
+
if (error instanceof Error) {
|
|
825
|
+
return `${error.name}: ${error.message}`;
|
|
826
|
+
}
|
|
827
|
+
return String(error);
|
|
828
|
+
}
|
|
829
|
+
async function withErrorDebug(config, method, fn) {
|
|
830
|
+
try {
|
|
831
|
+
return await fn();
|
|
832
|
+
} catch (error) {
|
|
833
|
+
debugLog(config, `${method} error`, formatError(error), "error");
|
|
834
|
+
throw error;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
799
837
|
async function timedSendMessage(driver, images, prompt) {
|
|
800
838
|
const start = performance.now();
|
|
801
839
|
const response = await driver.sendMessage(images, prompt);
|
|
@@ -1124,16 +1162,18 @@ function visualAI(config = {}) {
|
|
|
1124
1162
|
if (elements.length === 0) {
|
|
1125
1163
|
throw new VisualAIConfigError(`At least one element is required for ${methodName}()`);
|
|
1126
1164
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1165
|
+
return withErrorDebug(resolvedConfig, methodName, async () => {
|
|
1166
|
+
const img = await normalizeImage(image);
|
|
1167
|
+
const prompt = buildElementsVisibilityPrompt(elements, visible, options);
|
|
1168
|
+
debugLog(resolvedConfig, `${methodName} prompt`, prompt, "prompt");
|
|
1169
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1170
|
+
debugLog(resolvedConfig, `${methodName} response`, response.text, "response");
|
|
1171
|
+
const result = parseCheckResponse(response.text);
|
|
1172
|
+
return {
|
|
1173
|
+
...result,
|
|
1174
|
+
usage: processUsage(methodName, response.usage, response.durationSeconds, resolvedConfig)
|
|
1175
|
+
};
|
|
1176
|
+
});
|
|
1137
1177
|
}
|
|
1138
1178
|
return {
|
|
1139
1179
|
async check(image, statements, options) {
|
|
@@ -1141,61 +1181,64 @@ function visualAI(config = {}) {
|
|
|
1141
1181
|
if (stmts.length === 0) {
|
|
1142
1182
|
throw new VisualAIConfigError("At least one statement is required for check()");
|
|
1143
1183
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1184
|
+
return withErrorDebug(resolvedConfig, "check", async () => {
|
|
1185
|
+
const img = await normalizeImage(image);
|
|
1186
|
+
const prompt = buildCheckPrompt(stmts, { instructions: options?.instructions });
|
|
1187
|
+
debugLog(resolvedConfig, "check prompt", prompt, "prompt");
|
|
1188
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1189
|
+
debugLog(resolvedConfig, "check response", response.text, "response");
|
|
1190
|
+
const result = parseCheckResponse(response.text);
|
|
1191
|
+
return {
|
|
1192
|
+
...result,
|
|
1193
|
+
usage: processUsage("check", response.usage, response.durationSeconds, resolvedConfig)
|
|
1194
|
+
};
|
|
1195
|
+
});
|
|
1154
1196
|
},
|
|
1155
1197
|
async ask(image, userPrompt, options) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1198
|
+
return withErrorDebug(resolvedConfig, "ask", async () => {
|
|
1199
|
+
const img = await normalizeImage(image);
|
|
1200
|
+
const prompt = buildAskPrompt(userPrompt, { instructions: options?.instructions });
|
|
1201
|
+
debugLog(resolvedConfig, "ask prompt", prompt, "prompt");
|
|
1202
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1203
|
+
debugLog(resolvedConfig, "ask response", response.text, "response");
|
|
1204
|
+
const result = parseAskResponse(response.text);
|
|
1205
|
+
return {
|
|
1206
|
+
...result,
|
|
1207
|
+
usage: processUsage("ask", response.usage, response.durationSeconds, resolvedConfig)
|
|
1208
|
+
};
|
|
1209
|
+
});
|
|
1166
1210
|
},
|
|
1167
1211
|
async compare(imageA, imageB, options) {
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
if (!resolvedConfig.debug) {
|
|
1212
|
+
return withErrorDebug(resolvedConfig, "compare", async () => {
|
|
1213
|
+
const [imgA, imgB] = await Promise.all([normalizeImage(imageA), normalizeImage(imageB)]);
|
|
1214
|
+
const prompt = buildComparePrompt({
|
|
1215
|
+
userPrompt: options?.prompt,
|
|
1216
|
+
instructions: options?.instructions
|
|
1217
|
+
});
|
|
1218
|
+
debugLog(resolvedConfig, "compare prompt", prompt, "prompt");
|
|
1219
|
+
const response = await timedSendMessage(driver, [imgA, imgB], prompt);
|
|
1220
|
+
debugLog(resolvedConfig, "compare response", response.text, "response");
|
|
1221
|
+
const supportsAnnotatedDiff = resolvedConfig.provider === "google" && resolvedConfig.model === Model.Google.GEMINI_3_FLASH_PREVIEW;
|
|
1222
|
+
const effectiveDiffImage = options?.diffImage ?? (supportsAnnotatedDiff ? true : false);
|
|
1223
|
+
let diffImage;
|
|
1224
|
+
if (effectiveDiffImage) {
|
|
1225
|
+
try {
|
|
1226
|
+
diffImage = await generateAiDiff(imgA, imgB, resolvedConfig.model, driver);
|
|
1227
|
+
} catch (err) {
|
|
1228
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1186
1229
|
process.stderr.write(
|
|
1187
1230
|
`[visual-ai-assertions] warning: diff generation failed: ${msg}
|
|
1188
1231
|
`
|
|
1189
1232
|
);
|
|
1190
1233
|
}
|
|
1191
1234
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
};
|
|
1235
|
+
const result = parseCompareResponse(response.text);
|
|
1236
|
+
return {
|
|
1237
|
+
...result,
|
|
1238
|
+
...diffImage ? { diffImage } : {},
|
|
1239
|
+
usage: processUsage("compare", response.usage, response.durationSeconds, resolvedConfig)
|
|
1240
|
+
};
|
|
1241
|
+
});
|
|
1199
1242
|
},
|
|
1200
1243
|
elementsVisible(image, elements, options) {
|
|
1201
1244
|
return checkElementsVisibility(image, elements, true, options);
|
|
@@ -1204,57 +1247,65 @@ function visualAI(config = {}) {
|
|
|
1204
1247
|
return checkElementsVisibility(image, elements, false, options);
|
|
1205
1248
|
},
|
|
1206
1249
|
async accessibility(image, options) {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1250
|
+
return withErrorDebug(resolvedConfig, "accessibility", async () => {
|
|
1251
|
+
const img = await normalizeImage(image);
|
|
1252
|
+
const prompt = buildAccessibilityPrompt(options);
|
|
1253
|
+
debugLog(resolvedConfig, "accessibility prompt", prompt, "prompt");
|
|
1254
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1255
|
+
debugLog(resolvedConfig, "accessibility response", response.text, "response");
|
|
1256
|
+
const result = parseCheckResponse(response.text);
|
|
1257
|
+
return {
|
|
1258
|
+
...result,
|
|
1259
|
+
usage: processUsage(
|
|
1260
|
+
"accessibility",
|
|
1261
|
+
response.usage,
|
|
1262
|
+
response.durationSeconds,
|
|
1263
|
+
resolvedConfig
|
|
1264
|
+
)
|
|
1265
|
+
};
|
|
1266
|
+
});
|
|
1222
1267
|
},
|
|
1223
1268
|
async layout(image, options) {
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1269
|
+
return withErrorDebug(resolvedConfig, "layout", async () => {
|
|
1270
|
+
const img = await normalizeImage(image);
|
|
1271
|
+
const prompt = buildLayoutPrompt(options);
|
|
1272
|
+
debugLog(resolvedConfig, "layout prompt", prompt, "prompt");
|
|
1273
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1274
|
+
debugLog(resolvedConfig, "layout response", response.text, "response");
|
|
1275
|
+
const result = parseCheckResponse(response.text);
|
|
1276
|
+
return {
|
|
1277
|
+
...result,
|
|
1278
|
+
usage: processUsage("layout", response.usage, response.durationSeconds, resolvedConfig)
|
|
1279
|
+
};
|
|
1280
|
+
});
|
|
1234
1281
|
},
|
|
1235
1282
|
async pageLoad(image, options) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1283
|
+
return withErrorDebug(resolvedConfig, "pageLoad", async () => {
|
|
1284
|
+
const img = await normalizeImage(image);
|
|
1285
|
+
const prompt = buildPageLoadPrompt(options);
|
|
1286
|
+
debugLog(resolvedConfig, "pageLoad prompt", prompt, "prompt");
|
|
1287
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1288
|
+
debugLog(resolvedConfig, "pageLoad response", response.text, "response");
|
|
1289
|
+
const result = parseCheckResponse(response.text);
|
|
1290
|
+
return {
|
|
1291
|
+
...result,
|
|
1292
|
+
usage: processUsage("pageLoad", response.usage, response.durationSeconds, resolvedConfig)
|
|
1293
|
+
};
|
|
1294
|
+
});
|
|
1246
1295
|
},
|
|
1247
1296
|
async content(image, options) {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1297
|
+
return withErrorDebug(resolvedConfig, "content", async () => {
|
|
1298
|
+
const img = await normalizeImage(image);
|
|
1299
|
+
const prompt = buildContentPrompt(options);
|
|
1300
|
+
debugLog(resolvedConfig, "content prompt", prompt, "prompt");
|
|
1301
|
+
const response = await timedSendMessage(driver, [img], prompt);
|
|
1302
|
+
debugLog(resolvedConfig, "content response", response.text, "response");
|
|
1303
|
+
const result = parseCheckResponse(response.text);
|
|
1304
|
+
return {
|
|
1305
|
+
...result,
|
|
1306
|
+
usage: processUsage("content", response.usage, response.durationSeconds, resolvedConfig)
|
|
1307
|
+
};
|
|
1308
|
+
});
|
|
1258
1309
|
}
|
|
1259
1310
|
};
|
|
1260
1311
|
}
|
|
@@ -1330,7 +1381,6 @@ export {
|
|
|
1330
1381
|
Provider,
|
|
1331
1382
|
StatementResultSchema,
|
|
1332
1383
|
UsageInfoSchema,
|
|
1333
|
-
VALID_PROVIDERS,
|
|
1334
1384
|
VisualAIAssertionError,
|
|
1335
1385
|
VisualAIAuthError,
|
|
1336
1386
|
VisualAIConfigError,
|