visual-ai-assertions 0.13.1 → 0.14.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 +25 -0
- package/dist/index.cjs +50 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -7
- package/dist/index.d.ts +41 -7
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -493,6 +493,36 @@ type ImageInput = Buffer | Uint8Array | string;
|
|
|
493
493
|
* an image or a video.
|
|
494
494
|
*/
|
|
495
495
|
type MediaInput = ImageInput;
|
|
496
|
+
/**
|
|
497
|
+
* A single pre-sampled frame with an explicit timestamp. Use this shape when
|
|
498
|
+
* you want to control the timestamp the model sees; otherwise pass a bare
|
|
499
|
+
* `ImageInput` and the timestamp is derived from `fps` and the frame's position.
|
|
500
|
+
*/
|
|
501
|
+
interface TimestampedFrameInput {
|
|
502
|
+
/** Image source for the frame (buffer, data URL, base64, file path, or URL). */
|
|
503
|
+
image: ImageInput;
|
|
504
|
+
/** Timestamp in seconds, from the start of the sequence. Derived from `fps` when omitted. */
|
|
505
|
+
timestampSeconds?: number;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Pre-sampled frames supplied directly instead of a video file. Useful when you
|
|
509
|
+
* already have an array of screenshots and cannot (or would rather not) pass a
|
|
510
|
+
* video — this path never loads ffmpeg. Passed to `check()` / `ask()` in place
|
|
511
|
+
* of a `MediaInput`; downstream handling is identical to a sampled video.
|
|
512
|
+
*/
|
|
513
|
+
interface FramesInput {
|
|
514
|
+
/**
|
|
515
|
+
* Ordered frames, earliest first. Each entry is an image source or a
|
|
516
|
+
* `{ image, timestampSeconds }` pair. Must be non-empty and is capped at the
|
|
517
|
+
* same per-request frame limit as video sampling (60).
|
|
518
|
+
*/
|
|
519
|
+
frames: ReadonlyArray<ImageInput | TimestampedFrameInput>;
|
|
520
|
+
/**
|
|
521
|
+
* Frame rate used to derive timestamps for frames that don't carry their own
|
|
522
|
+
* `timestampSeconds` (frame `i` maps to `i / fps` seconds). Default `1`.
|
|
523
|
+
*/
|
|
524
|
+
fps?: number;
|
|
525
|
+
}
|
|
496
526
|
/** Supported image MIME types accepted by all providers. */
|
|
497
527
|
type SupportedMimeType = "image/jpeg" | "image/png" | "image/webp" | "image/gif";
|
|
498
528
|
/** Supported video MIME types the client can accept and sample frames from. */
|
|
@@ -633,9 +663,11 @@ interface VisualAIClient {
|
|
|
633
663
|
* frames automatically; statements pass if they are true at any sampled
|
|
634
664
|
* frame, and each statement result includes the timestamp where it
|
|
635
665
|
* matched. The `frames` metadata on the result reports which timestamps
|
|
636
|
-
* the model saw.
|
|
666
|
+
* the model saw. Pass a `FramesInput` (`{ frames, fps? }`) to supply
|
|
667
|
+
* pre-sampled frames directly — handled identically to a video timeline but
|
|
668
|
+
* without loading ffmpeg.
|
|
637
669
|
*
|
|
638
|
-
* @param input Image or video source as a buffer, URL, file path, or base64 string.
|
|
670
|
+
* @param input Image or video source as a buffer, URL, file path, or base64 string, or a `FramesInput` of pre-sampled frames.
|
|
639
671
|
* @param statements One or more statements to validate against the input.
|
|
640
672
|
* @param options Optional additional instructions and video sampling overrides.
|
|
641
673
|
* @returns A structured result describing pass/fail, issues, and statement reasoning.
|
|
@@ -658,15 +690,17 @@ interface VisualAIClient {
|
|
|
658
690
|
* console.log(result.statements[0].timestampSeconds); // e.g. 3.5
|
|
659
691
|
* ```
|
|
660
692
|
*/
|
|
661
|
-
check(input: MediaInput, statements: string | string[], options?: CheckOptions): Promise<CheckResult>;
|
|
693
|
+
check(input: MediaInput | FramesInput, statements: string | string[], options?: CheckOptions): Promise<CheckResult>;
|
|
662
694
|
/**
|
|
663
695
|
* Asks an open-ended question about an image or video and returns a structured summary.
|
|
664
696
|
*
|
|
665
697
|
* Video inputs are sampled into frames and analyzed as a chronological
|
|
666
698
|
* timeline. The result's `frameReferences` array surfaces which frames the
|
|
667
|
-
* model relied on for its answer.
|
|
699
|
+
* model relied on for its answer. Pass a `FramesInput` (`{ frames, fps? }`)
|
|
700
|
+
* to supply pre-sampled frames directly — handled identically to a video
|
|
701
|
+
* timeline but without loading ffmpeg.
|
|
668
702
|
*
|
|
669
|
-
* @param input Image or video source as a buffer, URL, file path, or base64 string.
|
|
703
|
+
* @param input Image or video source as a buffer, URL, file path, or base64 string, or a `FramesInput` of pre-sampled frames.
|
|
670
704
|
* @param prompt Prompt describing what to inspect in the input.
|
|
671
705
|
* @param options Optional additional instructions and video sampling overrides.
|
|
672
706
|
* @returns A summary with any detected issues.
|
|
@@ -678,7 +712,7 @@ interface VisualAIClient {
|
|
|
678
712
|
* const result = await client.ask(screenshot, "What looks visually broken on this page?");
|
|
679
713
|
* ```
|
|
680
714
|
*/
|
|
681
|
-
ask(input: MediaInput, prompt: string, options?: AskOptions): Promise<AskResult>;
|
|
715
|
+
ask(input: MediaInput | FramesInput, prompt: string, options?: AskOptions): Promise<AskResult>;
|
|
682
716
|
/**
|
|
683
717
|
* Compares two images and reports meaningful visual differences.
|
|
684
718
|
*
|
|
@@ -1055,4 +1089,4 @@ declare function assertVisualResult(result: CheckResult, label?: string): void;
|
|
|
1055
1089
|
*/
|
|
1056
1090
|
declare function assertVisualCompareResult(result: CompareResult, label?: string): void;
|
|
1057
1091
|
|
|
1058
|
-
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 Frame, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, type MediaInput, Model, type PageLoadOptions, Provider, type ProviderName, ReasoningEffort, type ReasoningEffortLevel, type StatementResult, StatementResultSchema, type SupportedMimeType, type SupportedVideoMimeType, type UsageInfo, UsageInfoSchema, type VideoFramesMetadata, type VideoSamplingOptions, VisualAIAssertionError, VisualAIAuthError, type VisualAIClient, type VisualAIConfig, VisualAIConfigError, VisualAIError, type VisualAIErrorCode, VisualAIImageError, type VisualAIKnownError, VisualAIProviderError, VisualAIRateLimitError, VisualAIResponseParseError, VisualAITruncationError, VisualAIVideoError, assertVisualCompareResult, assertVisualResult, formatCheckResult, formatCompareResult, isVisualAIKnownError, visualAI };
|
|
1092
|
+
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 Frame, type FramesInput, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, type MediaInput, Model, type PageLoadOptions, Provider, type ProviderName, ReasoningEffort, type ReasoningEffortLevel, type StatementResult, StatementResultSchema, type SupportedMimeType, type SupportedVideoMimeType, type TimestampedFrameInput, type UsageInfo, UsageInfoSchema, type VideoFramesMetadata, type VideoSamplingOptions, VisualAIAssertionError, VisualAIAuthError, type VisualAIClient, type VisualAIConfig, VisualAIConfigError, VisualAIError, type VisualAIErrorCode, VisualAIImageError, type VisualAIKnownError, VisualAIProviderError, VisualAIRateLimitError, VisualAIResponseParseError, VisualAITruncationError, VisualAIVideoError, assertVisualCompareResult, assertVisualResult, formatCheckResult, formatCompareResult, isVisualAIKnownError, visualAI };
|
package/dist/index.d.ts
CHANGED
|
@@ -493,6 +493,36 @@ type ImageInput = Buffer | Uint8Array | string;
|
|
|
493
493
|
* an image or a video.
|
|
494
494
|
*/
|
|
495
495
|
type MediaInput = ImageInput;
|
|
496
|
+
/**
|
|
497
|
+
* A single pre-sampled frame with an explicit timestamp. Use this shape when
|
|
498
|
+
* you want to control the timestamp the model sees; otherwise pass a bare
|
|
499
|
+
* `ImageInput` and the timestamp is derived from `fps` and the frame's position.
|
|
500
|
+
*/
|
|
501
|
+
interface TimestampedFrameInput {
|
|
502
|
+
/** Image source for the frame (buffer, data URL, base64, file path, or URL). */
|
|
503
|
+
image: ImageInput;
|
|
504
|
+
/** Timestamp in seconds, from the start of the sequence. Derived from `fps` when omitted. */
|
|
505
|
+
timestampSeconds?: number;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Pre-sampled frames supplied directly instead of a video file. Useful when you
|
|
509
|
+
* already have an array of screenshots and cannot (or would rather not) pass a
|
|
510
|
+
* video — this path never loads ffmpeg. Passed to `check()` / `ask()` in place
|
|
511
|
+
* of a `MediaInput`; downstream handling is identical to a sampled video.
|
|
512
|
+
*/
|
|
513
|
+
interface FramesInput {
|
|
514
|
+
/**
|
|
515
|
+
* Ordered frames, earliest first. Each entry is an image source or a
|
|
516
|
+
* `{ image, timestampSeconds }` pair. Must be non-empty and is capped at the
|
|
517
|
+
* same per-request frame limit as video sampling (60).
|
|
518
|
+
*/
|
|
519
|
+
frames: ReadonlyArray<ImageInput | TimestampedFrameInput>;
|
|
520
|
+
/**
|
|
521
|
+
* Frame rate used to derive timestamps for frames that don't carry their own
|
|
522
|
+
* `timestampSeconds` (frame `i` maps to `i / fps` seconds). Default `1`.
|
|
523
|
+
*/
|
|
524
|
+
fps?: number;
|
|
525
|
+
}
|
|
496
526
|
/** Supported image MIME types accepted by all providers. */
|
|
497
527
|
type SupportedMimeType = "image/jpeg" | "image/png" | "image/webp" | "image/gif";
|
|
498
528
|
/** Supported video MIME types the client can accept and sample frames from. */
|
|
@@ -633,9 +663,11 @@ interface VisualAIClient {
|
|
|
633
663
|
* frames automatically; statements pass if they are true at any sampled
|
|
634
664
|
* frame, and each statement result includes the timestamp where it
|
|
635
665
|
* matched. The `frames` metadata on the result reports which timestamps
|
|
636
|
-
* the model saw.
|
|
666
|
+
* the model saw. Pass a `FramesInput` (`{ frames, fps? }`) to supply
|
|
667
|
+
* pre-sampled frames directly — handled identically to a video timeline but
|
|
668
|
+
* without loading ffmpeg.
|
|
637
669
|
*
|
|
638
|
-
* @param input Image or video source as a buffer, URL, file path, or base64 string.
|
|
670
|
+
* @param input Image or video source as a buffer, URL, file path, or base64 string, or a `FramesInput` of pre-sampled frames.
|
|
639
671
|
* @param statements One or more statements to validate against the input.
|
|
640
672
|
* @param options Optional additional instructions and video sampling overrides.
|
|
641
673
|
* @returns A structured result describing pass/fail, issues, and statement reasoning.
|
|
@@ -658,15 +690,17 @@ interface VisualAIClient {
|
|
|
658
690
|
* console.log(result.statements[0].timestampSeconds); // e.g. 3.5
|
|
659
691
|
* ```
|
|
660
692
|
*/
|
|
661
|
-
check(input: MediaInput, statements: string | string[], options?: CheckOptions): Promise<CheckResult>;
|
|
693
|
+
check(input: MediaInput | FramesInput, statements: string | string[], options?: CheckOptions): Promise<CheckResult>;
|
|
662
694
|
/**
|
|
663
695
|
* Asks an open-ended question about an image or video and returns a structured summary.
|
|
664
696
|
*
|
|
665
697
|
* Video inputs are sampled into frames and analyzed as a chronological
|
|
666
698
|
* timeline. The result's `frameReferences` array surfaces which frames the
|
|
667
|
-
* model relied on for its answer.
|
|
699
|
+
* model relied on for its answer. Pass a `FramesInput` (`{ frames, fps? }`)
|
|
700
|
+
* to supply pre-sampled frames directly — handled identically to a video
|
|
701
|
+
* timeline but without loading ffmpeg.
|
|
668
702
|
*
|
|
669
|
-
* @param input Image or video source as a buffer, URL, file path, or base64 string.
|
|
703
|
+
* @param input Image or video source as a buffer, URL, file path, or base64 string, or a `FramesInput` of pre-sampled frames.
|
|
670
704
|
* @param prompt Prompt describing what to inspect in the input.
|
|
671
705
|
* @param options Optional additional instructions and video sampling overrides.
|
|
672
706
|
* @returns A summary with any detected issues.
|
|
@@ -678,7 +712,7 @@ interface VisualAIClient {
|
|
|
678
712
|
* const result = await client.ask(screenshot, "What looks visually broken on this page?");
|
|
679
713
|
* ```
|
|
680
714
|
*/
|
|
681
|
-
ask(input: MediaInput, prompt: string, options?: AskOptions): Promise<AskResult>;
|
|
715
|
+
ask(input: MediaInput | FramesInput, prompt: string, options?: AskOptions): Promise<AskResult>;
|
|
682
716
|
/**
|
|
683
717
|
* Compares two images and reports meaningful visual differences.
|
|
684
718
|
*
|
|
@@ -1055,4 +1089,4 @@ declare function assertVisualResult(result: CheckResult, label?: string): void;
|
|
|
1055
1089
|
*/
|
|
1056
1090
|
declare function assertVisualCompareResult(result: CompareResult, label?: string): void;
|
|
1057
1091
|
|
|
1058
|
-
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 Frame, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, type MediaInput, Model, type PageLoadOptions, Provider, type ProviderName, ReasoningEffort, type ReasoningEffortLevel, type StatementResult, StatementResultSchema, type SupportedMimeType, type SupportedVideoMimeType, type UsageInfo, UsageInfoSchema, type VideoFramesMetadata, type VideoSamplingOptions, VisualAIAssertionError, VisualAIAuthError, type VisualAIClient, type VisualAIConfig, VisualAIConfigError, VisualAIError, type VisualAIErrorCode, VisualAIImageError, type VisualAIKnownError, VisualAIProviderError, VisualAIRateLimitError, VisualAIResponseParseError, VisualAITruncationError, VisualAIVideoError, assertVisualCompareResult, assertVisualResult, formatCheckResult, formatCompareResult, isVisualAIKnownError, visualAI };
|
|
1092
|
+
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 Frame, type FramesInput, type ImageInput, type Issue, type IssueCategory, IssueCategorySchema, type IssuePriority, IssuePrioritySchema, IssueSchema, type KnownModelName, Layout, type LayoutCheckName, type LayoutOptions, type MediaInput, Model, type PageLoadOptions, Provider, type ProviderName, ReasoningEffort, type ReasoningEffortLevel, type StatementResult, StatementResultSchema, type SupportedMimeType, type SupportedVideoMimeType, type TimestampedFrameInput, type UsageInfo, UsageInfoSchema, type VideoFramesMetadata, type VideoSamplingOptions, VisualAIAssertionError, VisualAIAuthError, type VisualAIClient, type VisualAIConfig, VisualAIConfigError, VisualAIError, type VisualAIErrorCode, VisualAIImageError, type VisualAIKnownError, VisualAIProviderError, VisualAIRateLimitError, VisualAIResponseParseError, VisualAITruncationError, VisualAIVideoError, assertVisualCompareResult, assertVisualResult, formatCheckResult, formatCompareResult, isVisualAIKnownError, visualAI };
|
package/dist/index.js
CHANGED
|
@@ -1705,7 +1705,57 @@ function isVideoInput(input) {
|
|
|
1705
1705
|
}
|
|
1706
1706
|
return false;
|
|
1707
1707
|
}
|
|
1708
|
+
function isFramesInput(input) {
|
|
1709
|
+
return typeof input === "object" && input !== null && !Buffer.isBuffer(input) && !(input instanceof Uint8Array) && Array.isArray(input.frames);
|
|
1710
|
+
}
|
|
1711
|
+
function isTimestampedFrameInput(frame) {
|
|
1712
|
+
return typeof frame === "object" && !Buffer.isBuffer(frame) && !(frame instanceof Uint8Array) && "image" in frame;
|
|
1713
|
+
}
|
|
1714
|
+
async function normalizeFrames(input) {
|
|
1715
|
+
const rawFrames = input.frames;
|
|
1716
|
+
const fps = input.fps ?? DEFAULT_FPS;
|
|
1717
|
+
if (rawFrames.length === 0) {
|
|
1718
|
+
throw new VisualAIVideoError("frames must be a non-empty array of image inputs");
|
|
1719
|
+
}
|
|
1720
|
+
if (rawFrames.length > MAX_FRAMES_HARD_CAP) {
|
|
1721
|
+
throw new VisualAIVideoError(
|
|
1722
|
+
`frames length ${rawFrames.length} exceeds the hard cap of ${MAX_FRAMES_HARD_CAP}. Pass fewer frames or open an issue if you need a larger limit.`
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
if (!Number.isFinite(fps) || fps <= 0) {
|
|
1726
|
+
throw new VisualAIVideoError(`Invalid fps: ${fps}. Must be a finite number > 0.`);
|
|
1727
|
+
}
|
|
1728
|
+
const frames = await Promise.all(
|
|
1729
|
+
rawFrames.map(async (raw, index) => {
|
|
1730
|
+
const timestamped = isTimestampedFrameInput(raw);
|
|
1731
|
+
const imageInput = timestamped ? raw.image : raw;
|
|
1732
|
+
const explicit = timestamped ? raw.timestampSeconds : void 0;
|
|
1733
|
+
const timestampSeconds = explicit ?? index / fps;
|
|
1734
|
+
if (!Number.isFinite(timestampSeconds) || timestampSeconds < 0) {
|
|
1735
|
+
throw new VisualAIVideoError(
|
|
1736
|
+
`Invalid timestampSeconds for frame ${index}: ${String(timestampSeconds)}. Must be a finite number >= 0.`
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
const image = await normalizeImage(imageInput);
|
|
1740
|
+
return {
|
|
1741
|
+
data: image.data,
|
|
1742
|
+
mimeType: image.mimeType,
|
|
1743
|
+
get base64() {
|
|
1744
|
+
return image.base64;
|
|
1745
|
+
},
|
|
1746
|
+
timestampSeconds,
|
|
1747
|
+
index
|
|
1748
|
+
};
|
|
1749
|
+
})
|
|
1750
|
+
);
|
|
1751
|
+
const durationSeconds = frames.reduce((max, f) => Math.max(max, f.timestampSeconds), 0);
|
|
1752
|
+
await saveDebugFrames(frames);
|
|
1753
|
+
return { kind: "video", frames, durationSeconds };
|
|
1754
|
+
}
|
|
1708
1755
|
async function normalizeMedia(input, videoOptions) {
|
|
1756
|
+
if (isFramesInput(input)) {
|
|
1757
|
+
return normalizeFrames(input);
|
|
1758
|
+
}
|
|
1709
1759
|
if (isVideoInput(input)) {
|
|
1710
1760
|
const { path, cleanup } = await resolveVideoToPath(input);
|
|
1711
1761
|
try {
|