Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs-site/static/search-index.json
Original file line number Diff line number Diff line change
Expand Up @@ -3899,10 +3899,10 @@
{"objectID":"2c4c2bb10b6fea62d177994e6df1345d05606b65f8122b3c969cf214df3363f1","title":"CLI usage","url":"/docs/features/multimodal#cli-usage","content":"`bash","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"CLI usage","lvl3":""}},
{"objectID":"915a9e9701a9e6b02890e744550ed66754549288fa044c90bd2dd873e6b48c2a","title":"Auto-detect","url":"/docs/features/multimodal#auto-detect","content":"neurolink generate \"Describe this video\" --file demo.mp4 --provider google-ai","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Auto-detect","lvl3":""}},
{"objectID":"12ce623ba884b34886674b24daff6ce9e58465c68dc25f4f66c01229c6855513","title":"Explicit flag","url":"/docs/features/multimodal#explicit-flag","content":"neurolink generate \"Summarise this\" --video standup.mp4 --provider vertex","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Explicit flag","lvl3":""}},
{"objectID":"08774b3c1d8fd456cb7d64ad98281449b9271854310a9a713048ae2112572d5c","title":"Frame-extraction knobs","url":"/docs/features/multimodal#frame-extraction-knobs","content":"neurolink generate \"Describe each scene\" --file demo.mp4 \\\n --provider openai --video-frames 16 --video-quality 90 --video-format jpeg\n--transcribe-audiovideoOptions.transcribeAudiotranscribeAudio`.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Frame-extraction knobs","lvl3":""}},
{"objectID":"08774b3c1d8fd456cb7d64ad98281449b9271854310a9a713048ae2112572d5c","title":"Frame-extraction knobs","url":"/docs/features/multimodal#frame-extraction-knobs","content":"neurolink generate \"Describe each scene\" --file demo.mp4 \\\n --provider openai --video-frames 16 --video-quality 90 --video-format jpeg\n--transcribe-audiovideoOptions.transcribeAudiowhisper-1OPENAIAPIKEYOPENAIBASEURLSpoken Audio (transcribed)OPENAIAPIKEYNo transcript for : --transcribe-audiotranscribeAudio`.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Frame-extraction knobs","lvl3":""}},
{"objectID":"d0faa964a225271bc41d6c279e3e925829ac8e8828518387c39ba60d97cd363f","title":"Asking which mechanism applies","url":"/docs/features/multimodal#asking-which-mechanism-applies","content":"The capability table is exported, so a caller can ask before sending — useful\nfor choosing a provider, or for budgeting.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Asking which mechanism applies","lvl3":""}},
{"objectID":"10a8f5bf0ea5129e242eb09d5db285a4d91aac091c171050471ebd7eb528cb72","title":"Best practices","url":"/docs/features/multimodal#best-practices","content":"Ask an audio question of a Gemini provider only. Nothing on the frame\n path can hear the clip, so \"what was said\" has no answer there.\nKeep clips under 14 MB when you want native delivery. Re-encoding a\n screen recording at a lower bitrate usually gets a long clip under the\n ceiling without losing anything a model would read.\nRaise rather than for detail over time. Frames cost\n roughly 250 tokens each regardless of quality; more of them is what buys\n coverage of a clip where things change.\nLower for local runtimes. Ollama and llama.cpp hold every\n frame in the same memory as the model; four is the practical default.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Best practices","lvl3":""}},
{"objectID":"7bd6ebb59bff22b19f26c8c358eb7012f8a73d4f90eed575ca136f632c6f9bc6","title":"Troubleshooting","url":"/docs/features/multimodal#troubleshooting","content":"| Symptom | Cause | Fix |\n| --------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- |\n| Model describes the file but not its content | No keyframes extracted — ffmpeg missing — and the provider takes frames | Install ffmpeg, or use a Gemini provider, which needs none |\n| in the logs | The clip failed the inline gate; the reason is on the same line | Shorten or re-encode it, or accept the frames |\n| Model cannot hear speech on a Gemini provider | The clip exceeded the inline ceiling and fell back to frames | Get the source under 14 MB |\n| produced no transcript | Not implemented yet (#433); the flag is a no-op that only warns | None yet — track #1757 |\n| Only the first seconds of a long clip are described | The frame budget was hit before the end | Raise ; the interval then spreads evenly across the whole clip |","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Troubleshooting","lvl3":""}},
{"objectID":"7bd6ebb59bff22b19f26c8c358eb7012f8a73d4f90eed575ca136f632c6f9bc6","title":"Troubleshooting","url":"/docs/features/multimodal#troubleshooting","content":"| Symptom | Cause | Fix |\n| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| Model describes the file but not its content | No keyframes extracted — ffmpeg missing — and the provider takes frames | Install ffmpeg, or use a Gemini provider, which needs none |\n| in the logs | The clip failed the inline gate; the reason is on the same line | Shorten or re-encode it, or accept the frames |\n| Model cannot hear speech on a Gemini provider | The clip exceeded the inline ceiling and fell back to frames | Get the source under 14 MB, or pass |\n| produced no transcript | The warning names the reason: no audio track, unset, or audio over Whisper's 25 MB limit | Set , or split the recording |\n| Only the first seconds of a long clip are described | The frame budget was hit before the end | Raise ; the interval then spreads evenly across the whole clip |","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Troubleshooting","lvl3":""}},
{"objectID":"79318378a7df7cc816465eb447fa4bd2ba9e6344f17b0a9cf6661377bb03e7e2","title":"Combining Multiple Input Types","url":"/docs/features/multimodal#combining-multiple-input-types","content":"NeuroLink excels at combining different media types in a single request.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Combining Multiple Input Types","lvl3":""}},
{"objectID":"412f09080a5801a35250df393e2e0694602d5312e2b3d1b017fe469bf9dc0474","title":"Batch with Multimodal (CLI)","url":"/docs/features/multimodal#batch-with-multimodal-cli","content":"The command supports , , , and . The file(s) are attached identically to every prompt in the batch (a one-line notice is printed to stderr, unconditionally — it is not suppressed by , so it never corrupts output written to stdout):\n\n(auto-detect) is not available in , because it collides with the positional (the prompts-list path). Use the explicit / / / flags instead.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"Batch with Multimodal (CLI)","lvl3":""}},
{"objectID":"66d907236327d676dbe6e05193c2793f7c5990dce2ad113cf078eb1ff4c73216","title":"File validation & troubleshooting (CLI)","url":"/docs/features/multimodal#file-validation-troubleshooting-cli","content":"Before any provider call, the CLI validates local file inputs across , , and :\nA path that points at a directory, doesn't exist, or can't be read (e.g. a permissions error) is rejected up front with a clear error and troubleshooting hints — no cryptic / deep in processing. This also applies to the prompts-list positional itself.\nA large file (images > 10 MB, CSV/PDF > 50 MB) prints a non-blocking warning to stderr. Like the batch attachment notice, this is unconditional — visible without and regardless of — and never mixes into stdout, so output stays valid JSON even when large-file warnings fire.\n\nThis runs even under and without API keys configured.","hierarchy":{"lvl0":"Features","lvl1":"Multimodal Capabilities Guide","lvl2":"File validation & troubleshooting (CLI)","lvl3":""}},
Expand Down
1 change: 1 addition & 0 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,7 @@ console.log(result.content);
- [JsonTypeGuard](type-aliases/JsonTypeGuard.md)
- [ProcessorRegistration](type-aliases/ProcessorRegistration.md)
- [ProcessedAudio](type-aliases/ProcessedAudio.md)
- [VideoKeyframe](type-aliases/VideoKeyframe.md)
- [ProcessedVideo](type-aliases/ProcessedVideo.md)
- [ArchiveFormat](type-aliases/ArchiveFormat.md)
- [ArchiveDecompressionResult](type-aliases/ArchiveDecompressionResult.md)
Expand Down
9 changes: 7 additions & 2 deletions docs/api/type-aliases/FileProcessingResult.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ File processing result after detection and conversion

### images?

> `optional` **images?**: (`Buffer` \| `string`)[]
> `optional` **images?**: (`Buffer` \| `string` \| [`ImageWithAltText`](ImageWithAltText.md))[]

Additional images extracted from the file (e.g., video keyframes, audio cover art)
Additional images extracted from the file (e.g., video keyframes, audio
cover art).

An entry may carry alt text, which is how a video keyframe states the
timestamp it was sampled at. Bare bytes still work — the field was
widened, not changed.

---

Expand Down
29 changes: 6 additions & 23 deletions docs/api/type-aliases/GenerateOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,31 +249,14 @@ module's own default, unchanged unless a caller opts in here.

### videoOptions?

> `optional` **videoOptions?**: `object`
> `optional` **videoOptions?**: [`VideoProcessorOptions`](VideoProcessorOptions.md)

#### frames?
Video processing options — keyframe budget, encoder settings, and
whether to transcribe the clip's spoken audio.

> `optional` **frames?**: `number`

Frames to extract. Unset lets VideoProcessor pick from the clip's duration; clamped to 100.

#### quality?

> `optional` **quality?**: `number`

Frame encoder quality, clamped to 1-100. Default 80.

#### format?

> `optional` **format?**: `"jpeg"` \| `"png"`

Frame encoding. Default jpeg.

#### transcribeAudio?

> `optional` **transcribeAudio?**: `boolean`

Not implemented yet (#433) — warns rather than silently doing nothing.
The canonical shape, rather than a third structurally-identical copy of
it: `StreamOptions` and `TextGenerationOptions` declare the same field,
and the inline copies had already drifted apart in what they documented.

---

Expand Down
38 changes: 38 additions & 0 deletions docs/api/type-aliases/ProcessedVideo.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ suitable for sending to an LLM.

> **keyframes**: `Buffer`[]

### keyframeTimestampsSec

> **keyframeTimestampsSec**: `number`[]

When each entry of `keyframes` was sampled, in seconds from the start of
the clip. Same length, same order, ascending.

A parallel array rather than a richer `keyframes` element type: that
field is read by `fileReferenceRegistry` and by the detector as a plain
`Buffer[]`, and reshaping it would break them for a value they do not
need. Extraction can also drop an individual frame when its encode
fails, so the timestamps are recorded as frames are kept — never
reconstructed from an interval, which would silently mislabel every
frame after a dropped one.

### metadata

> **metadata**: `object`
Expand Down Expand Up @@ -79,6 +94,29 @@ suitable for sending to an LLM.

> `optional` **subtitleText?**: `string`

### transcript?

> `optional` **transcript?**: `string`

Speech transcribed from the clip's audio track, when
`VideoProcessorOptions.transcribeAudio` asked for it and it worked.

Distinct from `subtitleText`, which is an embedded subtitle stream the
file already carried. A recording can have one, both or neither.

### hasTranscript

> **hasTranscript**: `boolean`

### transcriptionSkippedReason?

> `optional` **transcriptionSkippedReason?**: `string`

Why no transcript was produced. Follows `ProcessedAudio`'s precedent:
"nobody asked", "there is no audio track", "no API key", "the call
failed" and "nobody was speaking" are five different situations that
otherwise all present as an absent transcript.

### hasKeyframes

> **hasKeyframes**: `boolean`
Expand Down
44 changes: 14 additions & 30 deletions docs/api/type-aliases/StreamOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ images: [

---

### videoOptions?

> `optional` **videoOptions?**: [`VideoProcessorOptions`](VideoProcessorOptions.md)

Video processing options (#478, #433).

Reconstructed option objects have to carry this the same way they carry
`csvOptions` and `pdfOptions`: the message builder hands it to the
detector, which hands it to `VideoProcessor`, and anything that rebuilds
the options along the way and omits it restores the defaults without
saying so.

---

### pdfOptions?

> `optional` **pdfOptions?**: `object`
Expand Down Expand Up @@ -190,36 +204,6 @@ module's own default, unchanged unless a caller opts in here.

---

### videoOptions?

> `optional` **videoOptions?**: `object`

#### frames?

> `optional` **frames?**: `number`

Frames to extract. Unset lets VideoProcessor pick from the clip's duration; clamped to 100.

#### quality?

> `optional` **quality?**: `number`

Frame encoder quality, clamped to 1-100. Default 80.

#### format?

> `optional` **format?**: `"jpeg"` \| `"png"`

Frame encoding. Default jpeg.

#### transcribeAudio?

> `optional` **transcribeAudio?**: `boolean`

Not implemented yet (#433) — warns rather than silently doing nothing.

---

### tts?

> `optional` **tts?**: [`TTSOptions`](TTSOptions.md)
Expand Down
48 changes: 14 additions & 34 deletions docs/api/type-aliases/TextGenerationOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,20 @@ const result = await neurolink.generate({

---

### videoOptions?

> `optional` **videoOptions?**: [`VideoProcessorOptions`](VideoProcessorOptions.md)

Video processing options (#478, #433).

Reconstructed option objects have to carry this the same way they carry
`csvOptions` and `pdfOptions`: the message builder hands it to the
detector, which hands it to `VideoProcessor`, and anything that rebuilds
the options along the way and omits it restores the defaults without
saying so.

---

### pdfOptions?

> `optional` **pdfOptions?**: `object`
Expand Down Expand Up @@ -656,40 +670,6 @@ module's own default, unchanged unless a caller opts in here.

---

### videoOptions?

> `optional` **videoOptions?**: `object`

Video processing options (mirrors `GenerateOptions.videoOptions`). Never
declared here before, so `buildGenerateTextOptions` had nowhere to
forward the caller's setting even once the allowlist itself named it.

#### frames?

> `optional` **frames?**: `number`

Frames to extract. Unset lets VideoProcessor pick from the clip's duration; clamped to 100.

#### quality?

> `optional` **quality?**: `number`

Frame encoder quality, clamped to 1-100. Default 80.

#### format?

> `optional` **format?**: `"jpeg"` \| `"png"`

Frame encoding. Default jpeg.

#### transcribeAudio?

> `optional` **transcribeAudio?**: `boolean`

Not implemented yet (#433) — warns rather than silently doing nothing.

---

### enableSummarization?

> `optional` **enableSummarization?**: `boolean`
Expand Down
30 changes: 30 additions & 0 deletions docs/api/type-aliases/VideoKeyframe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[**NeuroLink API Reference**](../README.md)

---

[NeuroLink API Reference](../README.md) / VideoKeyframe

# Type Alias: VideoKeyframe

> **VideoKeyframe** = `object`

One extracted keyframe and the moment it came from.

Internal to extraction: `ProcessedVideo` splits this back into the parallel
`keyframes` / `keyframeTimestampsSec` arrays its existing consumers expect.
Keeping the pair together while frames are being read and encoded is what
makes a dropped frame impossible to mislabel — the alternative is
reconstructing timestamps from an interval after the fact, which is wrong
for every frame following a failed encode.

## Properties

### buffer

> `readonly` **buffer**: `Buffer`

---

### timestampSec

> `readonly` **timestampSec**: `number`
14 changes: 14 additions & 0 deletions docs/api/type-aliases/VideoProcessorOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,17 @@ Encoder quality 1-100 for the extracted frames.
> `optional` **format?**: `"jpeg"` \| `"png"`

Frame encoding. Defaults to jpeg.

---

### transcribeAudio?

> `optional` **transcribeAudio?**: `boolean`

Transcribe the clip's spoken audio (#433). Off by default: it costs an
ffmpeg pass plus a Whisper call, and most attached video is silent
screen capture.

Best-effort — a clip with no audio track, no `OPENAI_API_KEY`, or a
failed call still processes, and the reason lands in
`ProcessedVideo.transcriptionSkippedReason`.
Loading
Loading