@eyepop.ai/eyepop 3.25.3 → 3.25.5
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/dist/eyepop.index.d.mts +83 -9
- package/dist/eyepop.index.d.ts +83 -9
- package/dist/eyepop.index.js +213 -86
- package/dist/eyepop.index.js.map +1 -1
- package/dist/eyepop.index.mjs +212 -86
- package/dist/eyepop.index.mjs.map +1 -1
- package/dist/eyepop.min.js +1 -1
- package/package.json +1 -1
package/dist/eyepop.index.js
CHANGED
|
@@ -8698,6 +8698,7 @@ __export(index_exports, {
|
|
|
8698
8698
|
ModelType: () => ModelType,
|
|
8699
8699
|
MotionModel: () => MotionModel,
|
|
8700
8700
|
PIPELINE_PATH: () => PIPELINE_PATH,
|
|
8701
|
+
PIPELINE_SOCKETS: () => PIPELINE_SOCKETS,
|
|
8701
8702
|
PipelineBackend: () => PipelineBackend,
|
|
8702
8703
|
PipelineCommandError: () => PipelineCommandError,
|
|
8703
8704
|
PipelineConfigurationError: () => PipelineConfigurationError,
|
|
@@ -9043,14 +9044,25 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
|
9043
9044
|
}
|
|
9044
9045
|
|
|
9045
9046
|
// endpoint.ts
|
|
9047
|
+
var ACCOUNT_CLAIM = "https://ident.eyepop.ai/account-uuid";
|
|
9048
|
+
var M2M_REFUSAL = "API key or user login required";
|
|
9049
|
+
function carriesAccount(token) {
|
|
9050
|
+
try {
|
|
9051
|
+
const payload = (token.split(".")[1] ?? "").replace(/-/g, "+").replace(/_/g, "/");
|
|
9052
|
+
return Boolean(JSON.parse(atob(payload))?.[ACCOUNT_CLAIM]);
|
|
9053
|
+
} catch {
|
|
9054
|
+
return false;
|
|
9055
|
+
}
|
|
9056
|
+
}
|
|
9046
9057
|
var Endpoint = class {
|
|
9047
9058
|
constructor(options) {
|
|
9059
|
+
this._exchanging = null;
|
|
9048
9060
|
this._options = options;
|
|
9049
9061
|
this._token = null;
|
|
9050
9062
|
this._expire_token_time = null;
|
|
9051
9063
|
this._client = null;
|
|
9052
9064
|
this._statusRetryHandlers = /* @__PURE__ */ new Map();
|
|
9053
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
9065
|
+
this._statusRetryHandlers.set(401, (statusCode, failedToken) => this.statusHandler401(statusCode, failedToken));
|
|
9054
9066
|
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
9055
9067
|
this._state = "Idle" /* Idle */;
|
|
9056
9068
|
this._message = null;
|
|
@@ -9066,7 +9078,7 @@ var Endpoint = class {
|
|
|
9066
9078
|
}
|
|
9067
9079
|
setStatusRetryHandlers(statusRetryHandlers = void 0) {
|
|
9068
9080
|
this._statusRetryHandlers = new Map(statusRetryHandlers);
|
|
9069
|
-
this._statusRetryHandlers.set(401, this.statusHandler401);
|
|
9081
|
+
this._statusRetryHandlers.set(401, (statusCode, failedToken) => this.statusHandler401(statusCode, failedToken));
|
|
9070
9082
|
}
|
|
9071
9083
|
eyepopUrl() {
|
|
9072
9084
|
if (this._options.eyepopUrl) {
|
|
@@ -9163,12 +9175,15 @@ var Endpoint = class {
|
|
|
9163
9175
|
validUntil: this._expire_token_time * 1e3
|
|
9164
9176
|
};
|
|
9165
9177
|
}
|
|
9166
|
-
statusHandler401(_statusCode) {
|
|
9167
|
-
this._token
|
|
9168
|
-
|
|
9178
|
+
statusHandler401(_statusCode, failedToken = this._token) {
|
|
9179
|
+
if (this._token === failedToken) {
|
|
9180
|
+
this._token = null;
|
|
9181
|
+
this._expire_token_time = null;
|
|
9182
|
+
}
|
|
9169
9183
|
}
|
|
9170
9184
|
async fetchWithRetry(fetcher, retries = 1) {
|
|
9171
9185
|
while (true) {
|
|
9186
|
+
const sentToken = this._token ?? void 0;
|
|
9172
9187
|
try {
|
|
9173
9188
|
const response = await fetcher();
|
|
9174
9189
|
if (response.ok) {
|
|
@@ -9176,7 +9191,7 @@ var Endpoint = class {
|
|
|
9176
9191
|
} else {
|
|
9177
9192
|
const handler = this._statusRetryHandlers.get(response.status);
|
|
9178
9193
|
if (handler != null) {
|
|
9179
|
-
handler(response.status);
|
|
9194
|
+
handler(response.status, sentToken);
|
|
9180
9195
|
if (retries--) {
|
|
9181
9196
|
this._requestLogger.info("received %d response, retry", response.status);
|
|
9182
9197
|
} else {
|
|
@@ -9210,54 +9225,74 @@ var Endpoint = class {
|
|
|
9210
9225
|
}
|
|
9211
9226
|
return `Bearer ${await this.currentAccessToken()}`;
|
|
9212
9227
|
}
|
|
9228
|
+
async exchange(client, credential, accountUuid) {
|
|
9229
|
+
const query = accountUuid ? `?account_uuid=${encodeURIComponent(accountUuid)}` : "";
|
|
9230
|
+
return await client.fetch(`${this.eyepopUrl()}/v1/auth/authenticate${query}`, {
|
|
9231
|
+
method: "POST",
|
|
9232
|
+
headers: { Authorization: `Bearer ${credential}` }
|
|
9233
|
+
});
|
|
9234
|
+
}
|
|
9235
|
+
async storeExchangedToken(response, now) {
|
|
9236
|
+
if (response.status != 200) {
|
|
9237
|
+
const message = await response.text();
|
|
9238
|
+
return Promise.reject(`Unexpected status ${response.status}: ${message}`);
|
|
9239
|
+
}
|
|
9240
|
+
const token = await response.json();
|
|
9241
|
+
this._token = token.access_token;
|
|
9242
|
+
this._expire_token_time = now + token.expires_in - 60;
|
|
9243
|
+
}
|
|
9244
|
+
/**
|
|
9245
|
+
* A token that already carries an account, or that the compute API refuses as an exchanged
|
|
9246
|
+
* token ("API key or user login required"), is used as is. Any other refusal is raised.
|
|
9247
|
+
*/
|
|
9248
|
+
async exchangeLoginToken(client, loginToken, validUntil, now) {
|
|
9249
|
+
if (!carriesAccount(loginToken)) {
|
|
9250
|
+
const response = await this.exchange(client, loginToken, this._options.accountId);
|
|
9251
|
+
if (response.status != 401 || !(await response.clone().text()).includes(M2M_REFUSAL)) {
|
|
9252
|
+
await this.storeExchangedToken(response, now);
|
|
9253
|
+
return;
|
|
9254
|
+
}
|
|
9255
|
+
}
|
|
9256
|
+
this._token = loginToken;
|
|
9257
|
+
this._expire_token_time = validUntil;
|
|
9258
|
+
}
|
|
9213
9259
|
async currentAccessToken() {
|
|
9260
|
+
if (!this._token || this._expire_token_time < Date.now() / 1e3) {
|
|
9261
|
+
this._exchanging ?? (this._exchanging = this.authenticate().finally(() => this._exchanging = null));
|
|
9262
|
+
await this._exchanging;
|
|
9263
|
+
}
|
|
9264
|
+
this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - Date.now() / 1e3);
|
|
9265
|
+
return this._token;
|
|
9266
|
+
}
|
|
9267
|
+
async authenticate() {
|
|
9214
9268
|
const now = Date.now() / 1e3;
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
} else if (this._options.eyepopUrl) {
|
|
9234
|
-
const credential = apiKeyCredential(auth);
|
|
9235
|
-
if (credential === void 0) {
|
|
9236
|
-
return Promise.reject("no valid auth option");
|
|
9237
|
-
}
|
|
9238
|
-
const headers = { Authorization: `Bearer ${credential}` };
|
|
9239
|
-
const post_url = `${this.eyepopUrl()}/v1/auth/authenticate`;
|
|
9240
|
-
const response = await this._client.fetch(post_url, {
|
|
9241
|
-
method: "POST",
|
|
9242
|
-
headers
|
|
9243
|
-
});
|
|
9244
|
-
if (response.status != 200) {
|
|
9245
|
-
const message = await response.text();
|
|
9246
|
-
return Promise.reject(`Unexpected status ${response.status}: ${message}`);
|
|
9247
|
-
}
|
|
9248
|
-
const data = await response.json();
|
|
9249
|
-
const token = data;
|
|
9250
|
-
this._token = token.access_token;
|
|
9251
|
-
this._expire_token_time = now + token.expires_in - 60;
|
|
9252
|
-
} else {
|
|
9269
|
+
this.updateState("Authenticating" /* Authenticating */);
|
|
9270
|
+
try {
|
|
9271
|
+
const auth = resolveAuth(this._options);
|
|
9272
|
+
if (!this._client) {
|
|
9273
|
+
return Promise.reject("endpoint not connected");
|
|
9274
|
+
} else if (auth?.isLocal !== void 0) {
|
|
9275
|
+
this._token = "implicit";
|
|
9276
|
+
this._expire_token_time = Number.MAX_VALUE;
|
|
9277
|
+
} else if (auth?.session !== void 0) {
|
|
9278
|
+
return Promise.reject("temporary access token expired");
|
|
9279
|
+
} else if (auth?.oAuth2 !== void 0 && this._options.eyepopUrl) {
|
|
9280
|
+
const login = await authenticateBrowserSession(auth.oAuth2, this._options);
|
|
9281
|
+
await this.exchangeLoginToken(this._client, login.accessToken, login.validUntil / 1e3, now);
|
|
9282
|
+
} else if (accessTokenCredential(auth) !== void 0) {
|
|
9283
|
+
await this.exchangeLoginToken(this._client, accessTokenCredential(auth), Number.MAX_VALUE, now);
|
|
9284
|
+
} else if (this._options.eyepopUrl) {
|
|
9285
|
+
const credential = apiKeyCredential(auth);
|
|
9286
|
+
if (credential === void 0) {
|
|
9253
9287
|
return Promise.reject("no valid auth option");
|
|
9254
9288
|
}
|
|
9255
|
-
|
|
9256
|
-
|
|
9289
|
+
await this.storeExchangedToken(await this.exchange(this._client, credential), now);
|
|
9290
|
+
} else {
|
|
9291
|
+
return Promise.reject("no valid auth option");
|
|
9257
9292
|
}
|
|
9293
|
+
} finally {
|
|
9294
|
+
this.updateState();
|
|
9258
9295
|
}
|
|
9259
|
-
this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - now);
|
|
9260
|
-
return this._token;
|
|
9261
9296
|
}
|
|
9262
9297
|
};
|
|
9263
9298
|
|
|
@@ -11612,6 +11647,8 @@ if (isBrowser) {
|
|
|
11612
11647
|
var PIPELINE_PATH = "/api/v1/pipeline";
|
|
11613
11648
|
var SUBPROTOCOL = "eyepop.v1";
|
|
11614
11649
|
var MAX_OPEN_SOURCES = 256;
|
|
11650
|
+
var PIPELINE_SOCKETS = 4;
|
|
11651
|
+
var EXTRA_SOCKET_RETRY_MS = 10 * 1e3;
|
|
11615
11652
|
var COMBINED_UPLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
11616
11653
|
var CHUNK = 1;
|
|
11617
11654
|
var COMBINED = 2;
|
|
@@ -11731,6 +11768,8 @@ var PipelineSocket = class {
|
|
|
11731
11768
|
// The ids of sources sent on this socket that have neither ended nor been refused, as the
|
|
11732
11769
|
// pipeline API counts them, cancelled ones included.
|
|
11733
11770
|
this.held = /* @__PURE__ */ new Set();
|
|
11771
|
+
// The held sources that are live. A live source never ends, so nothing queued behind it plays.
|
|
11772
|
+
this.lives = /* @__PURE__ */ new Set();
|
|
11734
11773
|
// The held sources the pipeline API took: only these end, so only these make room.
|
|
11735
11774
|
this.accepted = /* @__PURE__ */ new Set();
|
|
11736
11775
|
// The ends on this socket so far.
|
|
@@ -11739,6 +11778,7 @@ var PipelineSocket = class {
|
|
|
11739
11778
|
// the next send waits. Null once none is held.
|
|
11740
11779
|
this.cap = null;
|
|
11741
11780
|
this._roomWaiters = [];
|
|
11781
|
+
this.heartbeat = null;
|
|
11742
11782
|
this.closed = new Promise((resolve) => this._markClosed = resolve);
|
|
11743
11783
|
}
|
|
11744
11784
|
/** A source ended or the socket closed: wake the send waiting for room. */
|
|
@@ -11772,6 +11812,10 @@ var PipelineSocket = class {
|
|
|
11772
11812
|
return !this._closed && this.ws.readyState === 1;
|
|
11773
11813
|
}
|
|
11774
11814
|
markClosed() {
|
|
11815
|
+
if (this.heartbeat !== null) {
|
|
11816
|
+
clearInterval(this.heartbeat);
|
|
11817
|
+
this.heartbeat = null;
|
|
11818
|
+
}
|
|
11775
11819
|
this._closed = true;
|
|
11776
11820
|
this._markClosed();
|
|
11777
11821
|
this.freed();
|
|
@@ -11798,10 +11842,22 @@ var PipelineJob = class {
|
|
|
11798
11842
|
this.openSource = null;
|
|
11799
11843
|
// Stops the job's source on the pipeline, once it was sent.
|
|
11800
11844
|
this.stopOnPipeline = null;
|
|
11845
|
+
this._milliComputeUnits = void 0;
|
|
11801
11846
|
}
|
|
11802
11847
|
get cancelled() {
|
|
11803
11848
|
return this._cancelled;
|
|
11804
11849
|
}
|
|
11850
|
+
/** The source's milli compute units: the latest total of its `usage` messages and its `end`. */
|
|
11851
|
+
get milliComputeUnits() {
|
|
11852
|
+
return this._milliComputeUnits;
|
|
11853
|
+
}
|
|
11854
|
+
/** Keep a usage's or an end's milliComputeUnits: each carries the source's total so far. */
|
|
11855
|
+
noteMilliComputeUnits(message) {
|
|
11856
|
+
const value = message["milliComputeUnits"];
|
|
11857
|
+
if (Number.isInteger(value) && value >= 0) {
|
|
11858
|
+
this._milliComputeUnits = value;
|
|
11859
|
+
}
|
|
11860
|
+
}
|
|
11805
11861
|
command(id) {
|
|
11806
11862
|
const command = { type: "setSource", id, ...this.source };
|
|
11807
11863
|
if (this.params.componentParams !== void 0) {
|
|
@@ -11860,14 +11916,14 @@ var PipelineBackend = class {
|
|
|
11860
11916
|
constructor(ctx) {
|
|
11861
11917
|
this.ctx = ctx;
|
|
11862
11918
|
this._pipelineApiUrl = null;
|
|
11863
|
-
this.
|
|
11919
|
+
this._sockets = [];
|
|
11864
11920
|
this._connecting = null;
|
|
11865
11921
|
// Every job's setSource and every changePop goes out through this chain, in the order submitted.
|
|
11866
11922
|
this._sendChain = Promise.resolve();
|
|
11867
11923
|
this._disconnecting = false;
|
|
11868
11924
|
this._nextId = 0;
|
|
11869
11925
|
this._warned = /* @__PURE__ */ new Set();
|
|
11870
|
-
this.
|
|
11926
|
+
this._noExtraSocketUntil = 0;
|
|
11871
11927
|
// Reconnects at once since a source last played.
|
|
11872
11928
|
this._reconnects = 0;
|
|
11873
11929
|
this._currentPop = ctx.options.pop ?? null;
|
|
@@ -11881,13 +11937,9 @@ var PipelineBackend = class {
|
|
|
11881
11937
|
async disconnect(closeEndpoint) {
|
|
11882
11938
|
await closeEndpoint();
|
|
11883
11939
|
this._disconnecting = true;
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
}
|
|
11888
|
-
const socket = this._socket;
|
|
11889
|
-
this._socket = null;
|
|
11890
|
-
if (socket !== null) {
|
|
11940
|
+
const sockets = this._sockets;
|
|
11941
|
+
this._sockets = [];
|
|
11942
|
+
for (const socket of sockets) {
|
|
11891
11943
|
socket.ws.close(1e3);
|
|
11892
11944
|
await socket.closed;
|
|
11893
11945
|
}
|
|
@@ -11905,21 +11957,55 @@ var PipelineBackend = class {
|
|
|
11905
11957
|
this._sendChain = result.catch(() => void 0);
|
|
11906
11958
|
return result;
|
|
11907
11959
|
}
|
|
11908
|
-
|
|
11960
|
+
openSockets() {
|
|
11961
|
+
return this._sockets.filter((socket) => socket.open);
|
|
11962
|
+
}
|
|
11963
|
+
/** An open socket, or a new one with the last pop set on it. */
|
|
11909
11964
|
async ensureSocket() {
|
|
11910
|
-
|
|
11911
|
-
|
|
11965
|
+
const open = this.openSockets()[0];
|
|
11966
|
+
if (open !== void 0) {
|
|
11967
|
+
return open;
|
|
11912
11968
|
}
|
|
11913
11969
|
if (this._connecting === null) {
|
|
11914
|
-
this._connecting = this.openSocket().finally(() => this._connecting = null);
|
|
11970
|
+
this._connecting = this.openSocket(true).finally(() => this._connecting = null);
|
|
11915
11971
|
}
|
|
11916
11972
|
return await this._connecting;
|
|
11917
11973
|
}
|
|
11918
|
-
|
|
11974
|
+
/**
|
|
11975
|
+
* The socket for the next job; the caller runs it in submission order. A live job goes on a socket
|
|
11976
|
+
* of its own, an idle one or a new one, which `pipelineSockets` does not count. Any other job skips
|
|
11977
|
+
* the sockets with a live source: a new one while every other socket has a source and fewer than
|
|
11978
|
+
* `pipelineSockets` are open, else the one with room and the fewest sources.
|
|
11979
|
+
*/
|
|
11980
|
+
async socketForJob(job) {
|
|
11981
|
+
if (job.live) {
|
|
11982
|
+
return this.openSockets().find((socket) => socket.held.size === 0) ?? await this.openSocket(true);
|
|
11983
|
+
}
|
|
11984
|
+
const sockets = this.openSockets().filter((socket) => socket.lives.size === 0);
|
|
11985
|
+
if (sockets.length === 0) {
|
|
11986
|
+
return await this.openSocket(true);
|
|
11987
|
+
}
|
|
11988
|
+
if (sockets.length < (this.ctx.options.pipelineSockets ?? PIPELINE_SOCKETS) && sockets.every((socket) => socket.held.size > 0) && Date.now() >= this._noExtraSocketUntil) {
|
|
11989
|
+
try {
|
|
11990
|
+
return await this.openSocket(false);
|
|
11991
|
+
} catch (error) {
|
|
11992
|
+
this._noExtraSocketUntil = Date.now() + EXTRA_SOCKET_RETRY_MS;
|
|
11993
|
+
this.ctx.requestLogger.info("could not open another pipeline API socket: %s", error);
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
const rank = (socket) => (socket.hasRoom ? 0 : MAX_OPEN_SOURCES + 1) + socket.held.size;
|
|
11997
|
+
return sockets.reduce((best, socket) => rank(socket) < rank(best) ? socket : best);
|
|
11998
|
+
}
|
|
11999
|
+
/** A new socket with the last pop set on it; `patient` waits through 503 with Retry-After. */
|
|
12000
|
+
async openSocket(patient) {
|
|
11919
12001
|
if (this.ctx.client() === null) {
|
|
11920
12002
|
throw new PipelineSocketClosedError(void 0, "the endpoint is not connected");
|
|
11921
12003
|
}
|
|
11922
|
-
const ws = await this.upgrade(await this.socketUrl());
|
|
12004
|
+
const ws = await this.upgrade(await this.socketUrl(), patient);
|
|
12005
|
+
if (this._disconnecting) {
|
|
12006
|
+
ws.close(1e3);
|
|
12007
|
+
throw new PipelineSocketClosedError(void 0, "the endpoint disconnected");
|
|
12008
|
+
}
|
|
11923
12009
|
const socket = new PipelineSocket(ws);
|
|
11924
12010
|
ws.onmessage = (event) => {
|
|
11925
12011
|
if (typeof event.data === "string") {
|
|
@@ -11933,31 +12019,29 @@ var PipelineBackend = class {
|
|
|
11933
12019
|
ws.onerror = () => {
|
|
11934
12020
|
};
|
|
11935
12021
|
ws.onclose = (event) => this.closed(socket, event.code, event.reason);
|
|
11936
|
-
this.
|
|
11937
|
-
this.startHeartbeat(
|
|
12022
|
+
this._sockets.push(socket);
|
|
12023
|
+
this.startHeartbeat(socket);
|
|
11938
12024
|
if (this._currentPop !== null) {
|
|
11939
12025
|
try {
|
|
11940
12026
|
await this.setPopOn(socket, this._currentPop);
|
|
11941
12027
|
} catch (error) {
|
|
11942
|
-
this.
|
|
12028
|
+
this._sockets = this._sockets.filter((other) => other !== socket);
|
|
11943
12029
|
ws.close(1e3);
|
|
11944
12030
|
throw error;
|
|
11945
12031
|
}
|
|
11946
12032
|
}
|
|
11947
12033
|
return socket;
|
|
11948
12034
|
}
|
|
11949
|
-
startHeartbeat(
|
|
11950
|
-
|
|
11951
|
-
clearInterval(this._heartbeat);
|
|
11952
|
-
}
|
|
12035
|
+
startHeartbeat(socket) {
|
|
12036
|
+
const ws = socket.ws;
|
|
11953
12037
|
const ping = ws.ping;
|
|
11954
12038
|
if (typeof ping === "function") {
|
|
11955
|
-
|
|
12039
|
+
socket.heartbeat = setInterval(() => {
|
|
11956
12040
|
if (ws.readyState === 1) {
|
|
11957
12041
|
ping.call(ws);
|
|
11958
12042
|
}
|
|
11959
12043
|
}, HEARTBEAT_MS);
|
|
11960
|
-
|
|
12044
|
+
socket.heartbeat.unref?.();
|
|
11961
12045
|
}
|
|
11962
12046
|
}
|
|
11963
12047
|
async socketUrl() {
|
|
@@ -11995,7 +12079,7 @@ var PipelineBackend = class {
|
|
|
11995
12079
|
return (await this.ctx.credentialHeader()).replace(/^Bearer /, "");
|
|
11996
12080
|
}
|
|
11997
12081
|
/** Open the socket, waiting through 503 with Retry-After and exchanging the API key again after a 401. */
|
|
11998
|
-
async upgrade(url) {
|
|
12082
|
+
async upgrade(url, patient) {
|
|
11999
12083
|
const deadline = Date.now() + MAX_RETRY_MS;
|
|
12000
12084
|
let rejected = false;
|
|
12001
12085
|
while (true) {
|
|
@@ -12013,7 +12097,7 @@ var PipelineBackend = class {
|
|
|
12013
12097
|
continue;
|
|
12014
12098
|
}
|
|
12015
12099
|
const wait = error.status === 503 && error.retryAfter !== void 0 ? Math.max(error.retryAfter, 1) * 1e3 : void 0;
|
|
12016
|
-
if (wait === void 0 || Date.now() + wait > deadline) {
|
|
12100
|
+
if (!patient || wait === void 0 || Date.now() + wait > deadline) {
|
|
12017
12101
|
throw error;
|
|
12018
12102
|
}
|
|
12019
12103
|
this.ctx.requestLogger.info("the pipeline API answered 503, retrying in %d ms", wait);
|
|
@@ -12023,9 +12107,7 @@ var PipelineBackend = class {
|
|
|
12023
12107
|
}
|
|
12024
12108
|
closed(socket, code, reason) {
|
|
12025
12109
|
socket.markClosed();
|
|
12026
|
-
|
|
12027
|
-
this._socket = null;
|
|
12028
|
-
}
|
|
12110
|
+
this._sockets = this._sockets.filter((other) => other !== socket);
|
|
12029
12111
|
const error = new PipelineSocketClosedError(code, reason);
|
|
12030
12112
|
socket.sources.forEach((source) => source.close(error));
|
|
12031
12113
|
socket.commands.forEach((command) => command.reject(error));
|
|
@@ -12051,12 +12133,14 @@ var PipelineBackend = class {
|
|
|
12051
12133
|
const source = typeof id === "string" ? socket.sources.get(id) : void 0;
|
|
12052
12134
|
if (type === "end") {
|
|
12053
12135
|
socket.held.delete(id);
|
|
12136
|
+
socket.lives.delete(id);
|
|
12054
12137
|
socket.accepted.delete(id);
|
|
12055
12138
|
socket.freed();
|
|
12056
12139
|
} else if (type === "result" && socket.held.has(id)) {
|
|
12057
12140
|
socket.accepted.add(id);
|
|
12058
12141
|
} else if (type === "error" && message["error"] !== "unknown_command") {
|
|
12059
12142
|
socket.held.delete(id);
|
|
12143
|
+
socket.lives.delete(id);
|
|
12060
12144
|
socket.wake();
|
|
12061
12145
|
}
|
|
12062
12146
|
if (type === "send" || type === "credit") {
|
|
@@ -12098,6 +12182,10 @@ var PipelineBackend = class {
|
|
|
12098
12182
|
command(socket, message) {
|
|
12099
12183
|
const id = this.newId(message["type"]);
|
|
12100
12184
|
return new Promise((resolve, reject) => {
|
|
12185
|
+
if (!socket.open) {
|
|
12186
|
+
reject(new PipelineSocketClosedError(void 0, "the socket closed"));
|
|
12187
|
+
return;
|
|
12188
|
+
}
|
|
12101
12189
|
socket.commands.set(id, { resolve, reject });
|
|
12102
12190
|
socket.sendJson({ ...message, id });
|
|
12103
12191
|
});
|
|
@@ -12106,15 +12194,28 @@ var PipelineBackend = class {
|
|
|
12106
12194
|
pop() {
|
|
12107
12195
|
return this._currentPop;
|
|
12108
12196
|
}
|
|
12109
|
-
/**
|
|
12197
|
+
/**
|
|
12198
|
+
* Set the pop on every open socket's pipeline; a pop the first refuses rejects, and the last pop it
|
|
12199
|
+
* took stays. shortcut: a refusal from a later socket, which the first took, leaves the earlier ones
|
|
12200
|
+
* on the new pop; a refusal is the pop's own fault, so every socket answers it alike.
|
|
12201
|
+
*/
|
|
12110
12202
|
async changePop(pop) {
|
|
12111
12203
|
if (this.ctx.client() === null) {
|
|
12112
12204
|
this._currentPop = pop;
|
|
12113
12205
|
return;
|
|
12114
12206
|
}
|
|
12115
12207
|
await this.inOrder(async () => {
|
|
12116
|
-
|
|
12117
|
-
|
|
12208
|
+
const sockets = this.openSockets();
|
|
12209
|
+
if (sockets.length > 0) {
|
|
12210
|
+
for (const socket of sockets) {
|
|
12211
|
+
try {
|
|
12212
|
+
await this.setPopOn(socket, pop);
|
|
12213
|
+
} catch (error) {
|
|
12214
|
+
if (!(error instanceof PipelineSocketClosedError)) {
|
|
12215
|
+
throw error;
|
|
12216
|
+
}
|
|
12217
|
+
}
|
|
12218
|
+
}
|
|
12118
12219
|
this._currentPop = pop;
|
|
12119
12220
|
return;
|
|
12120
12221
|
}
|
|
@@ -12275,22 +12376,30 @@ var PipelineBackend = class {
|
|
|
12275
12376
|
}
|
|
12276
12377
|
/** Send the job's setSource; the caller runs it in submission order. */
|
|
12277
12378
|
async setSource(job) {
|
|
12278
|
-
if (job.cancelled) {
|
|
12279
|
-
return null;
|
|
12280
|
-
}
|
|
12281
|
-
let socket = await this.ensureSocket();
|
|
12282
|
-
await socket.room(job);
|
|
12283
12379
|
if (job.cancelled) {
|
|
12284
12380
|
return null;
|
|
12285
12381
|
}
|
|
12286
12382
|
const combined = await combinedMedia(job);
|
|
12287
|
-
socket
|
|
12383
|
+
let socket;
|
|
12384
|
+
while (true) {
|
|
12385
|
+
socket = await this.socketForJob(job);
|
|
12386
|
+
await socket.room(job);
|
|
12387
|
+
if (job.cancelled) {
|
|
12388
|
+
return null;
|
|
12389
|
+
}
|
|
12390
|
+
if (socket.open) {
|
|
12391
|
+
break;
|
|
12392
|
+
}
|
|
12393
|
+
}
|
|
12288
12394
|
const source = new OpenSource(this.newId("source"));
|
|
12289
12395
|
if (combined === null) {
|
|
12290
12396
|
uploadKeys(job).forEach((key) => source.uploads.set(key, new Credit()));
|
|
12291
12397
|
}
|
|
12292
12398
|
socket.sources.set(source.id, source);
|
|
12293
12399
|
socket.held.add(source.id);
|
|
12400
|
+
if (job.live) {
|
|
12401
|
+
socket.lives.add(source.id);
|
|
12402
|
+
}
|
|
12294
12403
|
source.endedBefore = socket.ended;
|
|
12295
12404
|
job.openSource = source;
|
|
12296
12405
|
job.stopOnPipeline = () => {
|
|
@@ -12326,7 +12435,11 @@ var PipelineBackend = class {
|
|
|
12326
12435
|
case "prediction":
|
|
12327
12436
|
job.predicted(message["prediction"]);
|
|
12328
12437
|
break;
|
|
12438
|
+
case "usage":
|
|
12439
|
+
job.noteMilliComputeUnits(message);
|
|
12440
|
+
break;
|
|
12329
12441
|
case "end":
|
|
12442
|
+
job.noteMilliComputeUnits(message);
|
|
12330
12443
|
if (message["reason"] === "eos") {
|
|
12331
12444
|
return null;
|
|
12332
12445
|
}
|
|
@@ -12566,6 +12679,9 @@ var WorkerEndpoint = class extends Endpoint {
|
|
|
12566
12679
|
if (options.jobQueueLength > MAX_OPEN_SOURCES) {
|
|
12567
12680
|
throw new PipelineConfigurationError("job_queue_length", `jobQueueLength ${options.jobQueueLength} exceeds ${MAX_OPEN_SOURCES}, the open sources a pipeline API socket takes`);
|
|
12568
12681
|
}
|
|
12682
|
+
if (options.pipelineSockets !== void 0 && !(Number.isInteger(options.pipelineSockets) && options.pipelineSockets > 0)) {
|
|
12683
|
+
throw new PipelineConfigurationError("pipeline_sockets", `pipelineSockets ${options.pipelineSockets} is not a positive whole number`);
|
|
12684
|
+
}
|
|
12569
12685
|
}
|
|
12570
12686
|
super(options);
|
|
12571
12687
|
const context = {
|
|
@@ -12887,6 +13003,7 @@ var InferAssetJob = class {
|
|
|
12887
13003
|
constructor(assetUrl, inferRequest, vlmFetch, requestLogger) {
|
|
12888
13004
|
this._response = null;
|
|
12889
13005
|
this._runInfo = null;
|
|
13006
|
+
this._milliComputeUnits = void 0;
|
|
12890
13007
|
this._controller = new AbortController();
|
|
12891
13008
|
this._vlmFetch = vlmFetch;
|
|
12892
13009
|
this._assetUrl = assetUrl;
|
|
@@ -12923,6 +13040,13 @@ var InferAssetJob = class {
|
|
|
12923
13040
|
}
|
|
12924
13041
|
return iterator();
|
|
12925
13042
|
}
|
|
13043
|
+
/**
|
|
13044
|
+
* The milli compute units vlm-server billed for the inference, 0 on a cache hit; undefined before it answered
|
|
13045
|
+
* or when it reported none.
|
|
13046
|
+
*/
|
|
13047
|
+
get milliComputeUnits() {
|
|
13048
|
+
return this._milliComputeUnits;
|
|
13049
|
+
}
|
|
12926
13050
|
start(done) {
|
|
12927
13051
|
this._response = new Promise(async (resolve, reject) => {
|
|
12928
13052
|
try {
|
|
@@ -12947,6 +13071,8 @@ var InferAssetJob = class {
|
|
|
12947
13071
|
const runInfo = response["run_info"];
|
|
12948
13072
|
if (predictions) {
|
|
12949
13073
|
this._runInfo = runInfo;
|
|
13074
|
+
const used = response["milli_compute_units"] ?? response["compute_units"];
|
|
13075
|
+
this._milliComputeUnits = typeof used === "number" ? used : void 0;
|
|
12950
13076
|
resolve(predictions);
|
|
12951
13077
|
break;
|
|
12952
13078
|
} else {
|
|
@@ -14541,6 +14667,7 @@ var index_default = EyePop;
|
|
|
14541
14667
|
ModelType,
|
|
14542
14668
|
MotionModel,
|
|
14543
14669
|
PIPELINE_PATH,
|
|
14670
|
+
PIPELINE_SOCKETS,
|
|
14544
14671
|
PipelineBackend,
|
|
14545
14672
|
PipelineCommandError,
|
|
14546
14673
|
PipelineConfigurationError,
|