@larkup/tool-video-intelligence 0.2.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.
Files changed (54) hide show
  1. package/.env.example +150 -0
  2. package/LICENSE +176 -0
  3. package/README.md +281 -0
  4. package/compose.gpu.yaml +14 -0
  5. package/compose.yaml +71 -0
  6. package/dist/agent.d.ts +131 -0
  7. package/dist/agent.js +2087 -0
  8. package/dist/brief.d.ts +2 -0
  9. package/dist/brief.js +37 -0
  10. package/dist/client.d.ts +46 -0
  11. package/dist/client.js +139 -0
  12. package/dist/contracts.d.ts +331 -0
  13. package/dist/contracts.js +1 -0
  14. package/dist/index.d.ts +87 -0
  15. package/dist/index.js +391 -0
  16. package/dist/runtime.d.ts +96 -0
  17. package/dist/runtime.js +592 -0
  18. package/dist/ui.d.ts +82 -0
  19. package/dist/ui.js +87 -0
  20. package/package.json +84 -0
  21. package/runtime/Dockerfile +119 -0
  22. package/runtime/app/__init__.py +3 -0
  23. package/runtime/app/__main__.py +19 -0
  24. package/runtime/app/api/__init__.py +0 -0
  25. package/runtime/app/api/deps.py +69 -0
  26. package/runtime/app/api/v1.py +166 -0
  27. package/runtime/app/config.py +78 -0
  28. package/runtime/app/db/__init__.py +0 -0
  29. package/runtime/app/db/schemas.py +162 -0
  30. package/runtime/app/db/store.py +466 -0
  31. package/runtime/app/main.py +27 -0
  32. package/runtime/app/model_configuration.py +157 -0
  33. package/runtime/app/services/__init__.py +0 -0
  34. package/runtime/app/services/brain.py +2221 -0
  35. package/runtime/app/services/embedding.py +473 -0
  36. package/runtime/app/services/jobs.py +237 -0
  37. package/runtime/app/services/motion.py +66 -0
  38. package/runtime/app/services/pipeline.py +1911 -0
  39. package/runtime/app/services/scene.py +161 -0
  40. package/runtime/app/services/storage.py +44 -0
  41. package/runtime/app/services/transcription.py +667 -0
  42. package/runtime/app/services/vision.py +1441 -0
  43. package/runtime/app/utils/__init__.py +0 -0
  44. package/runtime/app/utils/timing.py +99 -0
  45. package/runtime/app/worker.py +20 -0
  46. package/runtime/pyproject.toml +56 -0
  47. package/runtime/requirements-cpu.txt +15 -0
  48. package/runtime/requirements-smoke.txt +7 -0
  49. package/runtime/requirements.txt +14 -0
  50. package/runtime/uv.lock +3637 -0
  51. package/scripts/grant-cloud-credits.sh +43 -0
  52. package/scripts/runtime.mjs +156 -0
  53. package/scripts/validate-indexing.mjs +168 -0
  54. package/tool.manifest.json +617 -0
package/dist/index.js ADDED
@@ -0,0 +1,391 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { VideoIntelligenceClient } from './client.js';
3
+ import { attachVideoIntelligenceAgentClient } from './agent.js';
4
+ import { detectLocalRuntimeHost, ensureVideoRuntime, installLocalRuntime, removeVideoRuntime, restartVideoRuntime, stopVideoRuntime, } from './runtime.js';
5
+ export * from './brief.js';
6
+ export * from './agent.js';
7
+ export * from './client.js';
8
+ export * from './contracts.js';
9
+ export * from './runtime.js';
10
+ export * from './ui.js';
11
+ export const TOOL_META = {
12
+ id: 'video-intelligence',
13
+ name: 'Video Intelligence',
14
+ version: '0.1.0',
15
+ };
16
+ /** Managed infrastructure stays an implementation detail of Larkup Cloud. */
17
+ const MANAGED_CLOUD_ENDPOINT = process.env.LARKUP_VIDEO_INTELLIGENCE_CLOUD_ENDPOINT ??
18
+ 'https://7w1bab08jf.execute-api.eu-central-1.amazonaws.com';
19
+ export const TOOL_EXTENSION = {
20
+ id: TOOL_META.id,
21
+ apiVersion: '1',
22
+ createClient: createClientFromContext,
23
+ async ensureRuntime(context) {
24
+ const { mode, apiKey, endpoint } = resolveClientOptions(context.config);
25
+ const client = createClientFromContext(context);
26
+ if (mode === 'local') {
27
+ const kind = await resolveLocalKind();
28
+ const understanding = await resolveUnderstandingConfig(context.config);
29
+ await ensureVideoRuntime(client, kind, apiKey, endpoint, understanding);
30
+ return;
31
+ }
32
+ await ensureVideoRuntime(client, mode, undefined, undefined);
33
+ },
34
+ async restartRuntime(context) {
35
+ const { mode, apiKey, endpoint } = resolveClientOptions(context.config);
36
+ if (mode !== 'local') {
37
+ throw new Error('Only a local runtime can be restarted here.');
38
+ }
39
+ const kind = await resolveLocalKind();
40
+ const understanding = await resolveUnderstandingConfig(context.config);
41
+ await restartVideoRuntime(kind, apiKey, endpoint, understanding);
42
+ },
43
+ /** Pulls the Docker image or installs uv + syncs Python deps, without starting anything. */
44
+ async installRuntime(context) {
45
+ const { mode, apiKey, endpoint } = resolveClientOptions(context.config);
46
+ if (mode !== 'local') {
47
+ throw new Error('Only a local runtime can be installed here.');
48
+ }
49
+ const kind = await resolveLocalKind();
50
+ const understanding = await resolveUnderstandingConfig(context.config);
51
+ await installLocalRuntime(kind, apiKey, endpoint, understanding);
52
+ },
53
+ async stopRuntime(context) {
54
+ const { mode } = resolveClientOptions(context.config);
55
+ if (mode !== 'local') {
56
+ throw new Error('Only a local runtime can be stopped here.');
57
+ }
58
+ await stopVideoRuntime();
59
+ const client = createClientFromContext(context);
60
+ for (let attempt = 0; attempt < 10; attempt += 1) {
61
+ try {
62
+ await client.health();
63
+ }
64
+ catch {
65
+ return;
66
+ }
67
+ await new Promise((resolve) => setTimeout(resolve, 250));
68
+ }
69
+ throw new Error('The local runtime is still responding after stop. Check for another process using its URL.');
70
+ },
71
+ async verifyConfiguration(context) {
72
+ if (context.verifyKey === 'localRuntimeUrl' || context.verifyKey === 'customRuntimeUrl') {
73
+ await createClientFromContext(context).health();
74
+ return;
75
+ }
76
+ const understanding = await resolveUnderstandingConfig(context.config);
77
+ if (context.verifyKey === 'semanticVisionModel') {
78
+ await verifyProviderModel('Video vision', understanding.visionProvider, understanding.visionApiKey, understanding.semanticVisionModel, context.fetch);
79
+ return;
80
+ }
81
+ if (context.verifyKey === 'agentModel') {
82
+ await verifyProviderModel('Agent planning', understanding.agentProvider, understanding.agentApiKey, understanding.agentModel, context.fetch);
83
+ return;
84
+ }
85
+ if (context.verifyKey === 'audioApiKey') {
86
+ await verifyAudioProvider(understanding.audioProvider, understanding.audioApiKey, understanding.audioModel, context.fetch);
87
+ return;
88
+ }
89
+ throw new Error('This Video Intelligence setting cannot be verified.');
90
+ },
91
+ async removeRuntime(context) {
92
+ const { mode } = resolveClientOptions(context.config);
93
+ if (mode !== 'local')
94
+ return;
95
+ await removeVideoRuntime(await resolveLocalKind());
96
+ },
97
+ /** Docker/native detection, system suitability, and AI-model availability for the Install alert. */
98
+ async getHostCapabilities(context) {
99
+ const [host, understanding] = await Promise.all([
100
+ detectLocalRuntimeHost(),
101
+ resolveUnderstandingConfig(context.config),
102
+ ]);
103
+ const selectedModel = understanding.semanticVisionModel ?? 'google/gemini-3.6-flash';
104
+ const hasVisionKey = Boolean(understanding.visionApiKey);
105
+ const modelIsVideoCapable = /gemini.*flash|gemini.*vision|qwen.*vl|gpt-4o|gpt-4\.1/i.test(selectedModel);
106
+ const modelRequirement = !hasVisionKey
107
+ ? {
108
+ configured: false,
109
+ message: 'Video understanding needs a vision-capable provider and API key. Use the AI Models defaults or customize them in this tool; text-only providers such as DeepSeek need a separate vision provider.',
110
+ }
111
+ : !modelIsVideoCapable
112
+ ? {
113
+ configured: false,
114
+ message: `${selectedModel} is not a recommended video vision model. Use Gemini Flash or a Qwen-VL model before indexing video.`,
115
+ }
116
+ : {
117
+ configured: true,
118
+ provider: understanding.visionProvider,
119
+ model: selectedModel,
120
+ message: `Video understanding will use ${selectedModel}, loaded automatically from AI Models.`,
121
+ };
122
+ let running = false;
123
+ try {
124
+ await createClientFromContext(context).health();
125
+ running = true;
126
+ }
127
+ catch {
128
+ // Health is only a status probe. Never start a runtime while rendering settings.
129
+ }
130
+ return {
131
+ ...host,
132
+ running,
133
+ gatewayKeyAvailable: hasVisionKey,
134
+ gatewayKeySource: hasVisionKey ? 'global' : null,
135
+ modelRequirement,
136
+ };
137
+ },
138
+ async provisionRuntime(context) {
139
+ const current = context.config;
140
+ if (resolveRuntimeMode(current) !== 'managed-cloud')
141
+ return { config: {} };
142
+ if (typeof current.cloudInstallationId === 'string' &&
143
+ typeof current.cloudAccessKey === 'string' &&
144
+ current.cloudAccessKey) {
145
+ return {
146
+ config: {},
147
+ display: { userId: current.cloudInstallationId },
148
+ };
149
+ }
150
+ const installationId = typeof current.cloudInstallationId === 'string' && current.cloudInstallationId.length >= 32
151
+ ? current.cloudInstallationId
152
+ : randomUUID();
153
+ const client = new VideoIntelligenceClient({
154
+ mode: 'managed-cloud',
155
+ endpoint: MANAGED_CLOUD_ENDPOINT,
156
+ fetch: context.fetch,
157
+ });
158
+ const provisioned = await client.provisionDeviceAccess(installationId);
159
+ return {
160
+ config: { cloudInstallationId: installationId, cloudAccessKey: provisioned.apiKey },
161
+ display: { userId: installationId },
162
+ };
163
+ },
164
+ };
165
+ export default TOOL_EXTENSION;
166
+ function resolveRuntimeMode(config) {
167
+ const mode = config.runtimeMode;
168
+ // Older cloud-first installs persisted the then-internal local mode beside
169
+ // a cloudApiKey. Preserve that connection instead of treating it as a new
170
+ // local Docker selection after this manifest gained real runtime choices.
171
+ if (mode === 'local-docker' &&
172
+ typeof config.cloudApiKey === 'string' &&
173
+ config.cloudApiKey.trim() &&
174
+ !config.localRuntimeUrl &&
175
+ !config.localRuntimeApiKey)
176
+ return 'managed-cloud';
177
+ // 'local-docker'/'local-process' were the user-facing choices before the two
178
+ // local runtimes were merged into one auto-detected 'local' mode. Treat both
179
+ // as synonyms for 'local' so existing installs keep working with zero migration.
180
+ if (mode === 'local-docker' || mode === 'local-process' || mode === 'local')
181
+ return 'local';
182
+ return mode === 'custom-remote' || mode === 'managed-cloud' ? mode : 'managed-cloud';
183
+ }
184
+ function resolveClientOptions(config) {
185
+ const legacyCloudAccessKey = typeof config.cloudApiKey === 'string' && config.cloudApiKey.trim()
186
+ ? config.cloudApiKey
187
+ : undefined;
188
+ const mode = resolveRuntimeMode(config);
189
+ const endpoint = mode === 'managed-cloud'
190
+ ? MANAGED_CLOUD_ENDPOINT
191
+ : mode === 'local'
192
+ ? typeof config.localRuntimeUrl === 'string' && config.localRuntimeUrl.trim()
193
+ ? config.localRuntimeUrl
194
+ : 'http://127.0.0.1:8787'
195
+ : typeof config.customRuntimeUrl === 'string'
196
+ ? config.customRuntimeUrl
197
+ : '';
198
+ return {
199
+ mode,
200
+ endpoint,
201
+ apiKey: mode === 'managed-cloud'
202
+ ? typeof config.cloudAccessKey === 'string'
203
+ ? config.cloudAccessKey
204
+ : legacyCloudAccessKey
205
+ : mode === 'local'
206
+ ? typeof config.localRuntimeApiKey === 'string'
207
+ ? config.localRuntimeApiKey
208
+ : undefined
209
+ : typeof config.customRuntimeApiKey === 'string'
210
+ ? config.customRuntimeApiKey
211
+ : undefined,
212
+ };
213
+ }
214
+ /** Resolves 'local' to a concrete kind right before an action executes — never persisted. */
215
+ async function resolveLocalKind() {
216
+ const report = await detectLocalRuntimeHost();
217
+ return report.recommendedKind ?? 'local-process';
218
+ }
219
+ /**
220
+ * Builds the local runtime's video-understanding environment. The host passes
221
+ * the selected AI Models settings in this context, keeping a
222
+ * marketplace tool independent from the host application's source modules.
223
+ */
224
+ async function resolveUnderstandingConfig(config) {
225
+ const str = (value) => (typeof value === 'string' && value.trim() ? value : undefined);
226
+ const visionOverride = str(config.videoVisionProvider);
227
+ const globalVisionProvider = str(config.larkupVisionProvider);
228
+ const visionProvider = visionOverride && visionOverride !== 'auto'
229
+ ? visionOverride
230
+ : (globalVisionProvider ?? 'vercel_ai_gateway');
231
+ const visionApiKey = (visionOverride && visionOverride !== 'auto' ? str(config.videoVisionApiKey) : undefined) ??
232
+ (globalVisionProvider === visionProvider ? str(config.larkupVisionApiKey) : undefined) ??
233
+ str(config.visionGatewayApiKey) ??
234
+ str(config.larkupGatewayApiKey);
235
+ const visionModelOverride = str(config.semanticVisionModel);
236
+ let semanticVisionModel = visionModelOverride && visionModelOverride !== 'auto'
237
+ ? visionModelOverride
238
+ : globalVisionProvider === visionProvider
239
+ ? (str(config.larkupVisionModel) ?? str(config.larkupGatewayVisionModel))
240
+ : undefined;
241
+ if (!semanticVisionModel) {
242
+ semanticVisionModel =
243
+ visionProvider === 'google'
244
+ ? 'gemini-3.6-flash'
245
+ : visionProvider === 'openai'
246
+ ? 'gpt-4o-mini'
247
+ : 'google/gemini-3.6-flash';
248
+ }
249
+ const agentOverride = str(config.videoAgentProvider);
250
+ const globalAgentProvider = str(config.larkupAgentProvider);
251
+ const agentProvider = agentOverride && agentOverride !== 'auto'
252
+ ? agentOverride
253
+ : (globalAgentProvider ?? 'vercel_ai_gateway');
254
+ const agentApiKey = (agentOverride && agentOverride !== 'auto' ? str(config.videoAgentApiKey) : undefined) ??
255
+ (globalAgentProvider === agentProvider ? str(config.larkupAgentApiKey) : undefined) ??
256
+ (agentProvider === visionProvider ? visionApiKey : undefined);
257
+ const agentModelOverride = str(config.agentModel);
258
+ const agentModel = (agentModelOverride && agentModelOverride !== 'auto' ? agentModelOverride : undefined) ??
259
+ (globalAgentProvider === agentProvider ? str(config.larkupAgentModel) : undefined) ??
260
+ (agentProvider === 'google' ? 'google/gemini-3.5-flash-lite' : 'openai/gpt-5-mini');
261
+ return {
262
+ visionProvider,
263
+ visionApiKey,
264
+ semanticVisionModel,
265
+ agentProvider,
266
+ agentApiKey,
267
+ agentModel,
268
+ audioProvider: str(config.audioProvider),
269
+ audioApiKey: str(config.audioApiKey),
270
+ audioModel: {
271
+ openai: 'whisper-1',
272
+ groq: 'whisper-large-v3-turbo',
273
+ deepgram: 'nova-3',
274
+ elevenlabs: 'scribe_v2',
275
+ }[str(config.audioProvider) ?? ''],
276
+ videoEmbeddingProvider: str(config.videoEmbeddingProvider),
277
+ dashscopeApiKey: str(config.dashscopeApiKey),
278
+ dashscopeWorkspaceId: str(config.dashscopeWorkspaceId),
279
+ dashscopeRegion: str(config.dashscopeRegion),
280
+ runpodEmbeddingApiKey: str(config.runpodEmbeddingApiKey),
281
+ runpodEmbeddingEndpointId: str(config.runpodEmbeddingEndpointId),
282
+ hfEmbeddingUrl: str(config.hfEmbeddingUrl),
283
+ hfEmbeddingApiKey: str(config.hfEmbeddingApiKey),
284
+ };
285
+ }
286
+ function createClientFromContext(context) {
287
+ const { mode, endpoint, apiKey } = resolveClientOptions(context.config);
288
+ return attachVideoIntelligenceAgentClient(new VideoIntelligenceClient({ mode, endpoint, apiKey, fetch: context.fetch }), context.fetch);
289
+ }
290
+ async function verifyProviderModel(label, provider, apiKey, model, request = globalThis.fetch) {
291
+ if (!provider || !apiKey || !model) {
292
+ throw new Error(`${label} needs a provider, model, and API key before it can be verified.`);
293
+ }
294
+ const normalizedProvider = provider.trim().toLowerCase();
295
+ const normalizedModel = model.startsWith(`${normalizedProvider}/`)
296
+ ? model.slice(normalizedProvider.length + 1)
297
+ : model;
298
+ let response;
299
+ if (normalizedProvider === 'google') {
300
+ response = await request(`https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(normalizedModel)}:generateContent?key=${encodeURIComponent(apiKey)}`, {
301
+ method: 'POST',
302
+ headers: { 'Content-Type': 'application/json' },
303
+ body: JSON.stringify({
304
+ contents: [{ role: 'user', parts: [{ text: 'Reply with OK.' }] }],
305
+ generationConfig: { maxOutputTokens: 8 },
306
+ }),
307
+ });
308
+ }
309
+ else if (normalizedProvider === 'anthropic') {
310
+ response = await request('https://api.anthropic.com/v1/messages', {
311
+ method: 'POST',
312
+ headers: {
313
+ 'x-api-key': apiKey,
314
+ 'anthropic-version': '2023-06-01',
315
+ 'Content-Type': 'application/json',
316
+ },
317
+ body: JSON.stringify({
318
+ model: normalizedModel,
319
+ messages: [{ role: 'user', content: 'Reply with OK.' }],
320
+ max_tokens: 8,
321
+ }),
322
+ });
323
+ }
324
+ else if (['openai', 'vercel_ai_gateway', 'deepseek', 'mistral', 'cohere'].includes(normalizedProvider)) {
325
+ const baseUrl = {
326
+ openai: 'https://api.openai.com/v1',
327
+ vercel_ai_gateway: 'https://ai-gateway.vercel.sh/v1',
328
+ deepseek: 'https://api.deepseek.com',
329
+ mistral: 'https://api.mistral.ai/v1',
330
+ cohere: 'https://api.cohere.ai/compatibility/v1',
331
+ }[normalizedProvider];
332
+ response = await request(`${baseUrl}/chat/completions`, {
333
+ method: 'POST',
334
+ headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
335
+ body: JSON.stringify({
336
+ model: normalizedProvider === 'vercel_ai_gateway' ? model : normalizedModel,
337
+ messages: [{ role: 'user', content: 'Reply with OK.' }],
338
+ max_tokens: 8,
339
+ }),
340
+ });
341
+ }
342
+ else {
343
+ throw new Error(`${label} provider "${provider}" is not supported.`);
344
+ }
345
+ if (response.ok)
346
+ return;
347
+ const body = await response.json().catch(() => ({}));
348
+ const detail = body && typeof body === 'object' && 'error' in body
349
+ ? typeof body.error === 'string'
350
+ ? body.error
351
+ : body.error && typeof body.error === 'object' && 'message' in body.error
352
+ ? String(body.error.message)
353
+ : undefined
354
+ : undefined;
355
+ throw new Error(detail ? `${label} verification failed: ${detail}` : `${label} verification failed.`);
356
+ }
357
+ async function verifyAudioProvider(provider, apiKey, model, request = globalThis.fetch) {
358
+ if (!provider || !apiKey || !model) {
359
+ throw new Error('Audio transcription needs a provider and API key before it can be verified.');
360
+ }
361
+ const normalized = provider.trim().toLowerCase();
362
+ const target = normalized === 'openai'
363
+ ? `https://api.openai.com/v1/models/${encodeURIComponent(model)}`
364
+ : normalized === 'groq'
365
+ ? `https://api.groq.com/openai/v1/models/${encodeURIComponent(model)}`
366
+ : normalized === 'deepgram'
367
+ ? 'https://api.deepgram.com/v1/projects'
368
+ : normalized === 'elevenlabs'
369
+ ? 'https://api.elevenlabs.io/v1/user'
370
+ : '';
371
+ if (!target)
372
+ throw new Error(`Audio provider "${provider}" is not supported.`);
373
+ const response = await request(target, {
374
+ headers: normalized === 'deepgram'
375
+ ? { Authorization: `Token ${apiKey}` }
376
+ : normalized === 'elevenlabs'
377
+ ? { 'xi-api-key': apiKey }
378
+ : { Authorization: `Bearer ${apiKey}` },
379
+ });
380
+ if (response.ok)
381
+ return;
382
+ const body = await response.json().catch(() => ({}));
383
+ const detail = body && typeof body === 'object' && 'error' in body
384
+ ? typeof body.error === 'string'
385
+ ? body.error
386
+ : body.error && typeof body.error === 'object' && 'message' in body.error
387
+ ? String(body.error.message)
388
+ : undefined
389
+ : undefined;
390
+ throw new Error(detail ? `Audio verification failed: ${detail}` : 'Audio verification failed.');
391
+ }
@@ -0,0 +1,96 @@
1
+ import { VideoIntelligenceClient } from './client.js';
2
+ import type { LocalVideoRuntimeKind } from './contracts.js';
3
+ /** Optional AI/audio configuration injected into the local runtime. */
4
+ export interface VideoUnderstandingEnvConfig {
5
+ visionProvider?: string;
6
+ visionApiKey?: string;
7
+ semanticVisionModel?: string;
8
+ agentProvider?: string;
9
+ agentApiKey?: string;
10
+ agentModel?: string;
11
+ audioProvider?: string;
12
+ audioApiKey?: string;
13
+ audioModel?: string;
14
+ videoEmbeddingProvider?: string;
15
+ dashscopeApiKey?: string;
16
+ dashscopeWorkspaceId?: string;
17
+ dashscopeRegion?: string;
18
+ runpodEmbeddingApiKey?: string;
19
+ runpodEmbeddingEndpointId?: string;
20
+ hfEmbeddingUrl?: string;
21
+ hfEmbeddingApiKey?: string;
22
+ }
23
+ export interface LocalAcceleration {
24
+ /** The execution device selected for work that stays on the user's machine. */
25
+ device: 'cuda' | 'cpu';
26
+ /** True only when Docker can pass the detected NVIDIA GPU through safely. */
27
+ dockerSupported: boolean;
28
+ /** True when the native runtime can install CUDA-enabled dependencies. */
29
+ nativeSupported: boolean;
30
+ gpuName?: string;
31
+ gpuMemoryGB?: number;
32
+ message: string;
33
+ }
34
+ /** Starts the shipped local runtime only after the user selected local Docker mode. */
35
+ export declare function ensureVideoRuntime(client: VideoIntelligenceClient, mode: 'local-docker' | 'local-process' | 'managed-cloud' | 'custom-remote', localApiKey?: string, localRuntimeUrl?: string, understanding?: VideoUnderstandingEnvConfig): Promise<void>;
36
+ /** Recreates the local container so a changed shared key is applied immediately. */
37
+ export declare function restartVideoRuntime(mode: 'local-docker' | 'local-process', localApiKey?: string, localRuntimeUrl?: string, understanding?: VideoUnderstandingEnvConfig): Promise<void>;
38
+ /** Stops the local runtime without removing the installed image/dependencies. */
39
+ export declare function stopVideoRuntime(): Promise<void>;
40
+ /** Removes local runtime state only after the user explicitly removes the tool. */
41
+ export declare function removeVideoRuntime(kind: LocalVideoRuntimeKind): Promise<void>;
42
+ /**
43
+ * Prepares whatever a local kind needs, without starting it: pulls the Docker
44
+ * image, or installs uv (via astral's official curl|sh installer when it is
45
+ * missing) and syncs the native runtime's Python dependencies.
46
+ */
47
+ export declare function installLocalRuntime(kind: LocalVideoRuntimeKind, localApiKey?: string, localRuntimeUrl?: string, understanding?: VideoUnderstandingEnvConfig): Promise<void>;
48
+ export interface DockerHostStatus {
49
+ cliInstalled: boolean;
50
+ daemonRunning: boolean;
51
+ imagePulled: boolean;
52
+ message: string;
53
+ }
54
+ export interface GpuHostStatus {
55
+ available: boolean;
56
+ name?: string;
57
+ memoryGB?: number;
58
+ message: string;
59
+ }
60
+ /** `docker info` (not just `docker --version`) so a stopped daemon is distinguished from a missing CLI. */
61
+ export declare function detectDockerHost(): Promise<DockerHostStatus>;
62
+ /**
63
+ * Detect NVIDIA through its driver utility rather than assuming any GPU can
64
+ * run CUDA. Apple/AMD devices correctly stay on the efficient CPU path until
65
+ * there is a supported local operator build for them.
66
+ */
67
+ export declare function detectNvidiaGpu(): Promise<GpuHostStatus>;
68
+ /** Selects the fastest supported local path; it never selects a Larkup-managed worker. */
69
+ export declare function detectLocalAcceleration(): Promise<LocalAcceleration>;
70
+ export interface NativeHostStatus {
71
+ uvInstalled: boolean;
72
+ depsInstalled: boolean;
73
+ message: string;
74
+ }
75
+ export declare function detectNativeHost(): Promise<NativeHostStatus>;
76
+ export interface LocalRuntimeHostReport {
77
+ docker: DockerHostStatus;
78
+ native: NativeHostStatus;
79
+ recommendedKind: LocalVideoRuntimeKind | null;
80
+ installed: boolean;
81
+ system: {
82
+ platform: NodeJS.Platform;
83
+ cpus: number;
84
+ totalMemGB: number;
85
+ freeMemGB: number;
86
+ };
87
+ acceleration: LocalAcceleration;
88
+ suitability: {
89
+ level: 'good' | 'tight' | 'unknown';
90
+ message: string;
91
+ };
92
+ }
93
+ /** Live-detects Docker vs. native every call so the UI never trusts a stale persisted choice. */
94
+ export declare function detectLocalRuntimeHost(): Promise<LocalRuntimeHostReport>;
95
+ export declare function stopNativeVideoRuntime(pidFile: string): void;
96
+ export declare function videoUnderstandingEnvironment(config?: VideoUnderstandingEnvConfig): Record<string, string>;