@mars-sea/dsh-commandcode-provider 0.14.1 → 0.14.3

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/lib/index.js CHANGED
@@ -38,10 +38,30 @@ function peakHour(at, windows) {
38
38
  * today, so the combination becoming real is visible before it reaches a user.
39
39
  * This ordering is a latent choice, not a verified upstream rule.
40
40
  */
41
- function requestRates(price, at, contextTokens, table) {
41
+ function requestRates(price, at, contextTokens, table, peakHourCache) {
42
42
  const tier = price.contextTiers?.find((tier) => tier.maxContext === void 0 || contextTokens <= tier.maxContext);
43
43
  if (tier !== void 0) return tier;
44
- return price.peak !== void 0 && at !== null && peakHour(at, table.peakHours) ? price.peak : price;
44
+ if (price.peak === void 0 || at === null) return price;
45
+ let peak = peakHourCache?.get(at);
46
+ if (peak === void 0) {
47
+ peak = peakHour(at, table.peakHours);
48
+ peakHourCache?.set(at, peak);
49
+ }
50
+ return peak ? price.peak : price;
51
+ }
52
+ /**
53
+ * Price one request's token buckets against an already-resolved price row.
54
+ *
55
+ * Split out of the report's per-model lookup so a caller that prices the same
56
+ * candidate model against every fact — the analytics scenario matrix — resolves
57
+ * the price row once per model instead of once per (fact, model) pair. The
58
+ * arithmetic lives here alone so the two callers cannot drift.
59
+ */
60
+ function requestCost(tokens, price, at, table, peakHourCache) {
61
+ if (price.free === true) return 0;
62
+ const r = requestRates(price, at, tokens.uncachedInputTokens + tokens.cacheReadTokens + tokens.cacheWriteTokens, table, peakHourCache);
63
+ if (tokens.cacheWriteTokens > 0 && r.cacheWriteCost === void 0) return void 0;
64
+ return (tokens.uncachedInputTokens * r.inputCost + tokens.outputTokens * r.outputCost + tokens.cacheReadTokens * r.cacheReadCost + tokens.cacheWriteTokens * (r.cacheWriteCost ?? 0)) / 1e6;
45
65
  }
46
66
  /** Cache version and wire guard: changing bands/rates must refold historical groups. */
47
67
  function pricingKey(table) {
@@ -3257,6 +3277,7 @@ function foldRequests(session, log) {
3257
3277
  }
3258
3278
  return facts;
3259
3279
  }
3280
+ /** The zeroed accumulator `aggregate` adds into. Internal: nothing outside this module reads it. */
3260
3281
  const emptyMetrics = () => ({
3261
3282
  sessions: 0,
3262
3283
  requests: 0,
@@ -3277,17 +3298,13 @@ const emptyMetrics = () => ({
3277
3298
  decodeMs: 0,
3278
3299
  decodeTokens: 0
3279
3300
  });
3280
- function priceRequest(f, id, table, prices) {
3301
+ function priceRequest(f, id, table, prices, peakHourCache) {
3281
3302
  if (!f.tokens) return void 0;
3282
3303
  const price = prices.get(id);
3283
3304
  if (!price) return void 0;
3284
- if (price.free === true) return 0;
3285
- const context = f.tokens.uncachedInputTokens + f.tokens.cacheReadTokens + f.tokens.cacheWriteTokens;
3286
- const r = requestRates(price, f.at, context, table);
3287
- if (f.tokens.cacheWriteTokens > 0 && r.cacheWriteCost === void 0) return void 0;
3288
- return (f.tokens.uncachedInputTokens * r.inputCost + f.tokens.outputTokens * r.outputCost + f.tokens.cacheReadTokens * r.cacheReadCost + f.tokens.cacheWriteTokens * (r.cacheWriteCost ?? 0)) / 1e6;
3305
+ return requestCost(f.tokens, price, f.at, table, peakHourCache);
3289
3306
  }
3290
- function aggregate(facts, table, prices) {
3307
+ function aggregate(facts, table, prices, peakHourCache) {
3291
3308
  const m = emptyMetrics();
3292
3309
  m.sessions = new Set(facts.map((f) => f.session)).size;
3293
3310
  for (const f of facts) {
@@ -3298,7 +3315,7 @@ function aggregate(facts, table, prices) {
3298
3315
  m.usageSamples++;
3299
3316
  for (const k of COST_TOKEN_KEYS) m.tokens[k] += f.tokens[k];
3300
3317
  }
3301
- const cost = priceRequest(f, f.model, table, prices);
3318
+ const cost = priceRequest(f, f.model, table, prices, peakHourCache);
3302
3319
  if (cost !== void 0) {
3303
3320
  m.pricedRequests++;
3304
3321
  m.cost += cost;
@@ -3323,13 +3340,21 @@ function buildAnalytics(facts, query, table, catalog, now = Date.now()) {
3323
3340
  const selected = query.mode === "range" ? facts.filter((f) => f.at >= query.from && f.at < query.to) : facts;
3324
3341
  const prices = /* @__PURE__ */ new Map();
3325
3342
  for (const p of table.models) for (const id of [p.id, p.slug]) if (!prices.has(id)) prices.set(id, p);
3326
- const totals = aggregate(selected, table, prices);
3327
- const models = [...new Set(selected.map((f) => f.model))].map((id) => ({
3343
+ const peakHourCache = /* @__PURE__ */ new Map();
3344
+ const totals = aggregate(selected, table, prices, peakHourCache);
3345
+ const byModel = /* @__PURE__ */ new Map();
3346
+ for (const f of selected) {
3347
+ const bucket = byModel.get(f.model);
3348
+ if (bucket === void 0) byModel.set(f.model, [f]);
3349
+ else bucket.push(f);
3350
+ }
3351
+ const models = [...byModel].map(([id, bucket]) => ({
3328
3352
  id,
3329
- ...aggregate(selected.filter((f) => f.model === id), table, prices)
3353
+ ...aggregate(bucket, table, prices, peakHourCache)
3330
3354
  })).sort((a, b) => b.cost - a.cost || a.id.localeCompare(b.id));
3331
3355
  const scenarios = catalog.map((model) => {
3332
3356
  let cost = 0, pricedRequests = 0, excludedRequests = 0, unknownRequests = 0;
3357
+ const price = prices.get(model.id);
3333
3358
  for (const f of selected) {
3334
3359
  if (!f.tokens) {
3335
3360
  unknownRequests++;
@@ -3340,7 +3365,7 @@ function buildAnalytics(facts, query, table, catalog, now = Date.now()) {
3340
3365
  excludedRequests++;
3341
3366
  continue;
3342
3367
  }
3343
- const priced = priceRequest(f, model.id, table, prices);
3368
+ const priced = price === void 0 ? void 0 : requestCost(f.tokens, price, f.at, table, peakHourCache);
3344
3369
  if (priced === void 0) unknownRequests++;
3345
3370
  else {
3346
3371
  cost += priced;
@@ -6307,9 +6332,16 @@ const DEFAULT_API_BASE = "https://api.commandcode.ai";
6307
6332
  * under the historical name so existing importers are unaffected.
6308
6333
  */
6309
6334
  const DEFAULT_GENERATE_MAX_TOKENS = MAX_OUTPUT_TOKEN_LIMIT;
6335
+ /** Catalog fetch budget. Internal: both call sites are in this module. */
6310
6336
  const MODELS_TIMEOUT_MS = 1e4;
6311
6337
  /** How long the picker's plan-filter billing facts stay cached before refetching. */
6312
6338
  const BILLING_ACCESS_TTL_MS = 3e5;
6339
+ /**
6340
+ * How long a learned CLI-only protocol preference stays cached per account.
6341
+ * After this TTL a request may probe Provider API again, so an upgraded Go
6342
+ * account can recover without a restart.
6343
+ */
6344
+ const PROTOCOL_CACHE_TTL_MS = 9e5;
6313
6345
  /** The entry-plan tier weight (`individual-go` in KNOWN_SUBSCRIPTION_PLANS). */
6314
6346
  const GO_TIER_WEIGHT = 0;
6315
6347
  /** Hard cap on account rotations within one request (one attempt per distinct key). */
@@ -6960,6 +6992,31 @@ function priceRequestImage(block, model, imageCapable) {
6960
6992
  };
6961
6993
  }
6962
6994
  /**
6995
+ * Base64 payloads keyed by the cached request version itself.
6996
+ *
6997
+ * `readImageRequest()` returns one codec object per (attachment, encoder
6998
+ * target) and `requestImages()` reuses that same object for every occurrence in
6999
+ * one generation, but a history that carries an image also replays it in every
7000
+ * later message. Encoding per occurrence therefore re-base64s the same
7001
+ * multi-megabyte buffer once per replay — the single most expensive
7002
+ * per-occurrence step of an image request. Keying on the object makes an
7003
+ * occurrence a map hit, and the WeakMap ties the copy's lifetime to the
7004
+ * harness's own version cache rather than to this plugin.
7005
+ */
7006
+ const encodedRequestImages = /* @__PURE__ */ new WeakMap();
7007
+ function encodedRequestImage(version) {
7008
+ let encoded = encodedRequestImages.get(version);
7009
+ if (encoded === void 0) {
7010
+ const base64 = Buffer.from(version.data).toString("base64");
7011
+ encoded = {
7012
+ base64,
7013
+ dataUrl: `data:${version.mediaType};base64,${base64}`
7014
+ };
7015
+ encodedRequestImages.set(version, encoded);
7016
+ }
7017
+ return encoded;
7018
+ }
7019
+ /**
6963
7020
  * Convert one image reference to the final `/alpha/generate` wire format.
6964
7021
  * The official CLI accepts a base64 source internally, then toWireMessages()
6965
7022
  * sends a data URL plus mimeType. Use the request version's media type because
@@ -6969,7 +7026,7 @@ async function imageToCommandCode(ref, readImage) {
6969
7026
  const version = await readImage(ref);
6970
7027
  return {
6971
7028
  type: "image",
6972
- image: `data:${version.mediaType};base64,${Buffer.from(version.data).toString("base64")}`,
7029
+ image: encodedRequestImage(version).dataUrl,
6973
7030
  mimeType: version.mediaType
6974
7031
  };
6975
7032
  }
@@ -7102,10 +7159,9 @@ async function messagesToCC(messages, readImage) {
7102
7159
  * `{ type: 'image_url', image_url: { url: 'data:...;base64,...' } }`.
7103
7160
  */
7104
7161
  async function imageToOpenAI(ref, readImage) {
7105
- const version = await readImage(ref);
7106
7162
  return {
7107
7163
  type: "image_url",
7108
- image_url: { url: `data:${version.mediaType};base64,${Buffer.from(version.data).toString("base64")}` }
7164
+ image_url: { url: encodedRequestImage(await readImage(ref)).dataUrl }
7109
7165
  };
7110
7166
  }
7111
7167
  /**
@@ -7172,7 +7228,7 @@ async function imageToMessages(ref, readImage) {
7172
7228
  source: {
7173
7229
  type: "base64",
7174
7230
  media_type: version.mediaType,
7175
- data: Buffer.from(version.data).toString("base64")
7231
+ data: encodedRequestImage(version).base64
7176
7232
  }
7177
7233
  };
7178
7234
  }
@@ -8174,7 +8230,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
8174
8230
  /** Cached protocol decision for a key, or undefined when expired/unknown. */
8175
8231
  cachedProtocol(apiBase, apiKey) {
8176
8232
  const hit = this.protocolCache.get(this.accountCacheKey(apiBase, apiKey));
8177
- if (hit === void 0 || Date.now() - hit.at >= 9e5) return void 0;
8233
+ if (hit === void 0 || Date.now() - hit.at >= PROTOCOL_CACHE_TTL_MS) return void 0;
8178
8234
  return hit.protocol;
8179
8235
  }
8180
8236
  rememberProtocol(apiBase, apiKey, protocol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mars-sea/dsh-commandcode-provider",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
4
4
  "description": "Unofficial DeepSeek Harness LLM provider plugin for Command Code, ported from pi-commandcode-provider (MIT). Registers the 'commandcode' provider route with a Models-page card and live model catalog.",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "files": [
41
41
  "lib",
42
+ "!lib/**/*.map",
42
43
  "assets",
43
44
  "icon.svg",
44
45
  "locale",