@empiricalrun/test-gen 0.76.0 → 0.77.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/CHANGELOG.md +33 -0
- package/dist/agent/base/index.d.ts +25 -21
- package/dist/agent/base/index.d.ts.map +1 -1
- package/dist/agent/base/index.js +48 -37
- package/dist/agent/browsing/run.d.ts +1 -2
- package/dist/agent/browsing/run.d.ts.map +1 -1
- package/dist/agent/browsing/run.js +3 -9
- package/dist/agent/browsing/utils.d.ts +2 -9
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +5 -109
- package/dist/agent/chat/agent-loop.d.ts +5 -5
- package/dist/agent/chat/agent-loop.d.ts.map +1 -1
- package/dist/agent/chat/agent-loop.js +3 -8
- package/dist/agent/chat/exports.d.ts +5 -4
- package/dist/agent/chat/exports.d.ts.map +1 -1
- package/dist/agent/chat/exports.js +4 -7
- package/dist/agent/chat/index.d.ts +2 -2
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +23 -35
- package/dist/agent/chat/models.d.ts +0 -2
- package/dist/agent/chat/models.d.ts.map +1 -1
- package/dist/agent/chat/models.js +12 -26
- package/dist/agent/chat/prompt/pw-utils-docs.d.ts +1 -1
- package/dist/agent/chat/prompt/pw-utils-docs.d.ts.map +1 -1
- package/dist/agent/chat/prompt/pw-utils-docs.js +52 -0
- package/dist/agent/chat/prompt/repo.d.ts.map +1 -1
- package/dist/agent/chat/prompt/repo.js +11 -22
- package/dist/agent/chat/prompt/test-case-def.d.ts +2 -0
- package/dist/agent/chat/prompt/test-case-def.d.ts.map +1 -0
- package/dist/agent/chat/prompt/test-case-def.js +44 -0
- package/dist/agent/chat/state.d.ts +7 -6
- package/dist/agent/chat/state.d.ts.map +1 -1
- package/dist/agent/chat/state.js +15 -45
- package/dist/agent/chat/utils.d.ts +2 -2
- package/dist/agent/chat/utils.d.ts.map +1 -1
- package/dist/agent/chat/utils.js +14 -7
- package/dist/agent/cli.d.ts.map +1 -1
- package/dist/agent/cli.js +62 -58
- package/dist/agent/code-review/executor/index.d.ts +5 -0
- package/dist/agent/code-review/executor/index.d.ts.map +1 -0
- package/dist/agent/code-review/executor/index.js +13 -0
- package/dist/agent/code-review/index.d.ts +8 -3
- package/dist/agent/code-review/index.d.ts.map +1 -1
- package/dist/agent/code-review/index.js +115 -21
- package/dist/agent/code-review/parser.d.ts +5 -0
- package/dist/agent/code-review/parser.d.ts.map +1 -0
- package/dist/agent/code-review/parser.js +70 -0
- package/dist/agent/code-review/types.d.ts +36 -0
- package/dist/agent/code-review/types.d.ts.map +1 -0
- package/dist/agent/code-review/types.js +13 -0
- package/dist/agent/cua/index.d.ts.map +1 -1
- package/dist/agent/cua/index.js +18 -2
- package/dist/agent/cua/model.d.ts.map +1 -1
- package/dist/agent/cua/model.js +4 -1
- package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -1
- package/dist/agent/triage/index.d.ts +2 -2
- package/dist/agent/triage/index.d.ts.map +1 -1
- package/dist/agent/triage/index.js +8 -7
- package/dist/agent/video-analysis/executor/index.d.ts +5 -0
- package/dist/agent/video-analysis/executor/index.d.ts.map +1 -0
- package/dist/agent/video-analysis/executor/index.js +10 -0
- package/dist/agent/video-analysis/index.d.ts +2 -2
- package/dist/agent/video-analysis/index.d.ts.map +1 -1
- package/dist/agent/video-analysis/index.js +38 -13
- package/dist/artifacts/index.d.ts +1 -1
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/index.js +3 -1
- package/dist/artifacts/utils.d.ts.map +1 -1
- package/dist/bin/index.js +66 -21
- package/dist/constants/index.d.ts +14 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +33 -1
- package/dist/file/server.d.ts +1 -3
- package/dist/file/server.d.ts.map +1 -1
- package/dist/file/server.js +0 -13
- package/dist/file-info/adapters/file-system/index.d.ts.map +1 -1
- package/dist/file-info/adapters/file-system/reader.d.ts.map +1 -1
- package/dist/file-info/adapters/file-system/reader.js +8 -1
- package/dist/file-info/adapters/github/index.d.ts.map +1 -1
- package/dist/file-info/adapters/github/reader.d.ts +1 -1
- package/dist/file-info/adapters/github/reader.d.ts.map +1 -1
- package/dist/file-info/adapters/github/reader.js +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/tools/analyse-video/index.d.ts +5 -0
- package/dist/tools/analyse-video/index.d.ts.map +1 -0
- package/dist/tools/analyse-video/index.js +50 -0
- package/dist/tools/create-pull-request/index.js +4 -6
- package/dist/tools/create-pull-request/utils.d.ts +1 -1
- package/dist/tools/definitions/{fetch-video-analysis.d.ts → analyse-video.d.ts} +13 -8
- package/dist/tools/definitions/analyse-video.d.ts.map +1 -0
- package/dist/tools/definitions/analyse-video.js +60 -0
- package/dist/tools/definitions/review-pull-request.d.ts +3 -0
- package/dist/tools/definitions/review-pull-request.d.ts.map +1 -0
- package/dist/tools/definitions/review-pull-request.js +16 -0
- package/dist/tools/definitions/str_replace_editor.d.ts +1 -0
- package/dist/tools/definitions/str_replace_editor.d.ts.map +1 -1
- package/dist/tools/definitions/str_replace_editor.js +4 -1
- package/dist/tools/definitions/test-gen-browser.d.ts +0 -3
- package/dist/tools/definitions/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/definitions/test-gen-browser.js +33 -8
- package/dist/tools/delete-file/index.d.ts.map +1 -1
- package/dist/tools/delete-file/index.js +1 -19
- package/dist/tools/executor/base.d.ts +32 -0
- package/dist/tools/executor/base.d.ts.map +1 -0
- package/dist/tools/executor/base.js +114 -0
- package/dist/tools/executor/index.d.ts +3 -22
- package/dist/tools/executor/index.d.ts.map +1 -1
- package/dist/tools/executor/index.js +7 -100
- package/dist/tools/executor/utils/checkpoint.d.ts +1 -1
- package/dist/tools/executor/utils/checkpoint.d.ts.map +1 -1
- package/dist/tools/executor/utils/checkpoint.js +6 -2
- package/dist/tools/executor/utils/git.d.ts +2 -2
- package/dist/tools/executor/utils/git.d.ts.map +1 -1
- package/dist/tools/executor/utils/git.js +7 -3
- package/dist/tools/executor/utils/index.d.ts.map +1 -1
- package/dist/tools/executor/utils/index.js +1 -1
- package/dist/tools/fetch-session-diff/index.js +2 -2
- package/dist/tools/file-operations/create.d.ts.map +1 -1
- package/dist/tools/file-operations/create.js +1 -4
- package/dist/tools/file-operations/index.d.ts +2 -1
- package/dist/tools/file-operations/index.d.ts.map +1 -1
- package/dist/tools/file-operations/index.js +4 -1
- package/dist/tools/file-operations/insert.d.ts +1 -2
- package/dist/tools/file-operations/insert.d.ts.map +1 -1
- package/dist/tools/file-operations/insert.js +1 -4
- package/dist/tools/file-operations/replace.d.ts.map +1 -1
- package/dist/tools/file-operations/replace.js +1 -4
- package/dist/tools/grep/index.d.ts.map +1 -1
- package/dist/tools/grep/index.js +18 -11
- package/dist/tools/index.d.ts +5 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -16
- package/dist/tools/merge-conflicts/index.d.ts.map +1 -1
- package/dist/tools/merge-conflicts/index.js +1 -1
- package/dist/tools/rename-file/index.js +1 -1
- package/dist/tools/review-pull-request/index.d.ts.map +1 -1
- package/dist/tools/review-pull-request/index.js +45 -59
- package/dist/tools/run-test.d.ts.map +1 -1
- package/dist/tools/run-test.js +25 -3
- package/dist/tools/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.js +51 -47
- package/dist/utils/artifact-paths.d.ts +20 -0
- package/dist/utils/artifact-paths.d.ts.map +1 -0
- package/dist/utils/artifact-paths.js +16 -0
- package/dist/utils/dedup-image-fs.d.ts +2 -16
- package/dist/utils/dedup-image-fs.d.ts.map +1 -1
- package/dist/utils/dedup-image-fs.js +12 -16
- package/dist/utils/dedup-image.d.ts +1 -14
- package/dist/utils/dedup-image.d.ts.map +1 -1
- package/dist/utils/dedup-image.js +7 -62
- package/dist/utils/{local-ffmpeg-client.d.ts → ffmpeg/index.d.ts} +6 -7
- package/dist/utils/ffmpeg/index.d.ts.map +1 -0
- package/dist/utils/{local-ffmpeg-client.js → ffmpeg/index.js} +169 -53
- package/dist/utils/find-threshold.d.ts +8 -0
- package/dist/utils/find-threshold.d.ts.map +1 -0
- package/dist/utils/find-threshold.js +55 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +24 -0
- package/dist/utils/model.d.ts +1 -1
- package/dist/utils/model.d.ts.map +1 -1
- package/dist/utils/model.js +7 -5
- package/dist/utils/repo-tree.d.ts +0 -1
- package/dist/utils/repo-tree.d.ts.map +1 -1
- package/dist/utils/repo-tree.js +2 -14
- package/dist/utils/slug.js +1 -1
- package/dist/video-core/agent-orchestrator.d.ts +14 -0
- package/dist/video-core/agent-orchestrator.d.ts.map +1 -0
- package/dist/video-core/agent-orchestrator.js +78 -0
- package/dist/video-core/analysis-server.d.ts +24 -0
- package/dist/video-core/analysis-server.d.ts.map +1 -0
- package/dist/video-core/analysis-server.js +398 -0
- package/dist/video-core/analysis-viewer.html +1374 -0
- package/dist/video-core/index.d.ts +44 -0
- package/dist/video-core/index.d.ts.map +1 -0
- package/dist/video-core/index.js +204 -0
- package/dist/video-core/model-limits.d.ts +4 -0
- package/dist/video-core/model-limits.d.ts.map +1 -0
- package/dist/video-core/model-limits.js +67 -0
- package/dist/video-core/storage-manager.d.ts +5 -0
- package/dist/video-core/storage-manager.d.ts.map +1 -0
- package/dist/video-core/storage-manager.js +55 -0
- package/dist/video-core/types.d.ts +13 -0
- package/dist/video-core/types.d.ts.map +1 -0
- package/dist/video-core/types.js +2 -0
- package/dist/video-core/utils.d.ts +25 -0
- package/dist/video-core/utils.d.ts.map +1 -0
- package/dist/video-core/utils.js +211 -0
- package/dist/video-core/xml-parser.d.ts +3 -0
- package/dist/video-core/xml-parser.d.ts.map +1 -0
- package/dist/video-core/xml-parser.js +27 -0
- package/package.json +5 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/agent/chat/prompt/index.d.ts +0 -6
- package/dist/agent/chat/prompt/index.d.ts.map +0 -1
- package/dist/agent/chat/prompt/index.js +0 -200
- package/dist/agent/code-review/prompt.d.ts +0 -2
- package/dist/agent/code-review/prompt.d.ts.map +0 -1
- package/dist/agent/code-review/prompt.js +0 -55
- package/dist/agent/diagnosis-agent/index.d.ts +0 -11
- package/dist/agent/diagnosis-agent/index.d.ts.map +0 -1
- package/dist/agent/diagnosis-agent/index.js +0 -88
- package/dist/agent/diagnosis-agent/strict-mode-violation.d.ts +0 -10
- package/dist/agent/diagnosis-agent/strict-mode-violation.d.ts.map +0 -1
- package/dist/agent/diagnosis-agent/strict-mode-violation.js +0 -30
- package/dist/tools/definitions/extract-frames-from-video.d.ts +0 -39
- package/dist/tools/definitions/extract-frames-from-video.d.ts.map +0 -1
- package/dist/tools/definitions/extract-frames-from-video.js +0 -60
- package/dist/tools/definitions/fetch-video-analysis.d.ts.map +0 -1
- package/dist/tools/definitions/fetch-video-analysis.js +0 -61
- package/dist/tools/extract-frames-from-video/index.d.ts +0 -7
- package/dist/tools/extract-frames-from-video/index.d.ts.map +0 -1
- package/dist/tools/extract-frames-from-video/index.js +0 -145
- package/dist/tools/fetch-video-analysis/index.d.ts +0 -5
- package/dist/tools/fetch-video-analysis/index.d.ts.map +0 -1
- package/dist/tools/fetch-video-analysis/index.js +0 -149
- package/dist/tools/fetch-video-analysis/open-ai.d.ts +0 -6
- package/dist/tools/fetch-video-analysis/open-ai.d.ts.map +0 -1
- package/dist/tools/fetch-video-analysis/open-ai.js +0 -37
- package/dist/tools/fetch-video-analysis/utils.d.ts +0 -16
- package/dist/tools/fetch-video-analysis/utils.d.ts.map +0 -1
- package/dist/tools/fetch-video-analysis/utils.js +0 -121
- package/dist/tools/fetch-video-analysis/video-analysis.d.ts +0 -7
- package/dist/tools/fetch-video-analysis/video-analysis.d.ts.map +0 -1
- package/dist/tools/fetch-video-analysis/video-analysis.js +0 -70
- package/dist/tools/file-operations/shared/git-helper.d.ts +0 -4
- package/dist/tools/file-operations/shared/git-helper.d.ts.map +0 -1
- package/dist/tools/file-operations/shared/git-helper.js +0 -29
- package/dist/utils/local-ffmpeg-client.d.ts.map +0 -1
- package/eslint.config.mjs +0 -43
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.extractFramesFromVideo = exports.extractFramesFromVideoSchema = void 0;
|
|
7
|
-
const zod_1 = __importDefault(require("zod"));
|
|
8
|
-
exports.extractFramesFromVideoSchema = zod_1.default.object({
|
|
9
|
-
videoUrl: zod_1.default
|
|
10
|
-
.string()
|
|
11
|
-
.url("Must be a valid URL")
|
|
12
|
-
.describe("The URL of the video to extract frames from."),
|
|
13
|
-
params: zod_1.default
|
|
14
|
-
.object({
|
|
15
|
-
fps: zod_1.default
|
|
16
|
-
.number()
|
|
17
|
-
.int()
|
|
18
|
-
.min(1)
|
|
19
|
-
.max(120)
|
|
20
|
-
.default(30)
|
|
21
|
-
.optional()
|
|
22
|
-
.describe("Frames per second to extract from the video (default: 30)"),
|
|
23
|
-
threshold: zod_1.default
|
|
24
|
-
.number()
|
|
25
|
-
.min(0)
|
|
26
|
-
.max(0.5)
|
|
27
|
-
.default(0.001)
|
|
28
|
-
.optional()
|
|
29
|
-
.describe("Deduplication threshold (fraction of pixels that may differ to consider frames identical). Lower = stricter. Default: 0.001"),
|
|
30
|
-
startTime: zod_1.default
|
|
31
|
-
.string()
|
|
32
|
-
.optional()
|
|
33
|
-
.describe("Start time in format MM:SS, example 1 min 32 sec -> 01:32"),
|
|
34
|
-
duration: zod_1.default
|
|
35
|
-
.string()
|
|
36
|
-
.optional()
|
|
37
|
-
.describe("Duration time in format MM:SS, example 1 min 32 sec -> 01:32"),
|
|
38
|
-
})
|
|
39
|
-
.optional(),
|
|
40
|
-
});
|
|
41
|
-
exports.extractFramesFromVideo = {
|
|
42
|
-
schema: {
|
|
43
|
-
name: "extractFramesFromVideo",
|
|
44
|
-
description: `
|
|
45
|
-
Extracts frames from a video with precise timing control.
|
|
46
|
-
|
|
47
|
-
**Input:** Video URL (required), optional parameters for timing, frame rate, and output format
|
|
48
|
-
**Output:** PNG formate Frame URLs with metadata.
|
|
49
|
-
**Use when:** You need specific frames from a video segment, want to analyze particular time ranges, or need distinct frames fromthe video, or just need all frames from the video for further processing
|
|
50
|
-
|
|
51
|
-
**Key Features:**
|
|
52
|
-
- Time-based frame extraction (start time + duration)
|
|
53
|
-
- Configurable frame rate and deduplication threshold
|
|
54
|
-
- Frame metadata including timestamps and indices
|
|
55
|
-
- Memory-efficient processing for large videos`,
|
|
56
|
-
parameters: exports.extractFramesFromVideoSchema,
|
|
57
|
-
},
|
|
58
|
-
needsBrowser: false,
|
|
59
|
-
isInlineTool: false,
|
|
60
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-video-analysis.d.ts","sourceRoot":"","sources":["../../../src/tools/definitions/fetch-video-analysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2C9B,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC7C,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAcpC,CAAC"}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.fetchVideoAnalysis = exports.videoAnalysisSchema = void 0;
|
|
7
|
-
const zod_1 = __importDefault(require("zod"));
|
|
8
|
-
exports.videoAnalysisSchema = zod_1.default.object({
|
|
9
|
-
videoUrl: zod_1.default
|
|
10
|
-
.string()
|
|
11
|
-
.url("Must be a valid URL")
|
|
12
|
-
.describe("The URL of the video to analyze."),
|
|
13
|
-
params: zod_1.default
|
|
14
|
-
.object({
|
|
15
|
-
fps: zod_1.default
|
|
16
|
-
.number()
|
|
17
|
-
.int()
|
|
18
|
-
.min(1)
|
|
19
|
-
.max(120)
|
|
20
|
-
.default(30)
|
|
21
|
-
.optional()
|
|
22
|
-
.describe("Frames per second to extract from the video (default: 30)"),
|
|
23
|
-
threshold: zod_1.default
|
|
24
|
-
.number()
|
|
25
|
-
.min(0)
|
|
26
|
-
.max(0.5)
|
|
27
|
-
.default(0.001)
|
|
28
|
-
.optional()
|
|
29
|
-
.describe("Deduplication threshold (fraction of pixels that may differ to consider frames identical). Lower = stricter. Default: 0.001"),
|
|
30
|
-
model: zod_1.default
|
|
31
|
-
.string()
|
|
32
|
-
.default("gemini-2.5-pro")
|
|
33
|
-
.optional()
|
|
34
|
-
.describe("Gemini LLM model identifier to use for analysis defaults to gemini-2.5-pro"),
|
|
35
|
-
featureFlag: zod_1.default
|
|
36
|
-
.enum([
|
|
37
|
-
"none",
|
|
38
|
-
"send-all-frames",
|
|
39
|
-
"separate-threshold",
|
|
40
|
-
"ai-based-frames",
|
|
41
|
-
])
|
|
42
|
-
.default("none")
|
|
43
|
-
.optional()
|
|
44
|
-
.describe("Feature flag to use for analysis defaults to none"),
|
|
45
|
-
})
|
|
46
|
-
.optional(),
|
|
47
|
-
});
|
|
48
|
-
exports.fetchVideoAnalysis = {
|
|
49
|
-
schema: {
|
|
50
|
-
name: "fetchVideoAnalysis",
|
|
51
|
-
description: `
|
|
52
|
-
Analyzes Playwright test execution videos to identify test failures and UI issues.
|
|
53
|
-
|
|
54
|
-
**Input:** The video URL is mandatory (supports webm, mp4). DO NOT specify params UNLESS the user has explicitly asked for a specific param value to be tested.
|
|
55
|
-
**Output:** analysis summary of what happened during the test execution.
|
|
56
|
-
**Use when:** You have a failing test with a video recording and need to understand what went wrong`,
|
|
57
|
-
parameters: exports.videoAnalysisSchema,
|
|
58
|
-
},
|
|
59
|
-
needsBrowser: false,
|
|
60
|
-
isInlineTool: false,
|
|
61
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Tool } from "@empiricalrun/shared-types";
|
|
2
|
-
import type { z } from "zod";
|
|
3
|
-
import { type extractFramesFromVideoSchema } from "../definitions/extract-frames-from-video";
|
|
4
|
-
type ExtractFramesInput = z.infer<typeof extractFramesFromVideoSchema>;
|
|
5
|
-
export declare const extractFramesFromVideo: Tool<ExtractFramesInput>;
|
|
6
|
-
export {};
|
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/extract-frames-from-video/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,IAAI,EAEL,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,0CAA0C,CAAC;AAElD,KAAK,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AA0EvE,eAAO,MAAM,sBAAsB,EAAE,IAAI,CAAC,kBAAkB,CAkH3D,CAAC"}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.extractFramesFromVideo = void 0;
|
|
7
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
-
const fs_1 = require("fs");
|
|
9
|
-
const file_1 = require("../../utils/file");
|
|
10
|
-
const local_ffmpeg_client_1 = require("../../utils/local-ffmpeg-client");
|
|
11
|
-
const extract_frames_from_video_1 = require("../definitions/extract-frames-from-video");
|
|
12
|
-
function parseTimeToSeconds(timeString) {
|
|
13
|
-
const timeRegex = /^(\d{1,2}):(\d{2})$/;
|
|
14
|
-
const match = timeString.match(timeRegex);
|
|
15
|
-
if (!match || match.length < 3) {
|
|
16
|
-
throw new Error(`Invalid time format: ${timeString}. Expected MM:SS format (e.g., "01:32")`);
|
|
17
|
-
}
|
|
18
|
-
const minutesStr = match[1];
|
|
19
|
-
const secondsStr = match[2];
|
|
20
|
-
if (!minutesStr || !secondsStr) {
|
|
21
|
-
throw new Error(`Invalid time format: ${timeString}. Expected MM:SS format (e.g., "01:32")`);
|
|
22
|
-
}
|
|
23
|
-
const minutes = parseInt(minutesStr, 10);
|
|
24
|
-
const seconds = parseInt(secondsStr, 10);
|
|
25
|
-
if (seconds >= 60) {
|
|
26
|
-
throw new Error(`Invalid seconds: ${seconds}. Seconds must be 0-59`);
|
|
27
|
-
}
|
|
28
|
-
return minutes * 60 + seconds;
|
|
29
|
-
}
|
|
30
|
-
function getExtractFramesParams(params) {
|
|
31
|
-
return {
|
|
32
|
-
fps: params?.fps ?? 30,
|
|
33
|
-
threshold: params?.threshold ?? 0.001,
|
|
34
|
-
startTime: params?.startTime
|
|
35
|
-
? parseTimeToSeconds(params.startTime)
|
|
36
|
-
: undefined,
|
|
37
|
-
duration: params?.duration
|
|
38
|
-
? parseTimeToSeconds(params.duration)
|
|
39
|
-
: undefined,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function hashExtractParams(videoUrl, params) {
|
|
43
|
-
const sortedParams = Object.keys(params)
|
|
44
|
-
.sort()
|
|
45
|
-
.reduce((acc, key) => {
|
|
46
|
-
acc[key] = params[key];
|
|
47
|
-
return acc;
|
|
48
|
-
}, {});
|
|
49
|
-
const json = JSON.stringify({ videoUrl, ...sortedParams });
|
|
50
|
-
return node_crypto_1.default
|
|
51
|
-
.createHash("sha256")
|
|
52
|
-
.update(json)
|
|
53
|
-
.digest("hex")
|
|
54
|
-
.substring(0, 16);
|
|
55
|
-
}
|
|
56
|
-
exports.extractFramesFromVideo = {
|
|
57
|
-
...extract_frames_from_video_1.extractFramesFromVideo,
|
|
58
|
-
execute: async ({ input, trace, }) => {
|
|
59
|
-
const { videoUrl } = input;
|
|
60
|
-
const params = getExtractFramesParams(input.params);
|
|
61
|
-
const videoUrlHash = hashExtractParams(videoUrl, params);
|
|
62
|
-
const WORKING_DIR = `./extract-frames-artifacts/${videoUrlHash}`;
|
|
63
|
-
// const R2_BASE_URL = `https://video-analysis.empirical.run/${videoUrlHash}`;
|
|
64
|
-
const extractionSpan = trace?.span({
|
|
65
|
-
name: "extract-frames-from-video",
|
|
66
|
-
input: { videoUrl, params },
|
|
67
|
-
});
|
|
68
|
-
try {
|
|
69
|
-
const response = await fetch(videoUrl, { method: "HEAD" });
|
|
70
|
-
if (!response.ok) {
|
|
71
|
-
return {
|
|
72
|
-
result: `Failed to access video: ${response.statusText}`,
|
|
73
|
-
isError: true,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
const ffmpegClient = new local_ffmpeg_client_1.LocalFFmpegClient();
|
|
77
|
-
const extractionResult = await ffmpegClient.extractVideoFrames(videoUrl, WORKING_DIR, {
|
|
78
|
-
fps: params.fps,
|
|
79
|
-
threshold: params.threshold,
|
|
80
|
-
startTime: params.startTime,
|
|
81
|
-
duration: params.duration,
|
|
82
|
-
});
|
|
83
|
-
const { totalFramesCount, uniqueFrames, videoDurationSeconds } = extractionResult;
|
|
84
|
-
// Create response metadata
|
|
85
|
-
const extractFramesResponse = {
|
|
86
|
-
videoUrl,
|
|
87
|
-
videoDurationSeconds,
|
|
88
|
-
totalFramesCount,
|
|
89
|
-
extractedFramesCount: uniqueFrames.length,
|
|
90
|
-
timeRange: params.startTime !== undefined || params.duration !== undefined
|
|
91
|
-
? {
|
|
92
|
-
startTime: params.startTime || 0,
|
|
93
|
-
duration: params.duration || 0,
|
|
94
|
-
endTime: (params.startTime || 0) + (params.duration || 0),
|
|
95
|
-
}
|
|
96
|
-
: undefined,
|
|
97
|
-
frames: uniqueFrames.map((frame) => ({
|
|
98
|
-
index: frame.metadata.index,
|
|
99
|
-
path: frame.metadata.path,
|
|
100
|
-
frameId: `frame_${frame.metadata.index}`,
|
|
101
|
-
})),
|
|
102
|
-
};
|
|
103
|
-
// Convert frames to base64 images for multimodal response
|
|
104
|
-
const frameImageParts = await Promise.all(uniqueFrames.map(async (frame) => {
|
|
105
|
-
try {
|
|
106
|
-
const imageBuffer = await fs_1.promises.readFile(frame.metadata.path);
|
|
107
|
-
const base64Data = imageBuffer.toString("base64");
|
|
108
|
-
return {
|
|
109
|
-
type: "image/png",
|
|
110
|
-
base64Data: base64Data,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
console.warn(`Failed to read frame ${frame.metadata.path}:`, error);
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
}));
|
|
118
|
-
await (0, file_1.safeCleanupDirectory)(WORKING_DIR, "extract-frames-tool");
|
|
119
|
-
// Filter out any failed frame reads
|
|
120
|
-
const validFrameImageParts = frameImageParts.filter((part) => part !== null);
|
|
121
|
-
return {
|
|
122
|
-
result: [
|
|
123
|
-
{
|
|
124
|
-
type: "text",
|
|
125
|
-
text: JSON.stringify(extractFramesResponse, null, 2),
|
|
126
|
-
},
|
|
127
|
-
...validFrameImageParts,
|
|
128
|
-
],
|
|
129
|
-
isError: false,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
console.error("Error in extractFramesFromVideo", error);
|
|
134
|
-
extractionSpan?.end();
|
|
135
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
136
|
-
return {
|
|
137
|
-
result: `Error extracting frames from video: ${message}`,
|
|
138
|
-
isError: true,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
extractionSpan?.end();
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { Tool } from "@empiricalrun/shared-types";
|
|
2
|
-
import type { z } from "zod";
|
|
3
|
-
import { type videoAnalysisSchema } from "../definitions/fetch-video-analysis";
|
|
4
|
-
export declare const fetchVideoAnalysis: Tool<z.infer<typeof videoAnalysisSchema>>;
|
|
5
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/fetch-video-analysis/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,IAAI,EAKL,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,qCAAqC,CAAC;AAuC7C,eAAO,MAAM,kBAAkB,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAsKxE,CAAC"}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.fetchVideoAnalysis = void 0;
|
|
7
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const file_1 = require("../../utils/file");
|
|
10
|
-
const local_ffmpeg_client_1 = require("../../utils/local-ffmpeg-client");
|
|
11
|
-
const fetch_video_analysis_1 = require("../definitions/fetch-video-analysis");
|
|
12
|
-
const utils_1 = require("./utils");
|
|
13
|
-
const video_analysis_1 = require("./video-analysis");
|
|
14
|
-
function getVideoAnalysisParams(params) {
|
|
15
|
-
return {
|
|
16
|
-
model: params?.model || "gemini-2.5-pro",
|
|
17
|
-
fps: params?.fps ?? 30,
|
|
18
|
-
threshold: params?.threshold ?? 0.001,
|
|
19
|
-
featureFlag: params?.featureFlag ?? "send-all-frames",
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function hashObject(obj) {
|
|
23
|
-
const sortedObj = Object.keys(obj)
|
|
24
|
-
.sort()
|
|
25
|
-
.reduce((acc, key) => {
|
|
26
|
-
acc[key] = obj[key];
|
|
27
|
-
return acc;
|
|
28
|
-
}, {});
|
|
29
|
-
const json = JSON.stringify(sortedObj);
|
|
30
|
-
return node_crypto_1.default
|
|
31
|
-
.createHash("sha256")
|
|
32
|
-
.update(json)
|
|
33
|
-
.digest("hex")
|
|
34
|
-
.substring(0, 16);
|
|
35
|
-
}
|
|
36
|
-
exports.fetchVideoAnalysis = {
|
|
37
|
-
...fetch_video_analysis_1.fetchVideoAnalysis,
|
|
38
|
-
execute: async ({ input, trace, featureFlags, }) => {
|
|
39
|
-
const { videoUrl } = input;
|
|
40
|
-
const params = getVideoAnalysisParams(input.params);
|
|
41
|
-
const videoUrlHash = hashObject({
|
|
42
|
-
videoUrl,
|
|
43
|
-
...params,
|
|
44
|
-
});
|
|
45
|
-
const { model: selectedModel, fps: effectiveFps, threshold: effectiveThreshold, featureFlag: effectiveFeatureFlag, } = params;
|
|
46
|
-
const WORKING_DIR = `./video-analysis-artifacts/${videoUrlHash}`;
|
|
47
|
-
const R2_BASE_URL = `https://video-analysis.empirical.run/${videoUrlHash}/`;
|
|
48
|
-
const videoAnalysisSpan = trace?.span({
|
|
49
|
-
name: "video-analysis",
|
|
50
|
-
input: { videoUrl },
|
|
51
|
-
});
|
|
52
|
-
try {
|
|
53
|
-
const ffmpegClient = new local_ffmpeg_client_1.LocalFFmpegClient();
|
|
54
|
-
const response = await fetch(videoUrl, { method: "HEAD" });
|
|
55
|
-
if (!response.ok) {
|
|
56
|
-
return {
|
|
57
|
-
result: `Failed to access video at ${videoUrl}: ${response.statusText}`,
|
|
58
|
-
isError: true,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
const processingSpan = videoAnalysisSpan?.span({
|
|
62
|
-
name: "ffmpeg-processing",
|
|
63
|
-
input: {
|
|
64
|
-
videoUrl,
|
|
65
|
-
fps: effectiveFps,
|
|
66
|
-
threshold: effectiveThreshold,
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
try {
|
|
70
|
-
const extractionResult = await ffmpegClient.extractVideoFrames(videoUrl, WORKING_DIR, {
|
|
71
|
-
fps: effectiveFps,
|
|
72
|
-
threshold: effectiveThreshold,
|
|
73
|
-
});
|
|
74
|
-
const { totalFramesCount, uniqueFrames } = extractionResult;
|
|
75
|
-
processingSpan?.end({
|
|
76
|
-
output: {
|
|
77
|
-
totalFramesCount,
|
|
78
|
-
uniqueFramesCount: uniqueFrames.length,
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
console.log(`[video-analysis] Analyzing ${uniqueFrames.length} frames with LLM`);
|
|
82
|
-
const outputZipPath = node_path_1.default.join(WORKING_DIR, "frames.zip");
|
|
83
|
-
const zipUploadPromise = (0, utils_1.zipAndUploadFramesToR2)(uniqueFrames, outputZipPath, videoUrlHash).catch((error) => {
|
|
84
|
-
throw error; // Re-throw to maintain error in Promise.all
|
|
85
|
-
});
|
|
86
|
-
const { analysis: llmAnalysis, usage } = await (0, video_analysis_1.analyzeFramesWithLLM)(uniqueFrames, videoAnalysisSpan, selectedModel);
|
|
87
|
-
console.log(`[video-analysis] Finished Analyzing ${uniqueFrames.length} frames with LLM`);
|
|
88
|
-
let finalAnalysis = llmAnalysis;
|
|
89
|
-
let finalKeyFramesData = [];
|
|
90
|
-
if (featureFlags.includes("enableFetchVideoAnalysisToolMultiModal")) {
|
|
91
|
-
const { updatedAnalysis, keyFramesData } = await (0, utils_1.getUpdatedAnalysisAndToolResultImagePart)(llmAnalysis, uniqueFrames);
|
|
92
|
-
finalAnalysis = updatedAnalysis;
|
|
93
|
-
finalKeyFramesData = [...keyFramesData];
|
|
94
|
-
}
|
|
95
|
-
const videoInfo = {
|
|
96
|
-
total_frames_count: totalFramesCount,
|
|
97
|
-
unique_frames_count: uniqueFrames.length,
|
|
98
|
-
video_url: videoUrl,
|
|
99
|
-
analysis_id: videoUrlHash,
|
|
100
|
-
created_at: new Date().toISOString(),
|
|
101
|
-
params: {
|
|
102
|
-
fps: effectiveFps,
|
|
103
|
-
threshold: effectiveThreshold,
|
|
104
|
-
model: selectedModel,
|
|
105
|
-
featureFlag: effectiveFeatureFlag,
|
|
106
|
-
},
|
|
107
|
-
usage,
|
|
108
|
-
langfuse_trace_id: trace?.id || undefined,
|
|
109
|
-
frames_zip_url: `${R2_BASE_URL}frames.zip`,
|
|
110
|
-
analysis: finalAnalysis,
|
|
111
|
-
};
|
|
112
|
-
await Promise.all([
|
|
113
|
-
zipUploadPromise,
|
|
114
|
-
(0, utils_1.uploadSummaryToR2)(videoInfo, R2_BASE_URL),
|
|
115
|
-
]);
|
|
116
|
-
await (0, file_1.safeCleanupDirectory)(WORKING_DIR, "video-analysis-cleanup");
|
|
117
|
-
const toolResult = {
|
|
118
|
-
video_url: videoUrl,
|
|
119
|
-
analysis_id: videoUrlHash,
|
|
120
|
-
analysis: finalAnalysis,
|
|
121
|
-
};
|
|
122
|
-
return {
|
|
123
|
-
result: [
|
|
124
|
-
{ type: "text", text: JSON.stringify(toolResult, null, 2) },
|
|
125
|
-
...finalKeyFramesData,
|
|
126
|
-
],
|
|
127
|
-
isError: false,
|
|
128
|
-
usage,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
catch (ffmpegError) {
|
|
132
|
-
processingSpan?.end();
|
|
133
|
-
throw ffmpegError;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
console.error("Error in video analysis", error);
|
|
138
|
-
videoAnalysisSpan?.end();
|
|
139
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
-
return {
|
|
141
|
-
result: `Error analyzing video: ${message}`,
|
|
142
|
-
isError: true,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
finally {
|
|
146
|
-
videoAnalysisSpan?.end();
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"open-ai.d.ts","sourceRoot":"","sources":["../../../src/tools/fetch-video-analysis/open-ai.ts"],"names":[],"mappings":"AAOA,wBAAsB,aAAa,CAAC,EAClC,YAAY,EACZ,gBAAgB,EAChB,UAAU,GACX,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB,mBA+BA"}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.analyzeImages = analyzeImages;
|
|
7
|
-
const openai_1 = __importDefault(require("openai"));
|
|
8
|
-
const openai = new openai_1.default({
|
|
9
|
-
apiKey: ``,
|
|
10
|
-
});
|
|
11
|
-
async function analyzeImages({ systemPrompt, imageBase64Array, userPrompt, }) {
|
|
12
|
-
const response = await openai.responses.create({
|
|
13
|
-
model: "gpt-4.1-2025-04-14",
|
|
14
|
-
input: [
|
|
15
|
-
{
|
|
16
|
-
role: "system",
|
|
17
|
-
content: systemPrompt,
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
role: "user",
|
|
21
|
-
content: [
|
|
22
|
-
{
|
|
23
|
-
type: "input_text",
|
|
24
|
-
text: userPrompt,
|
|
25
|
-
},
|
|
26
|
-
...imageBase64Array.map((base64) => ({
|
|
27
|
-
detail: "auto",
|
|
28
|
-
type: "input_image",
|
|
29
|
-
image_url: `data:image/png;base64,${base64}`,
|
|
30
|
-
})),
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
});
|
|
35
|
-
console.log(response.output_text);
|
|
36
|
-
return response.output_text;
|
|
37
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type FileMap } from "@empiricalrun/r2-uploader";
|
|
2
|
-
import { ToolResultImagePart, UniqueFrameInfo, UniqueFrameWithMetadata, VideoAnalysisSummary } from "@empiricalrun/shared-types";
|
|
3
|
-
export declare const uploadFramesToR2: (videoInfo: Omit<VideoAnalysisSummary, "analysis">, frames: {
|
|
4
|
-
metadata: {
|
|
5
|
-
index: number;
|
|
6
|
-
path: string;
|
|
7
|
-
};
|
|
8
|
-
image: string;
|
|
9
|
-
}[], r2BaseUrl: string) => Promise<UniqueFrameInfo[]>;
|
|
10
|
-
export declare const uploadSummaryToR2: (videoInfo: VideoAnalysisSummary, r2BaseUrl: string) => Promise<string>;
|
|
11
|
-
export declare const zipAndUploadFramesToR2: (uniqueFrames: UniqueFrameWithMetadata[], outputZipPath: string, videoUrlHash: string) => Promise<FileMap>;
|
|
12
|
-
export declare const getUpdatedAnalysisAndToolResultImagePart: (llmAnalysis: string, uniqueFrames: UniqueFrameWithMetadata[]) => Promise<{
|
|
13
|
-
updatedAnalysis: string;
|
|
14
|
-
keyFramesData: ToolResultImagePart[];
|
|
15
|
-
}>;
|
|
16
|
-
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/fetch-video-analysis/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGb,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,4BAA4B,CAAC;AAuCpC,eAAO,MAAM,gBAAgB,GAC3B,WAAW,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC,EACjD,QAAQ;IACN,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,EACH,WAAW,MAAM,KAChB,OAAO,CAAC,eAAe,EAAE,CAqB3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,WAAW,oBAAoB,EAC/B,WAAW,MAAM,KAChB,OAAO,CAAC,MAAM,CAsBhB,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,cAAc,uBAAuB,EAAE,EACvC,eAAe,MAAM,EACrB,cAAc,MAAM,KACnB,OAAO,CAAC,OAAO,CAsBjB,CAAC;AAEF,eAAO,MAAM,wCAAwC,GACnD,aAAa,MAAM,EACnB,cAAc,uBAAuB,EAAE,KACtC,OAAO,CAAC;IACT,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,mBAAmB,EAAE,CAAC;CACtC,CA2CA,CAAC"}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getUpdatedAnalysisAndToolResultImagePart = exports.zipAndUploadFramesToR2 = exports.uploadSummaryToR2 = exports.uploadFramesToR2 = void 0;
|
|
7
|
-
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
8
|
-
const child_process_1 = require("child_process");
|
|
9
|
-
const fs_1 = require("fs");
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const uploadFilesToR2 = async (files, videoUrlHash) => {
|
|
12
|
-
const label = `video-analysis-upload:${videoUrlHash}`;
|
|
13
|
-
const start = Date.now();
|
|
14
|
-
console.log(`[${label}] preparing ${files.length} files for upload...`);
|
|
15
|
-
console.log(`[${label}] starting upload of ${files.length} files to video-analysis/${videoUrlHash}/...`);
|
|
16
|
-
const interval = setInterval(() => {
|
|
17
|
-
const secs = Math.round((Date.now() - start) / 1000);
|
|
18
|
-
console.log(`[${label}] uploading... ${secs}s elapsed`);
|
|
19
|
-
}, 1000);
|
|
20
|
-
try {
|
|
21
|
-
await (0, r2_uploader_1.uploadInMemoryFiles)({
|
|
22
|
-
files,
|
|
23
|
-
destinationDir: videoUrlHash,
|
|
24
|
-
uploadBucket: "video-analysis",
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
finally {
|
|
28
|
-
clearInterval(interval);
|
|
29
|
-
const secs = Math.round((Date.now() - start) / 1000);
|
|
30
|
-
console.log(`[${label}] upload complete in ${secs}s`);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
const uploadFramesToR2 = async (videoInfo, frames, r2BaseUrl) => {
|
|
34
|
-
const { analysis_id: videoUrlHash } = videoInfo;
|
|
35
|
-
const frameFiles = frames.map((f) => {
|
|
36
|
-
const fileName = `frame_${f.metadata.index}_${videoUrlHash}.png`;
|
|
37
|
-
const buffer = Buffer.from(f.image, "base64");
|
|
38
|
-
return { buffer, fileName, mimeType: "image/png" };
|
|
39
|
-
});
|
|
40
|
-
console.log(`[video-analysis-upload] uploading ${frameFiles.length} frames to video-analysis/${videoUrlHash}/...`);
|
|
41
|
-
await uploadFilesToR2(frameFiles, videoUrlHash);
|
|
42
|
-
return frames.map((f) => ({
|
|
43
|
-
index: f.metadata.index,
|
|
44
|
-
path: f.metadata.path,
|
|
45
|
-
fileName: `frame_${f.metadata.index}_${videoUrlHash}.png`,
|
|
46
|
-
url: `${r2BaseUrl}frame_${f.metadata.index}_${videoUrlHash}.png`,
|
|
47
|
-
}));
|
|
48
|
-
};
|
|
49
|
-
exports.uploadFramesToR2 = uploadFramesToR2;
|
|
50
|
-
const uploadSummaryToR2 = async (videoInfo, r2BaseUrl) => {
|
|
51
|
-
try {
|
|
52
|
-
const { analysis_id: videoUrlHash } = videoInfo;
|
|
53
|
-
const filesToUpload = [
|
|
54
|
-
{
|
|
55
|
-
buffer: Buffer.from(JSON.stringify(videoInfo, null, 2)),
|
|
56
|
-
fileName: "summary.json",
|
|
57
|
-
mimeType: "application/json",
|
|
58
|
-
},
|
|
59
|
-
];
|
|
60
|
-
console.log(`[video-analysis-upload] uploading ${filesToUpload.length} files to video-analysis/${videoUrlHash}/...`);
|
|
61
|
-
await uploadFilesToR2(filesToUpload, videoUrlHash);
|
|
62
|
-
return r2BaseUrl;
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
console.error("Error uploading video analysis to R2:", error);
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
exports.uploadSummaryToR2 = uploadSummaryToR2;
|
|
70
|
-
const zipAndUploadFramesToR2 = async (uniqueFrames, outputZipPath, videoUrlHash) => {
|
|
71
|
-
const filePaths = uniqueFrames.map((u) => u.metadata.path);
|
|
72
|
-
await new Promise((resolve, reject) => {
|
|
73
|
-
(0, child_process_1.execFile)("zip", ["-0", "-j", outputZipPath, ...filePaths], (err) => {
|
|
74
|
-
if (err)
|
|
75
|
-
return reject(err);
|
|
76
|
-
resolve();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
const tempUploadDir = path_1.default.dirname(outputZipPath);
|
|
80
|
-
console.log(`[zip-upload] Uploading zip file: ${outputZipPath} to video-analysis/${videoUrlHash}/`);
|
|
81
|
-
return await (0, r2_uploader_1.uploadDirectory)({
|
|
82
|
-
sourceDir: tempUploadDir,
|
|
83
|
-
fileList: [outputZipPath],
|
|
84
|
-
destinationDir: videoUrlHash,
|
|
85
|
-
uploadBucket: "video-analysis",
|
|
86
|
-
});
|
|
87
|
-
};
|
|
88
|
-
exports.zipAndUploadFramesToR2 = zipAndUploadFramesToR2;
|
|
89
|
-
const getUpdatedAnalysisAndToolResultImagePart = async (llmAnalysis, uniqueFrames) => {
|
|
90
|
-
const keyFramesMatch = llmAnalysis.match(/KEY FRAMES:\s*(.+)$/);
|
|
91
|
-
const updatedAnalysis = keyFramesMatch
|
|
92
|
-
? llmAnalysis.replace(/\n\nKEY FRAMES:\s*(.+)$/, "")
|
|
93
|
-
: llmAnalysis;
|
|
94
|
-
const keyFrameIndices = keyFramesMatch?.[1]
|
|
95
|
-
?.match(/<frame_(\d+)>/g)
|
|
96
|
-
?.map((match) => parseInt(match.match(/<frame_(\d+)>/)?.[1] || "0", 10)) || [];
|
|
97
|
-
const frameDataMap = new Map();
|
|
98
|
-
await Promise.all(uniqueFrames.map(async (frame) => {
|
|
99
|
-
try {
|
|
100
|
-
const fileBuffer = await fs_1.promises.readFile(frame.metadata.path);
|
|
101
|
-
const base64Data = fileBuffer.toString("base64");
|
|
102
|
-
frameDataMap.set(frame.metadata.index, base64Data);
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
console.error(`Failed to read frame file ${frame.metadata.path}:`, error);
|
|
106
|
-
}
|
|
107
|
-
}));
|
|
108
|
-
const keyFramesData = keyFrameIndices
|
|
109
|
-
.map((frameIndex) => {
|
|
110
|
-
const base64Data = frameDataMap.get(frameIndex);
|
|
111
|
-
return base64Data
|
|
112
|
-
? {
|
|
113
|
-
type: "image/png",
|
|
114
|
-
base64Data: base64Data,
|
|
115
|
-
}
|
|
116
|
-
: null;
|
|
117
|
-
})
|
|
118
|
-
.filter(Boolean);
|
|
119
|
-
return { updatedAnalysis, keyFramesData };
|
|
120
|
-
};
|
|
121
|
-
exports.getUpdatedAnalysisAndToolResultImagePart = getUpdatedAnalysisAndToolResultImagePart;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { TraceClient } from "@empiricalrun/llm";
|
|
2
|
-
import { SupportedChatModels, UniqueFrameWithMetadata, Usage } from "@empiricalrun/shared-types";
|
|
3
|
-
export declare function analyzeFramesWithLLM(uniqueFrames: UniqueFrameWithMetadata[], trace?: TraceClient, selectedModel?: SupportedChatModels, apiKey?: string): Promise<{
|
|
4
|
-
analysis: string;
|
|
5
|
-
usage: Usage;
|
|
6
|
-
}>;
|
|
7
|
-
//# sourceMappingURL=video-analysis.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"video-analysis.d.ts","sourceRoot":"","sources":["../../../src/tools/fetch-video-analysis/video-analysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAEL,mBAAmB,EACnB,uBAAuB,EACvB,KAAK,EACN,MAAM,4BAA4B,CAAC;AAIpC,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,uBAAuB,EAAE,EACvC,KAAK,CAAC,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,mBAAmB,EACnC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC,CA8E7C"}
|