@trillboards/edge-sdk 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/deploy/docker/Dockerfile.cloud-sense +117 -0
- package/deploy/docker/Dockerfile.cpu +30 -21
- package/deploy/docker/Dockerfile.cuda +30 -21
- package/deploy/docker/Dockerfile.openvino +30 -20
- package/deploy/docker/README.md +9 -4
- package/deploy/docker/connector-entrypoint.sh +122 -0
- package/deploy/helm/README.md +1 -1
- package/deploy/k8s/daemonset.yaml +1 -1
- package/deploy/macos/README.md +161 -0
- package/deploy/macos/TrillboardsSetupApp.m +603 -0
- package/deploy/macos/build-macos-pkg.sh +588 -0
- package/deploy/macos/com.trillboards.edge.plist +62 -0
- package/deploy/macos/com.trillboards.litertlm.plist +45 -0
- package/deploy/macos/pair.command +519 -0
- package/deploy/macos/scripts/ensure-litertlm.sh +62 -0
- package/deploy/macos/scripts/postinstall +153 -0
- package/deploy/macos/scripts/setup-litertlm.sh +272 -0
- package/deploy/macos/test-install-pair.sh +383 -0
- package/deploy/macos/test-setup-litertlm-reinstall.sh +109 -0
- package/deploy/systemd/install.sh +25 -15
- package/deploy/systemd/trillboards-edge.service +4 -1
- package/deploy/windows/README.md +70 -0
- package/deploy/windows/build-windows.ps1 +225 -0
- package/deploy/windows/bundle-windows-installer.ps1 +153 -0
- package/deploy/windows/check-feeds.js +26 -0
- package/deploy/windows/e2e/fixtures/person.jpg +0 -0
- package/deploy/windows/e2e/test-local-sense.ps1 +185 -0
- package/deploy/windows/install-update.ps1 +101 -0
- package/deploy/windows/installer.nsi +138 -0
- package/deploy/windows/register-litertlm-task.ps1 +42 -0
- package/deploy/windows/register-task.ps1 +44 -0
- package/deploy/windows/run-connector.ps1 +69 -0
- package/deploy/windows/setup-litertlm.ps1 +361 -0
- package/deploy/windows/stop-existing.ps1 +72 -0
- package/deploy/windows/trillboards-edge-task.vbs +25 -0
- package/deploy/windows/trillboards-edge.cmd +33 -0
- package/deploy/windows/trillboards-pair.cmd +276 -0
- package/deploy/windows/trillboards-setup.ps1 +456 -0
- package/deploy/windows/trillboards-setup.vbs +13 -0
- package/dist/AudienceAnalyzeClient.d.ts +136 -0
- package/dist/AudienceAnalyzeClient.d.ts.map +1 -0
- package/dist/AudienceAnalyzeClient.js +246 -0
- package/dist/AudienceAnalyzeClient.js.map +1 -0
- package/dist/ChannelProber.d.ts +150 -0
- package/dist/ChannelProber.d.ts.map +1 -0
- package/dist/ChannelProber.js +887 -0
- package/dist/ChannelProber.js.map +1 -0
- package/dist/CloudEscalationPolicy.d.ts +143 -0
- package/dist/CloudEscalationPolicy.d.ts.map +1 -0
- package/dist/CloudEscalationPolicy.js +137 -0
- package/dist/CloudEscalationPolicy.js.map +1 -0
- package/dist/CloudSensingManager.d.ts +533 -0
- package/dist/CloudSensingManager.d.ts.map +1 -0
- package/dist/CloudSensingManager.js +1254 -0
- package/dist/CloudSensingManager.js.map +1 -0
- package/dist/DiscoveryReportClient.d.ts +66 -0
- package/dist/DiscoveryReportClient.d.ts.map +1 -0
- package/dist/DiscoveryReportClient.js +85 -0
- package/dist/DiscoveryReportClient.js.map +1 -0
- package/dist/EdgeAgent.d.ts +12 -2
- package/dist/EdgeAgent.d.ts.map +1 -1
- package/dist/EdgeAgent.js +55 -13
- package/dist/EdgeAgent.js.map +1 -1
- package/dist/LiteRtVlmProvider.d.ts +193 -0
- package/dist/LiteRtVlmProvider.d.ts.map +1 -0
- package/dist/LiteRtVlmProvider.js +436 -0
- package/dist/LiteRtVlmProvider.js.map +1 -0
- package/dist/MosaicSource.d.ts +108 -0
- package/dist/MosaicSource.d.ts.map +1 -0
- package/dist/MosaicSource.js +274 -0
- package/dist/MosaicSource.js.map +1 -0
- package/dist/OnvifClient.d.ts +83 -0
- package/dist/OnvifClient.d.ts.map +1 -0
- package/dist/OnvifClient.js +209 -0
- package/dist/OnvifClient.js.map +1 -0
- package/dist/PairClient.d.ts +106 -0
- package/dist/PairClient.d.ts.map +1 -0
- package/dist/PairClient.js +186 -0
- package/dist/PairClient.js.map +1 -0
- package/dist/RecorderAddressRecovery.d.ts +53 -0
- package/dist/RecorderAddressRecovery.d.ts.map +1 -0
- package/dist/RecorderAddressRecovery.js +359 -0
- package/dist/RecorderAddressRecovery.js.map +1 -0
- package/dist/SceneChangeDetector.d.ts +291 -0
- package/dist/SceneChangeDetector.d.ts.map +1 -0
- package/dist/SceneChangeDetector.js +682 -0
- package/dist/SceneChangeDetector.js.map +1 -0
- package/dist/SensingProfileClient.d.ts +59 -0
- package/dist/SensingProfileClient.d.ts.map +1 -0
- package/dist/SensingProfileClient.js +78 -0
- package/dist/SensingProfileClient.js.map +1 -0
- package/dist/SetupSessionClient.d.ts +114 -0
- package/dist/SetupSessionClient.d.ts.map +1 -0
- package/dist/SetupSessionClient.js +95 -0
- package/dist/SetupSessionClient.js.map +1 -0
- package/dist/VlmAdmissionGate.d.ts +126 -0
- package/dist/VlmAdmissionGate.d.ts.map +1 -0
- package/dist/VlmAdmissionGate.js +166 -0
- package/dist/VlmAdmissionGate.js.map +1 -0
- package/dist/autoSense.d.ts +216 -0
- package/dist/autoSense.d.ts.map +1 -0
- package/dist/autoSense.js +521 -0
- package/dist/autoSense.js.map +1 -0
- package/dist/cli.d.ts +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +511 -21
- package/dist/cli.js.map +1 -1
- package/dist/combinedSense.d.ts +128 -0
- package/dist/combinedSense.d.ts.map +1 -0
- package/dist/combinedSense.js +261 -0
- package/dist/combinedSense.js.map +1 -0
- package/dist/config.d.ts +351 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +99 -2
- package/dist/config.js.map +1 -1
- package/dist/connectorCadence.d.ts +135 -0
- package/dist/connectorCadence.d.ts.map +1 -0
- package/dist/connectorCadence.js +151 -0
- package/dist/connectorCadence.js.map +1 -0
- package/dist/dataDir.d.ts +33 -0
- package/dist/dataDir.d.ts.map +1 -0
- package/dist/dataDir.js +76 -0
- package/dist/dataDir.js.map +1 -0
- package/dist/deployedCadence.d.ts +167 -0
- package/dist/deployedCadence.d.ts.map +1 -0
- package/dist/deployedCadence.js +209 -0
- package/dist/deployedCadence.js.map +1 -0
- package/dist/deployedCadenceSource.d.ts +48 -0
- package/dist/deployedCadenceSource.d.ts.map +1 -0
- package/dist/deployedCadenceSource.js +74 -0
- package/dist/deployedCadenceSource.js.map +1 -0
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.d.ts.map +1 -1
- package/dist/diagnose.js +13 -12
- package/dist/diagnose.js.map +1 -1
- package/dist/discovery.d.ts +131 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +499 -0
- package/dist/discovery.js.map +1 -0
- package/dist/gemmaModelOta.d.ts +118 -0
- package/dist/gemmaModelOta.d.ts.map +1 -0
- package/dist/gemmaModelOta.js +291 -0
- package/dist/gemmaModelOta.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +41 -18
- package/dist/init.js.map +1 -1
- package/dist/keepAwake.d.ts +33 -0
- package/dist/keepAwake.d.ts.map +1 -0
- package/dist/keepAwake.js +54 -0
- package/dist/keepAwake.js.map +1 -0
- package/dist/legacyRecorderProfile.d.ts +20 -0
- package/dist/legacyRecorderProfile.d.ts.map +1 -0
- package/dist/legacyRecorderProfile.js +27 -0
- package/dist/legacyRecorderProfile.js.map +1 -0
- package/dist/localInferenceSchema.d.ts +132 -0
- package/dist/localInferenceSchema.d.ts.map +1 -0
- package/dist/localInferenceSchema.js +274 -0
- package/dist/localInferenceSchema.js.map +1 -0
- package/dist/localSenseE2eHarness.d.ts +101 -0
- package/dist/localSenseE2eHarness.d.ts.map +1 -0
- package/dist/localSenseE2eHarness.js +279 -0
- package/dist/localSenseE2eHarness.js.map +1 -0
- package/dist/localSensing.d.ts +268 -0
- package/dist/localSensing.d.ts.map +1 -0
- package/dist/localSensing.js +492 -0
- package/dist/localSensing.js.map +1 -0
- package/dist/modelIntegrity.d.ts +59 -0
- package/dist/modelIntegrity.d.ts.map +1 -0
- package/dist/modelIntegrity.js +165 -0
- package/dist/modelIntegrity.js.map +1 -0
- package/dist/pairVerify.d.ts +100 -0
- package/dist/pairVerify.d.ts.map +1 -0
- package/dist/pairVerify.js +296 -0
- package/dist/pairVerify.js.map +1 -0
- package/dist/qrScanner.d.ts +33 -0
- package/dist/qrScanner.d.ts.map +1 -0
- package/dist/qrScanner.js +103 -0
- package/dist/qrScanner.js.map +1 -0
- package/dist/runtimePaths.d.ts +10 -0
- package/dist/runtimePaths.d.ts.map +1 -0
- package/dist/runtimePaths.js +79 -0
- package/dist/runtimePaths.js.map +1 -0
- package/dist/sensingRunMode.d.ts +68 -0
- package/dist/sensingRunMode.d.ts.map +1 -0
- package/dist/sensingRunMode.js +346 -0
- package/dist/sensingRunMode.js.map +1 -0
- package/dist/setupMode.d.ts +98 -0
- package/dist/setupMode.d.ts.map +1 -0
- package/dist/setupMode.js +828 -0
- package/dist/setupMode.js.map +1 -0
- package/dist/setupProgress.d.ts +31 -0
- package/dist/setupProgress.d.ts.map +1 -0
- package/dist/setupProgress.js +173 -0
- package/dist/setupProgress.js.map +1 -0
- package/package.json +13 -4
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteRtVlmProvider — runs the audience/sensing VLM ON-DEVICE via Google AI
|
|
3
|
+
* Edge **LiteRT-LM** (`litert-lm serve`), the SAME runtime the CTV agent-core
|
|
4
|
+
* uses for `gemma-4-E2B-it` (Gemma 4 E2B). This is the production runtime for the
|
|
5
|
+
* BYO-CCTV connector — NOT Ollama. Cross-platform (macOS/Linux/Windows), OpenAI-
|
|
6
|
+
* compatible API (`/v1/chat/completions`).
|
|
7
|
+
*
|
|
8
|
+
* ── Structured output via PLAIN completion + client-side validation (NOT
|
|
9
|
+
* tool-calling). litert-lm 0.13.1 DOES expose constrained tool-calling, but its
|
|
10
|
+
* ANTLR FC tool-call parser 500s whenever Gemma serializes a string with the
|
|
11
|
+
* special `<|"|>` quote token instead of a literal `"` — reproducibly, on whole
|
|
12
|
+
* classes of frames (measured 0/5 on an empty-room frame across full schema,
|
|
13
|
+
* no-free-text, temp 0.8, AND response_format). Empty scenes dominate real CCTV,
|
|
14
|
+
* so tool-calling is unusable here. The SAME frame returns clean valid JSON via
|
|
15
|
+
* PLAIN completion (8/8) — the `<|"|>` token is injected only by the tool-call/
|
|
16
|
+
* constrained path. So we:
|
|
17
|
+
* 1. render the schema as a human-readable key list ("key": one of [...] / type),
|
|
18
|
+
* 2. ask for a single bare minified JSON object (no tools, no response_format),
|
|
19
|
+
* 3. read choices[0].message.content, de-tokenize/repair, JSON.parse,
|
|
20
|
+
* 4. VALIDATE against the schema (required keys + enum membership), and
|
|
21
|
+
* 5. retry (perturbing temperature) on any HTTP / parse / validation failure.
|
|
22
|
+
* The structured-output GUARANTEE is the client-side validation+retry — the same
|
|
23
|
+
* contract the cloud VLM produces, just enforced on our side rather than by the
|
|
24
|
+
* (broken) on-device grammar.
|
|
25
|
+
*
|
|
26
|
+
* ── ACCELERATOR VARIANT: litert-lm advertises its GPU delegate as a SEPARATE
|
|
27
|
+
* MODEL ID — the imported id with a `,gpu` suffix (`GET /v1/models` on 0.13.1
|
|
28
|
+
* lists both `gemma-4-E2B-it.litertlm` and `gemma-4-E2B-it.litertlm,gpu`, and
|
|
29
|
+
* `serve_util.parse_model_spec` parses `model_id[,backend][,max_tokens]` out of
|
|
30
|
+
* the `model` string). Posting the bare id runs the CPU delegate, so until this
|
|
31
|
+
* shipped EVERY connector frame ran on CPU. MEASURED through this provider on an
|
|
32
|
+
* M4 Max, real kitchen frame at production capture params (640x480 JPEG q70),
|
|
33
|
+
* same 16-key prompt on both delegates in back-to-back windows: 39.22 s/frame on
|
|
34
|
+
* CPU (n=4) vs 10.66 s/frame on GPU (n=5) — 3.68x. So the provider RESOLVES the
|
|
35
|
+
* variant from the live model list at first use (`accelerator: 'auto'`, the
|
|
36
|
+
* default) rather than hardcoding either id, prefers GPU when the serve
|
|
37
|
+
* advertises it, and DEMOTES to CPU for the rest of the process if the GPU
|
|
38
|
+
* variant turns out not to work on this host (an unusable delegate must degrade,
|
|
39
|
+
* never silence sensing). `getAcceleratorHealth()` reports the outcome.
|
|
40
|
+
*
|
|
41
|
+
* NOTE for anyone re-measuring: changing the model spec makes litert-lm tear
|
|
42
|
+
* down and re-initialize the engine, so the first frame after a delegate switch
|
|
43
|
+
* carries model load — warm once before timing. `serve` is also a SERIAL
|
|
44
|
+
* http.server, and killing a client does NOT cancel its in-flight generation, so
|
|
45
|
+
* a stale backlog makes even `GET /v1/models` time out and reads as "the GPU is
|
|
46
|
+
* broken". Wait for a drained serve first. (.claude/lessons.md 2026-08-11.)
|
|
47
|
+
*
|
|
48
|
+
* Built-in http/https only (no extra dependency → survives bundling into the
|
|
49
|
+
* self-contained connector; trivially mockable in tests).
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* Which litert-lm delegate to run.
|
|
53
|
+
* - `'auto'` (default) — read `GET /v1/models` once and prefer the `,gpu`
|
|
54
|
+
* variant when the serve advertises it; otherwise CPU.
|
|
55
|
+
* - `'gpu'` — pin the `,gpu` variant without probing (still demotes to CPU if
|
|
56
|
+
* it fails at runtime, so a bad pin cannot take sensing down).
|
|
57
|
+
* - `'cpu'` — pin the bare model id; never probe, never demote.
|
|
58
|
+
*/
|
|
59
|
+
export type LiteRtAccelerator = 'auto' | 'gpu' | 'cpu';
|
|
60
|
+
export interface LiteRtVlmProviderOptions {
|
|
61
|
+
/** LiteRT-LM serve base URL (no trailing slash needed). Default http://localhost:9379. */
|
|
62
|
+
litertLmBaseUrl?: string;
|
|
63
|
+
/** Model id as imported in `litert-lm list`. Default gemma-4-E2B-it.litertlm. */
|
|
64
|
+
model?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Which delegate to run. Default `'auto'` — resolve the `,gpu` variant from the
|
|
67
|
+
* live `/v1/models` list and prefer it when present. See LiteRtAccelerator.
|
|
68
|
+
*/
|
|
69
|
+
accelerator?: LiteRtAccelerator;
|
|
70
|
+
/** Request timeout in ms. On-device inference can be slow → default 120000. */
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Total attempts for a single frame before giving up. Default 3. Attempt 0 is
|
|
74
|
+
* temperature 0 (deterministic data product); a retry perturbs temperature so
|
|
75
|
+
* a degenerate generation (dropped key / bad enum / unparseable) is re-rolled.
|
|
76
|
+
*/
|
|
77
|
+
maxAttempts?: number;
|
|
78
|
+
/** Backoff between retry attempts in ms. Default 250. */
|
|
79
|
+
retryBackoffMs?: number;
|
|
80
|
+
/** Log sink for the one-line variant-selection / demotion notices. Default console.log. */
|
|
81
|
+
log?: (message: string) => void;
|
|
82
|
+
}
|
|
83
|
+
/** What the provider resolved about this host's accelerator (surfaced in health). */
|
|
84
|
+
export interface LiteRtAcceleratorHealth {
|
|
85
|
+
/** The configured policy ('auto' | 'gpu' | 'cpu'). */
|
|
86
|
+
accelerator: LiteRtAccelerator;
|
|
87
|
+
/** The model id inference is actually posted under right now. */
|
|
88
|
+
selectedModel: string;
|
|
89
|
+
/** Which delegate that id runs. `null` until the first resolution completes. */
|
|
90
|
+
selectedDelegate: 'gpu' | 'cpu' | null;
|
|
91
|
+
/** Did `/v1/models` advertise the `,gpu` variant? `null` = not probed (pinned / not yet run). */
|
|
92
|
+
gpuAdvertised: boolean | null;
|
|
93
|
+
/** True once the GPU variant failed at runtime and the provider fell back to CPU. */
|
|
94
|
+
gpuDemoted: boolean;
|
|
95
|
+
/** Why it demoted / why GPU was not selected (human-readable; null when nothing to report). */
|
|
96
|
+
reason: string | null;
|
|
97
|
+
}
|
|
98
|
+
export interface LocalAnalyzeRequest {
|
|
99
|
+
/** Raw JPEG bytes for this frame. */
|
|
100
|
+
jpeg: Buffer;
|
|
101
|
+
/** The JSON Schema the output is validated against (the profile contract). */
|
|
102
|
+
schema: Record<string, unknown>;
|
|
103
|
+
/** The analysis prompt (same instruction the cloud VLM gets). */
|
|
104
|
+
prompt: string;
|
|
105
|
+
}
|
|
106
|
+
/** Minimal injectable HTTP transport — lets tests stub LiteRT-LM entirely. */
|
|
107
|
+
export interface HttpJsonPoster {
|
|
108
|
+
(url: string, options: {
|
|
109
|
+
method: string;
|
|
110
|
+
headers: Record<string, string>;
|
|
111
|
+
timeoutMs: number;
|
|
112
|
+
}, body: string): Promise<{
|
|
113
|
+
statusCode: number;
|
|
114
|
+
body: string;
|
|
115
|
+
}>;
|
|
116
|
+
}
|
|
117
|
+
export declare class LiteRtVlmProvider {
|
|
118
|
+
private readonly endpoint;
|
|
119
|
+
private readonly modelsEndpoint;
|
|
120
|
+
/** The bare model id as imported (never carries an accelerator suffix). */
|
|
121
|
+
private readonly baseModel;
|
|
122
|
+
private readonly accelerator;
|
|
123
|
+
private readonly timeoutMs;
|
|
124
|
+
private readonly maxAttempts;
|
|
125
|
+
private readonly retryBackoffMs;
|
|
126
|
+
private readonly poster;
|
|
127
|
+
private readonly log;
|
|
128
|
+
/** Resolved model id inference is posted under; null until the first resolve. */
|
|
129
|
+
private selectedModel;
|
|
130
|
+
/** In-flight resolution, so N concurrent frames share ONE /v1/models probe. */
|
|
131
|
+
private resolving;
|
|
132
|
+
/** Did the serve advertise the `,gpu` variant? null = not probed. */
|
|
133
|
+
private gpuAdvertised;
|
|
134
|
+
/** Set once the GPU variant failed a whole frame and we fell back to CPU. */
|
|
135
|
+
private gpuDemoted;
|
|
136
|
+
private acceleratorReason;
|
|
137
|
+
constructor(options?: LiteRtVlmProviderOptions, poster?: HttpJsonPoster);
|
|
138
|
+
/** The resolved chat-completions URL (exposed for assertions / logging). */
|
|
139
|
+
getEndpoint(): string;
|
|
140
|
+
/**
|
|
141
|
+
* The model id this provider posts inference under. Before the first
|
|
142
|
+
* `analyze()` on the `'auto'` policy this is the BASE (CPU) id — the variant is
|
|
143
|
+
* resolved from the live model list at first use.
|
|
144
|
+
*/
|
|
145
|
+
getModel(): string;
|
|
146
|
+
/** The `,gpu` variant id for the configured base model. */
|
|
147
|
+
private gpuModel;
|
|
148
|
+
/**
|
|
149
|
+
* What this provider resolved about the host's accelerator — the health
|
|
150
|
+
* surface for "did we get the GPU, and if not, why not". Read by the
|
|
151
|
+
* local-sensing client so an operator/support can see it without log-diving.
|
|
152
|
+
*/
|
|
153
|
+
getAcceleratorHealth(): LiteRtAcceleratorHealth;
|
|
154
|
+
/**
|
|
155
|
+
* Resolve which model id to post under, probing `GET /v1/models` at most once
|
|
156
|
+
* for a SUCCESSFUL answer. A failed probe resolves to CPU for this frame only
|
|
157
|
+
* and is re-probed on the next one — a serve that is still warming up must not
|
|
158
|
+
* pin the connector to CPU for the life of the process.
|
|
159
|
+
*/
|
|
160
|
+
private resolveModel;
|
|
161
|
+
/** GET the model-id list. Returns null on any transport/parse failure (never throws). */
|
|
162
|
+
private fetchModelIds;
|
|
163
|
+
/**
|
|
164
|
+
* Record that the GPU variant failed a whole frame (an unusable GPU delegate on
|
|
165
|
+
* this host) and pin the CPU delegate for the REST of the process. Idempotent —
|
|
166
|
+
* a SECOND concurrent frame that also failed on GPU must still be allowed to
|
|
167
|
+
* re-run on CPU, so the caller keys its retry on which model IT ran, not on
|
|
168
|
+
* whether it happened to be the one that recorded the demotion.
|
|
169
|
+
*/
|
|
170
|
+
private recordGpuDemotion;
|
|
171
|
+
/**
|
|
172
|
+
* Run on-device inference on a JPEG frame and return the parsed + validated
|
|
173
|
+
* structured object (the same shape the cloud VLM emits for this schema).
|
|
174
|
+
* Retries a transient failure (non-2xx / unparseable / schema-invalid) with a
|
|
175
|
+
* perturbed temperature; a non-empty-jpeg failure is fatal (no retry).
|
|
176
|
+
*
|
|
177
|
+
* If EVERY attempt failed while running the GPU variant, the provider demotes
|
|
178
|
+
* to the CPU delegate and re-runs the attempt budget once — an init failure on
|
|
179
|
+
* a host with no usable GPU delegate degrades the frame's latency instead of
|
|
180
|
+
* losing the frame.
|
|
181
|
+
*/
|
|
182
|
+
analyze(request: LocalAnalyzeRequest): Promise<Record<string, unknown>>;
|
|
183
|
+
/** The retry budget for one frame against one model id. */
|
|
184
|
+
private runAttempts;
|
|
185
|
+
/** A single inference attempt at the given temperature. */
|
|
186
|
+
private attemptOnce;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Default transport using Node's built-in http/https. No third-party deps so it
|
|
190
|
+
* survives bundling into the self-contained connector.
|
|
191
|
+
*/
|
|
192
|
+
export declare const defaultHttpJsonPoster: HttpJsonPoster;
|
|
193
|
+
//# sourceMappingURL=LiteRtVlmProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiteRtVlmProvider.d.ts","sourceRoot":"","sources":["../src/LiteRtVlmProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.defaultHttpJsonPoster = exports.LiteRtVlmProvider = void 0;
|
|
37
|
+
const http = __importStar(require("http"));
|
|
38
|
+
const https = __importStar(require("https"));
|
|
39
|
+
const url_1 = require("url");
|
|
40
|
+
/** Suffix litert-lm appends to a model id to advertise its GPU-delegate variant. */
|
|
41
|
+
const GPU_MODEL_SUFFIX = ',gpu';
|
|
42
|
+
/**
|
|
43
|
+
* Timeout for the one-shot `/v1/models` variant probe. Short: the serve is LOCAL,
|
|
44
|
+
* and a slow/absent model list must not stall the first frame behind the full
|
|
45
|
+
* inference timeout — it just means "CPU".
|
|
46
|
+
*/
|
|
47
|
+
const MODEL_LIST_TIMEOUT_MS = 5000;
|
|
48
|
+
/** Marks a failure as transient (worth a perturbed retry) vs. fatal. */
|
|
49
|
+
class RetryableLiteRtError extends Error {
|
|
50
|
+
}
|
|
51
|
+
class LiteRtVlmProvider {
|
|
52
|
+
constructor(options = {}, poster) {
|
|
53
|
+
/** Resolved model id inference is posted under; null until the first resolve. */
|
|
54
|
+
this.selectedModel = null;
|
|
55
|
+
/** In-flight resolution, so N concurrent frames share ONE /v1/models probe. */
|
|
56
|
+
this.resolving = null;
|
|
57
|
+
/** Did the serve advertise the `,gpu` variant? null = not probed. */
|
|
58
|
+
this.gpuAdvertised = null;
|
|
59
|
+
/** Set once the GPU variant failed a whole frame and we fell back to CPU. */
|
|
60
|
+
this.gpuDemoted = false;
|
|
61
|
+
this.acceleratorReason = null;
|
|
62
|
+
const base = (options.litertLmBaseUrl || 'http://localhost:9379').replace(/\/+$/, '');
|
|
63
|
+
this.endpoint = `${base}/v1/chat/completions`;
|
|
64
|
+
this.modelsEndpoint = `${base}/v1/models`;
|
|
65
|
+
const configuredModel = options.model || 'gemma-4-E2B-it.litertlm';
|
|
66
|
+
// An operator may paste the GPU id straight into `localSensing.model`. Treat
|
|
67
|
+
// that as "base id + accelerator gpu" so we never build `…,gpu,gpu`.
|
|
68
|
+
const pinnedGpuInModelId = configuredModel.endsWith(GPU_MODEL_SUFFIX);
|
|
69
|
+
this.baseModel = pinnedGpuInModelId
|
|
70
|
+
? configuredModel.slice(0, -GPU_MODEL_SUFFIX.length)
|
|
71
|
+
: configuredModel;
|
|
72
|
+
this.accelerator = pinnedGpuInModelId ? 'gpu' : (options.accelerator || 'auto');
|
|
73
|
+
this.timeoutMs = options.timeoutMs ?? 120000;
|
|
74
|
+
this.maxAttempts = Math.max(1, options.maxAttempts ?? 3);
|
|
75
|
+
this.retryBackoffMs = options.retryBackoffMs ?? 250;
|
|
76
|
+
this.poster = poster ?? exports.defaultHttpJsonPoster;
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
this.log = options.log ?? ((message) => console.log(message));
|
|
79
|
+
// A PINNED delegate needs no probe — resolve it up front so getModel() is
|
|
80
|
+
// truthful from construction.
|
|
81
|
+
if (this.accelerator === 'cpu')
|
|
82
|
+
this.selectedModel = this.baseModel;
|
|
83
|
+
if (this.accelerator === 'gpu')
|
|
84
|
+
this.selectedModel = this.gpuModel();
|
|
85
|
+
}
|
|
86
|
+
/** The resolved chat-completions URL (exposed for assertions / logging). */
|
|
87
|
+
getEndpoint() {
|
|
88
|
+
return this.endpoint;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The model id this provider posts inference under. Before the first
|
|
92
|
+
* `analyze()` on the `'auto'` policy this is the BASE (CPU) id — the variant is
|
|
93
|
+
* resolved from the live model list at first use.
|
|
94
|
+
*/
|
|
95
|
+
getModel() {
|
|
96
|
+
return this.selectedModel ?? this.baseModel;
|
|
97
|
+
}
|
|
98
|
+
/** The `,gpu` variant id for the configured base model. */
|
|
99
|
+
gpuModel() {
|
|
100
|
+
return `${this.baseModel}${GPU_MODEL_SUFFIX}`;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* What this provider resolved about the host's accelerator — the health
|
|
104
|
+
* surface for "did we get the GPU, and if not, why not". Read by the
|
|
105
|
+
* local-sensing client so an operator/support can see it without log-diving.
|
|
106
|
+
*/
|
|
107
|
+
getAcceleratorHealth() {
|
|
108
|
+
const selectedModel = this.getModel();
|
|
109
|
+
const selectedDelegate = this.selectedModel === null
|
|
110
|
+
? null
|
|
111
|
+
: (this.selectedModel.endsWith(GPU_MODEL_SUFFIX) ? 'gpu' : 'cpu');
|
|
112
|
+
return {
|
|
113
|
+
accelerator: this.accelerator,
|
|
114
|
+
selectedModel,
|
|
115
|
+
selectedDelegate,
|
|
116
|
+
gpuAdvertised: this.gpuAdvertised,
|
|
117
|
+
gpuDemoted: this.gpuDemoted,
|
|
118
|
+
reason: this.acceleratorReason,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Resolve which model id to post under, probing `GET /v1/models` at most once
|
|
123
|
+
* for a SUCCESSFUL answer. A failed probe resolves to CPU for this frame only
|
|
124
|
+
* and is re-probed on the next one — a serve that is still warming up must not
|
|
125
|
+
* pin the connector to CPU for the life of the process.
|
|
126
|
+
*/
|
|
127
|
+
async resolveModel() {
|
|
128
|
+
if (this.selectedModel !== null)
|
|
129
|
+
return this.selectedModel;
|
|
130
|
+
if (this.resolving)
|
|
131
|
+
return this.resolving;
|
|
132
|
+
this.resolving = (async () => {
|
|
133
|
+
const models = await this.fetchModelIds();
|
|
134
|
+
if (models === null) {
|
|
135
|
+
// Probe failed (serve down / unparseable). Run CPU for this frame and
|
|
136
|
+
// leave selectedModel unset so the next frame re-probes.
|
|
137
|
+
this.acceleratorReason =
|
|
138
|
+
`could not read ${this.modelsEndpoint} — running the CPU delegate for now`;
|
|
139
|
+
this.log(`[litert-lm] accelerator: CPU ("${this.baseModel}") — ${this.acceleratorReason}`);
|
|
140
|
+
return this.baseModel;
|
|
141
|
+
}
|
|
142
|
+
const gpuId = this.gpuModel();
|
|
143
|
+
this.gpuAdvertised = models.includes(gpuId);
|
|
144
|
+
if (this.gpuAdvertised) {
|
|
145
|
+
this.selectedModel = gpuId;
|
|
146
|
+
this.acceleratorReason = null;
|
|
147
|
+
this.log(`[litert-lm] accelerator: GPU — inference model "${gpuId}"`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
this.selectedModel = this.baseModel;
|
|
151
|
+
this.acceleratorReason =
|
|
152
|
+
`${this.modelsEndpoint} does not advertise "${gpuId}" (has: ${models.join(', ') || 'none'})`;
|
|
153
|
+
this.log(`[litert-lm] accelerator: CPU — inference model "${this.baseModel}" ` +
|
|
154
|
+
`(${this.acceleratorReason})`);
|
|
155
|
+
}
|
|
156
|
+
return this.selectedModel;
|
|
157
|
+
})().finally(() => {
|
|
158
|
+
this.resolving = null;
|
|
159
|
+
});
|
|
160
|
+
return this.resolving;
|
|
161
|
+
}
|
|
162
|
+
/** GET the model-id list. Returns null on any transport/parse failure (never throws). */
|
|
163
|
+
async fetchModelIds() {
|
|
164
|
+
let result;
|
|
165
|
+
try {
|
|
166
|
+
result = await this.poster(this.modelsEndpoint, { method: 'GET', headers: { Accept: 'application/json' }, timeoutMs: MODEL_LIST_TIMEOUT_MS }, '');
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
if (result.statusCode < 200 || result.statusCode >= 300)
|
|
172
|
+
return null;
|
|
173
|
+
try {
|
|
174
|
+
const parsed = JSON.parse(result.body);
|
|
175
|
+
if (!Array.isArray(parsed?.data))
|
|
176
|
+
return null;
|
|
177
|
+
return parsed.data
|
|
178
|
+
.map((m) => (m && typeof m.id === 'string' ? m.id : null))
|
|
179
|
+
.filter((id) => id !== null);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Record that the GPU variant failed a whole frame (an unusable GPU delegate on
|
|
187
|
+
* this host) and pin the CPU delegate for the REST of the process. Idempotent —
|
|
188
|
+
* a SECOND concurrent frame that also failed on GPU must still be allowed to
|
|
189
|
+
* re-run on CPU, so the caller keys its retry on which model IT ran, not on
|
|
190
|
+
* whether it happened to be the one that recorded the demotion.
|
|
191
|
+
*/
|
|
192
|
+
recordGpuDemotion(reason) {
|
|
193
|
+
if (this.accelerator === 'cpu' || this.gpuDemoted)
|
|
194
|
+
return;
|
|
195
|
+
this.gpuDemoted = true;
|
|
196
|
+
this.selectedModel = this.baseModel;
|
|
197
|
+
this.acceleratorReason = reason;
|
|
198
|
+
this.log(`[litert-lm] GPU delegate unusable on this host — falling back to the CPU model ` +
|
|
199
|
+
`"${this.baseModel}" for the rest of this run (${reason})`);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Run on-device inference on a JPEG frame and return the parsed + validated
|
|
203
|
+
* structured object (the same shape the cloud VLM emits for this schema).
|
|
204
|
+
* Retries a transient failure (non-2xx / unparseable / schema-invalid) with a
|
|
205
|
+
* perturbed temperature; a non-empty-jpeg failure is fatal (no retry).
|
|
206
|
+
*
|
|
207
|
+
* If EVERY attempt failed while running the GPU variant, the provider demotes
|
|
208
|
+
* to the CPU delegate and re-runs the attempt budget once — an init failure on
|
|
209
|
+
* a host with no usable GPU delegate degrades the frame's latency instead of
|
|
210
|
+
* losing the frame.
|
|
211
|
+
*/
|
|
212
|
+
async analyze(request) {
|
|
213
|
+
if (!request?.jpeg || request.jpeg.length === 0) {
|
|
214
|
+
throw new Error('LiteRtVlmProvider.analyze requires a non-empty jpeg buffer');
|
|
215
|
+
}
|
|
216
|
+
const model = await this.resolveModel();
|
|
217
|
+
try {
|
|
218
|
+
return await this.runAttempts(request, model);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
if (!(err instanceof RetryableLiteRtError))
|
|
222
|
+
throw err;
|
|
223
|
+
// Only the GPU variant has a fallback left; a CPU failure is a real failure.
|
|
224
|
+
if (this.accelerator === 'cpu' || !model.endsWith(GPU_MODEL_SUFFIX))
|
|
225
|
+
throw err;
|
|
226
|
+
this.recordGpuDemotion(err.message);
|
|
227
|
+
return this.runAttempts(request, this.baseModel);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** The retry budget for one frame against one model id. */
|
|
231
|
+
async runAttempts(request, model) {
|
|
232
|
+
let lastErr;
|
|
233
|
+
for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {
|
|
234
|
+
// attempt 0 → temperature 0 (deterministic); retries perturb to re-roll a
|
|
235
|
+
// degenerate generation (dropped key / out-of-enum value / unparseable).
|
|
236
|
+
const temperature = attempt === 0 ? 0 : 0.4;
|
|
237
|
+
try {
|
|
238
|
+
return await this.attemptOnce(request, temperature, model);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
lastErr = err;
|
|
242
|
+
if (!(err instanceof RetryableLiteRtError))
|
|
243
|
+
throw err;
|
|
244
|
+
if (attempt < this.maxAttempts - 1 && this.retryBackoffMs > 0) {
|
|
245
|
+
await new Promise((resolve) => setTimeout(resolve, this.retryBackoffMs));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
throw lastErr ?? new Error('LiteRT-LM analyze failed');
|
|
250
|
+
}
|
|
251
|
+
/** A single inference attempt at the given temperature. */
|
|
252
|
+
async attemptOnce(request, temperature, model) {
|
|
253
|
+
const dataUri = `data:image/jpeg;base64,${request.jpeg.toString('base64')}`;
|
|
254
|
+
const fullPrompt = `${request.prompt}\n\n`
|
|
255
|
+
+ 'Output ONLY a single minified JSON object (no markdown, no code fence, no prose) '
|
|
256
|
+
+ 'with EXACTLY these keys:\n'
|
|
257
|
+
+ renderSchemaAsFieldList(request.schema);
|
|
258
|
+
// PLAIN completion — no `tools`, no `tool_choice`, no `response_format`. Those
|
|
259
|
+
// route through litert-lm's broken constrained path (the `<|"|>` 500). Plain
|
|
260
|
+
// content is clean; we enforce the schema ourselves below.
|
|
261
|
+
const body = JSON.stringify({
|
|
262
|
+
model,
|
|
263
|
+
messages: [
|
|
264
|
+
{
|
|
265
|
+
role: 'user',
|
|
266
|
+
content: [
|
|
267
|
+
{ type: 'text', text: fullPrompt },
|
|
268
|
+
{ type: 'image_url', image_url: { url: dataUri } },
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
temperature,
|
|
273
|
+
});
|
|
274
|
+
let result;
|
|
275
|
+
try {
|
|
276
|
+
result = await this.poster(this.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, timeoutMs: this.timeoutMs }, body);
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
// A TRANSPORT failure is the definition of transient, and until this it was
|
|
280
|
+
// the one class of failure treated as FATAL — the poster rejects with a
|
|
281
|
+
// plain Error (ECONNRESET, "socket hang up", ECONNREFUSED, the timeout),
|
|
282
|
+
// and `runAttempts` re-throws anything that is not a RetryableLiteRtError.
|
|
283
|
+
//
|
|
284
|
+
// That inverted the GPU-demotion contract in exactly the case it exists
|
|
285
|
+
// for. `litert-lm serve` is a serial http.server, so a GPU delegate that
|
|
286
|
+
// is unusable on this host does not answer with a status code — it takes
|
|
287
|
+
// the socket down with it, or never answers at all. Both arrived here as
|
|
288
|
+
// fatal, so `analyze()` never reached `recordGpuDemotion`, and a host with
|
|
289
|
+
// a bad GPU driver lost EVERY frame forever while the CPU delegate that
|
|
290
|
+
// would have worked was one line away. The same hole ate a frame on every
|
|
291
|
+
// ordinary serve restart — including the ones the Gemma OTA watcher does
|
|
292
|
+
// on purpose — and on every machine sleep/wake.
|
|
293
|
+
//
|
|
294
|
+
// A malformed base URL is NOT transient: it is a config error that would
|
|
295
|
+
// fail identically three times, so it stays fatal and fast.
|
|
296
|
+
const message = err?.message ?? String(err);
|
|
297
|
+
if (/^Invalid LiteRT-LM URL/.test(message))
|
|
298
|
+
throw err;
|
|
299
|
+
throw new RetryableLiteRtError(`LiteRT-LM transport failure: ${message}`);
|
|
300
|
+
}
|
|
301
|
+
if (result.statusCode < 200 || result.statusCode >= 300) {
|
|
302
|
+
throw new RetryableLiteRtError(`LiteRT-LM returned HTTP ${result.statusCode}: ${String(result.body).slice(0, 200)}`);
|
|
303
|
+
}
|
|
304
|
+
let envelope;
|
|
305
|
+
try {
|
|
306
|
+
envelope = JSON.parse(result.body);
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
throw new RetryableLiteRtError(`Could not parse LiteRT-LM response JSON: ${err.message}`);
|
|
310
|
+
}
|
|
311
|
+
const content = envelope?.choices?.[0]?.message?.content;
|
|
312
|
+
if (typeof content !== 'string' || content.length === 0) {
|
|
313
|
+
throw new RetryableLiteRtError('LiteRT-LM returned no message content');
|
|
314
|
+
}
|
|
315
|
+
let parsed;
|
|
316
|
+
try {
|
|
317
|
+
parsed = repairAndParseJson(content);
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
throw new RetryableLiteRtError(`Could not parse a JSON object from LiteRT-LM content: ${err.message}`);
|
|
321
|
+
}
|
|
322
|
+
// The model may emit an integer-valued field as a float (e.g. person_count:
|
|
323
|
+
// 1.0). Coerce declared integer fields back to int before validating.
|
|
324
|
+
coerceIntegerFields(parsed, request.schema);
|
|
325
|
+
// The structured-output guarantee lives HERE — reject (and retry) any
|
|
326
|
+
// generation missing a required key or emitting an out-of-enum value.
|
|
327
|
+
const violation = validateAgainstSchema(parsed, request.schema);
|
|
328
|
+
if (violation) {
|
|
329
|
+
throw new RetryableLiteRtError(`LiteRT-LM output failed schema validation: ${violation}`);
|
|
330
|
+
}
|
|
331
|
+
return parsed;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
exports.LiteRtVlmProvider = LiteRtVlmProvider;
|
|
335
|
+
/**
|
|
336
|
+
* Render a JSON Schema's properties as a human-readable key list for the prompt.
|
|
337
|
+
* Enums render as `"key": one of [a, b, c]`; everything else as `"key": <type>`.
|
|
338
|
+
* (Embedding the raw JSON-schema OBJECT instead made Gemma echo the schema —
|
|
339
|
+
* the readable list reliably gets it to emit the data object.)
|
|
340
|
+
*/
|
|
341
|
+
function renderSchemaAsFieldList(schema) {
|
|
342
|
+
const props = (schema && typeof schema === 'object' ? schema.properties : null) || {};
|
|
343
|
+
return Object.entries(props)
|
|
344
|
+
.map(([key, spec]) => {
|
|
345
|
+
const s = spec;
|
|
346
|
+
if (Array.isArray(s?.enum))
|
|
347
|
+
return ` "${key}": one of [${s.enum.join(', ')}]`;
|
|
348
|
+
const t = Array.isArray(s?.type) ? s.type.find((x) => x !== 'null') : s?.type;
|
|
349
|
+
return ` "${key}": ${t || 'string'}`;
|
|
350
|
+
})
|
|
351
|
+
.join('\n');
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Extract + parse the JSON object from a plain-completion response. Tolerates a
|
|
355
|
+
* stray `<|"|>` quote token, ```json fences, and leading/trailing prose by
|
|
356
|
+
* slicing to the outermost { … }.
|
|
357
|
+
*/
|
|
358
|
+
function repairAndParseJson(text) {
|
|
359
|
+
let s = String(text).replace(/<\|"\|>/g, '"').replace(/```(?:json)?/gi, '');
|
|
360
|
+
const start = s.indexOf('{');
|
|
361
|
+
const end = s.lastIndexOf('}');
|
|
362
|
+
if (start >= 0 && end > start)
|
|
363
|
+
s = s.slice(start, end + 1);
|
|
364
|
+
return JSON.parse(s);
|
|
365
|
+
}
|
|
366
|
+
/** Coerce float-valued integers (e.g. 1.0 → 1) for fields the schema declares integer. */
|
|
367
|
+
function coerceIntegerFields(obj, schema) {
|
|
368
|
+
const props = (schema && typeof schema === 'object' ? schema.properties : null) || {};
|
|
369
|
+
for (const [key, spec] of Object.entries(props)) {
|
|
370
|
+
const t = spec?.type;
|
|
371
|
+
const isInt = t === 'integer' || (Array.isArray(t) && t.includes('integer'));
|
|
372
|
+
if (isInt && typeof obj[key] === 'number' && Number.isFinite(obj[key])) {
|
|
373
|
+
obj[key] = Math.round(obj[key]);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Validate a parsed object against the schema's required keys + enum membership.
|
|
379
|
+
* Returns a human-readable violation string, or null when valid.
|
|
380
|
+
*/
|
|
381
|
+
function validateAgainstSchema(obj, schema) {
|
|
382
|
+
const props = (schema && typeof schema === 'object' ? schema.properties : null) || {};
|
|
383
|
+
const required = Array.isArray(schema?.required) ? schema.required : [];
|
|
384
|
+
const missing = required.filter((k) => !(k in obj) || obj[k] === undefined || obj[k] === null);
|
|
385
|
+
if (missing.length)
|
|
386
|
+
return `missing required keys: ${missing.join(', ')}`;
|
|
387
|
+
const badEnum = [];
|
|
388
|
+
for (const [key, spec] of Object.entries(props)) {
|
|
389
|
+
const en = spec?.enum;
|
|
390
|
+
if (Array.isArray(en) && key in obj && obj[key] != null && !en.includes(obj[key])) {
|
|
391
|
+
badEnum.push(`${key}=${String(obj[key])}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (badEnum.length)
|
|
395
|
+
return `out-of-enum values: ${badEnum.join(', ')}`;
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Default transport using Node's built-in http/https. No third-party deps so it
|
|
400
|
+
* survives bundling into the self-contained connector.
|
|
401
|
+
*/
|
|
402
|
+
const defaultHttpJsonPoster = (url, options, body) => {
|
|
403
|
+
return new Promise((resolve, reject) => {
|
|
404
|
+
let parsed;
|
|
405
|
+
try {
|
|
406
|
+
parsed = new url_1.URL(url);
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
reject(new Error(`Invalid LiteRT-LM URL: ${err.message}`));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const mod = parsed.protocol === 'https:' ? https : http;
|
|
413
|
+
const bodyBuffer = Buffer.from(body, 'utf-8');
|
|
414
|
+
const req = mod.request(url, {
|
|
415
|
+
method: options.method,
|
|
416
|
+
headers: { ...options.headers, 'Content-Length': String(bodyBuffer.length) },
|
|
417
|
+
timeout: options.timeoutMs,
|
|
418
|
+
// litert-lm serve emits non-strict HTTP framing (bare LF on the status
|
|
419
|
+
// line) that Node's strict parser rejects with "Missing expected CR
|
|
420
|
+
// after response line". The connector talks to a LOCAL, trusted
|
|
421
|
+
// litert-lm serve, so tolerate it. (Found via the production E2E — the
|
|
422
|
+
// strict parser silently broke every on-device inference call.)
|
|
423
|
+
insecureHTTPParser: true,
|
|
424
|
+
}, (res) => {
|
|
425
|
+
const chunks = [];
|
|
426
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
427
|
+
res.on('end', () => resolve({ statusCode: res.statusCode || 0, body: Buffer.concat(chunks).toString('utf-8') }));
|
|
428
|
+
});
|
|
429
|
+
req.on('error', (err) => reject(err));
|
|
430
|
+
req.on('timeout', () => req.destroy(new Error('LiteRT-LM request timed out')));
|
|
431
|
+
req.write(bodyBuffer);
|
|
432
|
+
req.end();
|
|
433
|
+
});
|
|
434
|
+
};
|
|
435
|
+
exports.defaultHttpJsonPoster = defaultHttpJsonPoster;
|
|
436
|
+
//# sourceMappingURL=LiteRtVlmProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiteRtVlmProvider.js","sourceRoot":"","sources":["../src/LiteRtVlmProvider.ts"],"names":[],"mappings":"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"}
|