Skip to content

Add analyze_footage agent tool for TwelveLabs Pegasus visual queries - #2

Open
mohit-twelvelabs wants to merge 1 commit into
kevinrss01:mainfrom
mohit-twelvelabs:feat/twelvelabs-integration
Open

Add analyze_footage agent tool for TwelveLabs Pegasus visual queries#2
mohit-twelvelabs wants to merge 1 commit into
kevinrss01:mainfrom
mohit-twelvelabs:feat/twelvelabs-integration

Conversation

@mohit-twelvelabs

Copy link
Copy Markdown

Hi! I'm Mohit, I work at TwelveLabs (@mohit-twelvelabs).

What this adds

A new opt-in AI Gateway tool, analyze_footage, that lets the chat assistant ask TwelveLabs Pegasus a natural-language question about the visual content of an already-indexed clip — scenes, objects, people, on-screen action, on-screen text, or finding the moment that matches a description — before it plans timeline edits.

Framedeck already indexes every uploaded video in TwelveLabs and captures a one-shot summary at upload. This tool lets the agent run a fresh, focused visual question against that footage at edit time, so prompts like "find the drone shot and cut to it" or "where does the speaker pick up the product?" can be answered visually instead of relying only on the transcript.

It complements the existing investigate_transcription tool: visual questions route to analyze_footage, spoken-word questions to investigate_transcription. To make that routing work, get_library_assets_data now surfaces the twelveLabsVideoId for each indexed asset.

How it works

  • Reuses the existing TwelveLabsService.analyzeVideo (Pegasus) and the videoId already captured during upload indexing — no new pipeline, no new env vars beyond the existing 12LABS_API_KEY.
  • Wired into ToolsService the same way as the other tools (tool name in tool-names.ts, creator in tool-creators/, structural FootageAnalyzer dependency to keep the tool-creators decoupled from the Nest module).

Opt-in / non-breaking

The tool is additive. The analyzer is injected as an optional dependency: if it isn't configured the tool returns a skipped result and the assistant carries on. No defaults change and no existing tool or pipeline is touched.

How it was tested

  • pnpm exec jest src/ai-gateway/tools/tool-creators — all 46 tests across 11 suites pass, including the new analyze-footage.tools.spec.ts (no-network unit tests covering the skip, success, and error paths).
  • pnpm exec tsc --noEmit on apps/server — clean.
  • ESLint + Prettier on all changed files — clean.
  • Verified the live TwelveLabs SDK contract used by the wiring: client.analyze is present and a Marengo marengo3.0 text embedding returns a 512-dim vector against the live API. The end-to-end Pegasus call against real footage is server-side and slow, so the tool's request wiring is unit-tested and the SDK path is confirmed; a full footage run is pending a real uploaded asset.

You can grab a free API key at https://twelvelabs.io — there's a generous free tier.

…ries

Lets the chat assistant ask TwelveLabs Pegasus a natural-language question
about the visual content of an already-indexed clip (scenes, objects,
on-screen action, or locating the moment that matches a description) before
planning timeline edits. Reuses the videoId captured during upload indexing
and is exposed as an opt-in tool that skips gracefully when no analyzer is
configured, so default behavior is unchanged.

Surfaces twelveLabsVideoId through get_library_assets_data so the agent can
route visual questions to analyze_footage and transcript questions to
investigate_transcription. Includes no-network unit tests.
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds an analyze_footage tool that routes visual natural-language questions about indexed video clips to TwelveLabs Pegasus, complementing the existing investigate_transcription tool for spoken-word queries. It also surfaces the twelveLabsVideoId on each library asset so the agent knows which video to target.

  • New tool: createAnalyzeFootageTool in tool-creators/analyze-footage.tools.ts — wraps TwelveLabsService.analyzeVideo, returns completed/skipped/error results, and caps answers at 6 000 characters.
  • Library asset data enriched: get_library_assets_data now emits twelveLabsVideoId inline for any indexed asset, and the frontend hook propagates the twelveLabs reference to the editor.
  • Module wiring: TwelveLabsService is added as a provider to ToolsModule, creating a second instance alongside the one already registered in AppModule.

Confidence Score: 3/5

Safe to merge for deployments that already have 12LABS_API_KEY configured; deployments without the key will receive error results from the tool instead of the silent skip the PR description promises.

The opt-in design intent is broken: footageAnalyzer is unconditionally assigned in createToolDependencies, so when 12LABS_API_KEY is absent the tool errors rather than skips. Additionally, TwelveLabsService is now instantiated twice, creating two separate instances with independent caches. Neither issue is a crash or data-loss scenario, but both represent real behavioural gaps relative to what the PR claims.

tools.service.ts and tools.module.ts — the first contains the always-set footageAnalyzer that bypasses the skip path, and the second introduces the duplicate service registration.

Important Files Changed

Filename Overview
apps/server/src/ai-gateway/tools/tool-creators/analyze-footage.tools.ts New tool creator for visual footage analysis; correctly structured with skip/error/complete paths, but reason is incorrectly surfaced as the result note.
apps/server/src/ai-gateway/tools/tools.service.ts Wires TwelveLabsService into ToolDependencies; footageAnalyzer is always set, making the opt-in skip behavior unreachable when the API key is absent.
apps/server/src/ai-gateway/tools/tools.module.ts Registers TwelveLabsService as a provider, duplicating the instance already registered in AppModule.
apps/server/src/ai-gateway/tools/tool-creators/analyze-footage.tools.spec.ts Clean unit tests for skip, success, and error paths; no network calls, good coverage of the three status branches.
apps/server/src/ai-gateway/tools/tool-creators/types.ts Adds FootageAnalyzer, AnalyzeFootageInput, and AnalyzeFootageResult types; structural interface kept clean and decoupled from NestJS.
apps/server/src/ai-gateway/tools/tool-creators/query.tools.ts Surfaces twelveLabsVideoId in the library assets output with a clear hint to use analyze_footage; guarded correctly with type checks.
apps/frontend/src/app/projects/[project-id]/_editor-container/editor/hooks/use-get-library-assets-data.ts Surfaces twelveLabs reference on library asset data; type-cast with as TwelveLabsVideoReference
apps/server/src/prompts/prompts.service.ts Adds routing guidance for analyze_footage vs investigate_transcription in the system prompt; clear and accurate.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Agent as AI Agent
    participant GLT as get_library_assets_data
    participant AFT as analyze_footage tool
    participant TLS as TwelveLabsService
    participant TL as TwelveLabs API (Pegasus)

    Agent->>GLT: call (no args)
    GLT-->>Agent: asset list with twelveLabsVideoId
    Agent->>AFT: call(videoId, prompt)
    AFT->>TLS: "analyzeVideo({videoId, prompt})"
    alt API key configured
        TLS->>TL: "client.analyze({videoId, prompt})"
        TL-->>TLS: response.data
        TLS-->>AFT: answer string
        AFT-->>Agent: status completed with answer
    else API key missing
        TLS--xAFT: throws Missing 12LABS_API_KEY
        AFT-->>Agent: status error with error message
    end
    Note over AFT: footageAnalyzer always set so skip path unreachable
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Agent as AI Agent
    participant GLT as get_library_assets_data
    participant AFT as analyze_footage tool
    participant TLS as TwelveLabsService
    participant TL as TwelveLabs API (Pegasus)

    Agent->>GLT: call (no args)
    GLT-->>Agent: asset list with twelveLabsVideoId
    Agent->>AFT: call(videoId, prompt)
    AFT->>TLS: "analyzeVideo({videoId, prompt})"
    alt API key configured
        TLS->>TL: "client.analyze({videoId, prompt})"
        TL-->>TLS: response.data
        TLS-->>AFT: answer string
        AFT-->>Agent: status completed with answer
    else API key missing
        TLS--xAFT: throws Missing 12LABS_API_KEY
        AFT-->>Agent: status error with error message
    end
    Note over AFT: footageAnalyzer always set so skip path unreachable
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
apps/server/src/ai-gateway/tools/tools.service.ts:120-122
**`footageAnalyzer` is always set — the "skip" path is unreachable in production**

`footageAnalyzer` is unconditionally assigned in `createToolDependencies`, so `deps.footageAnalyzer` in `analyze-footage.tools.ts` is never `undefined`. When `12LABS_API_KEY` is absent, `getClient()` throws `Error('Missing 12LABS_API_KEY')`, which the tool catches and returns as `status: 'error'` — not the `status: 'skipped'` the PR description promises. The "no new env vars / opt-in skip" guarantee stated in the PR is not upheld: any deployment without the key will see error results from this tool rather than silent skips.

### Issue 2 of 3
apps/server/src/ai-gateway/tools/tool-creators/analyze-footage.tools.ts:86
**`reason` (the agent's calling justification) leaked back as the result `note`**

`reason` is the LLM's internal rationale for invoking the tool — not a meaningful description of what the tool found. Using it as `note` in the result means the model's calling justification is reflected verbatim in the tool's output, which the model will then read as context. None of the other tools in this codebase do this. The constant fallback `'Footage analysis completed successfully.'` is the right default.

```suggestion
          note: 'Footage analysis completed successfully.'
```

### Issue 3 of 3
apps/server/src/ai-gateway/tools/tools.module.ts:11
**Duplicate `TwelveLabsService` instances across modules**

`TwelveLabsService` is already registered as a provider in `AppModule` (for `VideoAnalysisService` and `UploadService`). Registering it again here creates a second independent instance with its own `client`, `cachedIndexIds`, and `cachedConfiguredIndexId`. For the current `analyzeVideo` call this is harmless, but if the service ever accumulates state (rate-limit counters, connection pools, etc.) the two instances won't share it. Consider moving `TwelveLabsService` into a dedicated module that both `AppModule` and `ToolsModule` can import.

Reviews (1): Last reviewed commit: "Add analyze_footage AI Gateway tool for ..." | Re-trigger Greptile

Comment on lines +120 to +122
footageAnalyzer: {
analyzeVideo: (args) => this.twelveLabsService.analyzeVideo(args),
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 footageAnalyzer is always set — the "skip" path is unreachable in production

footageAnalyzer is unconditionally assigned in createToolDependencies, so deps.footageAnalyzer in analyze-footage.tools.ts is never undefined. When 12LABS_API_KEY is absent, getClient() throws Error('Missing 12LABS_API_KEY'), which the tool catches and returns as status: 'error' — not the status: 'skipped' the PR description promises. The "no new env vars / opt-in skip" guarantee stated in the PR is not upheld: any deployment without the key will see error results from this tool rather than silent skips.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/ai-gateway/tools/tools.service.ts
Line: 120-122

Comment:
**`footageAnalyzer` is always set — the "skip" path is unreachable in production**

`footageAnalyzer` is unconditionally assigned in `createToolDependencies`, so `deps.footageAnalyzer` in `analyze-footage.tools.ts` is never `undefined`. When `12LABS_API_KEY` is absent, `getClient()` throws `Error('Missing 12LABS_API_KEY')`, which the tool catches and returns as `status: 'error'` — not the `status: 'skipped'` the PR description promises. The "no new env vars / opt-in skip" guarantee stated in the PR is not upheld: any deployment without the key will see error results from this tool rather than silent skips.

How can I resolve this? If you propose a fix, please make it concise.

status: 'completed',
videoId,
answer: trimmed.slice(0, MAX_ANSWER_CHARACTERS),
note: reason ?? 'Footage analysis completed successfully.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 reason (the agent's calling justification) leaked back as the result note

reason is the LLM's internal rationale for invoking the tool — not a meaningful description of what the tool found. Using it as note in the result means the model's calling justification is reflected verbatim in the tool's output, which the model will then read as context. None of the other tools in this codebase do this. The constant fallback 'Footage analysis completed successfully.' is the right default.

Suggested change
note: reason ?? 'Footage analysis completed successfully.',
note: 'Footage analysis completed successfully.'
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/ai-gateway/tools/tool-creators/analyze-footage.tools.ts
Line: 86

Comment:
**`reason` (the agent's calling justification) leaked back as the result `note`**

`reason` is the LLM's internal rationale for invoking the tool — not a meaningful description of what the tool found. Using it as `note` in the result means the model's calling justification is reflected verbatim in the tool's output, which the model will then read as context. None of the other tools in this codebase do this. The constant fallback `'Footage analysis completed successfully.'` is the right default.

```suggestion
          note: 'Footage analysis completed successfully.'
```

How can I resolve this? If you propose a fix, please make it concise.

imports: [RealtimeModule, AudioModule],
controllers: [ToolsController],
providers: [ToolsService],
providers: [ToolsService, TwelveLabsService],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicate TwelveLabsService instances across modules

TwelveLabsService is already registered as a provider in AppModule (for VideoAnalysisService and UploadService). Registering it again here creates a second independent instance with its own client, cachedIndexIds, and cachedConfiguredIndexId. For the current analyzeVideo call this is harmless, but if the service ever accumulates state (rate-limit counters, connection pools, etc.) the two instances won't share it. Consider moving TwelveLabsService into a dedicated module that both AppModule and ToolsModule can import.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/server/src/ai-gateway/tools/tools.module.ts
Line: 11

Comment:
**Duplicate `TwelveLabsService` instances across modules**

`TwelveLabsService` is already registered as a provider in `AppModule` (for `VideoAnalysisService` and `UploadService`). Registering it again here creates a second independent instance with its own `client`, `cachedIndexIds`, and `cachedConfiguredIndexId`. For the current `analyzeVideo` call this is harmless, but if the service ever accumulates state (rate-limit counters, connection pools, etc.) the two instances won't share it. Consider moving `TwelveLabsService` into a dedicated module that both `AppModule` and `ToolsModule` can import.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant