@openhoo/hoopilot 0.7.4 → 0.7.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/index.d.cts CHANGED
@@ -126,10 +126,16 @@ interface RequestObservation {
126
126
  /** One quota category (chat, completions, or premium_interactions/credits). */
127
127
  interface CopilotQuota {
128
128
  entitlement?: number;
129
+ hasQuota?: boolean;
129
130
  overageCount?: number;
131
+ overageEntitlement?: number;
130
132
  overagePermitted?: boolean;
131
133
  percentRemaining?: number;
134
+ quotaId?: string;
135
+ quotaResetAt?: string;
132
136
  remaining?: number;
137
+ timestampUtc?: string;
138
+ tokenBasedBilling?: boolean;
133
139
  unlimited?: boolean;
134
140
  used?: number;
135
141
  }
package/dist/index.d.ts CHANGED
@@ -126,10 +126,16 @@ interface RequestObservation {
126
126
  /** One quota category (chat, completions, or premium_interactions/credits). */
127
127
  interface CopilotQuota {
128
128
  entitlement?: number;
129
+ hasQuota?: boolean;
129
130
  overageCount?: number;
131
+ overageEntitlement?: number;
130
132
  overagePermitted?: boolean;
131
133
  percentRemaining?: number;
134
+ quotaId?: string;
135
+ quotaResetAt?: string;
132
136
  remaining?: number;
137
+ timestampUtc?: string;
138
+ tokenBasedBilling?: boolean;
133
139
  unlimited?: boolean;
134
140
  used?: number;
135
141
  }
package/dist/index.js CHANGED
@@ -329,22 +329,31 @@ function normalizeCopilotUsage(body) {
329
329
  }
330
330
  function normalizeQuotaDetail(detail) {
331
331
  const entitlement = numberOrUndefined(detail.entitlement);
332
+ const overageCount = numberOrUndefined(detail.overage_count);
332
333
  const remaining = numberOrUndefined(detail.remaining) ?? numberOrUndefined(detail.quota_remaining);
333
334
  return removeUndefinedQuota({
334
335
  entitlement,
335
- overageCount: numberOrUndefined(detail.overage_count),
336
+ hasQuota: typeof detail.has_quota === "boolean" ? detail.has_quota : void 0,
337
+ overageCount,
338
+ overageEntitlement: numberOrUndefined(detail.overage_entitlement),
336
339
  overagePermitted: typeof detail.overage_permitted === "boolean" ? detail.overage_permitted : void 0,
337
340
  percentRemaining: numberOrUndefined(detail.percent_remaining),
341
+ quotaId: stringOrUndefined(detail.quota_id),
342
+ quotaResetAt: stringOrUndefined(detail.quota_reset_at),
338
343
  remaining,
344
+ timestampUtc: stringOrUndefined(detail.timestamp_utc),
345
+ tokenBasedBilling: typeof detail.token_based_billing === "boolean" ? detail.token_based_billing : void 0,
339
346
  unlimited: typeof detail.unlimited === "boolean" ? detail.unlimited : void 0,
340
- used: usedFrom(entitlement, remaining)
347
+ used: usedFrom(entitlement, remaining, overageCount)
341
348
  });
342
349
  }
343
- function usedFrom(entitlement, remaining) {
350
+ function usedFrom(entitlement, remaining, overageCount) {
344
351
  if (entitlement === void 0 || remaining === void 0) {
345
352
  return void 0;
346
353
  }
347
- return Math.max(0, entitlement - remaining);
354
+ const base = entitlement - remaining;
355
+ const overage = remaining === 0 ? overageCount ?? 0 : 0;
356
+ return Math.max(0, base + overage);
348
357
  }
349
358
  function numberOrUndefined(value) {
350
359
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
@@ -1642,11 +1651,43 @@ var MetricsRegistry = class {
1642
1651
  gauge("remaining", "Remaining quota for the Copilot category.", (q) => q.remaining);
1643
1652
  gauge("entitlement", "Quota entitlement for the Copilot category.", (q) => q.entitlement);
1644
1653
  gauge("used", "Used quota (entitlement minus remaining) for the category.", (q) => q.used);
1654
+ gauge("overage_count", "Overage count for the Copilot category.", (q) => q.overageCount);
1655
+ gauge(
1656
+ "overage_entitlement",
1657
+ "Overage entitlement for the Copilot category.",
1658
+ (q) => q.overageEntitlement
1659
+ );
1645
1660
  gauge(
1646
1661
  "percent_remaining",
1647
1662
  "Percent of quota remaining for the Copilot category.",
1648
1663
  (q) => q.percentRemaining
1649
1664
  );
1665
+ booleanGauge(
1666
+ "unlimited",
1667
+ "Whether the Copilot quota category is unlimited.",
1668
+ (q) => q.unlimited
1669
+ );
1670
+ booleanGauge(
1671
+ "overage_permitted",
1672
+ "Whether overage is permitted for the Copilot category.",
1673
+ (q) => q.overagePermitted
1674
+ );
1675
+ booleanGauge("has_quota", "Whether the Copilot quota category has a quota.", (q) => q.hasQuota);
1676
+ booleanGauge(
1677
+ "token_based_billing",
1678
+ "Whether the Copilot quota category uses token-based billing.",
1679
+ (q) => q.tokenBasedBilling
1680
+ );
1681
+ dateGauge(
1682
+ "category_reset_timestamp_seconds",
1683
+ "Unix epoch of the Copilot category-specific quota reset.",
1684
+ (q) => q.quotaResetAt
1685
+ );
1686
+ dateGauge(
1687
+ "category_snapshot_timestamp_seconds",
1688
+ "Unix epoch of the Copilot category quota snapshot.",
1689
+ (q) => q.timestampUtc
1690
+ );
1650
1691
  const resetMs = usage.quotaResetDate ? Date.parse(usage.quotaResetDate) : Number.NaN;
1651
1692
  if (Number.isFinite(resetMs)) {
1652
1693
  lines.push(
@@ -1665,6 +1706,30 @@ var MetricsRegistry = class {
1665
1706
  })} 1`
1666
1707
  );
1667
1708
  }
1709
+ function booleanGauge(suffix, help, pick) {
1710
+ const present = categories.filter(([, quota]) => pick(quota) !== void 0);
1711
+ if (present.length === 0) {
1712
+ return;
1713
+ }
1714
+ lines.push(`# HELP hoopilot_copilot_quota_${suffix} ${help}`);
1715
+ lines.push(`# TYPE hoopilot_copilot_quota_${suffix} gauge`);
1716
+ for (const [category, quota] of present) {
1717
+ lines.push(
1718
+ `hoopilot_copilot_quota_${suffix}${labels({ category })} ${pick(quota) ? 1 : 0}`
1719
+ );
1720
+ }
1721
+ }
1722
+ function dateGauge(suffix, help, pick) {
1723
+ const present = categories.map(([category, quota]) => [category, Date.parse(pick(quota) ?? "")]).filter(([, timestamp]) => Number.isFinite(timestamp));
1724
+ if (present.length === 0) {
1725
+ return;
1726
+ }
1727
+ lines.push(`# HELP hoopilot_copilot_quota_${suffix} ${help}`);
1728
+ lines.push(`# TYPE hoopilot_copilot_quota_${suffix} gauge`);
1729
+ for (const [category, timestamp] of present) {
1730
+ lines.push(`hoopilot_copilot_quota_${suffix}${labels({ category })} ${timestamp / 1e3}`);
1731
+ }
1732
+ }
1668
1733
  }
1669
1734
  };
1670
1735
  function observeResponseUsage(response, fallbackModel, onUsage, signal) {
@@ -2362,8 +2427,8 @@ function metricsResponse(metrics) {
2362
2427
  });
2363
2428
  }
2364
2429
  async function handleUsage(metrics, readUsage, signal) {
2365
- const proxy = metrics.snapshot();
2366
2430
  const { copilot, error } = await readUsage(signal);
2431
+ const proxy = metrics.snapshot();
2367
2432
  const body = { copilot: copilot ?? null, object: "usage", proxy };
2368
2433
  if (error) {
2369
2434
  body.copilot_error = error;
@@ -2388,10 +2453,10 @@ function createUsageReader(client, metrics, now = Date.now, ttlMs = USAGE_CACHE_
2388
2453
  metrics.recordCopilotQuota(value);
2389
2454
  return { copilot: value };
2390
2455
  } catch (error) {
2391
- metrics.recordUpstream(usagePath, false);
2392
2456
  if (error instanceof CopilotAuthError) {
2393
2457
  return { error: error.message };
2394
2458
  }
2459
+ metrics.recordUpstream(usagePath, false);
2395
2460
  return { error: errorMessage(error) };
2396
2461
  }
2397
2462
  };