@octomil/browser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/dist/cache.d.ts +25 -0
  4. package/dist/cache.d.ts.map +1 -0
  5. package/dist/cache.js +202 -0
  6. package/dist/cache.js.map +1 -0
  7. package/dist/device-auth.d.ts +41 -0
  8. package/dist/device-auth.d.ts.map +1 -0
  9. package/dist/device-auth.js +203 -0
  10. package/dist/device-auth.js.map +1 -0
  11. package/dist/experiments.d.ts +44 -0
  12. package/dist/experiments.d.ts.map +1 -0
  13. package/dist/experiments.js +135 -0
  14. package/dist/experiments.js.map +1 -0
  15. package/dist/federated.d.ts +53 -0
  16. package/dist/federated.d.ts.map +1 -0
  17. package/dist/federated.js +180 -0
  18. package/dist/federated.js.map +1 -0
  19. package/dist/index.cjs +2148 -0
  20. package/dist/index.cjs.map +7 -0
  21. package/dist/index.d.ts +37 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +45 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/inference.d.ts +43 -0
  26. package/dist/inference.d.ts.map +1 -0
  27. package/dist/inference.js +213 -0
  28. package/dist/inference.js.map +1 -0
  29. package/dist/integrity.d.ts +19 -0
  30. package/dist/integrity.d.ts.map +1 -0
  31. package/dist/integrity.js +35 -0
  32. package/dist/integrity.js.map +1 -0
  33. package/dist/model-loader.d.ts +40 -0
  34. package/dist/model-loader.d.ts.map +1 -0
  35. package/dist/model-loader.js +232 -0
  36. package/dist/model-loader.js.map +1 -0
  37. package/dist/octomil.d.ts +92 -0
  38. package/dist/octomil.d.ts.map +1 -0
  39. package/dist/octomil.js +368 -0
  40. package/dist/octomil.js.map +1 -0
  41. package/dist/octomil.min.js +2849 -0
  42. package/dist/octomil.min.js.map +7 -0
  43. package/dist/privacy.d.ts +40 -0
  44. package/dist/privacy.d.ts.map +1 -0
  45. package/dist/privacy.js +118 -0
  46. package/dist/privacy.js.map +1 -0
  47. package/dist/rollouts.d.ts +43 -0
  48. package/dist/rollouts.d.ts.map +1 -0
  49. package/dist/rollouts.js +114 -0
  50. package/dist/rollouts.js.map +1 -0
  51. package/dist/secure-aggregation.d.ts +50 -0
  52. package/dist/secure-aggregation.d.ts.map +1 -0
  53. package/dist/secure-aggregation.js +174 -0
  54. package/dist/secure-aggregation.js.map +1 -0
  55. package/dist/streaming.d.ts +25 -0
  56. package/dist/streaming.d.ts.map +1 -0
  57. package/dist/streaming.js +148 -0
  58. package/dist/streaming.js.map +1 -0
  59. package/dist/telemetry.d.ts +41 -0
  60. package/dist/telemetry.d.ts.map +1 -0
  61. package/dist/telemetry.js +130 -0
  62. package/dist/telemetry.js.map +1 -0
  63. package/dist/types.d.ts +239 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +17 -0
  66. package/dist/types.js.map +1 -0
  67. package/package.json +62 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/types.ts", "../src/cache.ts", "../src/inference.ts", "../src/model-loader.ts", "../src/telemetry.ts", "../src/streaming.ts", "../src/octomil.ts", "../src/device-auth.ts", "../src/integrity.ts", "../src/federated.ts", "../src/secure-aggregation.ts", "../src/privacy.ts", "../src/rollouts.ts", "../src/experiments.ts"],
4
+ "sourcesContent": ["/**\n * @octomil/browser \u2014 In-browser ML inference via ONNX Runtime Web + WebGPU\n *\n * @example\n * ```ts\n * import { Octomil } from '@octomil/browser';\n *\n * const ml = new Octomil({\n * model: 'https://models.octomil.io/sentiment-v1.onnx',\n * backend: 'webgpu',\n * });\n *\n * await ml.load();\n * const result = await ml.predict({ text: 'This is amazing!' });\n * console.log(result.label, result.score);\n * ml.dispose();\n * ```\n *\n * @packageDocumentation\n */\n\n// Main class\nexport { Octomil } from \"./octomil.js\";\n\n// Sub-modules (for advanced usage)\nexport { InferenceEngine } from \"./inference.js\";\nexport { ModelLoader } from \"./model-loader.js\";\nexport { createModelCache, type ModelCache } from \"./cache.js\";\nexport {\n TelemetryReporter,\n initTelemetry,\n getTelemetry,\n disposeTelemetry,\n} from \"./telemetry.js\";\n\n// Device auth\nexport { DeviceAuthManager } from \"./device-auth.js\";\n\n// Model integrity\nexport { computeHash, verifyModelIntegrity, assertModelIntegrity } from \"./integrity.js\";\n\n// Streaming inference\nexport { StreamingInferenceEngine } from \"./streaming.js\";\n\n// Federated training\nexport { FederatedClient, WeightExtractor } from \"./federated.js\";\n\n// Secure aggregation\nexport {\n SecureAggregation,\n SecAggPlus,\n shamirSplit,\n shamirReconstruct,\n} from \"./secure-aggregation.js\";\n\n// Privacy filters\nexport {\n clipGradients,\n addGaussianNoise,\n quantize,\n dequantize,\n} from \"./privacy.js\";\n\n// Rollouts\nexport { RolloutsManager } from \"./rollouts.js\";\n\n// Experiments / A/B testing\nexport { ExperimentsClient } from \"./experiments.js\";\n\n// Types\nexport type {\n OctomilOptions,\n Backend,\n CacheStrategy,\n DownloadProgress,\n TensorData,\n NamedTensors,\n PredictInput,\n PredictOutput,\n ChatRole,\n ChatMessage,\n ChatResponse,\n ChatOptions,\n ChatChunk,\n CacheInfo,\n TelemetryEvent,\n ModelMetadata,\n OctomilErrorCode,\n DeviceAuthConfig,\n DeviceAuthToken,\n DeviceInfo,\n StreamingModality,\n StreamingOptions,\n StreamingChunk,\n StreamingResult,\n WeightMap,\n TrainingConfig,\n TrainStepResult,\n FederatedRound,\n RolloutStatus,\n RolloutVersion,\n RolloutConfig,\n Experiment,\n ExperimentVariant,\n} from \"./types.js\";\n\nexport type { QuantizedWeightMap } from \"./privacy.js\";\n\nexport { OctomilError } from \"./types.js\";\n", "/**\n * @octomil/browser \u2014 TypeScript type definitions\n *\n * All public interfaces and types for the browser inference SDK.\n */\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\n/** Inference backend. `\"webgpu\"` is preferred; `\"wasm\"` is the universal fallback. */\nexport type Backend = \"webgpu\" | \"wasm\";\n\n/** Model caching strategy. */\nexport type CacheStrategy = \"cache-api\" | \"indexeddb\" | \"none\";\n\n/** Options for initialising an {@link Octomil} instance. */\nexport interface OctomilOptions {\n /**\n * Model identifier \u2014 either a full URL to an `.onnx` file or\n * a name resolvable via the Octomil model registry.\n */\n model: string;\n\n /** Octomil server URL (used to resolve registry model names). */\n serverUrl?: string;\n\n /** Octomil API key for authenticated model downloads. */\n apiKey?: string;\n\n /**\n * Inference backend.\n * - `\"webgpu\"` \u2014 uses WebGPU when available (fastest).\n * - `\"wasm\"` \u2014 WASM SIMD fallback (universal).\n * - `undefined` \u2014 auto-detect (try WebGPU first, then WASM).\n */\n backend?: Backend;\n\n /**\n * Whether to report anonymous telemetry (latency, cache hits) to the\n * Octomil dashboard. Opt-in only.\n * @default false\n */\n telemetry?: boolean;\n\n /** Telemetry endpoint override. Only used when `telemetry` is `true`. */\n telemetryUrl?: string;\n\n /**\n * Model caching strategy.\n * @default \"cache-api\"\n */\n cacheStrategy?: CacheStrategy;\n\n /** Called during model download with progress information. */\n onProgress?: (progress: DownloadProgress) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Download progress\n// ---------------------------------------------------------------------------\n\n/** Progress information emitted during model download. */\nexport interface DownloadProgress {\n /** Bytes received so far. */\n loaded: number;\n /** Total bytes (may be 0 if the server omits Content-Length). */\n total: number;\n /** Percentage 0\u2013100 (NaN when total is unknown). */\n percent: number;\n}\n\n// ---------------------------------------------------------------------------\n// Inference input / output\n// ---------------------------------------------------------------------------\n\n/**\n * Named tensor map. Keys are input tensor names, values are the data.\n * When a model has a single input you can pass the data directly.\n */\nexport type TensorData = Float32Array | Int32Array | BigInt64Array | Uint8Array;\n\nexport interface NamedTensors {\n [name: string]: {\n data: TensorData;\n dims: number[];\n };\n}\n\n/**\n * Predict input \u2014 either a named tensor map for explicit control,\n * or a convenience payload that the model adapter will pre-process.\n */\nexport type PredictInput =\n | NamedTensors\n | { text: string }\n | { image: ImageData | HTMLCanvasElement | HTMLImageElement }\n | { raw: TensorData; dims: number[] };\n\n/** Result of a single inference call. */\nexport interface PredictOutput {\n /** Raw output tensors keyed by name. */\n tensors: NamedTensors;\n /** Top-level convenience fields (model-dependent). */\n label?: string;\n score?: number;\n scores?: number[];\n /** Inference wall-clock time in milliseconds. */\n latencyMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// Chat (OpenAI-compatible)\n// ---------------------------------------------------------------------------\n\n/** Role for a chat message. */\nexport type ChatRole = \"system\" | \"user\" | \"assistant\";\n\n/** A single message in a chat conversation. */\nexport interface ChatMessage {\n role: ChatRole;\n content: string;\n}\n\n/** Response from the chat API. */\nexport interface ChatResponse {\n message: ChatMessage;\n /** Token-generation latency in milliseconds. */\n latencyMs: number;\n /** Usage stats when available. */\n usage?: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\n/** Information about the cached model. */\nexport interface CacheInfo {\n /** Whether the model is currently cached. */\n cached: boolean;\n /** Size in bytes (0 if not cached). */\n sizeBytes: number;\n /** ISO-8601 timestamp of when the model was cached. */\n cachedAt?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Telemetry\n// ---------------------------------------------------------------------------\n\n/** A single telemetry event queued for delivery. */\nexport interface TelemetryEvent {\n type:\n | \"model_load\"\n | \"inference\"\n | \"cache_hit\"\n | \"cache_miss\"\n | \"error\"\n | \"streaming_start\"\n | \"streaming_chunk\"\n | \"streaming_complete\"\n | \"streaming_error\"\n | \"training_complete\"\n | \"rollout_status\"\n | \"experiment_metric\";\n model: string;\n durationMs?: number;\n metadata?: Record<string, unknown>;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Model metadata (from registry)\n// ---------------------------------------------------------------------------\n\n/** Metadata returned by the Octomil model registry. */\nexport interface ModelMetadata {\n name: string;\n version: string;\n format: \"onnx\";\n sizeBytes: number;\n url: string;\n checksum?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Device Auth\n// ---------------------------------------------------------------------------\n\nexport interface DeviceAuthConfig {\n serverUrl: string;\n apiKey: string;\n}\n\nexport interface DeviceAuthToken {\n accessToken: string;\n refreshToken: string;\n /** Epoch ms when the access token expires. */\n expiresAt: number;\n}\n\nexport interface DeviceInfo {\n userAgent: string;\n language: string;\n screenWidth: number;\n screenHeight: number;\n timezone: string;\n webgpu: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming\n// ---------------------------------------------------------------------------\n\nexport type StreamingModality = \"text\" | \"image\" | \"audio\" | \"video\";\n\nexport interface StreamingOptions {\n modality?: StreamingModality;\n signal?: AbortSignal;\n params?: Record<string, unknown>;\n}\n\nexport interface StreamingChunk {\n index: number;\n data: unknown;\n modality: StreamingModality;\n done: boolean;\n}\n\nexport interface StreamingResult {\n totalChunks: number;\n totalBytes: number;\n durationMs: number;\n ttfcMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// Chat (extended)\n// ---------------------------------------------------------------------------\n\nexport interface ChatOptions {\n temperature?: number;\n maxTokens?: number;\n topP?: number;\n stream?: boolean;\n signal?: AbortSignal;\n}\n\nexport interface ChatChunk {\n index: number;\n content: string;\n done: boolean;\n role?: ChatRole;\n}\n\n// ---------------------------------------------------------------------------\n// Federated Training\n// ---------------------------------------------------------------------------\n\nexport type WeightMap = Record<string, Float32Array>;\n\nexport interface TrainingConfig {\n modelId?: string;\n epochs: number;\n batchSize: number;\n learningRate: number;\n /** User-provided training step \u2014 browser ONNX doesn't support training natively. */\n onTrainStep: (\n weights: WeightMap,\n params: { epoch: number; batchSize: number; learningRate: number },\n ) => Promise<TrainStepResult>;\n}\n\nexport interface TrainStepResult {\n weights: WeightMap;\n loss?: number;\n}\n\nexport interface FederatedRound {\n id: string;\n federationId: string;\n roundNumber: number;\n status: \"pending\" | \"selecting\" | \"in_progress\" | \"aggregating\" | \"complete\";\n modelVersion: string;\n config: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Rollouts\n// ---------------------------------------------------------------------------\n\nexport type RolloutStatus = \"pending\" | \"canary\" | \"active\" | \"rolled_back\";\n\nexport interface RolloutVersion {\n version: string;\n status: RolloutStatus;\n percentage: number;\n createdAt: string;\n}\n\nexport interface RolloutConfig {\n modelId: string;\n versions: RolloutVersion[];\n}\n\n// ---------------------------------------------------------------------------\n// Experiments / A/B Testing\n// ---------------------------------------------------------------------------\n\nexport interface ExperimentVariant {\n id: string;\n name: string;\n modelId: string;\n modelVersion: string;\n trafficPercentage: number;\n}\n\nexport interface Experiment {\n id: string;\n name: string;\n status: \"draft\" | \"active\" | \"paused\" | \"completed\";\n variants: ExperimentVariant[];\n createdAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/** Error codes emitted by the SDK. */\nexport type OctomilErrorCode =\n | \"MODEL_NOT_FOUND\"\n | \"MODEL_LOAD_FAILED\"\n | \"INFERENCE_FAILED\"\n | \"BACKEND_UNAVAILABLE\"\n | \"CACHE_ERROR\"\n | \"NETWORK_ERROR\"\n | \"INVALID_INPUT\"\n | \"NOT_LOADED\"\n | \"SESSION_DISPOSED\";\n\n/** Structured error thrown by the SDK. */\nexport class OctomilError extends Error {\n readonly code: OctomilErrorCode;\n readonly cause?: unknown;\n\n constructor(code: OctomilErrorCode, message: string, cause?: unknown) {\n super(message);\n this.name = \"OctomilError\";\n this.code = code;\n this.cause = cause;\n }\n}\n", "/**\n * @octomil/browser \u2014 Model cache manager\n *\n * Caches downloaded ONNX model binaries using the Cache API (preferred)\n * or IndexedDB as a fallback. Cache entries are keyed by model URL so\n * that different model versions naturally invalidate stale entries.\n */\n\nimport type { CacheInfo, CacheStrategy } from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst CACHE_NAME = \"octomil-models-v1\";\nconst IDB_DB_NAME = \"octomil-models\";\nconst IDB_STORE_NAME = \"blobs\";\nconst IDB_VERSION = 1;\n\n// ---------------------------------------------------------------------------\n// Abstract interface\n// ---------------------------------------------------------------------------\n\nexport interface ModelCache {\n get(key: string): Promise<ArrayBuffer | null>;\n put(key: string, data: ArrayBuffer): Promise<void>;\n has(key: string): Promise<boolean>;\n remove(key: string): Promise<void>;\n info(key: string): Promise<CacheInfo>;\n}\n\n// ---------------------------------------------------------------------------\n// Cache API implementation\n// ---------------------------------------------------------------------------\n\nclass CacheApiModelCache implements ModelCache {\n private async open(): Promise<Cache> {\n return caches.open(CACHE_NAME);\n }\n\n async get(key: string): Promise<ArrayBuffer | null> {\n const cache = await this.open();\n const response = await cache.match(key);\n if (!response) return null;\n return response.arrayBuffer();\n }\n\n async put(key: string, data: ArrayBuffer): Promise<void> {\n const cache = await this.open();\n const response = new Response(data, {\n headers: {\n \"x-octomil-cached-at\": new Date().toISOString(),\n \"x-octomil-size\": String(data.byteLength),\n },\n });\n await cache.put(key, response);\n }\n\n async has(key: string): Promise<boolean> {\n const cache = await this.open();\n const match = await cache.match(key);\n return match !== undefined;\n }\n\n async remove(key: string): Promise<void> {\n const cache = await this.open();\n await cache.delete(key);\n }\n\n async info(key: string): Promise<CacheInfo> {\n const cache = await this.open();\n const response = await cache.match(key);\n if (!response) {\n return { cached: false, sizeBytes: 0 };\n }\n const sizeHeader = response.headers.get(\"x-octomil-size\");\n const cachedAtHeader = response.headers.get(\"x-octomil-cached-at\");\n return {\n cached: true,\n sizeBytes: sizeHeader ? parseInt(sizeHeader, 10) : 0,\n cachedAt: cachedAtHeader ?? undefined,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// IndexedDB implementation\n// ---------------------------------------------------------------------------\n\ninterface IDBModelEntry {\n key: string;\n data: ArrayBuffer;\n sizeBytes: number;\n cachedAt: string;\n}\n\nclass IndexedDBModelCache implements ModelCache {\n private dbPromise: Promise<IDBDatabase> | null = null;\n\n private openDB(): Promise<IDBDatabase> {\n if (this.dbPromise) return this.dbPromise;\n\n this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(IDB_DB_NAME, IDB_VERSION);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(IDB_STORE_NAME)) {\n db.createObjectStore(IDB_STORE_NAME, { keyPath: \"key\" });\n }\n };\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n\n return this.dbPromise;\n }\n\n private async tx(\n mode: IDBTransactionMode,\n ): Promise<IDBObjectStore> {\n const db = await this.openDB();\n const transaction = db.transaction(IDB_STORE_NAME, mode);\n return transaction.objectStore(IDB_STORE_NAME);\n }\n\n async get(key: string): Promise<ArrayBuffer | null> {\n const store = await this.tx(\"readonly\");\n return new Promise((resolve, reject) => {\n const request = store.get(key);\n request.onsuccess = () => {\n const entry = request.result as IDBModelEntry | undefined;\n resolve(entry?.data ?? null);\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async put(key: string, data: ArrayBuffer): Promise<void> {\n const store = await this.tx(\"readwrite\");\n const entry: IDBModelEntry = {\n key,\n data,\n sizeBytes: data.byteLength,\n cachedAt: new Date().toISOString(),\n };\n return new Promise((resolve, reject) => {\n const request = store.put(entry);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async has(key: string): Promise<boolean> {\n const store = await this.tx(\"readonly\");\n return new Promise((resolve, reject) => {\n const request = store.count(key);\n request.onsuccess = () => resolve(request.result > 0);\n request.onerror = () => reject(request.error);\n });\n }\n\n async remove(key: string): Promise<void> {\n const store = await this.tx(\"readwrite\");\n return new Promise((resolve, reject) => {\n const request = store.delete(key);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async info(key: string): Promise<CacheInfo> {\n const store = await this.tx(\"readonly\");\n return new Promise((resolve, reject) => {\n const request = store.get(key);\n request.onsuccess = () => {\n const entry = request.result as IDBModelEntry | undefined;\n if (!entry) {\n resolve({ cached: false, sizeBytes: 0 });\n } else {\n resolve({\n cached: true,\n sizeBytes: entry.sizeBytes,\n cachedAt: entry.cachedAt,\n });\n }\n };\n request.onerror = () => reject(request.error);\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// No-op implementation\n// ---------------------------------------------------------------------------\n\nclass NoopModelCache implements ModelCache {\n async get(_key: string): Promise<ArrayBuffer | null> {\n return null;\n }\n async put(_key: string, _data: ArrayBuffer): Promise<void> {\n /* no-op */\n }\n async has(_key: string): Promise<boolean> {\n return false;\n }\n async remove(_key: string): Promise<void> {\n /* no-op */\n }\n async info(_key: string): Promise<CacheInfo> {\n return { cached: false, sizeBytes: 0 };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a {@link ModelCache} instance for the given strategy.\n *\n * Falls back gracefully:\n * - `\"cache-api\"` \u2014 uses Cache API if available, else IndexedDB, else no-op.\n * - `\"indexeddb\"` \u2014 uses IndexedDB if available, else no-op.\n * - `\"none\"` \u2014 always no-op.\n */\nexport function createModelCache(strategy: CacheStrategy): ModelCache {\n if (strategy === \"none\") {\n return new NoopModelCache();\n }\n\n if (strategy === \"cache-api\") {\n if (typeof caches !== \"undefined\") {\n return new CacheApiModelCache();\n }\n // Fallback to IndexedDB when Cache API is unavailable.\n if (typeof indexedDB !== \"undefined\") {\n return new IndexedDBModelCache();\n }\n return new NoopModelCache();\n }\n\n if (strategy === \"indexeddb\") {\n if (typeof indexedDB !== \"undefined\") {\n return new IndexedDBModelCache();\n }\n return new NoopModelCache();\n }\n\n throw new OctomilError(\n \"CACHE_ERROR\",\n `Unknown cache strategy: ${strategy as string}`,\n );\n}\n", "/**\n * @octomil/browser \u2014 Inference engine\n *\n * Wraps ONNX Runtime Web to create an inference session from a model\n * buffer, auto-detect the best execution provider (WebGPU > WASM),\n * and run forward passes.\n */\n\nimport type * as ort from \"onnxruntime-web\";\nimport type { Backend, NamedTensors, PredictOutput } from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Resolved ONNX Runtime module \u2014 imported dynamically. */\ntype OrtModule = typeof ort;\n\n// ---------------------------------------------------------------------------\n// InferenceEngine\n// ---------------------------------------------------------------------------\n\nexport class InferenceEngine {\n private session: ort.InferenceSession | null = null;\n private ortModule: OrtModule | null = null;\n private resolvedBackend: Backend | null = null;\n\n // -----------------------------------------------------------------------\n // Public\n // -----------------------------------------------------------------------\n\n /**\n * Create an ONNX Runtime session from the given model bytes.\n *\n * @param modelData Raw ONNX model ArrayBuffer.\n * @param backend Requested backend (`\"webgpu\"`, `\"wasm\"`, or `undefined` for auto).\n */\n async createSession(\n modelData: ArrayBuffer,\n backend?: Backend,\n ): Promise<void> {\n const ortMod = await this.loadOrt();\n\n const provider = await this.resolveProvider(ortMod, backend);\n this.resolvedBackend = provider === \"webgpu\" ? \"webgpu\" : \"wasm\";\n\n const sessionOptions: ort.InferenceSession.SessionOptions = {\n executionProviders: [provider],\n graphOptimizationLevel: \"all\",\n };\n\n try {\n this.session = await ortMod.InferenceSession.create(\n modelData,\n sessionOptions,\n );\n } catch (err) {\n // If WebGPU failed, retry with WASM.\n if (provider === \"webgpu\") {\n try {\n this.session = await ortMod.InferenceSession.create(modelData, {\n executionProviders: [\"wasm\"],\n graphOptimizationLevel: \"all\",\n });\n this.resolvedBackend = \"wasm\";\n } catch (wasmErr) {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n \"Failed to create ONNX session with both WebGPU and WASM backends.\",\n wasmErr,\n );\n }\n } else {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n `Failed to create ONNX session: ${String(err)}`,\n err,\n );\n }\n }\n }\n\n /**\n * Run inference and return the output tensors plus timing info.\n */\n async run(inputs: NamedTensors): Promise<PredictOutput> {\n this.ensureSession();\n\n const ortMod = this.ortModule!;\n const session = this.session!;\n\n // Build ORT tensor feeds.\n const feeds: Record<string, ort.Tensor> = {};\n for (const [name, tensor] of Object.entries(inputs)) {\n feeds[name] = new ortMod.Tensor(\n inferOrtType(tensor.data),\n tensor.data,\n tensor.dims,\n );\n }\n\n const start = performance.now();\n\n let results: ort.InferenceSession.ReturnType;\n try {\n results = await session.run(feeds);\n } catch (err) {\n throw new OctomilError(\n \"INFERENCE_FAILED\",\n `Inference run failed: ${String(err)}`,\n err,\n );\n }\n\n const latencyMs = performance.now() - start;\n\n // Convert ORT outputs to NamedTensors.\n const tensors = this.convertOutputs(results);\n\n // Extract convenience fields from the first output tensor.\n const convenience = this.extractConvenience(tensors);\n\n return {\n tensors,\n latencyMs,\n ...convenience,\n };\n }\n\n /** Names of the model's input tensors. */\n get inputNames(): readonly string[] {\n this.ensureSession();\n return this.session!.inputNames;\n }\n\n /** Names of the model's output tensors. */\n get outputNames(): readonly string[] {\n this.ensureSession();\n return this.session!.outputNames;\n }\n\n /** The backend that was actually used after negotiation. */\n get activeBackend(): Backend | null {\n return this.resolvedBackend;\n }\n\n /** Release WASM / WebGPU resources. */\n dispose(): void {\n if (this.session) {\n // InferenceSession.release() returns a Promise but we fire-and-forget.\n void this.session.release();\n this.session = null;\n }\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n private async loadOrt(): Promise<OrtModule> {\n if (this.ortModule) return this.ortModule;\n\n try {\n // Dynamic import so tree-shaking works and the dependency is optional\n // at the type level.\n this.ortModule = (await import(\"onnxruntime-web\")) as OrtModule;\n return this.ortModule;\n } catch (err) {\n throw new OctomilError(\n \"BACKEND_UNAVAILABLE\",\n 'Failed to import onnxruntime-web. Make sure the package is installed: npm i onnxruntime-web',\n err,\n );\n }\n }\n\n private async resolveProvider(\n _ortMod: OrtModule,\n backend?: Backend,\n ): Promise<string> {\n if (backend === \"wasm\") return \"wasm\";\n\n if (backend === \"webgpu\" || backend === undefined) {\n const hasWebGPU = await this.detectWebGPU();\n if (hasWebGPU) return \"webgpu\";\n if (backend === \"webgpu\") {\n throw new OctomilError(\n \"BACKEND_UNAVAILABLE\",\n \"WebGPU was explicitly requested but is not available in this browser.\",\n );\n }\n }\n\n return \"wasm\";\n }\n\n private async detectWebGPU(): Promise<boolean> {\n if (typeof navigator === \"undefined\") return false;\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const gpu = (navigator as any).gpu;\n if (!gpu) return false;\n const adapter = await gpu.requestAdapter();\n return adapter !== null;\n } catch {\n return false;\n }\n }\n\n private ensureSession(): void {\n if (!this.session) {\n throw new OctomilError(\n \"SESSION_DISPOSED\",\n \"No active session. Call load() before running inference.\",\n );\n }\n }\n\n private convertOutputs(\n results: ort.InferenceSession.ReturnType,\n ): NamedTensors {\n const tensors: NamedTensors = {};\n\n for (const name of Object.keys(results)) {\n const ortTensor = results[name]!;\n tensors[name] = {\n data: ortTensor.data as Float32Array,\n dims: Array.from(ortTensor.dims),\n };\n }\n\n return tensors;\n }\n\n /**\n * Best-effort extraction of `label` / `score` / `scores` from the\n * first output tensor \u2014 only if it looks like a classification head.\n */\n private extractConvenience(\n tensors: NamedTensors,\n ): { label?: string; score?: number; scores?: number[] } {\n const names = Object.keys(tensors);\n if (names.length === 0) return {};\n\n const first = tensors[names[0]!]!;\n const data = first.data;\n\n if (!(data instanceof Float32Array)) return {};\n if (data.length === 0) return {};\n\n // Treat as class probabilities / logits.\n const scores = Array.from(data);\n let maxIdx = 0;\n let maxVal = -Infinity;\n for (let i = 0; i < scores.length; i++) {\n if (scores[i]! > maxVal) {\n maxVal = scores[i]!;\n maxIdx = i;\n }\n }\n\n return {\n label: String(maxIdx),\n score: maxVal,\n scores,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction inferOrtType(\n data: Float32Array | Int32Array | BigInt64Array | Uint8Array,\n): ort.Tensor.Type {\n if (data instanceof Float32Array) return \"float32\";\n if (data instanceof Int32Array) return \"int32\";\n if (data instanceof BigInt64Array) return \"int64\";\n if (data instanceof Uint8Array) return \"uint8\";\n return \"float32\";\n}\n", "/**\n * @octomil/browser \u2014 Model loader\n *\n * Downloads an ONNX model from a URL or the Octomil model registry,\n * caches it locally, and returns the raw `ArrayBuffer` for the\n * inference engine to consume.\n */\n\nimport type { ModelCache } from \"./cache.js\";\nimport type {\n DownloadProgress,\n OctomilOptions,\n ModelMetadata,\n} from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_RETRIES = 3;\nconst RETRY_DELAY_MS = 1_000;\n\n// ---------------------------------------------------------------------------\n// ModelLoader\n// ---------------------------------------------------------------------------\n\nexport class ModelLoader {\n private readonly modelId: string;\n private readonly serverUrl: string | undefined;\n private readonly apiKey: string | undefined;\n private readonly onProgress: ((p: DownloadProgress) => void) | undefined;\n private readonly cache: ModelCache;\n\n constructor(options: OctomilOptions, cache: ModelCache) {\n this.modelId = options.model;\n this.serverUrl = options.serverUrl;\n this.apiKey = options.apiKey;\n this.onProgress = options.onProgress;\n this.cache = cache;\n }\n\n // -----------------------------------------------------------------------\n // Public\n // -----------------------------------------------------------------------\n\n /**\n * Resolve the model URL, check the cache, download if needed,\n * and return the ONNX model bytes.\n */\n async load(): Promise<ArrayBuffer> {\n const url = await this.resolveModelUrl();\n\n // Check cache first.\n const cached = await this.cache.get(url);\n if (cached) {\n return cached;\n }\n\n // Download the model.\n const data = await this.download(url);\n\n // Validate \u2014 a minimal check that the buffer is non-empty.\n this.validate(data);\n\n // Cache for next time.\n await this.cache.put(url, data);\n\n return data;\n }\n\n /** Check whether the model is already cached. */\n async isCached(): Promise<boolean> {\n const url = await this.resolveModelUrl();\n return this.cache.has(url);\n }\n\n /** Remove the cached model. */\n async clearCache(): Promise<void> {\n const url = await this.resolveModelUrl();\n await this.cache.remove(url);\n }\n\n /** Get cache info for the model. */\n async getCacheInfo() {\n const url = await this.resolveModelUrl();\n return this.cache.info(url);\n }\n\n // -----------------------------------------------------------------------\n // Internal \u2014 URL resolution\n // -----------------------------------------------------------------------\n\n /**\n * If `modelId` looks like a URL (starts with http:// or https://) use it\n * directly. Otherwise treat it as a registry model name and resolve via\n * the Octomil server.\n */\n async resolveModelUrl(): Promise<string> {\n if (\n this.modelId.startsWith(\"http://\") ||\n this.modelId.startsWith(\"https://\")\n ) {\n return this.modelId;\n }\n\n if (!this.serverUrl) {\n throw new OctomilError(\n \"MODEL_NOT_FOUND\",\n `Cannot resolve model \"${this.modelId}\": no serverUrl configured.`,\n );\n }\n\n return this.fetchRegistryUrl(this.modelId);\n }\n\n private async fetchRegistryUrl(name: string): Promise<string> {\n const registryUrl = `${this.serverUrl}/api/v1/models/${encodeURIComponent(name)}/metadata`;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n };\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n let response: Response;\n try {\n response = await fetch(registryUrl, { headers });\n } catch (err) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to reach model registry at ${registryUrl}`,\n err,\n );\n }\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new OctomilError(\n \"MODEL_NOT_FOUND\",\n `Model \"${name}\" not found in registry.`,\n );\n }\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Registry returned HTTP ${response.status}: ${response.statusText}`,\n );\n }\n\n const metadata = (await response.json()) as ModelMetadata;\n return metadata.url;\n }\n\n // -----------------------------------------------------------------------\n // Internal \u2014 download\n // -----------------------------------------------------------------------\n\n private buildHeaders(extra: Record<string, string> = {}): Record<string, string> {\n const headers: Record<string, string> = {\n \"Accept-Encoding\": \"gzip, deflate, br\",\n ...extra,\n };\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n return headers;\n }\n\n private async download(url: string): Promise<ArrayBuffer> {\n const chunks: Uint8Array[] = [];\n let loaded = 0;\n let totalSize = 0;\n let attempt = 0;\n\n while (attempt <= MAX_RETRIES) {\n const headers = this.buildHeaders(\n loaded > 0 ? { Range: `bytes=${loaded}-` } : {},\n );\n\n let response: Response;\n try {\n response = await fetch(url, { headers });\n } catch (err) {\n attempt++;\n if (attempt > MAX_RETRIES) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to download model from ${url} after ${MAX_RETRIES} retries`,\n err,\n );\n }\n await this.delay(RETRY_DELAY_MS * attempt);\n continue;\n }\n\n // 416 Range Not Satisfiable \u2014 server doesn't support range requests\n // or we already have the full file. Start fresh.\n if (response.status === 416) {\n chunks.length = 0;\n loaded = 0;\n attempt++;\n if (attempt > MAX_RETRIES) {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n `Model download failed: range request rejected after ${MAX_RETRIES} retries`,\n );\n }\n await this.delay(RETRY_DELAY_MS * attempt);\n continue;\n }\n\n if (!response.ok && response.status !== 206) {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n `Model download failed: HTTP ${response.status} ${response.statusText}`,\n );\n }\n\n // Determine total size from Content-Length or Content-Range.\n if (totalSize === 0) {\n const contentRange = response.headers.get(\"Content-Range\");\n if (contentRange) {\n // Content-Range: bytes 0-999/5000\n const match = contentRange.match(/\\/(\\d+)$/);\n if (match) totalSize = parseInt(match[1]!, 10);\n } else {\n totalSize = parseInt(response.headers.get(\"Content-Length\") ?? \"0\", 10);\n }\n }\n\n // Stream the body, collecting chunks.\n if (!response.body) {\n const buf = await response.arrayBuffer();\n return buf;\n }\n\n const reader = response.body.getReader();\n let streamFailed = false;\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n chunks.push(value);\n loaded += value.byteLength;\n\n this.onProgress?.({\n loaded,\n total: totalSize,\n percent: totalSize > 0 ? (loaded / totalSize) * 100 : NaN,\n });\n }\n } catch (err) {\n // Stream interrupted \u2014 retry with range request from where we left off.\n streamFailed = true;\n attempt++;\n if (attempt > MAX_RETRIES) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Model download interrupted after ${MAX_RETRIES} retries`,\n err,\n );\n }\n await this.delay(RETRY_DELAY_MS * attempt);\n } finally {\n reader.releaseLock();\n }\n\n if (!streamFailed) break;\n }\n\n // Concatenate chunks into a single ArrayBuffer.\n const combined = new Uint8Array(loaded);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return combined.buffer;\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // -----------------------------------------------------------------------\n // Internal \u2014 validation\n // -----------------------------------------------------------------------\n\n private validate(data: ArrayBuffer): void {\n if (data.byteLength === 0) {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n \"Downloaded model is empty (0 bytes).\",\n );\n }\n\n // ONNX protobuf files start with 0x08 (field 1, varint type).\n // This is a lightweight sanity check, not a full format validation.\n const header = new Uint8Array(data, 0, Math.min(4, data.byteLength));\n if (header[0] !== 0x08) {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n \"Downloaded file does not appear to be a valid ONNX model.\",\n );\n }\n }\n}\n", "/**\n * @octomil/browser \u2014 Telemetry reporter\n *\n * Opt-in, batched, non-blocking telemetry. Events are queued in memory\n * and flushed periodically using `navigator.sendBeacon` (preferred) or\n * `fetch` with `keepalive: true`.\n */\n\nimport type { TelemetryEvent } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_FLUSH_INTERVAL_MS = 30_000; // 30 seconds\nconst DEFAULT_MAX_BATCH_SIZE = 50;\nconst DEFAULT_TELEMETRY_URL = \"https://api.octomil.io/v1/telemetry\";\n\n// ---------------------------------------------------------------------------\n// TelemetryReporter\n// ---------------------------------------------------------------------------\n\nexport interface TelemetryReporterOptions {\n /** Endpoint to POST batched events to. */\n url?: string;\n /** Flush interval in milliseconds. */\n flushIntervalMs?: number;\n /** Maximum events per batch. */\n maxBatchSize?: number;\n /** API key included in the `Authorization` header. */\n apiKey?: string;\n}\n\nexport class TelemetryReporter {\n private readonly url: string;\n private readonly flushIntervalMs: number;\n private readonly maxBatchSize: number;\n private readonly apiKey: string | undefined;\n\n private queue: TelemetryEvent[] = [];\n private timerId: ReturnType<typeof setInterval> | null = null;\n private disposed = false;\n\n constructor(options: TelemetryReporterOptions = {}) {\n this.url = options.url ?? DEFAULT_TELEMETRY_URL;\n this.flushIntervalMs =\n options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;\n this.apiKey = options.apiKey;\n\n this.startAutoFlush();\n }\n\n // -----------------------------------------------------------------------\n // Public\n // -----------------------------------------------------------------------\n\n /** Enqueue a telemetry event. Non-blocking, never throws. */\n track(event: TelemetryEvent): void {\n if (this.disposed) return;\n this.queue.push(event);\n\n if (this.queue.length >= this.maxBatchSize) {\n void this.flush();\n }\n }\n\n /** Flush all queued events immediately. */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, this.maxBatchSize);\n await this.send(batch);\n }\n\n /** Stop the flush timer and send remaining events. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n\n if (this.timerId !== null) {\n clearInterval(this.timerId);\n this.timerId = null;\n }\n\n // Best-effort final flush via beacon.\n if (this.queue.length > 0) {\n this.sendBeacon(this.queue.splice(0));\n }\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n private startAutoFlush(): void {\n if (typeof setInterval === \"undefined\") return;\n this.timerId = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n }\n\n private async send(events: TelemetryEvent[]): Promise<void> {\n const body = JSON.stringify({ events });\n\n try {\n // Try sendBeacon first \u2014 it survives page unload.\n if (this.sendBeacon(events)) return;\n\n // Fallback to fetch with keepalive.\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n await fetch(this.url, {\n method: \"POST\",\n headers,\n body,\n keepalive: true,\n });\n } catch {\n // Telemetry is best-effort. Swallow all errors.\n }\n }\n\n private sendBeacon(events: TelemetryEvent[]): boolean {\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) {\n return false;\n }\n\n try {\n const blob = new Blob([JSON.stringify({ events })], {\n type: \"application/json\",\n });\n return navigator.sendBeacon(this.url, blob);\n } catch {\n return false;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Singleton helpers\n// ---------------------------------------------------------------------------\n\nlet _reporter: TelemetryReporter | null = null;\n\nexport function initTelemetry(\n options: TelemetryReporterOptions = {},\n): TelemetryReporter {\n if (_reporter) {\n _reporter.dispose();\n }\n _reporter = new TelemetryReporter(options);\n return _reporter;\n}\n\nexport function getTelemetry(): TelemetryReporter | null {\n return _reporter;\n}\n\nexport function disposeTelemetry(): void {\n _reporter?.dispose();\n _reporter = null;\n}\n", "/**\n * @octomil/browser \u2014 Streaming inference engine\n *\n * Wraps server-sent events / streaming HTTP responses for incremental\n * inference results (text generation, audio, video, image tiles).\n */\n\nimport type {\n StreamingOptions,\n StreamingChunk,\n StreamingResult,\n TelemetryEvent,\n} from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// StreamingInferenceEngine\n// ---------------------------------------------------------------------------\n\nexport class StreamingInferenceEngine {\n private readonly serverUrl: string;\n private readonly apiKey?: string;\n private readonly onTelemetry?: (event: TelemetryEvent) => void;\n\n constructor(options: {\n serverUrl: string;\n apiKey?: string;\n onTelemetry?: (event: TelemetryEvent) => void;\n }) {\n this.serverUrl = options.serverUrl;\n this.apiKey = options.apiKey;\n this.onTelemetry = options.onTelemetry;\n }\n\n /**\n * Stream inference results from the server.\n *\n * Returns an async iterable of chunks. Supports cancellation via AbortSignal.\n */\n async *stream(\n modelId: string,\n input: Record<string, unknown>,\n options: StreamingOptions = {},\n ): AsyncGenerator<StreamingChunk, StreamingResult, undefined> {\n const abortController = new AbortController();\n const signal = options.signal\n ? this.combineSignals(options.signal, abortController.signal)\n : abortController.signal;\n\n const url = `${this.serverUrl}/api/v1/models/${encodeURIComponent(modelId)}/stream`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n };\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n const startTime = performance.now();\n let ttfc: number | null = null;\n let chunkCount = 0;\n let totalBytes = 0;\n\n this.onTelemetry?.({\n type: \"streaming_start\",\n model: modelId,\n metadata: { modality: options.modality ?? \"text\" },\n timestamp: Date.now(),\n });\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n input,\n modality: options.modality ?? \"text\",\n ...options.params,\n }),\n signal,\n });\n } catch (err) {\n this.onTelemetry?.({\n type: \"streaming_error\",\n model: modelId,\n metadata: { error: String(err) },\n timestamp: Date.now(),\n });\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Streaming request failed: ${String(err)}`,\n err,\n );\n }\n\n if (!response.ok) {\n throw new OctomilError(\n \"INFERENCE_FAILED\",\n `Streaming inference failed: HTTP ${response.status}`,\n );\n }\n\n if (!response.body) {\n throw new OctomilError(\n \"INFERENCE_FAILED\",\n \"Server did not return a streaming body.\",\n );\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") break;\n\n let parsed: StreamingChunk;\n try {\n parsed = JSON.parse(data) as StreamingChunk;\n } catch {\n continue;\n }\n\n chunkCount++;\n totalBytes += data.length;\n\n if (ttfc === null) {\n ttfc = performance.now() - startTime;\n this.onTelemetry?.({\n type: \"streaming_chunk\",\n model: modelId,\n durationMs: ttfc,\n metadata: { chunkIndex: 0, ttfc: true },\n timestamp: Date.now(),\n });\n }\n\n yield parsed;\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n const totalMs = performance.now() - startTime;\n\n this.onTelemetry?.({\n type: \"streaming_complete\",\n model: modelId,\n durationMs: totalMs,\n metadata: { chunkCount, totalBytes, ttfcMs: ttfc },\n timestamp: Date.now(),\n });\n\n return {\n totalChunks: chunkCount,\n totalBytes,\n durationMs: totalMs,\n ttfcMs: ttfc ?? totalMs,\n };\n }\n\n private combineSignals(...signals: AbortSignal[]): AbortSignal {\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n return controller.signal;\n }\n}\n", "/**\n * @octomil/browser \u2014 Main SDK entry point\n *\n * The `Octomil` class is the primary public interface. It orchestrates\n * model loading, caching, inference, and optional telemetry.\n *\n * @example\n * ```ts\n * import { Octomil } from '@octomil/browser';\n *\n * const ml = new Octomil({\n * model: 'https://models.octomil.io/sentiment-v1.onnx',\n * backend: 'webgpu',\n * });\n *\n * await ml.load();\n * const result = await ml.predict({ raw: inputData, dims: [1, 3, 224, 224] });\n * console.log(result.label, result.score);\n * ml.dispose();\n * ```\n */\n\nimport { createModelCache, type ModelCache } from \"./cache.js\";\nimport { InferenceEngine } from \"./inference.js\";\nimport { ModelLoader } from \"./model-loader.js\";\nimport { TelemetryReporter } from \"./telemetry.js\";\nimport { StreamingInferenceEngine } from \"./streaming.js\";\nimport type {\n Backend,\n CacheInfo,\n ChatChunk,\n ChatMessage,\n ChatOptions,\n ChatResponse,\n OctomilOptions,\n NamedTensors,\n PredictInput,\n PredictOutput,\n TelemetryEvent,\n} from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Octomil\n// ---------------------------------------------------------------------------\n\nexport class Octomil {\n private readonly options: Required<\n Pick<OctomilOptions, \"model\" | \"telemetry\" | \"cacheStrategy\">\n > &\n OctomilOptions;\n\n private readonly cache: ModelCache;\n private readonly loader: ModelLoader;\n private readonly engine: InferenceEngine;\n private telemetry: TelemetryReporter | null = null;\n\n private loaded = false;\n private disposed = false;\n\n constructor(options: OctomilOptions) {\n this.options = {\n telemetry: false,\n cacheStrategy: \"cache-api\",\n ...options,\n };\n\n this.cache = createModelCache(this.options.cacheStrategy);\n this.loader = new ModelLoader(this.options, this.cache);\n this.engine = new InferenceEngine();\n\n if (this.options.telemetry) {\n this.telemetry = new TelemetryReporter({\n url: this.options.telemetryUrl,\n apiKey: this.options.apiKey,\n });\n }\n }\n\n // -----------------------------------------------------------------------\n // Lifecycle\n // -----------------------------------------------------------------------\n\n /**\n * Download (or load from cache) the ONNX model and create the\n * inference session. Must be called before `predict()` or `chat()`.\n */\n async load(): Promise<void> {\n this.ensureNotDisposed();\n\n const start = performance.now();\n const wasCached = await this.loader.isCached();\n\n const modelData = await this.loader.load();\n await this.engine.createSession(modelData, this.options.backend);\n this.loaded = true;\n\n const durationMs = performance.now() - start;\n\n this.trackEvent({\n type: \"model_load\",\n model: this.options.model,\n durationMs,\n metadata: {\n backend: this.engine.activeBackend,\n cached: wasCached,\n sizeBytes: modelData.byteLength,\n },\n timestamp: Date.now(),\n });\n\n if (wasCached) {\n this.trackEvent({\n type: \"cache_hit\",\n model: this.options.model,\n timestamp: Date.now(),\n });\n } else {\n this.trackEvent({\n type: \"cache_miss\",\n model: this.options.model,\n timestamp: Date.now(),\n });\n }\n }\n\n // -----------------------------------------------------------------------\n // Inference\n // -----------------------------------------------------------------------\n\n /**\n * Run a single inference pass.\n *\n * Accepts either raw named tensors or convenience payloads\n * (`{ text }`, `{ image }`, `{ raw, dims }`).\n */\n async predict(input: PredictInput): Promise<PredictOutput> {\n this.ensureReady();\n\n const tensors = this.prepareTensors(input);\n const result = await this.engine.run(tensors);\n\n this.trackEvent({\n type: \"inference\",\n model: this.options.model,\n durationMs: result.latencyMs,\n metadata: { backend: this.engine.activeBackend },\n timestamp: Date.now(),\n });\n\n return result;\n }\n\n /**\n * Run inference on multiple inputs sequentially.\n * ONNX Runtime Web doesn't handle concurrent sessions well,\n * so we process one at a time.\n */\n async predictBatch(inputs: PredictInput[]): Promise<PredictOutput[]> {\n this.ensureReady();\n\n const start = performance.now();\n const results: PredictOutput[] = [];\n\n for (const input of inputs) {\n const tensors = this.prepareTensors(input);\n const result = await this.engine.run(tensors);\n results.push(result);\n }\n\n const totalMs = performance.now() - start;\n\n this.trackEvent({\n type: \"inference\",\n model: this.options.model,\n durationMs: totalMs,\n metadata: {\n backend: this.engine.activeBackend,\n batchSize: inputs.length,\n },\n timestamp: Date.now(),\n });\n\n return results;\n }\n\n /**\n * OpenAI-compatible chat completion.\n * Requires a server with streaming endpoint. Uses StreamingInferenceEngine\n * under the hood to collect the full response.\n */\n async chat(\n messages: ChatMessage[],\n options: ChatOptions = {},\n ): Promise<ChatResponse> {\n this.ensureReady();\n\n if (!this.options.serverUrl) {\n throw new OctomilError(\n \"INFERENCE_FAILED\",\n \"chat() requires serverUrl to be configured.\",\n );\n }\n\n const streaming = new StreamingInferenceEngine({\n serverUrl: this.options.serverUrl,\n apiKey: this.options.apiKey,\n onTelemetry: (e) => this.trackEvent(e),\n });\n\n const start = performance.now();\n let content = \"\";\n\n const generator = streaming.stream(this.options.model, {\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n }, { modality: \"text\", signal: options.signal });\n\n for await (const chunk of generator) {\n if (typeof chunk.data === \"string\") {\n content += chunk.data;\n }\n }\n\n return {\n message: { role: \"assistant\", content },\n latencyMs: performance.now() - start,\n };\n }\n\n /**\n * Streaming chat \u2014 yields chunks as they arrive.\n */\n async *chatStream(\n messages: ChatMessage[],\n options: ChatOptions = {},\n ): AsyncGenerator<ChatChunk, void, undefined> {\n this.ensureReady();\n\n if (!this.options.serverUrl) {\n throw new OctomilError(\n \"INFERENCE_FAILED\",\n \"chatStream() requires serverUrl to be configured.\",\n );\n }\n\n const streaming = new StreamingInferenceEngine({\n serverUrl: this.options.serverUrl,\n apiKey: this.options.apiKey,\n onTelemetry: (e) => this.trackEvent(e),\n });\n\n const generator = streaming.stream(this.options.model, {\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n }, { modality: \"text\", signal: options.signal });\n\n for await (const chunk of generator) {\n yield {\n index: chunk.index,\n content: typeof chunk.data === \"string\" ? chunk.data : JSON.stringify(chunk.data),\n done: chunk.done,\n role: \"assistant\",\n };\n }\n }\n\n // -----------------------------------------------------------------------\n // Cache\n // -----------------------------------------------------------------------\n\n /** Check whether the model binary is currently cached locally. */\n async isCached(): Promise<boolean> {\n this.ensureNotDisposed();\n return this.loader.isCached();\n }\n\n /** Remove the cached model binary. */\n async clearCache(): Promise<void> {\n this.ensureNotDisposed();\n return this.loader.clearCache();\n }\n\n /** Get cache metadata for the model. */\n async cacheInfo(): Promise<CacheInfo> {\n this.ensureNotDisposed();\n return this.loader.getCacheInfo();\n }\n\n // -----------------------------------------------------------------------\n // Introspection\n // -----------------------------------------------------------------------\n\n /** The inference backend currently in use (after `load()`). */\n get activeBackend(): Backend | null {\n return this.engine.activeBackend;\n }\n\n /** Input tensor names defined by the loaded model. */\n get inputNames(): readonly string[] {\n this.ensureReady();\n return this.engine.inputNames;\n }\n\n /** Output tensor names defined by the loaded model. */\n get outputNames(): readonly string[] {\n this.ensureReady();\n return this.engine.outputNames;\n }\n\n /** Whether `load()` has been called successfully. */\n get isLoaded(): boolean {\n return this.loaded;\n }\n\n // -----------------------------------------------------------------------\n // Cleanup\n // -----------------------------------------------------------------------\n\n /** Release all resources (WASM memory, WebGPU device, telemetry). */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.loaded = false;\n\n this.engine.dispose();\n this.telemetry?.dispose();\n this.telemetry = null;\n }\n\n // -----------------------------------------------------------------------\n // Private helpers\n // -----------------------------------------------------------------------\n\n private ensureNotDisposed(): void {\n if (this.disposed) {\n throw new OctomilError(\n \"SESSION_DISPOSED\",\n \"This Octomil instance has been disposed. Create a new one.\",\n );\n }\n }\n\n private ensureReady(): void {\n this.ensureNotDisposed();\n if (!this.loaded) {\n throw new OctomilError(\n \"NOT_LOADED\",\n \"Model not loaded. Call load() before predict() or chat().\",\n );\n }\n }\n\n /**\n * Normalise the various `PredictInput` shapes into a flat\n * `NamedTensors` map suitable for the inference engine.\n */\n private prepareTensors(input: PredictInput): NamedTensors {\n // Already a NamedTensors map \u2014 pass through.\n if (this.isNamedTensors(input)) {\n return input;\n }\n\n // { raw, dims } \u2014 wrap in the first input name.\n if (\"raw\" in input && \"dims\" in input) {\n const name = this.engine.inputNames[0];\n if (!name) {\n throw new OctomilError(\n \"INVALID_INPUT\",\n \"Model has no input tensors defined.\",\n );\n }\n return { [name]: { data: input.raw, dims: input.dims } };\n }\n\n // { text } \u2014 encode as a simple int32 character-code sequence.\n // Real tokenization would require a tokenizer; this is a minimal\n // placeholder that works for models expecting raw code-point inputs.\n if (\"text\" in input) {\n const name = this.engine.inputNames[0];\n if (!name) {\n throw new OctomilError(\n \"INVALID_INPUT\",\n \"Model has no input tensors defined.\",\n );\n }\n const codes = new Int32Array(\n Array.from(input.text).map((ch) => ch.codePointAt(0) ?? 0),\n );\n return { [name]: { data: codes, dims: [1, codes.length] } };\n }\n\n // { image } \u2014 extract pixel data from ImageData / Canvas / Image.\n if (\"image\" in input) {\n return this.imageToTensors(input.image);\n }\n\n throw new OctomilError(\n \"INVALID_INPUT\",\n \"Unrecognised PredictInput format. Provide named tensors, { text }, { image }, or { raw, dims }.\",\n );\n }\n\n /** Type guard for NamedTensors. */\n private isNamedTensors(input: PredictInput): input is NamedTensors {\n if (\"text\" in input || \"image\" in input || \"raw\" in input) return false;\n // If none of the convenience keys exist, treat as NamedTensors.\n const firstValue = Object.values(input)[0];\n return (\n firstValue !== undefined &&\n typeof firstValue === \"object\" &&\n \"data\" in firstValue &&\n \"dims\" in firstValue\n );\n }\n\n /**\n * Convert an image source to a Float32Array in NCHW format\n * (batch=1, channels=3, H, W) normalised to [0, 1].\n */\n private imageToTensors(\n source: ImageData | HTMLCanvasElement | HTMLImageElement,\n ): NamedTensors {\n let imageData: ImageData;\n\n if (source instanceof ImageData) {\n imageData = source;\n } else {\n // Draw onto an offscreen canvas to get pixel data.\n const canvas =\n source instanceof HTMLCanvasElement\n ? source\n : (() => {\n const c = document.createElement(\"canvas\");\n c.width = source.naturalWidth || source.width;\n c.height = source.naturalHeight || source.height;\n const ctx = c.getContext(\"2d\")!;\n ctx.drawImage(source, 0, 0);\n return c;\n })();\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new OctomilError(\n \"INVALID_INPUT\",\n \"Could not get 2D context from canvas.\",\n );\n }\n imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n }\n\n const { width, height, data: rgba } = imageData;\n const pixels = width * height;\n const float = new Float32Array(3 * pixels);\n\n // RGBA \u2192 CHW (R plane, G plane, B plane), normalised 0\u20131.\n for (let i = 0; i < pixels; i++) {\n float[i] = rgba[i * 4]! / 255; // R\n float[pixels + i] = rgba[i * 4 + 1]! / 255; // G\n float[2 * pixels + i] = rgba[i * 4 + 2]! / 255; // B\n }\n\n const name = this.engine.inputNames[0];\n if (!name) {\n throw new OctomilError(\n \"INVALID_INPUT\",\n \"Model has no input tensors defined.\",\n );\n }\n\n return {\n [name]: {\n data: float,\n dims: [1, 3, height, width],\n },\n };\n }\n\n private trackEvent(event: TelemetryEvent): void {\n this.telemetry?.track(event);\n }\n}\n", "/**\n * @octomil/browser \u2014 Device authentication manager\n *\n * Handles device registration, token bootstrap/refresh/revoke for\n * authenticated model downloads and training round participation.\n */\n\nimport { OctomilError } from \"./types.js\";\nimport type {\n DeviceAuthConfig,\n DeviceAuthToken,\n DeviceInfo,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst REFRESH_BUFFER_MS = 30_000; // refresh 30s before expiry\n\n// ---------------------------------------------------------------------------\n// DeviceAuthManager\n// ---------------------------------------------------------------------------\n\nexport class DeviceAuthManager {\n private readonly serverUrl: string;\n private readonly apiKey: string;\n private token: DeviceAuthToken | null = null;\n private deviceId: string | null = null;\n private refreshTimer: ReturnType<typeof setTimeout> | null = null;\n private disposed = false;\n\n constructor(config: DeviceAuthConfig) {\n this.serverUrl = config.serverUrl;\n this.apiKey = config.apiKey;\n }\n\n // -----------------------------------------------------------------------\n // Public\n // -----------------------------------------------------------------------\n\n /** Register this device and obtain an initial auth token. */\n async bootstrap(orgId: string): Promise<void> {\n this.ensureNotDisposed();\n\n this.deviceId = await this.generateDeviceId();\n const deviceInfo = this.collectDeviceInfo();\n\n const response = await this.request(\"/api/v1/devices/register\", {\n method: \"POST\",\n body: JSON.stringify({\n org_id: orgId,\n device_id: this.deviceId,\n platform: \"browser\",\n info: deviceInfo,\n }),\n });\n\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Device registration failed: HTTP ${response.status}`,\n );\n }\n\n const data = (await response.json()) as {\n token: string;\n expires_at: string;\n refresh_token: string;\n };\n\n this.token = {\n accessToken: data.token,\n refreshToken: data.refresh_token,\n expiresAt: new Date(data.expires_at).getTime(),\n };\n\n this.scheduleRefresh();\n }\n\n /** Get a valid access token, refreshing if needed. */\n async getToken(): Promise<string> {\n this.ensureNotDisposed();\n\n if (!this.token) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n \"Not authenticated. Call bootstrap() first.\",\n );\n }\n\n if (this.isTokenExpiringSoon()) {\n await this.refreshToken();\n }\n\n return this.token.accessToken;\n }\n\n /** Refresh the current token. */\n async refreshToken(): Promise<void> {\n this.ensureNotDisposed();\n\n if (!this.token) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n \"No token to refresh. Call bootstrap() first.\",\n );\n }\n\n const response = await this.request(\"/api/v1/auth/refresh\", {\n method: \"POST\",\n body: JSON.stringify({\n refresh_token: this.token.refreshToken,\n device_id: this.deviceId,\n }),\n });\n\n if (!response.ok) {\n // Token expired beyond repair \u2014 clear state\n this.token = null;\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Token refresh failed: HTTP ${response.status}`,\n );\n }\n\n const data = (await response.json()) as {\n token: string;\n expires_at: string;\n refresh_token: string;\n };\n\n this.token = {\n accessToken: data.token,\n refreshToken: data.refresh_token,\n expiresAt: new Date(data.expires_at).getTime(),\n };\n\n this.scheduleRefresh();\n }\n\n /** Revoke the current token and clear local state. */\n async revokeToken(): Promise<void> {\n this.ensureNotDisposed();\n\n if (!this.token) return;\n\n try {\n await this.request(\"/api/v1/auth/revoke\", {\n method: \"POST\",\n body: JSON.stringify({\n token: this.token.accessToken,\n device_id: this.deviceId,\n }),\n });\n } finally {\n this.clearState();\n }\n }\n\n /** Whether we currently hold a valid token. */\n get isAuthenticated(): boolean {\n return this.token !== null && !this.isTokenExpired();\n }\n\n /** Current device identifier. */\n get currentDeviceId(): string | null {\n return this.deviceId;\n }\n\n /** Release timers. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.clearRefreshTimer();\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n private async request(path: string, init: RequestInit): Promise<Response> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n };\n\n return fetch(`${this.serverUrl}${path}`, {\n ...init,\n headers: { ...headers, ...(init.headers as Record<string, string>) },\n });\n }\n\n private isTokenExpired(): boolean {\n if (!this.token) return true;\n return Date.now() >= this.token.expiresAt;\n }\n\n private isTokenExpiringSoon(): boolean {\n if (!this.token) return true;\n return Date.now() >= this.token.expiresAt - REFRESH_BUFFER_MS;\n }\n\n private scheduleRefresh(): void {\n this.clearRefreshTimer();\n\n if (!this.token) return;\n const delay = Math.max(0, this.token.expiresAt - Date.now() - REFRESH_BUFFER_MS);\n\n this.refreshTimer = setTimeout(() => {\n void this.refreshToken().catch(() => {\n // Best-effort auto-refresh; next getToken() call will retry.\n });\n }, delay);\n }\n\n private clearRefreshTimer(): void {\n if (this.refreshTimer !== null) {\n clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n\n private clearState(): void {\n this.token = null;\n this.clearRefreshTimer();\n }\n\n /** Generate a stable device ID by hashing browser fingerprint data. */\n async generateDeviceId(): Promise<string> {\n const raw = [\n typeof navigator !== \"undefined\" ? navigator.userAgent : \"unknown\",\n typeof screen !== \"undefined\" ? `${screen.width}x${screen.height}` : \"0x0\",\n typeof Intl !== \"undefined\"\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : \"UTC\",\n typeof navigator !== \"undefined\" ? navigator.language : \"en\",\n ].join(\"|\");\n\n const data = new TextEncoder().encode(raw);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n private collectDeviceInfo(): DeviceInfo {\n return {\n userAgent: typeof navigator !== \"undefined\" ? navigator.userAgent : \"unknown\",\n language: typeof navigator !== \"undefined\" ? navigator.language : \"en\",\n screenWidth: typeof screen !== \"undefined\" ? screen.width : 0,\n screenHeight: typeof screen !== \"undefined\" ? screen.height : 0,\n timezone:\n typeof Intl !== \"undefined\"\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : \"UTC\",\n webgpu: typeof navigator !== \"undefined\" && \"gpu\" in navigator,\n };\n }\n\n private ensureNotDisposed(): void {\n if (this.disposed) {\n throw new OctomilError(\n \"SESSION_DISPOSED\",\n \"DeviceAuthManager has been disposed.\",\n );\n }\n }\n}\n", "/**\n * @octomil/browser \u2014 Model integrity verification\n *\n * SHA-256 checksum computation and verification using the Web Crypto API.\n */\n\nimport { OctomilError } from \"./types.js\";\n\n/**\n * Compute the SHA-256 hash of an ArrayBuffer, returned as a hex string.\n */\nexport async function computeHash(data: ArrayBuffer): Promise<string> {\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Verify that `data` matches the expected SHA-256 hex digest.\n * Returns `true` if the hash matches, `false` otherwise.\n */\nexport async function verifyModelIntegrity(\n data: ArrayBuffer,\n expectedHash: string,\n): Promise<boolean> {\n const actual = await computeHash(data);\n return actual === expectedHash.toLowerCase();\n}\n\n/**\n * Same as `verifyModelIntegrity` but throws on mismatch.\n */\nexport async function assertModelIntegrity(\n data: ArrayBuffer,\n expectedHash: string,\n): Promise<void> {\n const match = await verifyModelIntegrity(data, expectedHash);\n if (!match) {\n throw new OctomilError(\n \"MODEL_LOAD_FAILED\",\n \"Model integrity check failed: SHA-256 hash mismatch. \" +\n \"The downloaded model may be corrupted or tampered with.\",\n );\n }\n}\n", "/**\n * @octomil/browser \u2014 Federated learning client\n *\n * Participates in federated training rounds: local weight extraction,\n * delta computation, and update submission. Actual gradient computation\n * is delegated to user-provided training hooks since ONNX Runtime Web\n * has limited training support.\n */\n\nimport type {\n TrainingConfig,\n FederatedRound,\n WeightMap,\n TelemetryEvent,\n} from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// WeightExtractor\n// ---------------------------------------------------------------------------\n\n/** Extract and compare model weights stored as named Float32Arrays. */\nexport class WeightExtractor {\n /**\n * Compute element-wise delta between two weight maps.\n * `delta = after - before`\n */\n static computeDelta(before: WeightMap, after: WeightMap): WeightMap {\n const delta: WeightMap = {};\n for (const key of Object.keys(before)) {\n const b = before[key];\n const a = after[key];\n if (!b || !a || b.length !== a.length) {\n throw new OctomilError(\n \"INVALID_INPUT\",\n `Weight dimension mismatch for \"${key}\".`,\n );\n }\n const d = new Float32Array(b.length);\n for (let i = 0; i < b.length; i++) {\n d[i] = a[i]! - b[i]!;\n }\n delta[key] = d;\n }\n return delta;\n }\n\n /** Apply a delta to weights: `result = weights + delta`. */\n static applyDelta(weights: WeightMap, delta: WeightMap): WeightMap {\n const result: WeightMap = {};\n for (const key of Object.keys(weights)) {\n const w = weights[key];\n const d = delta[key];\n if (!w) continue;\n if (!d || w.length !== d.length) {\n result[key] = new Float32Array(w);\n continue;\n }\n const r = new Float32Array(w.length);\n for (let i = 0; i < w.length; i++) {\n r[i] = w[i]! + d[i]!;\n }\n result[key] = r;\n }\n return result;\n }\n\n /** Compute L2 norm of a weight map (flattened). */\n static l2Norm(weights: WeightMap): number {\n let sumSq = 0;\n for (const arr of Object.values(weights)) {\n if (!arr) continue;\n for (let i = 0; i < arr.length; i++) {\n sumSq += arr[i]! * arr[i]!;\n }\n }\n return Math.sqrt(sumSq);\n }\n}\n\n// ---------------------------------------------------------------------------\n// FederatedClient\n// ---------------------------------------------------------------------------\n\nexport class FederatedClient {\n private readonly serverUrl: string;\n private readonly apiKey?: string;\n private readonly deviceId: string;\n private readonly onTelemetry?: (event: TelemetryEvent) => void;\n\n constructor(options: {\n serverUrl: string;\n apiKey?: string;\n deviceId: string;\n onTelemetry?: (event: TelemetryEvent) => void;\n }) {\n this.serverUrl = options.serverUrl;\n this.apiKey = options.apiKey;\n this.deviceId = options.deviceId;\n this.onTelemetry = options.onTelemetry;\n }\n\n /** Fetch the current training round from the server. */\n async getTrainingRound(federationId: string): Promise<FederatedRound> {\n const response = await this.request(\n `/api/v1/federations/${encodeURIComponent(federationId)}/rounds/current`,\n );\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to fetch training round: HTTP ${response.status}`,\n );\n }\n return (await response.json()) as FederatedRound;\n }\n\n /** Join a training round. */\n async joinRound(federationId: string, roundId: string): Promise<void> {\n const response = await this.request(\n `/api/v1/federations/${encodeURIComponent(federationId)}/rounds/${encodeURIComponent(roundId)}/join`,\n {\n method: \"POST\",\n body: JSON.stringify({ device_id: this.deviceId }),\n },\n );\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to join round: HTTP ${response.status}`,\n );\n }\n }\n\n /**\n * Run local training using a user-provided step function.\n *\n * Browser ONNX Runtime Web does not support training natively, so the\n * caller provides `onTrainStep` which receives the current weights and\n * a batch of data, and returns updated weights.\n */\n async train(\n initialWeights: WeightMap,\n config: TrainingConfig,\n ): Promise<{ finalWeights: WeightMap; delta: WeightMap }> {\n const start = performance.now();\n let weights = this.cloneWeights(initialWeights);\n\n for (let epoch = 0; epoch < config.epochs; epoch++) {\n const stepResult = await config.onTrainStep(weights, {\n epoch,\n batchSize: config.batchSize,\n learningRate: config.learningRate,\n });\n weights = stepResult.weights;\n }\n\n const delta = WeightExtractor.computeDelta(initialWeights, weights);\n const durationMs = performance.now() - start;\n\n this.onTelemetry?.({\n type: \"training_complete\",\n model: config.modelId ?? \"unknown\",\n durationMs,\n metadata: {\n epochs: config.epochs,\n deltaNorm: WeightExtractor.l2Norm(delta),\n },\n timestamp: Date.now(),\n });\n\n return { finalWeights: weights, delta };\n }\n\n /** Submit a weight update to the aggregation server. */\n async submitUpdate(\n federationId: string,\n roundId: string,\n delta: WeightMap,\n metrics?: Record<string, number>,\n ): Promise<void> {\n // Serialize WeightMap to a transferable format\n const serialized: Record<string, { data: number[]; shape: number[] }> = {};\n for (const [key, arr] of Object.entries(delta)) {\n if (!arr) continue;\n serialized[key] = {\n data: Array.from(arr),\n shape: [arr.length],\n };\n }\n\n const response = await this.request(\n `/api/v1/federations/${encodeURIComponent(federationId)}/rounds/${encodeURIComponent(roundId)}/submit`,\n {\n method: \"POST\",\n body: JSON.stringify({\n device_id: this.deviceId,\n delta: serialized,\n metrics,\n }),\n },\n );\n\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to submit update: HTTP ${response.status}`,\n );\n }\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n private async request(path: string, init?: RequestInit): Promise<Response> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n return fetch(`${this.serverUrl}${path}`, {\n ...init,\n headers: { ...headers, ...(init?.headers as Record<string, string>) },\n });\n }\n\n private cloneWeights(weights: WeightMap): WeightMap {\n const cloned: WeightMap = {};\n for (const [key, arr] of Object.entries(weights)) {\n if (arr) cloned[key] = new Float32Array(arr);\n }\n return cloned;\n }\n}\n", "/**\n * @octomil/browser \u2014 Secure aggregation (SecAgg / SecAgg+)\n *\n * Implements pairwise masking using ECDH key exchange and Shamir's\n * secret sharing, all via the Web Crypto API. Ensures the server\n * only sees the aggregate of client updates, never individual deltas.\n */\n\nimport type { WeightMap } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// SecureAggregation (basic pairwise masking)\n// ---------------------------------------------------------------------------\n\nexport class SecureAggregation {\n private keyPair: CryptoKeyPair | null = null;\n\n /** Generate an ECDH key pair for this round. */\n async generateKeyPair(): Promise<{ publicKey: JsonWebKey }> {\n this.keyPair = await crypto.subtle.generateKey(\n { name: \"ECDH\", namedCurve: \"P-256\" },\n true,\n [\"deriveBits\"],\n );\n const publicKey = await crypto.subtle.exportKey(\n \"jwk\",\n this.keyPair.publicKey,\n );\n return { publicKey };\n }\n\n /** Derive a shared secret with a peer using ECDH. */\n async deriveSharedSecret(peerPublicKeyJwk: JsonWebKey): Promise<ArrayBuffer> {\n if (!this.keyPair) {\n throw new Error(\"Call generateKeyPair() first.\");\n }\n const peerKey = await crypto.subtle.importKey(\n \"jwk\",\n peerPublicKeyJwk,\n { name: \"ECDH\", namedCurve: \"P-256\" },\n false,\n [],\n );\n return crypto.subtle.deriveBits(\n { name: \"ECDH\", public: peerKey },\n this.keyPair.privateKey,\n 256,\n );\n }\n\n /**\n * Generate a deterministic PRG mask from a shared secret.\n * Uses the secret as a seed to produce `length` float values.\n */\n async createMask(secret: ArrayBuffer, length: number): Promise<Float32Array> {\n // Expand seed via HKDF-SHA256, then interpret as float offsets\n const keyMaterial = await crypto.subtle.importKey(\n \"raw\",\n secret,\n \"HKDF\",\n false,\n [\"deriveBits\"],\n );\n const bitsNeeded = length * 4 * 8; // Float32 = 4 bytes\n const bits = await crypto.subtle.deriveBits(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new Uint8Array(32),\n info: new TextEncoder().encode(\"octomil-secagg-mask\"),\n },\n keyMaterial,\n Math.min(bitsNeeded, 8160), // deriveBits max\n );\n\n // If we need more bits than one deriveBits call can give,\n // tile the output.\n const mask = new Float32Array(length);\n const source = new Float32Array(bits);\n for (let i = 0; i < length; i++) {\n mask[i] = source[i % source.length]!;\n }\n return mask;\n }\n\n /** Add masks to a weight update: masked = delta + sum(masks). */\n maskUpdate(delta: WeightMap, masks: Map<string, Float32Array>): WeightMap {\n const masked: WeightMap = {};\n for (const [key, arr] of Object.entries(delta)) {\n if (!arr) continue;\n const result = new Float32Array(arr);\n const mask = masks.get(key);\n if (mask) {\n for (let i = 0; i < result.length; i++) {\n result[i] = result[i]! + (mask[i] ?? 0);\n }\n }\n masked[key] = result;\n }\n return masked;\n }\n\n /** Remove masks of dropped peers from the aggregated sum. */\n unmask(\n maskedSum: WeightMap,\n droppedMasks: Map<string, Float32Array>,\n ): WeightMap {\n const unmasked: WeightMap = {};\n for (const [key, arr] of Object.entries(maskedSum)) {\n if (!arr) continue;\n const result = new Float32Array(arr);\n const mask = droppedMasks.get(key);\n if (mask) {\n for (let i = 0; i < result.length; i++) {\n result[i] = result[i]! - (mask[i] ?? 0);\n }\n }\n unmasked[key] = result;\n }\n return unmasked;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shamir's Secret Sharing (for SecAgg+)\n// ---------------------------------------------------------------------------\n\n// Operates in GF(2^31 - 1) \u2014 a Mersenne prime field\nconst PRIME = 2147483647;\n\nfunction modPow(base: number, exp: number, mod: number): number {\n let result = 1;\n base = base % mod;\n while (exp > 0) {\n if (exp % 2 === 1) {\n result = Number((BigInt(result) * BigInt(base)) % BigInt(mod));\n }\n exp = Math.floor(exp / 2);\n base = Number((BigInt(base) * BigInt(base)) % BigInt(mod));\n }\n return result;\n}\n\nfunction modInverse(a: number, mod: number): number {\n return modPow(a, mod - 2, mod);\n}\n\nexport interface SecretShare {\n x: number;\n y: number;\n}\n\n/**\n * Split a secret into `numShares` shares requiring `threshold` to reconstruct.\n */\nexport function shamirSplit(\n secret: number,\n threshold: number,\n numShares: number,\n): SecretShare[] {\n // Generate random coefficients for polynomial\n const coeffs = [secret % PRIME];\n for (let i = 1; i < threshold; i++) {\n const randomBytes = new Uint32Array(1);\n crypto.getRandomValues(randomBytes);\n coeffs.push(randomBytes[0]! % PRIME);\n }\n\n const shares: SecretShare[] = [];\n for (let x = 1; x <= numShares; x++) {\n let y = 0;\n for (let i = 0; i < coeffs.length; i++) {\n y = Number(\n (BigInt(y) + BigInt(coeffs[i]!) * BigInt(modPow(x, i, PRIME))) %\n BigInt(PRIME),\n );\n }\n shares.push({ x, y });\n }\n return shares;\n}\n\n/**\n * Reconstruct a secret from `threshold` shares via Lagrange interpolation.\n */\nexport function shamirReconstruct(shares: SecretShare[]): number {\n let secret = 0;\n const n = shares.length;\n\n for (let i = 0; i < n; i++) {\n let num = 1;\n let den = 1;\n for (let j = 0; j < n; j++) {\n if (i === j) continue;\n num = Number(\n (BigInt(num) * BigInt(PRIME - shares[j]!.x)) % BigInt(PRIME),\n );\n den = Number(\n (BigInt(den) *\n BigInt((shares[i]!.x - shares[j]!.x + PRIME) % PRIME)) %\n BigInt(PRIME),\n );\n }\n const lagrange = Number(\n (BigInt(num) * BigInt(modInverse(den, PRIME))) % BigInt(PRIME),\n );\n secret = Number(\n (BigInt(secret) + BigInt(shares[i]!.y) * BigInt(lagrange)) %\n BigInt(PRIME),\n );\n }\n\n return secret;\n}\n\n// ---------------------------------------------------------------------------\n// SecAggPlus\n// ---------------------------------------------------------------------------\n\nexport class SecAggPlus extends SecureAggregation {\n private readonly threshold: number;\n\n constructor(threshold: number) {\n super();\n this.threshold = threshold;\n }\n\n /**\n * Split a shared secret into Shamir shares so that any `threshold`\n * surviving peers can reconstruct the mask of a dropped peer.\n */\n splitSecret(secret: number, numPeers: number): SecretShare[] {\n return shamirSplit(secret, this.threshold, numPeers);\n }\n\n /** Reconstruct a dropped peer's secret from collected shares. */\n reconstructSecret(shares: SecretShare[]): number {\n if (shares.length < this.threshold) {\n throw new Error(\n `Need at least ${this.threshold} shares, got ${shares.length}.`,\n );\n }\n return shamirReconstruct(shares.slice(0, this.threshold));\n }\n}\n", "/**\n * @octomil/browser \u2014 Privacy filters\n *\n * Differential privacy (gradient clipping + noise injection) and\n * quantization for communication-efficient federated learning.\n */\n\nimport type { WeightMap } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Gradient Clipping\n// ---------------------------------------------------------------------------\n\n/**\n * Clip gradients by L2 norm. If the L2 norm of the flattened weight map\n * exceeds `maxNorm`, scale all values down proportionally.\n */\nexport function clipGradients(delta: WeightMap, maxNorm: number): WeightMap {\n let sumSq = 0;\n for (const arr of Object.values(delta)) {\n if (!arr) continue;\n for (let i = 0; i < arr.length; i++) {\n sumSq += arr[i]! * arr[i]!;\n }\n }\n const norm = Math.sqrt(sumSq);\n\n if (norm <= maxNorm) {\n return delta; // No clipping needed\n }\n\n const scale = maxNorm / norm;\n const clipped: WeightMap = {};\n for (const [key, arr] of Object.entries(delta)) {\n if (!arr) continue;\n const c = new Float32Array(arr.length);\n for (let i = 0; i < arr.length; i++) {\n c[i] = arr[i]! * scale;\n }\n clipped[key] = c;\n }\n return clipped;\n}\n\n// ---------------------------------------------------------------------------\n// Gaussian Noise Injection\n// ---------------------------------------------------------------------------\n\n/**\n * Add calibrated Gaussian noise for (epsilon, delta)-differential privacy.\n *\n * Noise std = sensitivity * sqrt(2 * ln(1.25/deltaDP)) / epsilon\n *\n * @param delta Weight deltas to perturb.\n * @param epsilon Privacy budget.\n * @param sensitivity L2 sensitivity (typically the clipping norm).\n * @param deltaDP DP delta parameter.\n */\nexport function addGaussianNoise(\n delta: WeightMap,\n epsilon: number,\n sensitivity: number,\n deltaDP: number,\n): WeightMap {\n const sigma = (sensitivity * Math.sqrt(2 * Math.log(1.25 / deltaDP))) / epsilon;\n const noisy: WeightMap = {};\n\n for (const [key, arr] of Object.entries(delta)) {\n if (!arr) continue;\n const n = new Float32Array(arr.length);\n for (let i = 0; i < arr.length; i++) {\n n[i] = arr[i]! + gaussianRandom() * sigma;\n }\n noisy[key] = n;\n }\n return noisy;\n}\n\n/** Box-Muller transform for generating Gaussian random numbers. */\nfunction gaussianRandom(): number {\n let u1: number;\n let u2: number;\n do {\n u1 = Math.random();\n u2 = Math.random();\n } while (u1 === 0);\n return Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);\n}\n\n// ---------------------------------------------------------------------------\n// Quantization\n// ---------------------------------------------------------------------------\n\nexport interface QuantizedWeightMap {\n [key: string]: {\n data: Int8Array | Int16Array;\n scale: number;\n zeroPoint: number;\n };\n}\n\n/**\n * Quantize weights to reduced precision (8 or 16 bit).\n * Uses min-max symmetric quantization.\n */\nexport function quantize(\n delta: WeightMap,\n bits: 8 | 16 = 8,\n): QuantizedWeightMap {\n const maxVal = bits === 8 ? 127 : 32767;\n const result: QuantizedWeightMap = {};\n\n for (const [key, arr] of Object.entries(delta)) {\n if (!arr) continue;\n\n let absMax = 0;\n for (let i = 0; i < arr.length; i++) {\n const abs = Math.abs(arr[i]!);\n if (abs > absMax) absMax = abs;\n }\n\n const scale = absMax > 0 ? absMax / maxVal : 1;\n const quantized = bits === 8 ? new Int8Array(arr.length) : new Int16Array(arr.length);\n\n for (let i = 0; i < arr.length; i++) {\n quantized[i] = Math.round(arr[i]! / scale);\n }\n\n result[key] = { data: quantized, scale, zeroPoint: 0 };\n }\n\n return result;\n}\n\n/**\n * Dequantize back to Float32Array.\n */\nexport function dequantize(quantized: QuantizedWeightMap): WeightMap {\n const result: WeightMap = {};\n for (const [key, entry] of Object.entries(quantized)) {\n if (!entry) continue;\n const arr = new Float32Array(entry.data.length);\n for (let i = 0; i < entry.data.length; i++) {\n arr[i] = (entry.data[i]! - entry.zeroPoint) * entry.scale;\n }\n result[key] = arr;\n }\n return result;\n}\n", "/**\n * @octomil/browser \u2014 Rollout and canary management\n *\n * Resolves which model version a device should use based on server-side\n * rollout configuration. Uses deterministic hashing for stable canary\n * group assignment.\n */\n\nimport type { RolloutConfig, RolloutVersion, TelemetryEvent } from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// RolloutsManager\n// ---------------------------------------------------------------------------\n\nexport class RolloutsManager {\n private readonly serverUrl: string;\n private readonly apiKey?: string;\n private readonly cacheTtlMs: number;\n private readonly onTelemetry?: (event: TelemetryEvent) => void;\n\n private configCache = new Map<\n string,\n { config: RolloutConfig; fetchedAt: number }\n >();\n\n constructor(options: {\n serverUrl: string;\n apiKey?: string;\n cacheTtlMs?: number;\n onTelemetry?: (event: TelemetryEvent) => void;\n }) {\n this.serverUrl = options.serverUrl;\n this.apiKey = options.apiKey;\n this.cacheTtlMs = options.cacheTtlMs ?? 5 * 60 * 1000; // 5 min\n this.onTelemetry = options.onTelemetry;\n }\n\n /**\n * Resolve which version a device should use.\n *\n * Logic:\n * 1. If a canary version exists, check if device is in canary group.\n * 2. Otherwise return the active version.\n */\n async resolveVersion(modelId: string, deviceId: string): Promise<string> {\n const config = await this.getRolloutConfig(modelId);\n\n // Check for canary version\n const canary = config.versions.find((v) => v.status === \"canary\");\n if (canary && this.isInCanaryGroup(modelId, deviceId, canary.percentage)) {\n return canary.version;\n }\n\n // Fall back to active version\n const active = config.versions.find((v) => v.status === \"active\");\n if (active) return active.version;\n\n throw new OctomilError(\n \"MODEL_NOT_FOUND\",\n `No active version found for model \"${modelId}\".`,\n );\n }\n\n /** Fetch rollout configuration, with caching. */\n async getRolloutConfig(modelId: string): Promise<RolloutConfig> {\n const cached = this.configCache.get(modelId);\n if (cached && Date.now() - cached.fetchedAt < this.cacheTtlMs) {\n return cached.config;\n }\n\n const url = `${this.serverUrl}/api/v1/models/${encodeURIComponent(modelId)}/rollout`;\n const headers: Record<string, string> = { Accept: \"application/json\" };\n if (this.apiKey) headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n\n const response = await fetch(url, { headers });\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to fetch rollout config: HTTP ${response.status}`,\n );\n }\n\n const config = (await response.json()) as RolloutConfig;\n this.configCache.set(modelId, { config, fetchedAt: Date.now() });\n return config;\n }\n\n /**\n * Deterministic check: is this device in the canary group?\n * Uses a simple hash of (deviceId + modelId) to assign a 0-99 bucket.\n */\n isInCanaryGroup(\n modelId: string,\n deviceId: string,\n canaryPercentage: number,\n ): boolean {\n const bucket = deterministicBucket(deviceId + modelId);\n return bucket < canaryPercentage;\n }\n\n /** Get all available versions for a model. */\n async getAvailableVersions(modelId: string): Promise<RolloutVersion[]> {\n const config = await this.getRolloutConfig(modelId);\n return config.versions;\n }\n\n /** Report rollout success/failure to the server. */\n async reportRolloutStatus(\n modelId: string,\n version: string,\n status: \"success\" | \"failure\",\n ): Promise<void> {\n const url = `${this.serverUrl}/api/v1/models/${encodeURIComponent(modelId)}/rollout/status`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (this.apiKey) headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ version, status }),\n });\n\n this.onTelemetry?.({\n type: \"rollout_status\",\n model: modelId,\n metadata: { version, status },\n timestamp: Date.now(),\n });\n }\n\n /** Clear the rollout config cache. */\n clearCache(): void {\n this.configCache.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Simple deterministic hash \u2192 bucket [0, 100).\n * Uses djb2 hash for speed (no crypto needed for bucketing).\n */\nfunction deterministicBucket(input: string): number {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % 100;\n}\n", "/**\n * @octomil/browser \u2014 A/B testing and experiments client\n *\n * Deterministic variant assignment, experiment config caching,\n * and metric reporting for model experiments.\n */\n\nimport type {\n Experiment,\n ExperimentVariant,\n TelemetryEvent,\n} from \"./types.js\";\nimport { OctomilError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// ExperimentsClient\n// ---------------------------------------------------------------------------\n\nexport class ExperimentsClient {\n private readonly serverUrl: string;\n private readonly apiKey?: string;\n private readonly cacheTtlMs: number;\n private readonly onTelemetry?: (event: TelemetryEvent) => void;\n\n private experimentsCache: {\n experiments: Experiment[];\n fetchedAt: number;\n } | null = null;\n\n constructor(options: {\n serverUrl: string;\n apiKey?: string;\n cacheTtlMs?: number;\n onTelemetry?: (event: TelemetryEvent) => void;\n }) {\n this.serverUrl = options.serverUrl;\n this.apiKey = options.apiKey;\n this.cacheTtlMs = options.cacheTtlMs ?? 5 * 60 * 1000; // 5 min\n this.onTelemetry = options.onTelemetry;\n }\n\n /** Fetch all active experiments (cached). */\n async getActiveExperiments(): Promise<Experiment[]> {\n if (\n this.experimentsCache &&\n Date.now() - this.experimentsCache.fetchedAt < this.cacheTtlMs\n ) {\n return this.experimentsCache.experiments;\n }\n\n const url = `${this.serverUrl}/api/v1/experiments?status=active`;\n const headers: Record<string, string> = { Accept: \"application/json\" };\n if (this.apiKey) headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n\n const response = await fetch(url, { headers });\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to fetch experiments: HTTP ${response.status}`,\n );\n }\n\n const data = (await response.json()) as { experiments: Experiment[] };\n this.experimentsCache = {\n experiments: data.experiments,\n fetchedAt: Date.now(),\n };\n return data.experiments;\n }\n\n /** Get full experiment config by ID. */\n async getExperimentConfig(experimentId: string): Promise<Experiment> {\n const url = `${this.serverUrl}/api/v1/experiments/${encodeURIComponent(experimentId)}`;\n const headers: Record<string, string> = { Accept: \"application/json\" };\n if (this.apiKey) headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n\n const response = await fetch(url, { headers });\n if (!response.ok) {\n throw new OctomilError(\n \"NETWORK_ERROR\",\n `Failed to fetch experiment: HTTP ${response.status}`,\n );\n }\n\n return (await response.json()) as Experiment;\n }\n\n /**\n * Deterministic variant assignment.\n * Hash(deviceId + experimentId) \u2192 bucket \u2192 variant by cumulative traffic %.\n */\n getVariant(experiment: Experiment, deviceId: string): ExperimentVariant | null {\n if (experiment.variants.length === 0) return null;\n\n const bucket = deterministicBucket(deviceId + experiment.id);\n\n let cumulative = 0;\n for (const variant of experiment.variants) {\n cumulative += variant.trafficPercentage;\n if (bucket < cumulative) {\n return variant;\n }\n }\n\n // Fallback to last variant if percentages don't sum to 100\n return experiment.variants[experiment.variants.length - 1] ?? null;\n }\n\n /** Check if a device is enrolled in a specific experiment. */\n isEnrolled(experiment: Experiment, deviceId: string): boolean {\n return this.getVariant(experiment, deviceId) !== null;\n }\n\n /**\n * Find which experiment (if any) affects a given model, and return\n * the variant this device should use.\n */\n async resolveModelExperiment(\n modelId: string,\n deviceId: string,\n ): Promise<{ experiment: Experiment; variant: ExperimentVariant } | null> {\n const experiments = await this.getActiveExperiments();\n for (const exp of experiments) {\n const affectsModel = exp.variants.some((v) => v.modelId === modelId);\n if (!affectsModel) continue;\n\n const variant = this.getVariant(exp, deviceId);\n if (variant) {\n return { experiment: exp, variant };\n }\n }\n return null;\n }\n\n /** Report a metric for an experiment. */\n async trackMetric(\n experimentId: string,\n metricName: string,\n value: number,\n deviceId?: string,\n ): Promise<void> {\n const url = `${this.serverUrl}/api/v1/experiments/${encodeURIComponent(experimentId)}/metrics`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (this.apiKey) headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n metric_name: metricName,\n value,\n device_id: deviceId,\n timestamp: Date.now(),\n }),\n });\n\n this.onTelemetry?.({\n type: \"experiment_metric\",\n model: experimentId,\n metadata: { metricName, value },\n timestamp: Date.now(),\n });\n }\n\n /** Clear the experiment cache. */\n clearCache(): void {\n this.experimentsCache = null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction deterministicBucket(input: string): number {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % 100;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ctx", "deterministicBucket"]
7
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @octomil/browser — In-browser ML inference via ONNX Runtime Web + WebGPU
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { Octomil } from '@octomil/browser';
7
+ *
8
+ * const ml = new Octomil({
9
+ * model: 'https://models.octomil.io/sentiment-v1.onnx',
10
+ * backend: 'webgpu',
11
+ * });
12
+ *
13
+ * await ml.load();
14
+ * const result = await ml.predict({ text: 'This is amazing!' });
15
+ * console.log(result.label, result.score);
16
+ * ml.dispose();
17
+ * ```
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+ export { Octomil } from "./octomil.js";
22
+ export { InferenceEngine } from "./inference.js";
23
+ export { ModelLoader } from "./model-loader.js";
24
+ export { createModelCache, type ModelCache } from "./cache.js";
25
+ export { TelemetryReporter, initTelemetry, getTelemetry, disposeTelemetry, } from "./telemetry.js";
26
+ export { DeviceAuthManager } from "./device-auth.js";
27
+ export { computeHash, verifyModelIntegrity, assertModelIntegrity } from "./integrity.js";
28
+ export { StreamingInferenceEngine } from "./streaming.js";
29
+ export { FederatedClient, WeightExtractor } from "./federated.js";
30
+ export { SecureAggregation, SecAggPlus, shamirSplit, shamirReconstruct, } from "./secure-aggregation.js";
31
+ export { clipGradients, addGaussianNoise, quantize, dequantize, } from "./privacy.js";
32
+ export { RolloutsManager } from "./rollouts.js";
33
+ export { ExperimentsClient } from "./experiments.js";
34
+ export type { OctomilOptions, Backend, CacheStrategy, DownloadProgress, TensorData, NamedTensors, PredictInput, PredictOutput, ChatRole, ChatMessage, ChatResponse, ChatOptions, ChatChunk, CacheInfo, TelemetryEvent, ModelMetadata, OctomilErrorCode, DeviceAuthConfig, DeviceAuthToken, DeviceInfo, StreamingModality, StreamingOptions, StreamingChunk, StreamingResult, WeightMap, TrainingConfig, TrainStepResult, FederatedRound, RolloutStatus, RolloutVersion, RolloutConfig, Experiment, ExperimentVariant, } from "./types.js";
35
+ export type { QuantizedWeightMap } from "./privacy.js";
36
+ export { OctomilError } from "./types.js";
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGzF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGlE,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,UAAU,GACX,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,YAAY,EACV,cAAc,EACd,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,SAAS,EACT,SAAS,EACT,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,SAAS,EACT,cAAc,EACd,eAAe,EACf,cAAc,EACd,aAAa,EACb,cAAc,EACd,aAAa,EACb,UAAU,EACV,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @octomil/browser — In-browser ML inference via ONNX Runtime Web + WebGPU
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { Octomil } from '@octomil/browser';
7
+ *
8
+ * const ml = new Octomil({
9
+ * model: 'https://models.octomil.io/sentiment-v1.onnx',
10
+ * backend: 'webgpu',
11
+ * });
12
+ *
13
+ * await ml.load();
14
+ * const result = await ml.predict({ text: 'This is amazing!' });
15
+ * console.log(result.label, result.score);
16
+ * ml.dispose();
17
+ * ```
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+ // Main class
22
+ export { Octomil } from "./octomil.js";
23
+ // Sub-modules (for advanced usage)
24
+ export { InferenceEngine } from "./inference.js";
25
+ export { ModelLoader } from "./model-loader.js";
26
+ export { createModelCache } from "./cache.js";
27
+ export { TelemetryReporter, initTelemetry, getTelemetry, disposeTelemetry, } from "./telemetry.js";
28
+ // Device auth
29
+ export { DeviceAuthManager } from "./device-auth.js";
30
+ // Model integrity
31
+ export { computeHash, verifyModelIntegrity, assertModelIntegrity } from "./integrity.js";
32
+ // Streaming inference
33
+ export { StreamingInferenceEngine } from "./streaming.js";
34
+ // Federated training
35
+ export { FederatedClient, WeightExtractor } from "./federated.js";
36
+ // Secure aggregation
37
+ export { SecureAggregation, SecAggPlus, shamirSplit, shamirReconstruct, } from "./secure-aggregation.js";
38
+ // Privacy filters
39
+ export { clipGradients, addGaussianNoise, quantize, dequantize, } from "./privacy.js";
40
+ // Rollouts
41
+ export { RolloutsManager } from "./rollouts.js";
42
+ // Experiments / A/B testing
43
+ export { ExperimentsClient } from "./experiments.js";
44
+ export { OctomilError } from "./types.js";
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,aAAa;AACb,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,mCAAmC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAmB,MAAM,YAAY,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,cAAc;AACd,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,kBAAkB;AAClB,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEzF,sBAAsB;AACtB,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,qBAAqB;AACrB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAElE,qBAAqB;AACrB,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,kBAAkB;AAClB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,UAAU,GACX,MAAM,cAAc,CAAC;AAEtB,WAAW;AACX,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,4BAA4B;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAyCrD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @octomil/browser — Inference engine
3
+ *
4
+ * Wraps ONNX Runtime Web to create an inference session from a model
5
+ * buffer, auto-detect the best execution provider (WebGPU > WASM),
6
+ * and run forward passes.
7
+ */
8
+ import type { Backend, NamedTensors, PredictOutput } from "./types.js";
9
+ export declare class InferenceEngine {
10
+ private session;
11
+ private ortModule;
12
+ private resolvedBackend;
13
+ /**
14
+ * Create an ONNX Runtime session from the given model bytes.
15
+ *
16
+ * @param modelData Raw ONNX model ArrayBuffer.
17
+ * @param backend Requested backend (`"webgpu"`, `"wasm"`, or `undefined` for auto).
18
+ */
19
+ createSession(modelData: ArrayBuffer, backend?: Backend): Promise<void>;
20
+ /**
21
+ * Run inference and return the output tensors plus timing info.
22
+ */
23
+ run(inputs: NamedTensors): Promise<PredictOutput>;
24
+ /** Names of the model's input tensors. */
25
+ get inputNames(): readonly string[];
26
+ /** Names of the model's output tensors. */
27
+ get outputNames(): readonly string[];
28
+ /** The backend that was actually used after negotiation. */
29
+ get activeBackend(): Backend | null;
30
+ /** Release WASM / WebGPU resources. */
31
+ dispose(): void;
32
+ private loadOrt;
33
+ private resolveProvider;
34
+ private detectWebGPU;
35
+ private ensureSession;
36
+ private convertOutputs;
37
+ /**
38
+ * Best-effort extraction of `label` / `score` / `scores` from the
39
+ * first output tensor — only if it looks like a classification head.
40
+ */
41
+ private extractConvenience;
42
+ }
43
+ //# sourceMappingURL=inference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../src/inference.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAcvE,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,eAAe,CAAwB;IAM/C;;;;;OAKG;IACG,aAAa,CACjB,SAAS,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC;IA0ChB;;OAEG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IA4CvD,0CAA0C;IAC1C,IAAI,UAAU,IAAI,SAAS,MAAM,EAAE,CAGlC;IAED,2CAA2C;IAC3C,IAAI,WAAW,IAAI,SAAS,MAAM,EAAE,CAGnC;IAED,4DAA4D;IAC5D,IAAI,aAAa,IAAI,OAAO,GAAG,IAAI,CAElC;IAED,uCAAuC;IACvC,OAAO,IAAI,IAAI;YAYD,OAAO;YAiBP,eAAe;YAoBf,YAAY;IAc1B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,cAAc;IAgBtB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CA6B3B"}