@empiricalrun/test-gen 0.73.1 → 0.74.1
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 +94 -0
- package/dist/actions/utils/index.d.ts.map +1 -1
- package/dist/actions/utils/index.js +1 -2
- package/dist/agent/browsing/index.d.ts.map +1 -1
- package/dist/agent/browsing/index.js +9 -7
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +3 -4
- package/dist/agent/chat/agent-loop.d.ts +7 -8
- package/dist/agent/chat/agent-loop.d.ts.map +1 -1
- package/dist/agent/chat/agent-loop.js +13 -17
- 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 +9 -4
- package/dist/agent/chat/filesystem-cache.d.ts +12 -0
- package/dist/agent/chat/filesystem-cache.d.ts.map +1 -0
- package/dist/agent/chat/filesystem-cache.js +101 -0
- package/dist/agent/chat/index.d.ts +4 -1
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +44 -22
- package/dist/agent/chat/models.d.ts +2 -1
- package/dist/agent/chat/models.d.ts.map +1 -1
- package/dist/agent/chat/models.js +25 -3
- package/dist/agent/chat/prompt/index.d.ts +3 -1
- package/dist/agent/chat/prompt/index.d.ts.map +1 -1
- package/dist/agent/chat/prompt/index.js +77 -2
- package/dist/agent/chat/prompt/repo.d.ts.map +1 -1
- package/dist/agent/chat/prompt/repo.js +1 -0
- package/dist/agent/chat/state.d.ts +6 -5
- package/dist/agent/chat/state.d.ts.map +1 -1
- package/dist/agent/chat/state.js +35 -5
- package/dist/agent/chat/utils.d.ts +1 -0
- package/dist/agent/chat/utils.d.ts.map +1 -1
- package/dist/agent/chat/utils.js +16 -3
- package/dist/agent/cua/index.js +1 -1
- package/dist/agent/cua/model.js +1 -1
- package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -1
- package/dist/agent/cua/pw-codegen/pw-pause/index.js +0 -1
- package/dist/agent/master/browser-tests/fixtures.d.ts.map +1 -1
- package/dist/agent/master/browser-tests/fixtures.js +0 -1
- package/dist/agent/master/element-annotation.d.ts.map +1 -1
- package/dist/agent/master/element-annotation.js +1 -2
- package/dist/agent/master/execute-browser-action.d.ts.map +1 -1
- package/dist/agent/master/execute-browser-action.js +8 -6
- package/dist/agent/master/icon-descriptor/index.js +2 -2
- package/dist/agent/master/next-action.js +1 -1
- package/dist/agent/master/planner.js +1 -1
- package/dist/agent/master/scroller.js +2 -2
- package/dist/agent/master/with-hints.d.ts.map +1 -1
- package/dist/agent/master/with-hints.js +6 -5
- package/dist/agent/planner/run-time-planner.js +1 -1
- package/dist/agent/planner/run.d.ts.map +1 -1
- package/dist/agent/planner/run.js +4 -2
- package/dist/auth/cli-auth.js +1 -1
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +4 -6
- package/dist/bin/index.js +30 -163
- package/dist/bin/utils/context.js +1 -1
- package/dist/bin/utils/index.d.ts +0 -12
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/index.js +0 -70
- package/dist/bin/utils/platform/web/index.d.ts +2 -2
- package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.js +7 -5
- package/dist/bin/utils/scenarios/index.d.ts +11 -3
- package/dist/bin/utils/scenarios/index.d.ts.map +1 -1
- package/dist/browser-injected-scripts/annotate-elements.spec.js +0 -5
- package/dist/browser-injected-scripts/annotate-elements.spec.ts +0 -5
- package/dist/dashboard/client.d.ts +2 -2
- package/dist/dashboard/client.d.ts.map +1 -1
- package/dist/dashboard/client.js +4 -1
- package/dist/file-info/adapters/file-system/index.d.ts +9 -0
- package/dist/file-info/adapters/file-system/index.d.ts.map +1 -0
- package/dist/file-info/adapters/file-system/index.js +25 -0
- package/dist/file-info/adapters/file-system/reader.d.ts +6 -0
- package/dist/file-info/adapters/file-system/reader.d.ts.map +1 -0
- package/dist/file-info/{file-system.js → adapters/file-system/reader.js} +16 -0
- package/dist/file-info/adapters/github/index.d.ts +11 -0
- package/dist/file-info/adapters/github/index.d.ts.map +1 -0
- package/dist/file-info/adapters/github/index.js +29 -0
- package/dist/file-info/adapters/github/reader.d.ts +16 -0
- package/dist/file-info/adapters/github/reader.d.ts.map +1 -0
- package/dist/file-info/adapters/github/reader.js +185 -0
- package/dist/file-info/index.d.ts +4 -0
- package/dist/file-info/index.d.ts.map +1 -0
- package/dist/file-info/index.js +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/recorder/index.d.ts +7 -3
- package/dist/recorder/index.d.ts.map +1 -1
- package/dist/recorder/index.js +181 -64
- package/dist/recorder/temp-files.d.ts +1 -1
- package/dist/recorder/temp-files.d.ts.map +1 -1
- package/dist/recorder/temp-files.js +2 -2
- package/dist/recorder/upload.d.ts +1 -1
- package/dist/recorder/upload.d.ts.map +1 -1
- package/dist/recorder/upload.js +3 -3
- package/dist/tools/{commit-and-create-pr.d.ts → commit-and-create-pr/index.d.ts} +1 -1
- package/dist/tools/commit-and-create-pr/index.d.ts.map +1 -0
- package/dist/tools/{commit-and-create-pr.js → commit-and-create-pr/index.js} +8 -28
- package/dist/tools/definitions/commit-and-create-pr.d.ts +3 -0
- package/dist/tools/definitions/commit-and-create-pr.d.ts.map +1 -0
- package/dist/tools/definitions/commit-and-create-pr.js +31 -0
- package/dist/tools/definitions/delete-file.d.ts +3 -0
- package/dist/tools/definitions/delete-file.d.ts.map +1 -0
- package/dist/tools/definitions/delete-file.js +20 -0
- package/dist/tools/{download-build.d.ts → definitions/download-build.d.ts} +2 -2
- package/dist/tools/definitions/download-build.d.ts.map +1 -0
- package/dist/tools/definitions/download-build.js +18 -0
- package/dist/tools/definitions/fetch-video-analysis.d.ts +11 -0
- package/dist/tools/definitions/fetch-video-analysis.d.ts.map +1 -0
- package/dist/tools/definitions/fetch-video-analysis.js +26 -0
- package/dist/tools/definitions/grep.d.ts +3 -0
- package/dist/tools/definitions/grep.d.ts.map +1 -0
- package/dist/tools/definitions/grep.js +29 -0
- package/dist/tools/definitions/merge-conflicts.d.ts +3 -0
- package/dist/tools/definitions/merge-conflicts.d.ts.map +1 -0
- package/dist/tools/definitions/merge-conflicts.js +24 -0
- package/dist/tools/definitions/run-test.d.ts +2 -2
- package/dist/tools/definitions/run-test.d.ts.map +1 -1
- package/dist/tools/definitions/run-test.js +4 -7
- package/dist/tools/definitions/str_replace_editor.d.ts.map +1 -1
- package/dist/tools/definitions/str_replace_editor.js +9 -5
- package/dist/tools/definitions/test-gen-browser.d.ts +5 -5
- package/dist/tools/definitions/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/definitions/test-gen-browser.js +4 -7
- package/dist/tools/definitions/upgrade-packages.d.ts +36 -0
- package/dist/tools/definitions/upgrade-packages.d.ts.map +1 -0
- package/dist/tools/definitions/upgrade-packages.js +21 -0
- package/dist/tools/definitions/utils.d.ts +15 -0
- package/dist/tools/definitions/utils.d.ts.map +1 -0
- package/dist/tools/definitions/utils.js +16 -0
- package/dist/tools/{delete-file.d.ts → delete-file/index.d.ts} +1 -1
- package/dist/tools/delete-file/index.d.ts.map +1 -0
- package/dist/tools/{delete-file.js → delete-file/index.js} +4 -16
- package/dist/tools/diagnosis-fetcher.d.ts.map +1 -1
- package/dist/tools/diagnosis-fetcher.js +13 -11
- package/dist/tools/download-build/index.d.ts +3 -0
- package/dist/tools/download-build/index.d.ts.map +1 -0
- package/dist/tools/{download-build.js → download-build/index.js} +4 -14
- package/dist/tools/executor/index.d.ts.map +1 -1
- package/dist/tools/executor/index.js +19 -4
- package/dist/tools/executor/utils/git.js +1 -1
- package/dist/tools/executor/utils/pr-description.d.ts +1 -1
- package/dist/tools/executor/utils/pr-description.d.ts.map +1 -1
- package/dist/tools/fetch-image/index.d.ts +10 -1
- package/dist/tools/fetch-image/index.d.ts.map +1 -1
- package/dist/tools/fetch-image/index.js +1 -0
- package/dist/tools/fetch-last-successful-test-run/index.d.ts +3 -0
- package/dist/tools/fetch-last-successful-test-run/index.d.ts.map +1 -0
- package/dist/tools/fetch-last-successful-test-run/index.js +60 -0
- package/dist/tools/fetch-video-analysis/index.d.ts +5 -0
- package/dist/tools/fetch-video-analysis/index.d.ts.map +1 -0
- package/dist/tools/fetch-video-analysis/index.js +89 -0
- package/dist/tools/fetch-video-analysis/local-ffmpeg-client.d.ts +24 -0
- package/dist/tools/fetch-video-analysis/local-ffmpeg-client.d.ts.map +1 -0
- package/dist/tools/fetch-video-analysis/local-ffmpeg-client.js +209 -0
- package/dist/tools/fetch-video-analysis/utils.d.ts +10 -0
- package/dist/tools/fetch-video-analysis/utils.d.ts.map +1 -0
- package/dist/tools/fetch-video-analysis/utils.js +72 -0
- package/dist/tools/fetch-video-analysis/video-analysis.d.ts +7 -0
- package/dist/tools/fetch-video-analysis/video-analysis.d.ts.map +1 -0
- package/dist/tools/fetch-video-analysis/video-analysis.js +54 -0
- package/dist/tools/file-operations/create.d.ts +11 -0
- package/dist/tools/file-operations/create.d.ts.map +1 -0
- package/dist/tools/file-operations/create.js +60 -0
- package/dist/tools/file-operations/index.d.ts +15 -0
- package/dist/tools/file-operations/index.d.ts.map +1 -0
- package/dist/tools/file-operations/index.js +143 -0
- package/dist/tools/file-operations/insert.d.ts +11 -0
- package/dist/tools/file-operations/insert.d.ts.map +1 -0
- package/dist/tools/file-operations/insert.js +61 -0
- package/dist/tools/file-operations/replace.d.ts +11 -0
- package/dist/tools/file-operations/replace.d.ts.map +1 -0
- package/dist/tools/file-operations/replace.js +80 -0
- package/dist/tools/file-operations/shared/git-helper.d.ts +4 -0
- package/dist/tools/file-operations/shared/git-helper.d.ts.map +1 -0
- package/dist/tools/file-operations/shared/git-helper.js +29 -0
- package/dist/tools/file-operations/shared/helpers.d.ts +19 -0
- package/dist/tools/file-operations/shared/helpers.d.ts.map +1 -0
- package/dist/tools/file-operations/shared/helpers.js +133 -0
- package/dist/tools/file-operations/view/index.d.ts +14 -0
- package/dist/tools/file-operations/view/index.d.ts.map +1 -0
- package/dist/tools/file-operations/view/index.js +56 -0
- package/dist/tools/grep/index.d.ts.map +1 -1
- package/dist/tools/grep/index.js +2 -9
- package/dist/tools/grep/ripgrep/index.d.ts +2 -2
- package/dist/tools/grep/ripgrep/index.d.ts.map +1 -1
- package/dist/tools/grep/ripgrep/index.js +3 -3
- package/dist/tools/grep/types.d.ts.map +1 -0
- package/dist/tools/index.d.ts +7 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +66 -22
- package/dist/tools/issues/create-issue.d.ts +3 -0
- package/dist/tools/issues/create-issue.d.ts.map +1 -0
- package/dist/tools/issues/create-issue.js +76 -0
- package/dist/tools/issues/index.d.ts +4 -0
- package/dist/tools/issues/index.d.ts.map +1 -0
- package/dist/tools/issues/index.js +9 -0
- package/dist/tools/issues/list-issues.d.ts +3 -0
- package/dist/tools/issues/list-issues.d.ts.map +1 -0
- package/dist/tools/issues/list-issues.js +32 -0
- package/dist/tools/issues/metadata-schema.d.ts +24 -0
- package/dist/tools/issues/metadata-schema.d.ts.map +1 -0
- package/dist/tools/issues/metadata-schema.js +22 -0
- package/dist/tools/issues/update-issue.d.ts +3 -0
- package/dist/tools/issues/update-issue.d.ts.map +1 -0
- package/dist/tools/issues/update-issue.js +75 -0
- package/dist/tools/issues/utils.d.ts +5 -0
- package/dist/tools/issues/utils.d.ts.map +1 -0
- package/dist/tools/issues/utils.js +26 -0
- package/dist/tools/list-environments.d.ts.map +1 -1
- package/dist/tools/list-environments.js +4 -0
- package/dist/tools/{merge-conflicts.d.ts → merge-conflicts/index.d.ts} +1 -1
- package/dist/tools/merge-conflicts/index.d.ts.map +1 -0
- package/dist/tools/{merge-conflicts.js → merge-conflicts/index.js} +3 -19
- package/dist/tools/test-gen-browser.js +4 -4
- package/dist/tools/test-run-fetcher/index.d.ts.map +1 -1
- package/dist/tools/test-run-fetcher/index.js +4 -0
- package/dist/tools/trace-dot-zip/index.d.ts +3 -0
- package/dist/tools/trace-dot-zip/index.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/index.js +48 -0
- package/dist/tools/trace-dot-zip/types.d.ts +86 -0
- package/dist/tools/trace-dot-zip/types.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/types.js +2 -0
- package/dist/tools/trace-dot-zip/utils/console-trace.d.ts +7 -0
- package/dist/tools/trace-dot-zip/utils/console-trace.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/utils/console-trace.js +34 -0
- package/dist/tools/trace-dot-zip/utils/extract-zip.d.ts +21 -0
- package/dist/tools/trace-dot-zip/utils/extract-zip.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/utils/extract-zip.js +174 -0
- package/dist/tools/trace-dot-zip/utils/network-trace.d.ts +21 -0
- package/dist/tools/trace-dot-zip/utils/network-trace.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/utils/network-trace.js +189 -0
- package/dist/tools/triage-summary/index.d.ts +3 -0
- package/dist/tools/triage-summary/index.d.ts.map +1 -0
- package/dist/tools/triage-summary/index.js +51 -0
- package/dist/tools/triage-summary/types.d.ts +5 -0
- package/dist/tools/triage-summary/types.d.ts.map +1 -0
- package/dist/tools/triage-summary/types.js +2 -0
- package/dist/tools/triage-summary/utils.d.ts +4 -0
- package/dist/tools/triage-summary/utils.d.ts.map +1 -0
- package/dist/tools/triage-summary/utils.js +16 -0
- package/dist/tools/upgrade-packages/index.d.ts.map +1 -1
- package/dist/tools/upgrade-packages/index.js +5 -15
- package/dist/tools/view-failed-test-run-report/index.d.ts +12 -0
- package/dist/tools/view-failed-test-run-report/index.d.ts.map +1 -0
- package/dist/tools/view-failed-test-run-report/index.js +163 -0
- package/dist/trace-utils/index.d.ts +4 -0
- package/dist/trace-utils/index.d.ts.map +1 -0
- package/dist/trace-utils/index.js +10 -0
- package/dist/utils/dedup-image-fs.d.ts +27 -0
- package/dist/utils/dedup-image-fs.d.ts.map +1 -0
- package/dist/utils/dedup-image-fs.js +88 -0
- package/dist/utils/dedup-image.d.ts +25 -0
- package/dist/utils/dedup-image.d.ts.map +1 -0
- package/dist/utils/dedup-image.js +80 -0
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/env.js +0 -1
- package/dist/utils/json.js +1 -1
- package/dist/utils/model.d.ts +3 -0
- package/dist/utils/model.d.ts.map +1 -0
- package/dist/utils/model.js +18 -0
- package/dist/utils/playwright-report-parser.d.ts +13 -0
- package/dist/utils/playwright-report-parser.d.ts.map +1 -0
- package/dist/utils/playwright-report-parser.js +138 -0
- package/dist/utils/slug.d.ts +1 -0
- package/dist/utils/slug.d.ts.map +1 -1
- package/dist/utils/slug.js +9 -1
- package/dist/utils/stripAnsi.d.ts.map +1 -1
- package/dist/utils/stripAnsi.js +1 -3
- package/eslint.config.mjs +43 -0
- package/package.json +22 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/agent/codegen/generate-code-apply-changes.d.ts +0 -13
- package/dist/agent/codegen/generate-code-apply-changes.d.ts.map +0 -1
- package/dist/agent/codegen/generate-code-apply-changes.js +0 -379
- package/dist/agent/codegen/repo-edit.d.ts +0 -23
- package/dist/agent/codegen/repo-edit.d.ts.map +0 -1
- package/dist/agent/codegen/repo-edit.js +0 -81
- package/dist/agent/codegen/run.d.ts +0 -20
- package/dist/agent/codegen/run.d.ts.map +0 -1
- package/dist/agent/codegen/run.js +0 -116
- package/dist/agent/enrich-prompt/index.d.ts +0 -12
- package/dist/agent/enrich-prompt/index.d.ts.map +0 -1
- package/dist/agent/enrich-prompt/index.js +0 -80
- package/dist/agent/enrich-prompt/utils.d.ts +0 -6
- package/dist/agent/enrich-prompt/utils.d.ts.map +0 -1
- package/dist/agent/enrich-prompt/utils.js +0 -11
- package/dist/agent/infer-agent/index.d.ts +0 -10
- package/dist/agent/infer-agent/index.d.ts.map +0 -1
- package/dist/agent/infer-agent/index.js +0 -60
- package/dist/evals/add-scenario-agent.evals.d.ts +0 -4
- package/dist/evals/add-scenario-agent.evals.d.ts.map +0 -1
- package/dist/evals/add-scenario-agent.evals.js +0 -44
- package/dist/evals/infer-master-or-code-agent.evals.d.ts +0 -4
- package/dist/evals/infer-master-or-code-agent.evals.d.ts.map +0 -1
- package/dist/evals/infer-master-or-code-agent.evals.js +0 -22
- package/dist/file-info/file-system.d.ts +0 -3
- package/dist/file-info/file-system.d.ts.map +0 -1
- package/dist/file-info/github.d.ts +0 -3
- package/dist/file-info/github.d.ts.map +0 -1
- package/dist/file-info/github.js +0 -107
- package/dist/tools/commit-and-create-pr.d.ts.map +0 -1
- package/dist/tools/delete-file.d.ts.map +0 -1
- package/dist/tools/download-build.d.ts.map +0 -1
- package/dist/tools/grep/ripgrep/types.d.ts.map +0 -1
- package/dist/tools/merge-conflicts.d.ts.map +0 -1
- package/dist/tools/str_replace_editor.d.ts +0 -22
- package/dist/tools/str_replace_editor.d.ts.map +0 -1
- package/dist/tools/str_replace_editor.js +0 -429
- /package/dist/tools/grep/{ripgrep/types.d.ts → types.d.ts} +0 -0
- /package/dist/tools/grep/{ripgrep/types.js → types.js} +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
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 crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const fetch_video_analysis_1 = require("../definitions/fetch-video-analysis");
|
|
9
|
+
const local_ffmpeg_client_1 = require("./local-ffmpeg-client");
|
|
10
|
+
const utils_1 = require("./utils");
|
|
11
|
+
const video_analysis_1 = require("./video-analysis");
|
|
12
|
+
exports.fetchVideoAnalysis = {
|
|
13
|
+
...fetch_video_analysis_1.fetchVideoAnalysis,
|
|
14
|
+
execute: async ({ input, trace, }) => {
|
|
15
|
+
const { videoUrl } = input;
|
|
16
|
+
const videoUrlHash = crypto_1.default
|
|
17
|
+
.createHash("sha256")
|
|
18
|
+
.update(videoUrl)
|
|
19
|
+
.digest("hex")
|
|
20
|
+
.substring(0, 16);
|
|
21
|
+
const R2_BASE_URL = `https://video-analysis.empirical.run/${videoUrlHash}/`;
|
|
22
|
+
const videoAnalysisSpan = trace?.span({
|
|
23
|
+
name: "video-analysis",
|
|
24
|
+
input: { videoUrl },
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
const ffmpegClient = new local_ffmpeg_client_1.LocalFFmpegClient();
|
|
28
|
+
const response = await fetch(videoUrl, { method: "HEAD" });
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
return {
|
|
31
|
+
result: `Failed to access video at ${videoUrl}: ${response.statusText}`,
|
|
32
|
+
isError: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const processingSpan = videoAnalysisSpan?.span({
|
|
36
|
+
name: "ffmpeg-processing",
|
|
37
|
+
input: { videoUrl },
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
const { totalFramesCount, uniqueFramesCount, uniqueFrames } = await ffmpegClient.extractVideoFrames(videoUrl, "./temp");
|
|
41
|
+
processingSpan?.end({
|
|
42
|
+
output: {
|
|
43
|
+
totalFramesCount,
|
|
44
|
+
uniqueFramesCount,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
console.log(`[video-analysis] Analyzing ${uniqueFrames.length} frames with LLM`);
|
|
48
|
+
const videoInfoWithoutAnalysis = {
|
|
49
|
+
total_frames_count: totalFramesCount,
|
|
50
|
+
unique_frames_count: uniqueFramesCount,
|
|
51
|
+
video_url: videoUrl,
|
|
52
|
+
video_url_hash: videoUrlHash,
|
|
53
|
+
created_at: new Date().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
const framesUploadPromise = (0, utils_1.uploadFramesToR2)(videoInfoWithoutAnalysis, uniqueFrames, R2_BASE_URL);
|
|
56
|
+
const { analysis: llmAnalysis, usage } = await (0, video_analysis_1.analyzeFramesWithLLM)(uniqueFrames.map((frame) => frame.image), videoAnalysisSpan, "gemini-2.5-pro");
|
|
57
|
+
console.log(`[video-analysis] Finished Analyzing ${uniqueFrames.length} frames with LLM`);
|
|
58
|
+
const videoInfo = {
|
|
59
|
+
...videoInfoWithoutAnalysis,
|
|
60
|
+
llm_analysis: llmAnalysis,
|
|
61
|
+
video_url_hash: videoUrlHash,
|
|
62
|
+
};
|
|
63
|
+
const uniqueFramesWithUrls = await framesUploadPromise;
|
|
64
|
+
await (0, utils_1.uploadAnalysisToR2)(videoInfo, uniqueFramesWithUrls, R2_BASE_URL);
|
|
65
|
+
return {
|
|
66
|
+
result: JSON.stringify(videoInfo, null, 2),
|
|
67
|
+
isError: false,
|
|
68
|
+
usage,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (ffmpegError) {
|
|
72
|
+
processingSpan?.end();
|
|
73
|
+
throw ffmpegError;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error("Error in video analysis", error);
|
|
78
|
+
videoAnalysisSpan?.end();
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
return {
|
|
81
|
+
result: `Error analyzing video: ${message}`,
|
|
82
|
+
isError: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
videoAnalysisSpan?.end();
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare class LocalFFmpegClient {
|
|
2
|
+
private static readonly MAX_VIDEO_DURATION_SECONDS;
|
|
3
|
+
private static readonly CHUNK_DURATION_SECONDS;
|
|
4
|
+
constructor();
|
|
5
|
+
private checkFFmpegAvailability;
|
|
6
|
+
private getVideoDuration;
|
|
7
|
+
private validateVideoChunk;
|
|
8
|
+
private downloadVideo;
|
|
9
|
+
private createVideoChunks;
|
|
10
|
+
private extractFramesToFiles;
|
|
11
|
+
private processVideoChunks;
|
|
12
|
+
extractVideoFrames(videoUrl: string, outputDir: string): Promise<{
|
|
13
|
+
totalFramesCount: number;
|
|
14
|
+
uniqueFramesCount: number;
|
|
15
|
+
uniqueFrames: {
|
|
16
|
+
metadata: {
|
|
17
|
+
index: number;
|
|
18
|
+
path: string;
|
|
19
|
+
};
|
|
20
|
+
image: string;
|
|
21
|
+
}[];
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=local-ffmpeg-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-ffmpeg-client.d.ts","sourceRoot":"","sources":["../../../src/tools/fetch-video-analysis/local-ffmpeg-client.ts"],"names":[],"mappings":"AASA,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,0BAA0B,CAAW;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAU;;IAMxD,OAAO,CAAC,uBAAuB;YAWjB,gBAAgB;YAiBhB,kBAAkB;YAgClB,aAAa;YAeb,iBAAiB;YAoDjB,oBAAoB;YAiCpB,kBAAkB;IAyD1B,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QACT,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE;YACZ,QAAQ,EAAE;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;YAC1C,KAAK,EAAE,MAAM,CAAC;SACf,EAAE,CAAC;KACL,CAAC;CAgEH"}
|
|
@@ -0,0 +1,209 @@
|
|
|
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.LocalFFmpegClient = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const util_1 = require("util");
|
|
11
|
+
const dedup_image_fs_1 = require("../../utils/dedup-image-fs");
|
|
12
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
13
|
+
class LocalFFmpegClient {
|
|
14
|
+
static MAX_VIDEO_DURATION_SECONDS = 15 * 60; // 15 minutes
|
|
15
|
+
static CHUNK_DURATION_SECONDS = 2 * 60; // 2 minutes
|
|
16
|
+
constructor() {
|
|
17
|
+
this.checkFFmpegAvailability();
|
|
18
|
+
}
|
|
19
|
+
checkFFmpegAvailability() {
|
|
20
|
+
try {
|
|
21
|
+
(0, child_process_1.execSync)("ffmpeg -version", { stdio: "ignore" });
|
|
22
|
+
(0, child_process_1.execSync)("ffprobe -version", { stdio: "ignore" });
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new Error(`ffmpeg and ffprobe are required for video processing: ${error}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async getVideoDuration(videoPath) {
|
|
29
|
+
const command = `ffprobe -v quiet -show_entries format=duration -of csv=p=0 "${videoPath}"`;
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execAsync(command);
|
|
32
|
+
const duration = parseFloat(stdout.trim());
|
|
33
|
+
if (isNaN(duration)) {
|
|
34
|
+
throw new Error("Could not determine video duration");
|
|
35
|
+
}
|
|
36
|
+
return duration;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw new Error(`Failed to get video duration: ${error}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async validateVideoChunk(chunkPath) {
|
|
43
|
+
const command = `ffprobe -v error -show_entries format=duration -of csv=p=0 "${chunkPath}"`;
|
|
44
|
+
try {
|
|
45
|
+
const { stdout, stderr } = await execAsync(command);
|
|
46
|
+
if (stderr && stderr.toLowerCase().includes("error")) {
|
|
47
|
+
console.warn(`Chunk validation found errors: ${stderr}`);
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const duration = parseFloat(stdout.trim());
|
|
51
|
+
if (isNaN(duration) || duration <= 0) {
|
|
52
|
+
console.warn(`Chunk has invalid duration: ${duration}`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
// Check file size as additional validation
|
|
56
|
+
const stats = await fs_1.promises.stat(chunkPath);
|
|
57
|
+
if (stats.size < 1024) {
|
|
58
|
+
// Less than 1KB is suspicious for a video chunk
|
|
59
|
+
console.warn(`Chunk file size too small: ${stats.size} bytes`);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.warn(`Chunk validation failed: ${error}`);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async downloadVideo(videoUrl, outputPath) {
|
|
70
|
+
console.log(`Downloading video from: ${videoUrl}`);
|
|
71
|
+
const response = await fetch(videoUrl);
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new Error(`Failed to download video: ${response.statusText}`);
|
|
74
|
+
}
|
|
75
|
+
const buffer = await response.arrayBuffer();
|
|
76
|
+
await fs_1.promises.writeFile(outputPath, Buffer.from(buffer));
|
|
77
|
+
}
|
|
78
|
+
async createVideoChunks(videoPath, outputDir, duration) {
|
|
79
|
+
const chunkPaths = [];
|
|
80
|
+
const chunkCount = Math.ceil(duration / LocalFFmpegClient.CHUNK_DURATION_SECONDS);
|
|
81
|
+
console.log(`Creating ${chunkCount} chunks of ${LocalFFmpegClient.CHUNK_DURATION_SECONDS} seconds each`);
|
|
82
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
83
|
+
const startTime = i * LocalFFmpegClient.CHUNK_DURATION_SECONDS;
|
|
84
|
+
const chunkPath = path_1.default.join(outputDir, `chunk_${i.toString().padStart(3, "0")}.mp4`);
|
|
85
|
+
const remainingDuration = duration - startTime;
|
|
86
|
+
const chunkDuration = Math.min(LocalFFmpegClient.CHUNK_DURATION_SECONDS, remainingDuration);
|
|
87
|
+
const command = `ffmpeg -i "${videoPath}" -ss ${startTime} -t ${chunkDuration} -c:v libx264 -c:a aac -preset ultrafast -crf 28 "${chunkPath}"`;
|
|
88
|
+
try {
|
|
89
|
+
await execAsync(command);
|
|
90
|
+
// Validate the created chunk
|
|
91
|
+
const isValid = await this.validateVideoChunk(chunkPath);
|
|
92
|
+
if (isValid) {
|
|
93
|
+
chunkPaths.push(chunkPath);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.warn(`Chunk ${i} appears corrupted, skipping...`);
|
|
97
|
+
try {
|
|
98
|
+
await fs_1.promises.unlink(chunkPath);
|
|
99
|
+
}
|
|
100
|
+
catch (unlinkError) {
|
|
101
|
+
console.warn(`Failed to remove corrupted chunk: ${unlinkError}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
throw new Error(`Failed to create chunk ${i}: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return chunkPaths;
|
|
110
|
+
}
|
|
111
|
+
async extractFramesToFiles(videoPath, outputDir, chunkIndex) {
|
|
112
|
+
await fs_1.promises.mkdir(outputDir, { recursive: true });
|
|
113
|
+
const framePrefix = chunkIndex !== undefined ? `chunk${chunkIndex}_frame` : "frame";
|
|
114
|
+
const framePattern = path_1.default.join(outputDir, `${framePrefix}_%04d.png`);
|
|
115
|
+
const command = `ffmpeg -i "${videoPath}" -vf "fps=30" "${framePattern}"`;
|
|
116
|
+
console.log(`Extracting frames with command: ${command}`);
|
|
117
|
+
try {
|
|
118
|
+
await execAsync(command);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
throw new Error(`Frame extraction failed: ${error}`);
|
|
122
|
+
}
|
|
123
|
+
const files = await fs_1.promises.readdir(outputDir);
|
|
124
|
+
const frameFiles = files
|
|
125
|
+
.filter((file) => file.startsWith(framePrefix) && file.endsWith(".png"))
|
|
126
|
+
.sort()
|
|
127
|
+
.map((file) => path_1.default.join(outputDir, file));
|
|
128
|
+
console.log(`Extracted ${frameFiles.length} frames from ${chunkIndex !== undefined ? `chunk ${chunkIndex}` : "video"}`);
|
|
129
|
+
return frameFiles;
|
|
130
|
+
}
|
|
131
|
+
async processVideoChunks(chunkPaths, workingDir) {
|
|
132
|
+
const allFramePaths = [];
|
|
133
|
+
const consolidatedFramesDir = path_1.default.join(workingDir, "consolidated_frames");
|
|
134
|
+
await fs_1.promises.mkdir(consolidatedFramesDir, { recursive: true });
|
|
135
|
+
let globalFrameIndex = 0;
|
|
136
|
+
for (let i = 0; i < chunkPaths.length; i++) {
|
|
137
|
+
const chunkPath = chunkPaths[i];
|
|
138
|
+
const chunkFramesDir = path_1.default.join(workingDir, `chunk_${i}_frames`);
|
|
139
|
+
console.log(`Processing chunk ${i + 1}/${chunkPaths.length}`);
|
|
140
|
+
if (chunkPath === undefined) {
|
|
141
|
+
throw new Error(`Chunk path is undefined for chunk ${i + 1}`);
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const chunkFramePaths = await this.extractFramesToFiles(chunkPath, chunkFramesDir, i);
|
|
145
|
+
for (const framePath of chunkFramePaths) {
|
|
146
|
+
const newFramePath = path_1.default.join(consolidatedFramesDir, `frame_${globalFrameIndex.toString().padStart(6, "0")}.png`);
|
|
147
|
+
await fs_1.promises.rename(framePath, newFramePath);
|
|
148
|
+
allFramePaths.push(newFramePath);
|
|
149
|
+
globalFrameIndex++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.warn(`Failed to process chunk ${i + 1}: ${error}. Skipping this chunk.`);
|
|
154
|
+
// Continue with other chunks instead of failing completely
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await fs_1.promises.rm(chunkFramesDir, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.warn(`Failed to clean up chunk frames directory: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
if (global.gc) {
|
|
163
|
+
global.gc();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return allFramePaths;
|
|
167
|
+
}
|
|
168
|
+
async extractVideoFrames(videoUrl, outputDir) {
|
|
169
|
+
const workingDir = path_1.default.join(process.cwd(), outputDir);
|
|
170
|
+
const videoPath = path_1.default.join(workingDir, `video-${Date.now()}.webm`);
|
|
171
|
+
try {
|
|
172
|
+
await fs_1.promises.mkdir(workingDir, { recursive: true });
|
|
173
|
+
await this.downloadVideo(videoUrl, videoPath);
|
|
174
|
+
const duration = await this.getVideoDuration(videoPath);
|
|
175
|
+
console.log(`Video duration: ${Math.round(duration)} seconds`);
|
|
176
|
+
if (duration > LocalFFmpegClient.MAX_VIDEO_DURATION_SECONDS) {
|
|
177
|
+
throw new Error(`Video duration (${Math.round(duration)}s) exceeds maximum allowed duration (${LocalFFmpegClient.MAX_VIDEO_DURATION_SECONDS}s)`);
|
|
178
|
+
}
|
|
179
|
+
const chunkPaths = await this.createVideoChunks(videoPath, workingDir, duration);
|
|
180
|
+
const allFramePaths = await this.processVideoChunks(chunkPaths, workingDir);
|
|
181
|
+
const allFramesCount = allFramePaths.length;
|
|
182
|
+
const uniqueImages = await (0, dedup_image_fs_1.deduplicateImageFiles)({
|
|
183
|
+
imagePaths: allFramePaths,
|
|
184
|
+
batchSize: 50,
|
|
185
|
+
threshold: 0.001,
|
|
186
|
+
logPrefix: "ffmpeg-chunk-frame-dedup",
|
|
187
|
+
});
|
|
188
|
+
console.log(`Filtered to ${uniqueImages.length} unique frames from ${allFramesCount} total frames across ${chunkPaths.length} chunks`);
|
|
189
|
+
if (uniqueImages.length === 0) {
|
|
190
|
+
if (chunkPaths.length === 0) {
|
|
191
|
+
throw new Error("No valid video chunks were created. The video may be corrupted or in an unsupported format.");
|
|
192
|
+
}
|
|
193
|
+
throw new Error("No frames were extracted from the video");
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
totalFramesCount: allFramesCount,
|
|
197
|
+
uniqueFramesCount: uniqueImages.length,
|
|
198
|
+
uniqueFrames: uniqueImages,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
throw new Error(`Frame extraction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
await fs_1.promises.rm(workingDir, { recursive: true });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.LocalFFmpegClient = LocalFFmpegClient;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { UniqueFrameInfo, VideoAnalysisInfo } from "@empiricalrun/shared-types";
|
|
2
|
+
export declare const uploadFramesToR2: (videoInfo: Omit<VideoAnalysisInfo, "llm_analysis">, frames: {
|
|
3
|
+
metadata: {
|
|
4
|
+
index: number;
|
|
5
|
+
path: string;
|
|
6
|
+
};
|
|
7
|
+
image: string;
|
|
8
|
+
}[], r2BaseUrl: string) => Promise<UniqueFrameInfo[]>;
|
|
9
|
+
export declare const uploadAnalysisToR2: (videoInfo: VideoAnalysisInfo, uniqueFrames: UniqueFrameInfo[], r2BaseUrl: string) => Promise<string>;
|
|
10
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/fetch-video-analysis/utils.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,iBAAiB,EAElB,MAAM,4BAA4B,CAAC;AAmCpC,eAAO,MAAM,gBAAgB,GAC3B,WAAW,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,EAClD,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,kBAAkB,GAC7B,WAAW,iBAAiB,EAC5B,cAAc,eAAe,EAAE,EAC/B,WAAW,MAAM,KAChB,OAAO,CAAC,MAAM,CAwChB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.uploadAnalysisToR2 = exports.uploadFramesToR2 = void 0;
|
|
4
|
+
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
5
|
+
const uploadFilesToR2 = async (files, videoUrlHash) => {
|
|
6
|
+
const label = `video-analysis-upload:${videoUrlHash}`;
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
console.log(`[${label}] preparing ${files.length} files for upload...`);
|
|
9
|
+
console.log(`[${label}] starting upload of ${files.length} files to video-analysis/${videoUrlHash}/...`);
|
|
10
|
+
const interval = setInterval(() => {
|
|
11
|
+
const secs = Math.round((Date.now() - start) / 1000);
|
|
12
|
+
console.log(`[${label}] uploading... ${secs}s elapsed`);
|
|
13
|
+
}, 1000);
|
|
14
|
+
try {
|
|
15
|
+
await (0, r2_uploader_1.uploadInMemoryFiles)({
|
|
16
|
+
files,
|
|
17
|
+
destinationDir: videoUrlHash,
|
|
18
|
+
uploadBucket: "video-analysis",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
clearInterval(interval);
|
|
23
|
+
const secs = Math.round((Date.now() - start) / 1000);
|
|
24
|
+
console.log(`[${label}] upload complete in ${secs}s`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const uploadFramesToR2 = async (videoInfo, frames, r2BaseUrl) => {
|
|
28
|
+
const { video_url_hash: videoUrlHash } = videoInfo;
|
|
29
|
+
const frameFiles = frames.map((f) => {
|
|
30
|
+
const fileName = `frame_${f.metadata.index}_${videoUrlHash}.png`;
|
|
31
|
+
const buffer = Buffer.from(f.image, "base64");
|
|
32
|
+
return { buffer, fileName, mimeType: "image/png" };
|
|
33
|
+
});
|
|
34
|
+
console.log(`[video-analysis-upload] uploading ${frameFiles.length} frames to video-analysis/${videoUrlHash}/...`);
|
|
35
|
+
await uploadFilesToR2(frameFiles, videoUrlHash);
|
|
36
|
+
return frames.map((f) => ({
|
|
37
|
+
index: f.metadata.index,
|
|
38
|
+
path: f.metadata.path,
|
|
39
|
+
fileName: `frame_${f.metadata.index}_${videoUrlHash}.png`,
|
|
40
|
+
url: `${r2BaseUrl}frame_${f.metadata.index}_${videoUrlHash}.png`,
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
exports.uploadFramesToR2 = uploadFramesToR2;
|
|
44
|
+
const uploadAnalysisToR2 = async (videoInfo, uniqueFrames, r2BaseUrl) => {
|
|
45
|
+
try {
|
|
46
|
+
const { video_url_hash: videoUrlHash, total_frames_count, unique_frames_count, video_url, llm_analysis, created_at, } = videoInfo;
|
|
47
|
+
const summary = {
|
|
48
|
+
total_frames_count,
|
|
49
|
+
unique_frames_count,
|
|
50
|
+
video_url,
|
|
51
|
+
llm_analysis,
|
|
52
|
+
video_url_hash: videoUrlHash,
|
|
53
|
+
uniqueFrames,
|
|
54
|
+
created_at,
|
|
55
|
+
};
|
|
56
|
+
const filesToUpload = [
|
|
57
|
+
{
|
|
58
|
+
buffer: Buffer.from(JSON.stringify(summary, null, 2)),
|
|
59
|
+
fileName: "summary.json",
|
|
60
|
+
mimeType: "application/json",
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
console.log(`[video-analysis-upload] uploading ${filesToUpload.length} files to video-analysis/${videoUrlHash}/...`);
|
|
64
|
+
await uploadFilesToR2(filesToUpload, videoUrlHash);
|
|
65
|
+
return r2BaseUrl;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error("Error uploading video analysis to R2:", error);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
exports.uploadAnalysisToR2 = uploadAnalysisToR2;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TraceClient } from "@empiricalrun/llm";
|
|
2
|
+
import { SupportedChatModels, Usage } from "@empiricalrun/shared-types";
|
|
3
|
+
export declare function analyzeFramesWithLLM(frameBase64Data: string[], trace?: TraceClient, selectedModel?: SupportedChatModels, apiKey?: string): Promise<{
|
|
4
|
+
analysis: string;
|
|
5
|
+
usage: Usage;
|
|
6
|
+
}>;
|
|
7
|
+
//# sourceMappingURL=video-analysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,KAAK,EACN,MAAM,4BAA4B,CAAC;AAIpC,wBAAsB,oBAAoB,CACxC,eAAe,EAAE,MAAM,EAAE,EACzB,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,CAgE7C"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeFramesWithLLM = analyzeFramesWithLLM;
|
|
4
|
+
const chat_1 = require("@empiricalrun/llm/chat");
|
|
5
|
+
const models_1 = require("../../agent/chat/models");
|
|
6
|
+
async function analyzeFramesWithLLM(frameBase64Data, trace, selectedModel, apiKey) {
|
|
7
|
+
const llmSpan = trace?.span({
|
|
8
|
+
name: "llm-frame-analysis",
|
|
9
|
+
input: { frameCount: frameBase64Data.length },
|
|
10
|
+
});
|
|
11
|
+
const selectedChatModel = selectedModel || (0, models_1.getDefaultChatModelId)();
|
|
12
|
+
try {
|
|
13
|
+
// TODO: Move to canonical chat model
|
|
14
|
+
let chatModel = new chat_1.GeminiChatModel(selectedChatModel, [], apiKey || process.env.GOOGLE_API_KEY);
|
|
15
|
+
chatModel.validateEnvVarsForAuth();
|
|
16
|
+
const frameAttachments = frameBase64Data.map((frameBase64, index) => ({
|
|
17
|
+
base64Data: frameBase64,
|
|
18
|
+
contentType: "image/png",
|
|
19
|
+
name: `frame-${index}.png`,
|
|
20
|
+
}));
|
|
21
|
+
const userMessage = `
|
|
22
|
+
Analyze the ${frameBase64Data.length} frames that are extracted from a screen recording.`;
|
|
23
|
+
chatModel.pushUserMessage(userMessage, frameAttachments);
|
|
24
|
+
const systemPrompt = `
|
|
25
|
+
You are given a set of unique frames from a screen recording. Your job is to return a verbose bullet list of what is going on in the video. Don't miss out on anything.
|
|
26
|
+
`;
|
|
27
|
+
// Get LLM response
|
|
28
|
+
const response = await chatModel.getLLMResponse({
|
|
29
|
+
systemPrompt,
|
|
30
|
+
tools: [], // No tools needed for this analysis
|
|
31
|
+
selectedModel: selectedChatModel,
|
|
32
|
+
trace: llmSpan,
|
|
33
|
+
hasThinkingEnabled: false,
|
|
34
|
+
hasInterleavedThinkingEnabled: false,
|
|
35
|
+
});
|
|
36
|
+
if (!response) {
|
|
37
|
+
throw new Error("No response from LLM");
|
|
38
|
+
}
|
|
39
|
+
chatModel.pushMessage(response);
|
|
40
|
+
const usage = chatModel.getUsage();
|
|
41
|
+
const analysisResult = chatModel.getHumanReadableLatestMessage();
|
|
42
|
+
llmSpan?.end({ output: { analysis: analysisResult?.textMessage } });
|
|
43
|
+
return {
|
|
44
|
+
analysis: analysisResult?.textMessage || "No analysis generated",
|
|
45
|
+
usage,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error("Error in LLM frame analysis", error);
|
|
50
|
+
llmSpan?.end();
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
throw new Error(`Error analyzing frames with AI: ${message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CollectArtifacts, ToolResult } from "@empiricalrun/shared-types";
|
|
2
|
+
import type { StrReplaceInputParams } from "./shared/helpers";
|
|
3
|
+
declare function fileCreateExecutor({ input, filePath, absoluteFilePath, repoDir, collectArtifacts, }: {
|
|
4
|
+
input: StrReplaceInputParams;
|
|
5
|
+
filePath: string;
|
|
6
|
+
absoluteFilePath: string;
|
|
7
|
+
repoDir: string;
|
|
8
|
+
collectArtifacts?: CollectArtifacts;
|
|
9
|
+
}): Promise<ToolResult>;
|
|
10
|
+
export { fileCreateExecutor };
|
|
11
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/tools/file-operations/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAM1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE9D,iBAAe,kBAAkB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,OAAO,EACP,gBAAgB,GACjB,EAAE;IACD,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,GAAG,OAAO,CAAC,UAAU,CAAC,CAuDtB;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
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.fileCreateExecutor = fileCreateExecutor;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const web_1 = require("../../bin/utils/platform/web");
|
|
10
|
+
const git_helper_1 = require("./shared/git-helper");
|
|
11
|
+
async function fileCreateExecutor({ input, filePath, absoluteFilePath, repoDir, collectArtifacts, }) {
|
|
12
|
+
if (input.file_text === undefined || input.file_text === null) {
|
|
13
|
+
return {
|
|
14
|
+
result: "Error: file_text is required for create command",
|
|
15
|
+
isError: true,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (filePath.endsWith("test.ts")) {
|
|
19
|
+
return {
|
|
20
|
+
result: "Error: Creating test.ts files is not allowed. Did you mean spec.ts?",
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (filePath.endsWith("spec.ts") && !filePath.startsWith("tests/")) {
|
|
25
|
+
return {
|
|
26
|
+
result: "Error: Creating spec.ts files is not allowed outside tests/ directory",
|
|
27
|
+
isError: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const fileName = path_1.default.basename(filePath);
|
|
31
|
+
if (!fileName.includes(".")) {
|
|
32
|
+
return {
|
|
33
|
+
result: `Error: File name must include a file extension. This tool cannot create empty directories.
|
|
34
|
+
If you need to create a file which is inside a directory that does not exist, you can expect this tool to create
|
|
35
|
+
the required directories recursively for the new file.`,
|
|
36
|
+
isError: true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const parentDir = path_1.default.dirname(absoluteFilePath);
|
|
40
|
+
if (parentDir !== "." && !fs_1.default.existsSync(parentDir)) {
|
|
41
|
+
// Ensure parent directory exists
|
|
42
|
+
fs_1.default.mkdirSync(parentDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
fs_1.default.writeFileSync(absoluteFilePath, input.file_text);
|
|
45
|
+
// Collect git patch artifact
|
|
46
|
+
await (0, git_helper_1.collectGitPatchArtifact)(filePath, repoDir, "create", collectArtifacts);
|
|
47
|
+
let createTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
|
|
48
|
+
if (!createTypescriptResult.success) {
|
|
49
|
+
return {
|
|
50
|
+
result: `File ${filePath} has been created. However, type checks are failing with errors:\n\n${createTypescriptResult.errors.join("\n")}`,
|
|
51
|
+
isError: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return {
|
|
56
|
+
result: `Successfully created file ${filePath}`,
|
|
57
|
+
isError: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CollectArtifacts, IDashboardAPIClient, Tool, ToolResult } from "@empiricalrun/shared-types";
|
|
2
|
+
import { StrReplaceInputParams } from "./shared/helpers";
|
|
3
|
+
/**
|
|
4
|
+
* Our implementation of Claude's built-in text editor tool
|
|
5
|
+
* https://docs.anthropic.com/en/docs/build-with-claude/tool-use/text-editor-tool
|
|
6
|
+
*/
|
|
7
|
+
declare function strReplaceEditorExecutor({ input, repoPath, collectArtifacts, apiClient, }: {
|
|
8
|
+
input: StrReplaceInputParams;
|
|
9
|
+
repoPath: string;
|
|
10
|
+
collectArtifacts?: CollectArtifacts;
|
|
11
|
+
apiClient?: IDashboardAPIClient;
|
|
12
|
+
}): Promise<ToolResult>;
|
|
13
|
+
declare const textEditorTools: Tool[];
|
|
14
|
+
export { strReplaceEditorExecutor, textEditorTools };
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/file-operations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,IAAI,EAEJ,UAAU,EACX,MAAM,4BAA4B,CAAC;AAQpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAGzD;;;GAGG;AACH,iBAAe,wBAAwB,CAAC,EACtC,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,SAAS,GACV,EAAE;IACD,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC,GAAG,OAAO,CAAC,UAAU,CAAC,CAoDtB;AA+ED,QAAA,MAAM,eAAe,EAAE,IAAI,EAK1B,CAAC;AAEF,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,CAAC"}
|