@eyepop.ai/eyepop 3.25.4 → 3.25.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.
@@ -64,6 +64,13 @@ interface Prediction extends StreamTime {
64
64
  source_url?: string;
65
65
  source_type?: string;
66
66
  system_timestamp?: number;
67
+ /**
68
+ * Compute units the frame's remote calls (a VLM ability's calls to vlm-server) used, in whole units. Absent
69
+ * when they used none, as for a local model.
70
+ */
71
+ compute_units?: number;
72
+ /** The same in milli compute units; from prediction version 4. */
73
+ milli_compute_units?: number;
67
74
  objects?: Array<PredictedObject>;
68
75
  classes?: Array<PredictedClass>;
69
76
  texts?: Array<PredictedText>;
@@ -1000,6 +1007,12 @@ interface AssetUuidSource {
1000
1007
  type Source = FileSource | StreamSource | PathSource | UrlSource | AssetUuidSource | MediaStreamSource;
1001
1008
  interface ResultStream extends AsyncIterable<Prediction> {
1002
1009
  cancel(): void;
1010
+ /**
1011
+ * What the stream's remote calls (a VLM ability's calls to vlm-server) used so far, in milli compute units:
1012
+ * a running total while it runs, its total once it ended. Undefined until reported, and where a backend does
1013
+ * not report it; a local model reports none. Informational: the called API bills its calls itself.
1014
+ */
1015
+ readonly milliComputeUnits?: number | undefined;
1003
1016
  }
1004
1017
  declare enum PopComponentType {
1005
1018
  FORWARD = "forward",
@@ -1325,9 +1338,15 @@ interface Options {
1325
1338
  */
1326
1339
  apiKey?: string | undefined;
1327
1340
  /**
1328
- * Bearer token accepted by the compute API.
1341
+ * Bearer token accepted by the compute API: a login token, which the SDK exchanges at the
1342
+ * compute API, or an already exchanged token, which it uses as is.
1329
1343
  */
1330
1344
  accessToken?: string | undefined;
1345
+ /**
1346
+ * The account a login token (`accessToken` or `oAuth2`) is exchanged for, sent to the compute API
1347
+ * as `account_uuid`. Needed when the user has no single account to pick.
1348
+ */
1349
+ accountId?: string | undefined;
1331
1350
  /**
1332
1351
  * Temporary authentication token for client side execution.
1333
1352
  */
@@ -1425,6 +1444,7 @@ declare class Endpoint<T extends Endpoint<T>> {
1425
1444
  protected _options: Options;
1426
1445
  private _token;
1427
1446
  private _expire_token_time;
1447
+ private _exchanging;
1428
1448
  protected _client: HttpClient | null;
1429
1449
  private _statusRetryHandlers;
1430
1450
  protected _limit: Semaphore;
@@ -1445,11 +1465,19 @@ declare class Endpoint<T extends Endpoint<T>> {
1445
1465
  connect(): Promise<T>;
1446
1466
  disconnect(wait?: boolean): Promise<void>;
1447
1467
  session(): Promise<Session>;
1448
- protected statusHandler401(_statusCode: number): void;
1468
+ protected statusHandler401(_statusCode: number, failedToken?: string | null): void;
1449
1469
  protected fetchWithRetry(fetcher: () => Promise<Response>, retries?: number): Promise<Response>;
1450
1470
  protected reconnect(): Promise<T>;
1451
1471
  protected authorizationHeader(): Promise<string>;
1472
+ private exchange;
1473
+ private storeExchangedToken;
1474
+ /**
1475
+ * A token that already carries an account, or that the compute API refuses as an exchanged
1476
+ * token ("API key or user login required"), is used as is. Any other refusal is raised.
1477
+ */
1478
+ private exchangeLoginToken;
1452
1479
  private currentAccessToken;
1480
+ private authenticate;
1453
1481
  }
1454
1482
 
1455
1483
  /**
@@ -1635,9 +1663,15 @@ declare class InferAssetJob implements ResultStream {
1635
1663
  protected _inferRequest: InferRequest;
1636
1664
  protected _response: Promise<Prediction[]> | null;
1637
1665
  protected _runInfo: InferRunInfo | null;
1666
+ protected _milliComputeUnits: number | undefined;
1638
1667
  protected _controller: AbortController;
1639
1668
  protected _requestLogger: Logger;
1640
1669
  [Symbol.asyncIterator](): AsyncIterator<Prediction>;
1670
+ /**
1671
+ * The milli compute units vlm-server billed for the inference, 0 on a cache hit; undefined before it answered
1672
+ * or when it reported none.
1673
+ */
1674
+ get milliComputeUnits(): number | undefined;
1641
1675
  constructor(assetUrl: string, inferRequest: InferRequest, vlmFetch: VlmFetch, requestLogger: Logger);
1642
1676
  start(done: () => void): InferAssetJob;
1643
1677
  cancel(): void;
@@ -2111,8 +2145,13 @@ declare class PipelineJob implements ResultStream {
2111
2145
  readonly whenCancelled: Promise<void>;
2112
2146
  openSource: OpenSource | null;
2113
2147
  stopOnPipeline: (() => void) | null;
2148
+ private _milliComputeUnits;
2114
2149
  constructor(source: Message, media: Media[], live: boolean, params: ProcessParams, mediaStream?: MediaStream | undefined);
2115
2150
  get cancelled(): boolean;
2151
+ /** The source's milli compute units: the latest total of its `usage` messages and its `end`. */
2152
+ get milliComputeUnits(): number | undefined;
2153
+ /** Keep a usage's or an end's milliComputeUnits: each carries the source's total so far. */
2154
+ noteMilliComputeUnits(message: Message): void;
2116
2155
  command(id: string): Message;
2117
2156
  predicted(prediction: Prediction): void;
2118
2157
  finish(error?: Error | null): void;
@@ -64,6 +64,13 @@ interface Prediction extends StreamTime {
64
64
  source_url?: string;
65
65
  source_type?: string;
66
66
  system_timestamp?: number;
67
+ /**
68
+ * Compute units the frame's remote calls (a VLM ability's calls to vlm-server) used, in whole units. Absent
69
+ * when they used none, as for a local model.
70
+ */
71
+ compute_units?: number;
72
+ /** The same in milli compute units; from prediction version 4. */
73
+ milli_compute_units?: number;
67
74
  objects?: Array<PredictedObject>;
68
75
  classes?: Array<PredictedClass>;
69
76
  texts?: Array<PredictedText>;
@@ -1000,6 +1007,12 @@ interface AssetUuidSource {
1000
1007
  type Source = FileSource | StreamSource | PathSource | UrlSource | AssetUuidSource | MediaStreamSource;
1001
1008
  interface ResultStream extends AsyncIterable<Prediction> {
1002
1009
  cancel(): void;
1010
+ /**
1011
+ * What the stream's remote calls (a VLM ability's calls to vlm-server) used so far, in milli compute units:
1012
+ * a running total while it runs, its total once it ended. Undefined until reported, and where a backend does
1013
+ * not report it; a local model reports none. Informational: the called API bills its calls itself.
1014
+ */
1015
+ readonly milliComputeUnits?: number | undefined;
1003
1016
  }
1004
1017
  declare enum PopComponentType {
1005
1018
  FORWARD = "forward",
@@ -1325,9 +1338,15 @@ interface Options {
1325
1338
  */
1326
1339
  apiKey?: string | undefined;
1327
1340
  /**
1328
- * Bearer token accepted by the compute API.
1341
+ * Bearer token accepted by the compute API: a login token, which the SDK exchanges at the
1342
+ * compute API, or an already exchanged token, which it uses as is.
1329
1343
  */
1330
1344
  accessToken?: string | undefined;
1345
+ /**
1346
+ * The account a login token (`accessToken` or `oAuth2`) is exchanged for, sent to the compute API
1347
+ * as `account_uuid`. Needed when the user has no single account to pick.
1348
+ */
1349
+ accountId?: string | undefined;
1331
1350
  /**
1332
1351
  * Temporary authentication token for client side execution.
1333
1352
  */
@@ -1425,6 +1444,7 @@ declare class Endpoint<T extends Endpoint<T>> {
1425
1444
  protected _options: Options;
1426
1445
  private _token;
1427
1446
  private _expire_token_time;
1447
+ private _exchanging;
1428
1448
  protected _client: HttpClient | null;
1429
1449
  private _statusRetryHandlers;
1430
1450
  protected _limit: Semaphore;
@@ -1445,11 +1465,19 @@ declare class Endpoint<T extends Endpoint<T>> {
1445
1465
  connect(): Promise<T>;
1446
1466
  disconnect(wait?: boolean): Promise<void>;
1447
1467
  session(): Promise<Session>;
1448
- protected statusHandler401(_statusCode: number): void;
1468
+ protected statusHandler401(_statusCode: number, failedToken?: string | null): void;
1449
1469
  protected fetchWithRetry(fetcher: () => Promise<Response>, retries?: number): Promise<Response>;
1450
1470
  protected reconnect(): Promise<T>;
1451
1471
  protected authorizationHeader(): Promise<string>;
1472
+ private exchange;
1473
+ private storeExchangedToken;
1474
+ /**
1475
+ * A token that already carries an account, or that the compute API refuses as an exchanged
1476
+ * token ("API key or user login required"), is used as is. Any other refusal is raised.
1477
+ */
1478
+ private exchangeLoginToken;
1452
1479
  private currentAccessToken;
1480
+ private authenticate;
1453
1481
  }
1454
1482
 
1455
1483
  /**
@@ -1635,9 +1663,15 @@ declare class InferAssetJob implements ResultStream {
1635
1663
  protected _inferRequest: InferRequest;
1636
1664
  protected _response: Promise<Prediction[]> | null;
1637
1665
  protected _runInfo: InferRunInfo | null;
1666
+ protected _milliComputeUnits: number | undefined;
1638
1667
  protected _controller: AbortController;
1639
1668
  protected _requestLogger: Logger;
1640
1669
  [Symbol.asyncIterator](): AsyncIterator<Prediction>;
1670
+ /**
1671
+ * The milli compute units vlm-server billed for the inference, 0 on a cache hit; undefined before it answered
1672
+ * or when it reported none.
1673
+ */
1674
+ get milliComputeUnits(): number | undefined;
1641
1675
  constructor(assetUrl: string, inferRequest: InferRequest, vlmFetch: VlmFetch, requestLogger: Logger);
1642
1676
  start(done: () => void): InferAssetJob;
1643
1677
  cancel(): void;
@@ -2111,8 +2145,13 @@ declare class PipelineJob implements ResultStream {
2111
2145
  readonly whenCancelled: Promise<void>;
2112
2146
  openSource: OpenSource | null;
2113
2147
  stopOnPipeline: (() => void) | null;
2148
+ private _milliComputeUnits;
2114
2149
  constructor(source: Message, media: Media[], live: boolean, params: ProcessParams, mediaStream?: MediaStream | undefined);
2115
2150
  get cancelled(): boolean;
2151
+ /** The source's milli compute units: the latest total of its `usage` messages and its `end`. */
2152
+ get milliComputeUnits(): number | undefined;
2153
+ /** Keep a usage's or an end's milliComputeUnits: each carries the source's total so far. */
2154
+ noteMilliComputeUnits(message: Message): void;
2116
2155
  command(id: string): Message;
2117
2156
  predicted(prediction: Prediction): void;
2118
2157
  finish(error?: Error | null): void;
@@ -9044,14 +9044,25 @@ if ("document" in globalThis && "implementation" in globalThis.document) {
9044
9044
  }
9045
9045
 
9046
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
+ }
9047
9057
  var Endpoint = class {
9048
9058
  constructor(options) {
9059
+ this._exchanging = null;
9049
9060
  this._options = options;
9050
9061
  this._token = null;
9051
9062
  this._expire_token_time = null;
9052
9063
  this._client = null;
9053
9064
  this._statusRetryHandlers = /* @__PURE__ */ new Map();
9054
- this._statusRetryHandlers.set(401, (statusCode) => this.statusHandler401(statusCode));
9065
+ this._statusRetryHandlers.set(401, (statusCode, failedToken) => this.statusHandler401(statusCode, failedToken));
9055
9066
  this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
9056
9067
  this._state = "Idle" /* Idle */;
9057
9068
  this._message = null;
@@ -9067,7 +9078,7 @@ var Endpoint = class {
9067
9078
  }
9068
9079
  setStatusRetryHandlers(statusRetryHandlers = void 0) {
9069
9080
  this._statusRetryHandlers = new Map(statusRetryHandlers);
9070
- this._statusRetryHandlers.set(401, (statusCode) => this.statusHandler401(statusCode));
9081
+ this._statusRetryHandlers.set(401, (statusCode, failedToken) => this.statusHandler401(statusCode, failedToken));
9071
9082
  }
9072
9083
  eyepopUrl() {
9073
9084
  if (this._options.eyepopUrl) {
@@ -9164,12 +9175,15 @@ var Endpoint = class {
9164
9175
  validUntil: this._expire_token_time * 1e3
9165
9176
  };
9166
9177
  }
9167
- statusHandler401(_statusCode) {
9168
- this._token = null;
9169
- this._expire_token_time = null;
9178
+ statusHandler401(_statusCode, failedToken = this._token) {
9179
+ if (this._token === failedToken) {
9180
+ this._token = null;
9181
+ this._expire_token_time = null;
9182
+ }
9170
9183
  }
9171
9184
  async fetchWithRetry(fetcher, retries = 1) {
9172
9185
  while (true) {
9186
+ const sentToken = this._token ?? void 0;
9173
9187
  try {
9174
9188
  const response = await fetcher();
9175
9189
  if (response.ok) {
@@ -9177,7 +9191,7 @@ var Endpoint = class {
9177
9191
  } else {
9178
9192
  const handler = this._statusRetryHandlers.get(response.status);
9179
9193
  if (handler != null) {
9180
- handler(response.status);
9194
+ handler(response.status, sentToken);
9181
9195
  if (retries--) {
9182
9196
  this._requestLogger.info("received %d response, retry", response.status);
9183
9197
  } else {
@@ -9211,54 +9225,74 @@ var Endpoint = class {
9211
9225
  }
9212
9226
  return `Bearer ${await this.currentAccessToken()}`;
9213
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
+ }
9214
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() {
9215
9268
  const now = Date.now() / 1e3;
9216
- if (!this._token || this._expire_token_time < now) {
9217
- this.updateState("Authenticating" /* Authenticating */);
9218
- try {
9219
- const auth = resolveAuth(this._options);
9220
- if (!this._client) {
9221
- return Promise.reject("endpoint not connected");
9222
- } else if (auth?.isLocal !== void 0) {
9223
- this._token = "implicit";
9224
- this._expire_token_time = Number.MAX_VALUE;
9225
- } else if (auth?.session !== void 0) {
9226
- return Promise.reject("temporary access token expired");
9227
- } else if (auth?.oAuth2 !== void 0 && this._options.eyepopUrl) {
9228
- const session = await authenticateBrowserSession(auth.oAuth2, this._options);
9229
- this._token = session.accessToken;
9230
- this._expire_token_time = session.validUntil / 1e3;
9231
- } else if (accessTokenCredential(auth) !== void 0) {
9232
- this._token = accessTokenCredential(auth);
9233
- this._expire_token_time = Number.MAX_VALUE;
9234
- } else if (this._options.eyepopUrl) {
9235
- const credential = apiKeyCredential(auth);
9236
- if (credential === void 0) {
9237
- return Promise.reject("no valid auth option");
9238
- }
9239
- const headers = { Authorization: `Bearer ${credential}` };
9240
- const post_url = `${this.eyepopUrl()}/v1/auth/authenticate`;
9241
- const response = await this._client.fetch(post_url, {
9242
- method: "POST",
9243
- headers
9244
- });
9245
- if (response.status != 200) {
9246
- const message = await response.text();
9247
- return Promise.reject(`Unexpected status ${response.status}: ${message}`);
9248
- }
9249
- const data = await response.json();
9250
- const token = data;
9251
- this._token = token.access_token;
9252
- this._expire_token_time = now + token.expires_in - 60;
9253
- } 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) {
9254
9287
  return Promise.reject("no valid auth option");
9255
9288
  }
9256
- } finally {
9257
- this.updateState();
9289
+ await this.storeExchangedToken(await this.exchange(this._client, credential), now);
9290
+ } else {
9291
+ return Promise.reject("no valid auth option");
9258
9292
  }
9293
+ } finally {
9294
+ this.updateState();
9259
9295
  }
9260
- this._logger.debug("using access token, valid for at least %d seconds", this._expire_token_time - now);
9261
- return this._token;
9262
9296
  }
9263
9297
  };
9264
9298
 
@@ -11808,10 +11842,22 @@ var PipelineJob = class {
11808
11842
  this.openSource = null;
11809
11843
  // Stops the job's source on the pipeline, once it was sent.
11810
11844
  this.stopOnPipeline = null;
11845
+ this._milliComputeUnits = void 0;
11811
11846
  }
11812
11847
  get cancelled() {
11813
11848
  return this._cancelled;
11814
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
+ }
11815
11861
  command(id) {
11816
11862
  const command = { type: "setSource", id, ...this.source };
11817
11863
  if (this.params.componentParams !== void 0) {
@@ -12389,7 +12435,11 @@ var PipelineBackend = class {
12389
12435
  case "prediction":
12390
12436
  job.predicted(message["prediction"]);
12391
12437
  break;
12438
+ case "usage":
12439
+ job.noteMilliComputeUnits(message);
12440
+ break;
12392
12441
  case "end":
12442
+ job.noteMilliComputeUnits(message);
12393
12443
  if (message["reason"] === "eos") {
12394
12444
  return null;
12395
12445
  }
@@ -12953,6 +13003,7 @@ var InferAssetJob = class {
12953
13003
  constructor(assetUrl, inferRequest, vlmFetch, requestLogger) {
12954
13004
  this._response = null;
12955
13005
  this._runInfo = null;
13006
+ this._milliComputeUnits = void 0;
12956
13007
  this._controller = new AbortController();
12957
13008
  this._vlmFetch = vlmFetch;
12958
13009
  this._assetUrl = assetUrl;
@@ -12989,6 +13040,13 @@ var InferAssetJob = class {
12989
13040
  }
12990
13041
  return iterator();
12991
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
+ }
12992
13050
  start(done) {
12993
13051
  this._response = new Promise(async (resolve, reject) => {
12994
13052
  try {
@@ -13013,6 +13071,8 @@ var InferAssetJob = class {
13013
13071
  const runInfo = response["run_info"];
13014
13072
  if (predictions) {
13015
13073
  this._runInfo = runInfo;
13074
+ const used = response["milli_compute_units"] ?? response["compute_units"];
13075
+ this._milliComputeUnits = typeof used === "number" ? used : void 0;
13016
13076
  resolve(predictions);
13017
13077
  break;
13018
13078
  } else {
@@ -14536,17 +14596,17 @@ var EyePop;
14536
14596
  if (typeof auth.oAuth2 === "boolean") {
14537
14597
  const oAuth2 = opts.eyepopUrl && opts.eyepopUrl.match(/https:(.*).staging.eyepop.xyz/i) ? {
14538
14598
  oAuth2: {
14539
- domain: "dev-eyepop.us.auth0.com",
14599
+ domain: "auth0.eyepop.xyz",
14540
14600
  clientId: "jktx3YO2UnbkNPvr05PQWf26t1kNTJyg",
14541
14601
  audience: "https://dev-app.eyepop.ai",
14542
- scope: "admin:clouds access:inference-api access:datasets"
14602
+ scope: "access:inference-api access:datasets"
14543
14603
  }
14544
14604
  } : {
14545
14605
  oAuth2: {
14546
- domain: "eyepop.us.auth0.com",
14606
+ domain: "auth0.eyepop.ai",
14547
14607
  clientId: "Lb9ubA9Hf3jlaqWLUx8XgA0zvotgViCl",
14548
14608
  audience: "https://api.eyepop.ai",
14549
- scope: "admin:clouds access:inference-api access:datasets"
14609
+ scope: "access:inference-api access:datasets"
14550
14610
  }
14551
14611
  };
14552
14612
  if (opts.oAuth2 !== void 0) {