@overshoot/sdk 0.1.0-alpha.3 → 0.1.0-alpha.4

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/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Overshoot SDK
2
2
 
3
- > **⚠️ Alpha Release**: This is an alpha version (0.1.0-alpha.3). The API may change in future versions.
3
+ > **Warning: Alpha Release**: This is an alpha version (0.1.0-alpha.3). The API may change in future versions.
4
4
 
5
5
  TypeScript SDK for real-time AI vision analysis on live video streams.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- npm install overshoot@alpha
10
+ npm install @overshoot/sdk@alpha
11
11
  ```
12
12
 
13
13
  Or install a specific alpha version:
@@ -21,7 +21,7 @@ npm install @overshoot/sdk@0.1.0-alpha.3
21
21
  ### Camera Source
22
22
 
23
23
  ```typescript
24
- import { RealtimeVision } from "overshoot";
24
+ import { RealtimeVision } from "@overshoot/sdk";
25
25
 
26
26
  const vision = new RealtimeVision({
27
27
  apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
@@ -56,6 +56,8 @@ const vision = new RealtimeVision({
56
56
  await vision.start();
57
57
  ```
58
58
 
59
+ > **Note:** Video files automatically loop continuously until you call `stop()`.
60
+
59
61
  ## Configuration
60
62
 
61
63
  ### RealtimeVisionConfig
@@ -71,19 +73,19 @@ interface RealtimeVisionConfig {
71
73
  // Optional
72
74
  source?: StreamSource; // Video source (default: environment-facing camera)
73
75
  backend?: "overshoot"; // Model backend (default: "overshoot")
74
- model?: string; // Model name (default: "Qwen/Qwen3-VL-30B-A3B-Instruct")
76
+ model?: string; // Model name (see Available Models below)
75
77
  outputSchema?: Record<string, any>; // JSON schema for structured output
76
78
  onError?: (error: Error) => void;
77
79
  debug?: boolean; // Enable debug logging (default: false)
78
80
 
79
81
  processing?: {
80
- fps?: number; // Actual source frames per second (1-120)
82
+ fps?: number; // Source frames per second (1-120, auto-detected for cameras)
81
83
  sampling_ratio?: number; // Fraction of frames to process (0-1, default: 0.1)
82
- clip_length_seconds?: number; // Size of each clip that the VLM infers on (0.1-60, default: 1.0)
83
- delay_seconds?: number; // Shift between clips (0-60, default: 1.0)
84
+ clip_length_seconds?: number; // Duration of each clip sent to the model (0.1-60, default: 1.0)
85
+ delay_seconds?: number; // Interval between inference runs (0-60, default: 1.0)
84
86
  };
85
87
 
86
- iceServers?: RTCIceServer[]; // Custom WebRTC ICE servers
88
+ iceServers?: RTCIceServer[]; // Custom WebRTC ICE servers (uses Overshoot TURN servers by default, see RealtimeVision.ts)
87
89
  }
88
90
  ```
89
91
 
@@ -95,6 +97,84 @@ type StreamSource =
95
97
  | { type: "video"; file: File };
96
98
  ```
97
99
 
100
+ ### Available Models
101
+
102
+ | Model | Description |
103
+ | -------------------------------- | ------------------------------------------------------------------------------------ |
104
+ | `Qwen/Qwen3-VL-30B-A3B-Instruct` | Default. Very fast and performant general-purpose vision-language model. |
105
+ | `Qwen/Qwen3-VL-8B-Instruct` | Similar latency to 30B. Particularly good at OCR and text extraction tasks. |
106
+ | `OpenGVLab/InternVL3_5-30B-A3B` | Excels at capturing visual detail. More verbose output, higher latency. |
107
+
108
+ ### Processing Parameters Explained
109
+
110
+ The processing parameters control how video frames are sampled and sent to the model:
111
+
112
+ - **`fps`**: The frame rate of your video source. Auto-detected for camera streams; defaults to 30 for video files.
113
+ - **`sampling_ratio`**: What fraction of frames to include in each clip (0.1 = 10% of frames).
114
+ - **`clip_length_seconds`**: Duration of video captured for each inference (e.g., 1.0 = 1 second of video).
115
+ - **`delay_seconds`**: How often inference runs (e.g., 1.0 = one inference per second).
116
+
117
+ **Example:** With `fps=30`, `clip_length_seconds=1.0`, `sampling_ratio=0.1`:
118
+
119
+ - Each clip captures 1 second of video (30 frames at 30fps)
120
+ - 10% of frames are sampled = 3 frames sent to the model
121
+ - If `delay_seconds=1.0`, you get ~1 inference result per second
122
+
123
+ #### Configuration by Use Case
124
+
125
+ Different applications need different processing configurations:
126
+
127
+ **Real-time tracking** (low latency, frequent updates):
128
+
129
+ ```typescript
130
+ processing: {
131
+ sampling_ratio: 0.7,
132
+ clip_length_seconds: 0.5,
133
+ delay_seconds: 0.3,
134
+ }
135
+ ```
136
+
137
+ **Event detection** (monitoring for specific occurrences):
138
+
139
+ ```typescript
140
+ processing: {
141
+ sampling_ratio: 0.2,
142
+ clip_length_seconds: 5.0,
143
+ delay_seconds: 5.0,
144
+ }
145
+ ```
146
+
147
+ ### Structured Output (JSON Schema)
148
+
149
+ Use `outputSchema` to constrain the model's output to a specific JSON structure. The schema follows [JSON Schema](https://json-schema.org/) specification.
150
+
151
+ ```typescript
152
+ const vision = new RealtimeVision({
153
+ apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
154
+ apiKey: "your-api-key",
155
+ prompt: "Detect objects and return structured data",
156
+ outputSchema: {
157
+ type: "object",
158
+ properties: {
159
+ objects: {
160
+ type: "array",
161
+ items: { type: "string" },
162
+ },
163
+ count: { type: "integer" },
164
+ },
165
+ required: ["objects", "count"],
166
+ },
167
+ onResult: (result) => {
168
+ const data = JSON.parse(result.result);
169
+ console.log(`Found ${data.count} objects:`, data.objects);
170
+ },
171
+ });
172
+ ```
173
+
174
+ The model will return valid JSON matching your schema. If the model cannot produce valid output, `result.ok` will be `false` and `result.error` will contain details.
175
+
176
+ > **Note:** `result.result` is always a string. When using `outputSchema`, you must parse it with `JSON.parse()`.
177
+
98
178
  ## API Methods
99
179
 
100
180
  ```typescript
@@ -106,11 +186,196 @@ await vision.stop(); // Stop and cleanup resources
106
186
  await vision.updatePrompt(newPrompt); // Update task while running
107
187
 
108
188
  // State access
109
- vision.getMediaStream(); // Get MediaStream for video preview
110
- vision.getStreamId(); // Get current stream ID
189
+ vision.getMediaStream(); // Get MediaStream for video preview (null if not started)
190
+ vision.getStreamId(); // Get current stream ID (null if not started)
111
191
  vision.isActive(); // Check if stream is running
112
192
  ```
113
193
 
194
+ ## Stream Lifecycle
195
+
196
+ ### Keepalive
197
+
198
+ Streams have a server-side lease (typically 300 seconds). The SDK automatically sends keepalive requests to maintain the connection. If the keepalive fails (e.g., network issues), the stream will stop and `onError` will be called.
199
+
200
+ You don't need to manage keepalives manually - just call `start()` and the SDK handles the rest.
201
+
202
+ ### State and Memory
203
+
204
+ The SDK does not maintain memory or state between inference calls - each frame clip is processed independently. If your application needs to track state over time (e.g., counting repetitions, detecting transitions), implement this in your `onResult` callback:
205
+
206
+ ```typescript
207
+ let lastPosition = "up";
208
+ let repCount = 0;
209
+
210
+ const vision = new RealtimeVision({
211
+ // ...config
212
+ onResult: (result) => {
213
+ const data = JSON.parse(result.result);
214
+
215
+ // Track state transitions externally
216
+ if (lastPosition === "down" && data.position === "up") {
217
+ repCount++;
218
+ }
219
+ lastPosition = data.position;
220
+ },
221
+ });
222
+ ```
223
+
224
+ For result deduplication (e.g., avoiding repeated announcements), track previous results and implement cooldown logic in your application code.
225
+
226
+ ## Prompt Engineering
227
+
228
+ Prompt quality significantly affects results. Here are some tips:
229
+
230
+ **Be specific about output format:**
231
+
232
+ ```typescript
233
+ prompt: "Count the people visible. Return only a number.";
234
+ ```
235
+
236
+ **Include examples for complex tasks:**
237
+
238
+ ```typescript
239
+ prompt: `Describe the primary action happening. Examples:
240
+ - "Person walking left"
241
+ - "Car turning right"
242
+ - "Dog sitting still"`;
243
+ ```
244
+
245
+ **Request minimal output for lower latency:**
246
+
247
+ ```typescript
248
+ prompt: "Is there a person in frame? Answer only 'yes' or 'no'.";
249
+ ```
250
+
251
+ **Provide context when needed:**
252
+
253
+ ```typescript
254
+ prompt: `You are monitoring a ${locationName}. Alert if you see: ${alertConditions.join(", ")}.`;
255
+ ```
256
+
257
+ **Use JSON schema for structured data:**
258
+
259
+ ```typescript
260
+ prompt: "Analyze the scene",
261
+ outputSchema: {
262
+ type: "object",
263
+ properties: {
264
+ description: { type: "string" },
265
+ alert: { type: "boolean" }
266
+ },
267
+ required: ["description", "alert"]
268
+ }
269
+ ```
270
+
271
+ > **Note:** Prompt effectiveness varies by model. Test different approaches to find what works best for your use case.
272
+
273
+ ## React Integration
274
+
275
+ When using the SDK in React applications, ensure proper cleanup:
276
+
277
+ ```typescript
278
+ import { useEffect, useRef, useState } from "react";
279
+ import { RealtimeVision } from "@overshoot/sdk";
280
+
281
+ function VisionComponent() {
282
+ const visionRef = useRef<RealtimeVision | null>(null);
283
+ const videoRef = useRef<HTMLVideoElement>(null);
284
+ const [isRunning, setIsRunning] = useState(false);
285
+
286
+ const startVision = async () => {
287
+ const vision = new RealtimeVision({
288
+ apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
289
+ apiKey: "your-api-key",
290
+ prompt: "Describe what you see",
291
+ onResult: (result) => {
292
+ console.log(result.result);
293
+ },
294
+ onError: (error) => {
295
+ console.error("Vision error:", error);
296
+ setIsRunning(false);
297
+ },
298
+ });
299
+
300
+ await vision.start();
301
+ visionRef.current = vision;
302
+ setIsRunning(true);
303
+
304
+ // Attach stream to video element for preview
305
+ const stream = vision.getMediaStream();
306
+ if (stream && videoRef.current) {
307
+ videoRef.current.srcObject = stream;
308
+ videoRef.current.play().catch(console.error);
309
+ }
310
+ };
311
+
312
+ // Cleanup on unmount
313
+ useEffect(() => {
314
+ return () => {
315
+ visionRef.current?.stop();
316
+ };
317
+ }, []);
318
+
319
+ return (
320
+ <div>
321
+ <video ref={videoRef} autoPlay playsInline muted />
322
+ <button onClick={startVision} disabled={isRunning}>
323
+ Start
324
+ </button>
325
+ <button onClick={() => visionRef.current?.stop()} disabled={!isRunning}>
326
+ Stop
327
+ </button>
328
+ </div>
329
+ );
330
+ }
331
+ ```
332
+
333
+ ## Advanced: Custom Video Sources with StreamClient
334
+
335
+ For advanced use cases like streaming from a canvas, screen capture, or other custom sources, use `StreamClient` directly:
336
+
337
+ ```typescript
338
+ import { StreamClient } from "@overshoot/sdk";
339
+
340
+ const client = new StreamClient({
341
+ baseUrl: "https://cluster1.overshoot.ai/api/v0.2",
342
+ apiKey: "your-api-key",
343
+ });
344
+
345
+ // Get stream from any source (canvas, screen capture, etc.)
346
+ const canvas = document.querySelector("canvas");
347
+ const stream = canvas.captureStream(30);
348
+ const videoTrack = stream.getVideoTracks()[0];
349
+
350
+ // Set up WebRTC connection
351
+ const peerConnection = new RTCPeerConnection({ iceServers: [...] }); // See default ice servers in RealtimeVison.ts file
352
+ peerConnection.addTrack(videoTrack, stream);
353
+
354
+ const offer = await peerConnection.createOffer();
355
+ await peerConnection.setLocalDescription(offer);
356
+
357
+ // Create stream on server
358
+ const response = await client.createStream({
359
+ webrtc: { type: "offer", sdp: peerConnection.localDescription.sdp },
360
+ processing: { sampling_ratio: 0.5, fps: 30, clip_length_seconds: 1.0, delay_seconds: 1.0 },
361
+ inference: {
362
+ prompt: "Analyze the content",
363
+ backend: "overshoot",
364
+ model: "Qwen/Qwen3-VL-30B-A3B-Instruct",
365
+ },
366
+ });
367
+
368
+ await peerConnection.setRemoteDescription(response.webrtc);
369
+
370
+ // Connect WebSocket for results
371
+ const ws = client.connectWebSocket(response.stream_id);
372
+ ws.onopen = () => ws.send(JSON.stringify({ api_key: "your-api-key" }));
373
+ ws.onmessage = (event) => {
374
+ const result = JSON.parse(event.data);
375
+ console.log("Result:", result);
376
+ };
377
+ ```
378
+
114
379
  ## Examples
115
380
 
116
381
  ### Object Detection with Structured Output
@@ -152,26 +417,6 @@ const vision = new RealtimeVision({
152
417
  await vision.start();
153
418
  ```
154
419
 
155
- ### Video Preview Display
156
-
157
- ```typescript
158
- const vision = new RealtimeVision({
159
- apiUrl: "https://cluster1.overshoot.ai/api/v0.2",
160
- apiKey: "your-api-key",
161
- prompt: "Describe what you see",
162
- onResult: (result) => console.log(result.result),
163
- });
164
-
165
- await vision.start();
166
-
167
- // Attach to video element for preview
168
- const videoElement = document.querySelector("video");
169
- const stream = vision.getMediaStream();
170
- if (stream) {
171
- videoElement.srcObject = stream;
172
- }
173
- ```
174
-
175
420
  ### Dynamic Prompt Updates
176
421
 
177
422
  ```typescript
@@ -246,7 +491,7 @@ interface StreamInferenceResult {
246
491
  model_backend: "overshoot";
247
492
  model_name: string; // Model used
248
493
  prompt: string; // Task that was run
249
- result: string; // Model output (text or JSON string)
494
+ result: string; // Model output (always a string - parse JSON if using outputSchema)
250
495
  inference_latency_ms: number; // Model inference time
251
496
  total_latency_ms: number; // End-to-end latency
252
497
  ok: boolean; // Success status
package/dist/index.d.mts CHANGED
@@ -64,20 +64,6 @@ type StreamConfigResponse = {
64
64
  created_at?: string;
65
65
  updated_at?: string;
66
66
  };
67
- type FeedbackCreateRequest = {
68
- rating: number;
69
- category: string;
70
- feedback?: string;
71
- };
72
- type FeedbackResponse = {
73
- id: string;
74
- stream_id: string;
75
- rating: number;
76
- category: string;
77
- feedback: string;
78
- created_at?: string;
79
- updated_at?: string;
80
- };
81
67
  type KeepaliveResponse = {
82
68
  status: "ok";
83
69
  stream_id: string;
@@ -105,8 +91,6 @@ declare class StreamClient {
105
91
  createStream(request: StreamCreateRequest): Promise<StreamCreateResponse>;
106
92
  renewLease(streamId: string): Promise<KeepaliveResponse>;
107
93
  updatePrompt(streamId: string, prompt: string): Promise<StreamConfigResponse>;
108
- submitFeedback(streamId: string, feedback: FeedbackCreateRequest): Promise<StatusResponse>;
109
- getAllFeedback(): Promise<FeedbackResponse[]>;
110
94
  connectWebSocket(streamId: string): WebSocket;
111
95
  /**
112
96
  * Health check endpoint (for testing, uses internal port if available)
@@ -203,6 +187,8 @@ declare class RealtimeVision {
203
187
  private streamId;
204
188
  private keepaliveInterval;
205
189
  private videoElement;
190
+ private canvasElement;
191
+ private canvasAnimationFrameId;
206
192
  private isRunning;
207
193
  constructor(config: RealtimeVisionConfig);
208
194
  /**
@@ -253,14 +239,6 @@ declare class RealtimeVision {
253
239
  * Stop the vision stream and clean up resources
254
240
  */
255
241
  stop(): Promise<void>;
256
- /**
257
- * Submit feedback for the stream
258
- */
259
- submitFeedback(feedback: {
260
- rating: number;
261
- category: string;
262
- feedback?: string;
263
- }): Promise<void>;
264
242
  /**
265
243
  * Get the current stream ID
266
244
  */
@@ -299,4 +277,4 @@ declare class ServerError extends ApiError {
299
277
  constructor(message: string, requestId?: string, details?: any);
300
278
  }
301
279
 
302
- export { ApiError, type ErrorResponse, type FeedbackCreateRequest, type FeedbackResponse, type KeepaliveResponse, NetworkError, NotFoundError, RealtimeVision, type RealtimeVisionConfig, ServerError, type StatusResponse, StreamClient, type StreamClientMeta, type StreamConfigResponse, type StreamCreateRequest, type StreamCreateResponse, type StreamInferenceConfig, type StreamInferenceResult, type StreamProcessingConfig, type StreamSource, UnauthorizedError, ValidationError, type WebRtcAnswer, type WebRtcOffer };
280
+ export { ApiError, type ErrorResponse, type KeepaliveResponse, NetworkError, NotFoundError, RealtimeVision, type RealtimeVisionConfig, ServerError, type StatusResponse, StreamClient, type StreamClientMeta, type StreamConfigResponse, type StreamCreateRequest, type StreamCreateResponse, type StreamInferenceConfig, type StreamInferenceResult, type StreamProcessingConfig, type StreamSource, UnauthorizedError, ValidationError, type WebRtcAnswer, type WebRtcOffer };
package/dist/index.d.ts CHANGED
@@ -64,20 +64,6 @@ type StreamConfigResponse = {
64
64
  created_at?: string;
65
65
  updated_at?: string;
66
66
  };
67
- type FeedbackCreateRequest = {
68
- rating: number;
69
- category: string;
70
- feedback?: string;
71
- };
72
- type FeedbackResponse = {
73
- id: string;
74
- stream_id: string;
75
- rating: number;
76
- category: string;
77
- feedback: string;
78
- created_at?: string;
79
- updated_at?: string;
80
- };
81
67
  type KeepaliveResponse = {
82
68
  status: "ok";
83
69
  stream_id: string;
@@ -105,8 +91,6 @@ declare class StreamClient {
105
91
  createStream(request: StreamCreateRequest): Promise<StreamCreateResponse>;
106
92
  renewLease(streamId: string): Promise<KeepaliveResponse>;
107
93
  updatePrompt(streamId: string, prompt: string): Promise<StreamConfigResponse>;
108
- submitFeedback(streamId: string, feedback: FeedbackCreateRequest): Promise<StatusResponse>;
109
- getAllFeedback(): Promise<FeedbackResponse[]>;
110
94
  connectWebSocket(streamId: string): WebSocket;
111
95
  /**
112
96
  * Health check endpoint (for testing, uses internal port if available)
@@ -203,6 +187,8 @@ declare class RealtimeVision {
203
187
  private streamId;
204
188
  private keepaliveInterval;
205
189
  private videoElement;
190
+ private canvasElement;
191
+ private canvasAnimationFrameId;
206
192
  private isRunning;
207
193
  constructor(config: RealtimeVisionConfig);
208
194
  /**
@@ -253,14 +239,6 @@ declare class RealtimeVision {
253
239
  * Stop the vision stream and clean up resources
254
240
  */
255
241
  stop(): Promise<void>;
256
- /**
257
- * Submit feedback for the stream
258
- */
259
- submitFeedback(feedback: {
260
- rating: number;
261
- category: string;
262
- feedback?: string;
263
- }): Promise<void>;
264
242
  /**
265
243
  * Get the current stream ID
266
244
  */
@@ -299,4 +277,4 @@ declare class ServerError extends ApiError {
299
277
  constructor(message: string, requestId?: string, details?: any);
300
278
  }
301
279
 
302
- export { ApiError, type ErrorResponse, type FeedbackCreateRequest, type FeedbackResponse, type KeepaliveResponse, NetworkError, NotFoundError, RealtimeVision, type RealtimeVisionConfig, ServerError, type StatusResponse, StreamClient, type StreamClientMeta, type StreamConfigResponse, type StreamCreateRequest, type StreamCreateResponse, type StreamInferenceConfig, type StreamInferenceResult, type StreamProcessingConfig, type StreamSource, UnauthorizedError, ValidationError, type WebRtcAnswer, type WebRtcOffer };
280
+ export { ApiError, type ErrorResponse, type KeepaliveResponse, NetworkError, NotFoundError, RealtimeVision, type RealtimeVisionConfig, ServerError, type StatusResponse, StreamClient, type StreamClientMeta, type StreamConfigResponse, type StreamCreateRequest, type StreamCreateResponse, type StreamInferenceConfig, type StreamInferenceResult, type StreamProcessingConfig, type StreamSource, UnauthorizedError, ValidationError, type WebRtcAnswer, type WebRtcOffer };
package/dist/index.js CHANGED
@@ -132,17 +132,6 @@ var StreamClient = class {
132
132
  }
133
133
  );
134
134
  }
135
- async submitFeedback(streamId, feedback) {
136
- return this.request(`/streams/${streamId}/feedback`, {
137
- method: "POST",
138
- body: JSON.stringify(feedback)
139
- });
140
- }
141
- async getAllFeedback() {
142
- return this.request("/streams/feedback", {
143
- method: "GET"
144
- });
145
- }
146
135
  connectWebSocket(streamId) {
147
136
  const wsUrl = this.baseUrl.replace("http://", "ws://").replace("https://", "wss://");
148
137
  return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);
@@ -192,13 +181,11 @@ var DEFAULTS = {
192
181
  }
193
182
  ]
194
183
  };
195
- console.log("defaults", DEFAULTS);
196
184
  var CONSTRAINTS = {
197
185
  SAMPLING_RATIO: { min: 0, max: 1 },
198
186
  FPS: { min: 1, max: 120 },
199
187
  CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },
200
- DELAY_SECONDS: { min: 0, max: 60 },
201
- RATING: { min: 1, max: 5 }
188
+ DELAY_SECONDS: { min: 0, max: 60 }
202
189
  };
203
190
  var Logger = class {
204
191
  constructor(debugEnabled = false) {
@@ -233,6 +220,8 @@ var RealtimeVision = class {
233
220
  this.streamId = null;
234
221
  this.keepaliveInterval = null;
235
222
  this.videoElement = null;
223
+ this.canvasElement = null;
224
+ this.canvasAnimationFrameId = null;
236
225
  this.isRunning = false;
237
226
  this.validateConfig(config);
238
227
  this.config = config;
@@ -342,7 +331,31 @@ var RealtimeVision = class {
342
331
  });
343
332
  await video.play();
344
333
  this.logger.debug("Video playback started");
345
- const stream = video.captureStream();
334
+ let stream;
335
+ if (typeof video.captureStream === "function") {
336
+ this.logger.debug("Using native video.captureStream()");
337
+ stream = video.captureStream();
338
+ } else {
339
+ this.logger.debug(
340
+ "captureStream not supported, using canvas fallback for Safari"
341
+ );
342
+ const canvas = document.createElement("canvas");
343
+ canvas.width = video.videoWidth || 640;
344
+ canvas.height = video.videoHeight || 480;
345
+ const ctx = canvas.getContext("2d");
346
+ if (!ctx) {
347
+ throw new Error("Failed to get canvas 2D context");
348
+ }
349
+ const drawFrame = () => {
350
+ if (!video.paused && !video.ended) {
351
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
352
+ this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);
353
+ }
354
+ };
355
+ drawFrame();
356
+ stream = canvas.captureStream(30);
357
+ this.canvasElement = canvas;
358
+ }
346
359
  if (!stream) {
347
360
  throw new Error("Failed to capture video stream");
348
361
  }
@@ -605,29 +618,6 @@ var RealtimeVision = class {
605
618
  await this.cleanup();
606
619
  this.isRunning = false;
607
620
  }
608
- /**
609
- * Submit feedback for the stream
610
- */
611
- async submitFeedback(feedback) {
612
- if (!this.streamId) {
613
- throw new Error("No active stream");
614
- }
615
- if (feedback.rating < CONSTRAINTS.RATING.min || feedback.rating > CONSTRAINTS.RATING.max) {
616
- throw new ValidationError2(
617
- `rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`
618
- );
619
- }
620
- if (!feedback.category || typeof feedback.category !== "string") {
621
- throw new ValidationError2("category must be a non-empty string");
622
- }
623
- this.logger.debug("Submitting feedback");
624
- await this.client.submitFeedback(this.streamId, {
625
- rating: feedback.rating,
626
- category: feedback.category,
627
- feedback: feedback.feedback ?? ""
628
- });
629
- this.logger.info("Feedback submitted");
630
- }
631
621
  /**
632
622
  * Get the current stream ID
633
623
  */
@@ -664,6 +654,14 @@ var RealtimeVision = class {
664
654
  this.mediaStream.getTracks().forEach((track) => track.stop());
665
655
  this.mediaStream = null;
666
656
  }
657
+ if (this.canvasAnimationFrameId) {
658
+ cancelAnimationFrame(this.canvasAnimationFrameId);
659
+ this.canvasAnimationFrameId = null;
660
+ }
661
+ if (this.canvasElement) {
662
+ this.canvasElement.remove();
663
+ this.canvasElement = null;
664
+ }
667
665
  if (this.videoElement) {
668
666
  this.videoElement.pause();
669
667
  URL.revokeObjectURL(this.videoElement.src);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.js","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n FeedbackCreateRequest,\n FeedbackResponse,\n StatusResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n async submitFeedback(\n streamId: string,\n feedback: FeedbackCreateRequest,\n ): Promise<StatusResponse> {\n return this.request<StatusResponse>(`/streams/${streamId}/feedback`, {\n method: \"POST\",\n body: JSON.stringify(feedback),\n });\n }\n\n async getAllFeedback(): Promise<FeedbackResponse[]> {\n return this.request<FeedbackResponse[]>(\"/streams/feedback\", {\n method: \"GET\",\n });\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\nconsole.log(\"defaults\", DEFAULTS);\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n RATING: { min: 1, max: 5 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n const stream = video.captureStream();\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Submit feedback for the stream\n */\n async submitFeedback(feedback: {\n rating: number;\n category: string;\n feedback?: string;\n }): Promise<void> {\n if (!this.streamId) {\n throw new Error(\"No active stream\");\n }\n\n if (\n feedback.rating < CONSTRAINTS.RATING.min ||\n feedback.rating > CONSTRAINTS.RATING.max\n ) {\n throw new ValidationError(\n `rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`,\n );\n }\n\n if (!feedback.category || typeof feedback.category !== \"string\") {\n throw new ValidationError(\"category must be a non-empty string\");\n }\n\n this.logger.debug(\"Submitting feedback\");\n await this.client.submitFeedback(this.streamId, {\n rating: feedback.rating,\n category: feedback.category,\n feedback: feedback.feedback ?? \"\",\n });\n this.logger.info(\"Feedback submitted\");\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.js","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n private canvasElement: HTMLCanvasElement | null = null;\n private canvasAnimationFrameId: number | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n let stream: MediaStream;\n\n // Check if captureStream is supported (Chrome, Firefox)\n if (typeof video.captureStream === \"function\") {\n this.logger.debug(\"Using native video.captureStream()\");\n stream = video.captureStream();\n } else {\n // Safari fallback: use canvas to capture the video stream\n this.logger.debug(\n \"captureStream not supported, using canvas fallback for Safari\",\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth || 640;\n canvas.height = video.videoHeight || 480;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Failed to get canvas 2D context\");\n }\n\n // Draw video frames to canvas continuously\n const drawFrame = () => {\n if (!video.paused && !video.ended) {\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);\n }\n };\n drawFrame();\n\n // Capture stream from canvas (30 fps)\n stream = canvas.captureStream(30);\n this.canvasElement = canvas;\n }\n\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.canvasAnimationFrameId) {\n cancelAnimationFrame(this.canvasAnimationFrameId);\n this.canvasAnimationFrameId = null;\n }\n\n if (this.canvasElement) {\n this.canvasElement.remove();\n this.canvasElement = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -130,17 +130,6 @@ var StreamClient = class {
130
130
  }
131
131
  );
132
132
  }
133
- async submitFeedback(streamId, feedback) {
134
- return this.request(`/streams/${streamId}/feedback`, {
135
- method: "POST",
136
- body: JSON.stringify(feedback)
137
- });
138
- }
139
- async getAllFeedback() {
140
- return this.request("/streams/feedback", {
141
- method: "GET"
142
- });
143
- }
144
133
  connectWebSocket(streamId) {
145
134
  const wsUrl = this.baseUrl.replace("http://", "ws://").replace("https://", "wss://");
146
135
  return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);
@@ -190,13 +179,11 @@ var DEFAULTS = {
190
179
  }
191
180
  ]
192
181
  };
193
- console.log("defaults", DEFAULTS);
194
182
  var CONSTRAINTS = {
195
183
  SAMPLING_RATIO: { min: 0, max: 1 },
196
184
  FPS: { min: 1, max: 120 },
197
185
  CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },
198
- DELAY_SECONDS: { min: 0, max: 60 },
199
- RATING: { min: 1, max: 5 }
186
+ DELAY_SECONDS: { min: 0, max: 60 }
200
187
  };
201
188
  var Logger = class {
202
189
  constructor(debugEnabled = false) {
@@ -231,6 +218,8 @@ var RealtimeVision = class {
231
218
  this.streamId = null;
232
219
  this.keepaliveInterval = null;
233
220
  this.videoElement = null;
221
+ this.canvasElement = null;
222
+ this.canvasAnimationFrameId = null;
234
223
  this.isRunning = false;
235
224
  this.validateConfig(config);
236
225
  this.config = config;
@@ -340,7 +329,31 @@ var RealtimeVision = class {
340
329
  });
341
330
  await video.play();
342
331
  this.logger.debug("Video playback started");
343
- const stream = video.captureStream();
332
+ let stream;
333
+ if (typeof video.captureStream === "function") {
334
+ this.logger.debug("Using native video.captureStream()");
335
+ stream = video.captureStream();
336
+ } else {
337
+ this.logger.debug(
338
+ "captureStream not supported, using canvas fallback for Safari"
339
+ );
340
+ const canvas = document.createElement("canvas");
341
+ canvas.width = video.videoWidth || 640;
342
+ canvas.height = video.videoHeight || 480;
343
+ const ctx = canvas.getContext("2d");
344
+ if (!ctx) {
345
+ throw new Error("Failed to get canvas 2D context");
346
+ }
347
+ const drawFrame = () => {
348
+ if (!video.paused && !video.ended) {
349
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
350
+ this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);
351
+ }
352
+ };
353
+ drawFrame();
354
+ stream = canvas.captureStream(30);
355
+ this.canvasElement = canvas;
356
+ }
344
357
  if (!stream) {
345
358
  throw new Error("Failed to capture video stream");
346
359
  }
@@ -603,29 +616,6 @@ var RealtimeVision = class {
603
616
  await this.cleanup();
604
617
  this.isRunning = false;
605
618
  }
606
- /**
607
- * Submit feedback for the stream
608
- */
609
- async submitFeedback(feedback) {
610
- if (!this.streamId) {
611
- throw new Error("No active stream");
612
- }
613
- if (feedback.rating < CONSTRAINTS.RATING.min || feedback.rating > CONSTRAINTS.RATING.max) {
614
- throw new ValidationError2(
615
- `rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`
616
- );
617
- }
618
- if (!feedback.category || typeof feedback.category !== "string") {
619
- throw new ValidationError2("category must be a non-empty string");
620
- }
621
- this.logger.debug("Submitting feedback");
622
- await this.client.submitFeedback(this.streamId, {
623
- rating: feedback.rating,
624
- category: feedback.category,
625
- feedback: feedback.feedback ?? ""
626
- });
627
- this.logger.info("Feedback submitted");
628
- }
629
619
  /**
630
620
  * Get the current stream ID
631
621
  */
@@ -662,6 +652,14 @@ var RealtimeVision = class {
662
652
  this.mediaStream.getTracks().forEach((track) => track.stop());
663
653
  this.mediaStream = null;
664
654
  }
655
+ if (this.canvasAnimationFrameId) {
656
+ cancelAnimationFrame(this.canvasAnimationFrameId);
657
+ this.canvasAnimationFrameId = null;
658
+ }
659
+ if (this.canvasElement) {
660
+ this.canvasElement.remove();
661
+ this.canvasElement = null;
662
+ }
665
663
  if (this.videoElement) {
666
664
  this.videoElement.pause();
667
665
  URL.revokeObjectURL(this.videoElement.src);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.mjs","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n FeedbackCreateRequest,\n FeedbackResponse,\n StatusResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n async submitFeedback(\n streamId: string,\n feedback: FeedbackCreateRequest,\n ): Promise<StatusResponse> {\n return this.request<StatusResponse>(`/streams/${streamId}/feedback`, {\n method: \"POST\",\n body: JSON.stringify(feedback),\n });\n }\n\n async getAllFeedback(): Promise<FeedbackResponse[]> {\n return this.request<FeedbackResponse[]>(\"/streams/feedback\", {\n method: \"GET\",\n });\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\nconsole.log(\"defaults\", DEFAULTS);\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n RATING: { min: 1, max: 5 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n const stream = video.captureStream();\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Submit feedback for the stream\n */\n async submitFeedback(feedback: {\n rating: number;\n category: string;\n feedback?: string;\n }): Promise<void> {\n if (!this.streamId) {\n throw new Error(\"No active stream\");\n }\n\n if (\n feedback.rating < CONSTRAINTS.RATING.min ||\n feedback.rating > CONSTRAINTS.RATING.max\n ) {\n throw new ValidationError(\n `rating must be between ${CONSTRAINTS.RATING.min} and ${CONSTRAINTS.RATING.max}`,\n );\n }\n\n if (!feedback.category || typeof feedback.category !== \"string\") {\n throw new ValidationError(\"category must be a non-empty string\");\n }\n\n this.logger.debug(\"Submitting feedback\");\n await this.client.submitFeedback(this.streamId, {\n rating: feedback.rating,\n category: feedback.category,\n feedback: feedback.feedback ?? \"\",\n });\n this.logger.info(\"Feedback submitted\");\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/client/errors.ts","../src/client/client.ts","../src/client/RealtimeVision.ts"],"names":["ValidationError"],"mappings":"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","file":"index.mjs","sourcesContent":["export class ApiError extends Error {\n readonly statusCode?: number;\n readonly requestId?: string;\n readonly details?: any;\n\n constructor(\n message: string,\n statusCode?: number,\n requestId?: string,\n details?: any,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.requestId = requestId;\n this.details = details;\n }\n}\n\nexport class UnauthorizedError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 401, requestId);\n this.name = \"UnauthorizedError\";\n }\n}\n\nexport class ValidationError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 422, requestId, details);\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, requestId?: string) {\n super(message, 404, requestId);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class NetworkError extends ApiError {\n readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"NetworkError\";\n this.cause = cause;\n }\n}\n\nexport class ServerError extends ApiError {\n constructor(message: string, requestId?: string, details?: any) {\n super(message, 500, requestId, details);\n this.name = \"ServerError\";\n }\n}\n","import type {\n StreamCreateRequest,\n StreamCreateResponse,\n KeepaliveResponse,\n StreamConfigResponse,\n ErrorResponse,\n} from \"./types\";\nimport {\n ApiError,\n ValidationError,\n NotFoundError,\n NetworkError,\n ServerError,\n UnauthorizedError,\n} from \"./errors\";\n\ntype ClientConfig = {\n baseUrl: string;\n apiKey: string;\n};\n\nexport class StreamClient {\n private baseUrl: string;\n private apiKey: string;\n\n constructor(config: ClientConfig) {\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new Error(\"apiKey is required and must be a string\");\n }\n\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorData: ErrorResponse = await response.json().catch(() => ({\n error: \"unknown_error\",\n message: response.statusText,\n }));\n\n const message = errorData.message || errorData.error;\n\n if (response.status === 401) {\n throw new UnauthorizedError(\n message || \"Invalid or revoked API key\",\n errorData.request_id,\n );\n }\n if (response.status === 422 || response.status === 400) {\n throw new ValidationError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n if (response.status === 404) {\n throw new NotFoundError(message, errorData.request_id);\n }\n if (response.status >= 500) {\n throw new ServerError(\n message,\n errorData.request_id,\n errorData.details,\n );\n }\n\n throw new ApiError(\n message,\n response.status,\n errorData.request_id,\n errorData.details,\n );\n }\n\n return await response.json();\n } catch (error) {\n if (error instanceof ApiError) {\n throw error;\n }\n\n if (error instanceof Error) {\n throw new NetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NetworkError(\"Unknown network error\");\n }\n }\n async createStream(\n request: StreamCreateRequest,\n ): Promise<StreamCreateResponse> {\n return this.request<StreamCreateResponse>(\"/streams\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n }\n\n async renewLease(streamId: string): Promise<KeepaliveResponse> {\n return this.request<KeepaliveResponse>(`/streams/${streamId}/keepalive`, {\n method: \"POST\",\n });\n }\n\n async updatePrompt(\n streamId: string,\n prompt: string,\n ): Promise<StreamConfigResponse> {\n return this.request<StreamConfigResponse>(\n `/streams/${streamId}/config/prompt`,\n {\n method: \"PATCH\",\n body: JSON.stringify({ prompt }),\n },\n );\n }\n\n connectWebSocket(streamId: string): WebSocket {\n const wsUrl = this.baseUrl\n .replace(\"http://\", \"ws://\")\n .replace(\"https://\", \"wss://\");\n return new WebSocket(`${wsUrl}/ws/streams/${streamId}`);\n }\n\n /**\n * Health check endpoint (for testing, uses internal port if available)\n * Note: This endpoint may not be available via the main API\n */\n async healthCheck(): Promise<string> {\n const url = `${this.baseUrl}/healthz`;\n const response = await fetch(url, {\n credentials: \"include\",\n });\n return response.text();\n }\n}\n","import { StreamClient } from \"./client\";\n\nimport {\n type StreamInferenceResult,\n type StreamProcessingConfig,\n type StreamSource,\n} from \"./types\";\n\n/**\n * Default configuration values for RealtimeVision\n */\nconst DEFAULTS = {\n BACKEND: \"overshoot\" as const,\n MODEL: \"Qwen/Qwen3-VL-30B-A3B-Instruct\",\n SOURCE: { type: \"camera\", cameraFacing: \"environment\" } as const,\n SAMPLING_RATIO: 0.1,\n CLIP_LENGTH_SECONDS: 1.0,\n DELAY_SECONDS: 1.0,\n FALLBACK_FPS: 30,\n ICE_SERVERS: [\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turn:turn.overshoot.ai:3478?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=udp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n {\n urls: \"turns:turn.overshoot.ai:443?transport=tcp\",\n username: \"1769538895:c66a907c-61f4-4ec2-93a6-9d6b932776bb\",\n credential: \"Fu9L4CwyYZvsOLc+23psVAo3i/Y=\",\n },\n ] as RTCIceServer[],\n} as const;\n\n/**\n * Validation constraints\n */\nconst CONSTRAINTS = {\n SAMPLING_RATIO: { min: 0, max: 1 },\n FPS: { min: 1, max: 120 },\n CLIP_LENGTH_SECONDS: { min: 0.1, max: 60 },\n DELAY_SECONDS: { min: 0, max: 60 },\n} as const;\n\n/**\n * Logger utility for controlled logging\n */\nclass Logger {\n private debugEnabled: boolean;\n\n constructor(debugEnabled: boolean = false) {\n this.debugEnabled = debugEnabled;\n }\n\n debug(...args: any[]): void {\n if (this.debugEnabled) {\n console.log(\"[RealtimeVision Debug]\", ...args);\n }\n }\n\n info(...args: any[]): void {\n console.log(\"[RealtimeVision]\", ...args);\n }\n\n warn(...args: any[]): void {\n console.warn(\"[RealtimeVision]\", ...args);\n }\n\n error(...args: any[]): void {\n console.error(\"[RealtimeVision]\", ...args);\n }\n}\n\nexport interface RealtimeVisionConfig {\n /**\n * Base URL for the API (e.g., \"https://api.example.com\")\n */\n apiUrl: string;\n\n /**\n * API key for authentication\n * Required for all API requests\n */\n apiKey: string;\n\n /**\n * The prompt/task to run on window segments of the stream.\n * This runs continuously (at a defined window interval).\n *\n * Examples:\n * - \"Read any visible text\"\n * - \"Detect objects and return as JSON array\"\n * - \"Describe facial expression\"\n */\n prompt: string;\n\n /**\n * Video source configuration\n * Defaults to camera with environment facing if not specified\n */\n source?: StreamSource;\n\n /**\n * Model backend to use\n */\n backend?: \"overshoot\";\n\n /**\n * Model name to use for inference\n */\n model?: string;\n\n /**\n * Optional JSON schema for structured output\n */\n outputSchema?: Record<string, any>;\n\n /**\n * Called when a new inference result arrives (~1 per second)\n */\n onResult: (result: StreamInferenceResult) => void;\n\n /**\n * Called when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom processing configuration\n * All fields are optional and will use defaults if not provided\n */\n processing?: {\n /**\n * Sampling ratio (0-1). Controls what fraction of frames are processed.\n */\n sampling_ratio?: number;\n /**\n * Frames per second (1-120)\n */\n fps?: number;\n /**\n * Clip length in seconds (0.1-60)\n */\n clip_length_seconds?: number;\n /**\n * Delay in seconds (0-60)\n */\n delay_seconds?: number;\n };\n\n /**\n * ICE servers for WebRTC connection\n * If not provided, uses default TURN servers\n */\n iceServers?: RTCIceServer[];\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nclass ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RealtimeVision {\n private config: RealtimeVisionConfig;\n private client: StreamClient;\n private logger: Logger;\n\n private mediaStream: MediaStream | null = null;\n private peerConnection: RTCPeerConnection | null = null;\n private webSocket: WebSocket | null = null;\n private streamId: string | null = null;\n private keepaliveInterval: number | null = null;\n private videoElement: HTMLVideoElement | null = null;\n private canvasElement: HTMLCanvasElement | null = null;\n private canvasAnimationFrameId: number | null = null;\n\n private isRunning = false;\n\n constructor(config: RealtimeVisionConfig) {\n this.validateConfig(config);\n this.config = config;\n this.logger = new Logger(config.debug ?? false);\n this.client = new StreamClient({\n baseUrl: config.apiUrl,\n apiKey: config.apiKey,\n });\n }\n\n /**\n * Validate configuration values\n */\n private validateConfig(config: RealtimeVisionConfig): void {\n if (!config.apiUrl || typeof config.apiUrl !== \"string\") {\n throw new ValidationError(\"apiUrl is required and must be a string\");\n }\n\n if (!config.apiKey || typeof config.apiKey !== \"string\") {\n throw new ValidationError(\"apiKey is required and must be a string\");\n }\n\n if (!config.prompt || typeof config.prompt !== \"string\") {\n throw new ValidationError(\"prompt is required and must be a string\");\n }\n\n if (config.source) {\n if (config.source.type === \"camera\") {\n if (\n config.source.cameraFacing !== \"user\" &&\n config.source.cameraFacing !== \"environment\"\n ) {\n throw new ValidationError(\n 'cameraFacing must be \"user\" or \"environment\"',\n );\n }\n } else if (config.source.type === \"video\") {\n if (!(config.source.file instanceof File)) {\n throw new ValidationError(\"video source must provide a File object\");\n }\n } else {\n throw new ValidationError('source.type must be \"camera\" or \"video\"');\n }\n }\n\n if (config.processing?.sampling_ratio !== undefined) {\n const ratio = config.processing.sampling_ratio;\n if (\n ratio < CONSTRAINTS.SAMPLING_RATIO.min ||\n ratio > CONSTRAINTS.SAMPLING_RATIO.max\n ) {\n throw new ValidationError(\n `sampling_ratio must be between ${CONSTRAINTS.SAMPLING_RATIO.min} and ${CONSTRAINTS.SAMPLING_RATIO.max}`,\n );\n }\n }\n\n if (config.processing?.fps !== undefined) {\n const fps = config.processing.fps;\n if (fps < CONSTRAINTS.FPS.min || fps > CONSTRAINTS.FPS.max) {\n throw new ValidationError(\n `fps must be between ${CONSTRAINTS.FPS.min} and ${CONSTRAINTS.FPS.max}`,\n );\n }\n }\n\n if (config.processing?.clip_length_seconds !== undefined) {\n const clip = config.processing.clip_length_seconds;\n if (\n clip < CONSTRAINTS.CLIP_LENGTH_SECONDS.min ||\n clip > CONSTRAINTS.CLIP_LENGTH_SECONDS.max\n ) {\n throw new ValidationError(\n `clip_length_seconds must be between ${CONSTRAINTS.CLIP_LENGTH_SECONDS.min} and ${CONSTRAINTS.CLIP_LENGTH_SECONDS.max}`,\n );\n }\n }\n\n if (config.processing?.delay_seconds !== undefined) {\n const delay = config.processing.delay_seconds;\n if (\n delay < CONSTRAINTS.DELAY_SECONDS.min ||\n delay > CONSTRAINTS.DELAY_SECONDS.max\n ) {\n throw new ValidationError(\n `delay_seconds must be between ${CONSTRAINTS.DELAY_SECONDS.min} and ${CONSTRAINTS.DELAY_SECONDS.max}`,\n );\n }\n }\n }\n\n /**\n * Create media stream from the configured source\n */\n private async createMediaStream(source: StreamSource): Promise<MediaStream> {\n this.logger.debug(\"Creating media stream from source:\", source.type);\n\n switch (source.type) {\n case \"camera\":\n return await navigator.mediaDevices.getUserMedia({\n video: { facingMode: { ideal: source.cameraFacing } },\n audio: false,\n });\n\n case \"video\":\n const video = document.createElement(\"video\");\n video.src = URL.createObjectURL(source.file);\n video.muted = true;\n video.loop = true;\n video.playsInline = true;\n\n this.logger.debug(\"Loading video file:\", source.file.name);\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error(\"Video loading timeout after 10 seconds\"));\n }, 10000);\n\n video.onloadedmetadata = () => {\n clearTimeout(timeout);\n this.logger.debug(\"Video metadata loaded\");\n resolve();\n };\n\n video.onerror = (e) => {\n clearTimeout(timeout);\n this.logger.error(\"Video loading error:\", e);\n reject(new Error(\"Failed to load video file\"));\n };\n\n if (video.readyState >= 1) {\n clearTimeout(timeout);\n resolve();\n }\n });\n\n await video.play();\n this.logger.debug(\"Video playback started\");\n\n let stream: MediaStream;\n\n // Check if captureStream is supported (Chrome, Firefox)\n if (typeof video.captureStream === \"function\") {\n this.logger.debug(\"Using native video.captureStream()\");\n stream = video.captureStream();\n } else {\n // Safari fallback: use canvas to capture the video stream\n this.logger.debug(\n \"captureStream not supported, using canvas fallback for Safari\",\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth || 640;\n canvas.height = video.videoHeight || 480;\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Failed to get canvas 2D context\");\n }\n\n // Draw video frames to canvas continuously\n const drawFrame = () => {\n if (!video.paused && !video.ended) {\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n this.canvasAnimationFrameId = requestAnimationFrame(drawFrame);\n }\n };\n drawFrame();\n\n // Capture stream from canvas (30 fps)\n stream = canvas.captureStream(30);\n this.canvasElement = canvas;\n }\n\n if (!stream) {\n throw new Error(\"Failed to capture video stream\");\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n throw new Error(\"Video stream has no video tracks\");\n }\n\n this.videoElement = video;\n return stream;\n\n default:\n throw new Error(`Unknown source type: ${(source as any).type}`);\n }\n }\n\n /**\n * Get FPS from media stream\n */\n private async getStreamFps(\n stream: MediaStream | null,\n source: StreamSource,\n ): Promise<number> {\n if (!stream) {\n this.logger.warn(\"Stream is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (!videoTracks || videoTracks.length === 0) {\n this.logger.warn(\"No video tracks found, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n const videoTrack = videoTracks[0];\n if (!videoTrack) {\n this.logger.warn(\"First video track is null, using fallback FPS\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n // For camera sources, get FPS from track settings\n if (source.type === \"camera\") {\n const settings = videoTrack.getSettings();\n const fps = settings.frameRate ?? DEFAULTS.FALLBACK_FPS;\n this.logger.debug(\"Detected camera FPS:\", fps);\n return fps;\n }\n\n // For video file sources, try to get FPS from video element\n if (source.type === \"video\" && this.videoElement) {\n await new Promise<void>((resolve, reject) => {\n if (this.videoElement!.readyState >= 1) {\n resolve();\n } else {\n this.videoElement!.onloadedmetadata = () => resolve();\n this.videoElement!.onerror = () =>\n reject(new Error(\"Failed to load video metadata\"));\n }\n });\n\n // For video files, use fallback FPS or user-specified config\n this.logger.debug(\"Using fallback FPS for video file\");\n return DEFAULTS.FALLBACK_FPS;\n }\n\n return DEFAULTS.FALLBACK_FPS;\n }\n\n /**\n * Get processing configuration with defaults applied\n */\n private getProcessingConfig(detectedFps: number): StreamProcessingConfig {\n const userProcessing = this.config.processing || {};\n\n return {\n sampling_ratio: userProcessing.sampling_ratio ?? DEFAULTS.SAMPLING_RATIO,\n fps: userProcessing.fps ?? detectedFps,\n clip_length_seconds:\n userProcessing.clip_length_seconds ?? DEFAULTS.CLIP_LENGTH_SECONDS,\n delay_seconds: userProcessing.delay_seconds ?? DEFAULTS.DELAY_SECONDS,\n };\n }\n\n /**\n * Get the effective source configuration\n */\n private getSource(): StreamSource {\n return this.config.source ?? DEFAULTS.SOURCE;\n }\n\n /**\n * Start the vision stream\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"Vision stream already running\");\n }\n\n try {\n const source = this.getSource();\n this.logger.debug(\"Starting stream with source type:\", source.type);\n\n if (source.type === \"video\") {\n this.logger.debug(\"Video file:\", {\n name: source.file.name,\n size: source.file.size,\n type: source.file.type,\n });\n\n if (!source.file || !(source.file instanceof File)) {\n throw new Error(\"Invalid video file\");\n }\n }\n\n // Create media stream\n this.mediaStream = await this.createMediaStream(source);\n const videoTrack = this.mediaStream.getVideoTracks()[0];\n if (!videoTrack) {\n throw new Error(\"No video track available\");\n }\n\n // Get FPS for the stream\n const detectedFps = await this.getStreamFps(this.mediaStream, source);\n\n // Set up WebRTC peer connection\n const iceServers = this.config.iceServers ?? DEFAULTS.ICE_SERVERS;\n this.logger.debug(\"Creating peer connection with ICE servers\");\n this.peerConnection = new RTCPeerConnection({ iceServers });\n\n // Set up ICE logging\n this.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n this.logger.debug(\"ICE candidate:\", {\n type: event.candidate.type,\n protocol: event.candidate.protocol,\n });\n } else {\n this.logger.debug(\"ICE gathering complete\");\n }\n };\n\n this.peerConnection.oniceconnectionstatechange = () => {\n this.logger.debug(\n \"ICE connection state:\",\n this.peerConnection?.iceConnectionState,\n );\n };\n\n this.peerConnection.addTrack(videoTrack, this.mediaStream);\n\n // Create and set local offer\n const offer = await this.peerConnection.createOffer();\n await this.peerConnection.setLocalDescription(offer);\n\n if (!this.peerConnection.localDescription) {\n throw new Error(\"Failed to create local description\");\n }\n\n // Create stream on server\n this.logger.debug(\"Creating stream on server\");\n const response = await this.client.createStream({\n webrtc: {\n type: \"offer\",\n sdp: this.peerConnection.localDescription.sdp,\n },\n processing: this.getProcessingConfig(detectedFps),\n inference: {\n prompt: this.config.prompt,\n backend: this.config.backend ?? DEFAULTS.BACKEND,\n model: this.config.model ?? DEFAULTS.MODEL,\n output_schema_json: this.config.outputSchema,\n },\n });\n\n this.logger.debug(\"Backend response received:\", {\n stream_id: response.stream_id,\n has_turn_servers: !!response.turn_servers,\n });\n\n // Set remote description\n await this.peerConnection.setRemoteDescription(response.webrtc);\n\n this.streamId = response.stream_id;\n this.logger.info(\"Stream started:\", this.streamId);\n\n // Set up keepalive\n this.setupKeepalive(response.lease?.ttl_seconds);\n\n // Connect WebSocket for results\n this.setupWebSocket(response.stream_id);\n\n this.isRunning = true;\n } catch (error) {\n await this.handleFatalError(error);\n throw error;\n }\n }\n\n /**\n * Set up keepalive interval with error handling\n */\n private setupKeepalive(ttlSeconds: number | undefined): void {\n if (!ttlSeconds) {\n return;\n }\n\n const intervalMs = (ttlSeconds / 2) * 1000;\n this.logger.debug(\"Setting up keepalive with interval:\", intervalMs, \"ms\");\n\n this.keepaliveInterval = window.setInterval(async () => {\n try {\n if (this.streamId) {\n await this.client.renewLease(this.streamId);\n this.logger.debug(\"Lease renewed\");\n }\n } catch (error) {\n this.logger.error(\"Keepalive failed:\", error);\n const keepaliveError = new Error(\n `Keepalive failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n await this.handleFatalError(keepaliveError);\n }\n }, intervalMs);\n }\n\n /**\n * Set up WebSocket connection with error handling\n */\n private setupWebSocket(streamId: string): void {\n this.logger.debug(\"Connecting WebSocket for stream:\", streamId);\n this.webSocket = this.client.connectWebSocket(streamId);\n\n this.webSocket.onopen = () => {\n this.logger.debug(\"WebSocket connected\");\n if (this.webSocket) {\n this.webSocket.send(JSON.stringify({ api_key: this.config.apiKey }));\n }\n };\n\n this.webSocket.onmessage = (event) => {\n try {\n const result: StreamInferenceResult = JSON.parse(event.data);\n this.config.onResult(result);\n } catch (error) {\n const parseError = new Error(\n `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`,\n );\n this.handleNonFatalError(parseError);\n }\n };\n\n this.webSocket.onerror = () => {\n this.logger.error(\"WebSocket error occurred\");\n const error = new Error(\"WebSocket error occurred\");\n this.handleFatalError(error);\n };\n\n this.webSocket.onclose = (event) => {\n if (this.isRunning) {\n if (event.code === 1008) {\n this.logger.error(\"WebSocket authentication failed\");\n const error = new Error(\n \"WebSocket authentication failed: Invalid or revoked API key\",\n );\n this.handleFatalError(error);\n } else {\n this.logger.warn(\"WebSocket closed unexpectedly:\", event.code);\n const error = new Error(\"WebSocket closed unexpectedly\");\n this.handleFatalError(error);\n }\n } else {\n this.logger.debug(\"WebSocket closed\");\n }\n };\n }\n\n /**\n * Handle non-fatal errors (report but don't stop stream)\n */\n private handleNonFatalError(error: Error): void {\n this.logger.warn(\"Non-fatal error:\", error.message);\n if (this.config.onError) {\n this.config.onError(error);\n }\n }\n\n /**\n * Handle fatal errors (stop stream and report)\n */\n private async handleFatalError(error: unknown): Promise<void> {\n this.logger.error(\"Fatal error:\", error);\n await this.cleanup();\n this.isRunning = false;\n\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n\n if (this.config.onError) {\n this.config.onError(normalizedError);\n }\n }\n\n /**\n * Update the prompt/task while stream is running\n */\n async updatePrompt(prompt: string): Promise<void> {\n if (!this.isRunning || !this.streamId) {\n throw new Error(\"Vision stream not running\");\n }\n\n if (!prompt || typeof prompt !== \"string\") {\n throw new ValidationError(\"prompt must be a non-empty string\");\n }\n\n this.logger.debug(\"Updating prompt\");\n await this.client.updatePrompt(this.streamId, prompt);\n this.logger.info(\"Prompt updated\");\n }\n\n /**\n * Stop the vision stream and clean up resources\n */\n async stop(): Promise<void> {\n this.logger.info(\"Stopping stream\");\n await this.cleanup();\n this.isRunning = false;\n }\n\n /**\n * Get the current stream ID\n */\n getStreamId(): string | null {\n return this.streamId;\n }\n\n /**\n * Get the media stream (for displaying video preview)\n */\n getMediaStream(): MediaStream | null {\n return this.mediaStream;\n }\n\n /**\n * Check if the stream is running\n */\n isActive(): boolean {\n return this.isRunning;\n }\n\n private async cleanup(): Promise<void> {\n this.logger.debug(\"Cleaning up resources\");\n\n if (this.keepaliveInterval) {\n window.clearInterval(this.keepaliveInterval);\n this.keepaliveInterval = null;\n }\n\n if (this.webSocket) {\n this.webSocket.close();\n this.webSocket = null;\n }\n\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop());\n this.mediaStream = null;\n }\n\n if (this.canvasAnimationFrameId) {\n cancelAnimationFrame(this.canvasAnimationFrameId);\n this.canvasAnimationFrameId = null;\n }\n\n if (this.canvasElement) {\n this.canvasElement.remove();\n this.canvasElement = null;\n }\n\n if (this.videoElement) {\n this.videoElement.pause();\n URL.revokeObjectURL(this.videoElement.src);\n this.videoElement.remove();\n this.videoElement = null;\n }\n\n this.streamId = null;\n this.logger.debug(\"Cleanup complete\");\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overshoot/sdk",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },