pi-cache-optimizer 2.6.14 → 2.6.16

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.
Files changed (2) hide show
  1. package/index.ts +85 -25
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -1992,10 +1992,16 @@ function describeMissingOpenAICompatibleProxyCompat(model: PiModel): string[] {
1992
1992
  if (!isOpenAICompatibleProxyApi(model.api)) return missing;
1993
1993
  if (isOfficialOpenAIBaseUrl(model)) return missing;
1994
1994
 
1995
- if (compat.sendSessionAffinityHeaders !== true) {
1995
+ if (compat.sendSessionAffinityHeaders === undefined) {
1996
1996
  missing.push("sendSessionAffinityHeaders");
1997
1997
  }
1998
1998
 
1999
+ // Explicit `sendSessionAffinityHeaders: false` is a valid safe opt-out for
2000
+ // proxies/CDNs/WAFs that block Pi's custom affinity headers with HTTP 403.
2001
+ // Treat only a missing/undefined value as missing compat; do not mark an
2002
+ // intentional false override as ⚠️ compat or let /cache-optimizer fix turn it
2003
+ // back to true.
2004
+ //
1999
2005
  // NOTE: supportsLongCacheRetention is intentionally NOT checked here.
2000
2006
  // Per spec, it is optional/risky advisory text only and must NOT trigger
2001
2007
  // the ⚠️ compat marker. The before_provider_request hook proactively
@@ -3937,6 +3943,22 @@ function isSessionAffinity403Applicable(model: PiModel): boolean {
3937
3943
  return getCompat(model).sendSessionAffinityHeaders === true;
3938
3944
  }
3939
3945
 
3946
+ /**
3947
+ * Whether an OpenAI SDK header/User-Agent 403 diagnostic applies.
3948
+ *
3949
+ * Some third-party OpenAI-compatible proxies/CDNs/WAFs allow a plain curl
3950
+ * request but block the OpenAI JS SDK's default request fingerprint, especially
3951
+ * its `User-Agent: OpenAI/JS ...` and `X-Stainless-*` headers. This is distinct
3952
+ * from session-affinity header blocking: it applies when session-affinity
3953
+ * headers are NOT enabled but the model still uses a non-official
3954
+ * OpenAI-compatible proxy transport.
3955
+ */
3956
+ function isOpenAISdkHeader403Applicable(model: PiModel): boolean {
3957
+ if (!isOpenAICompatibleProxyApi(model.api)) return false;
3958
+ if (isOfficialOpenAIBaseUrl(model)) return false;
3959
+ return getCompat(model).sendSessionAffinityHeaders !== true;
3960
+ }
3961
+
3940
3962
  /**
3941
3963
  * Detect router / channel profiles from a PiModel and return diagnostic notes.
3942
3964
  *
@@ -4134,7 +4156,7 @@ function getCompatCheckNotApplicableLines(model: PiModel): string[] {
4134
4156
  return ["ℹ️ Compat check not applicable for this model."];
4135
4157
  }
4136
4158
 
4137
- function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean; sessionAffinity403?: boolean } = {}): string {
4159
+ function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean; sessionAffinity403?: boolean; openAISdkHeader403?: boolean } = {}): string {
4138
4160
  const lines: string[] = [];
4139
4161
  lines.push(`Provider: ${model.provider}`);
4140
4162
  lines.push(`Model: ${model.id}`);
@@ -4210,6 +4232,20 @@ function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400
4210
4232
  lines.push(" (session_id, x-client-request-id, x-session-affinity) and return 403. If you");
4211
4233
  lines.push(" see 403 errors, run /cache-optimizer fix to set sendSessionAffinityHeaders: false.");
4212
4234
  }
4235
+ } else if (isOpenAISdkHeader403Applicable(model)) {
4236
+ lines.push("");
4237
+ if (options.openAISdkHeader403) {
4238
+ lines.push("⚠️ A 403 response was observed while sendSessionAffinityHeaders is not enabled.");
4239
+ lines.push(" The proxy/CDN may be blocking the OpenAI JS SDK request fingerprint");
4240
+ lines.push(" (for example User-Agent: OpenAI/JS ... or X-Stainless-* headers). This");
4241
+ lines.push(" is provider/WAF-specific; /cache-optimizer fix will not auto-write headers.");
4242
+ lines.push(` Manual workaround: add a provider-level headers.User-Agent override in ${getModelsJsonDisplayPath()}`);
4243
+ lines.push(" only after testing the value with the affected provider.");
4244
+ } else {
4245
+ lines.push("ℹ️ If 403 persists after disabling sendSessionAffinityHeaders, some CDNs/WAFs");
4246
+ lines.push(" may block the OpenAI JS SDK User-Agent / X-Stainless-* headers. Test the");
4247
+ lines.push(" provider manually before adding a provider-level headers.User-Agent override.");
4248
+ }
4213
4249
  }
4214
4250
 
4215
4251
  // ── Router/channel diagnostics ──
@@ -4402,12 +4438,19 @@ function buildCompatDiagnosis(model: PiModel): string | undefined {
4402
4438
  if (isPromptCacheRetention400Applicable(model)) {
4403
4439
  lines.push(getPromptCacheRetentionUnsupportedHint());
4404
4440
  }
4405
- // Advisory for 403 session-affinity header blocking (only when enabled).
4441
+ // Advisory for 403 session-affinity header blocking (only when enabled),
4442
+ // or OpenAI SDK header/User-Agent blocking after session affinity is disabled.
4406
4443
  if (isSessionAffinity403Applicable(model)) {
4407
4444
  lines.push(
4408
4445
  "ℹ️ Session affinity headers are enabled. If you see 403 \"blocked\" errors,",
4409
4446
  " the proxy/CDN may be blocking Pi's custom headers. Set sendSessionAffinityHeaders: false.",
4410
4447
  );
4448
+ } else if (isOpenAISdkHeader403Applicable(model)) {
4449
+ lines.push(
4450
+ "ℹ️ If 403 persists with sendSessionAffinityHeaders disabled, the proxy/CDN may",
4451
+ " be blocking the OpenAI JS SDK User-Agent / X-Stainless-* headers. Test a",
4452
+ " provider-level headers.User-Agent override manually; /fix does not auto-write it.",
4453
+ );
4411
4454
  }
4412
4455
  appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);
4413
4456
  } else {
@@ -5686,6 +5729,7 @@ export const __internals_for_tests = {
5686
5729
  isCompatCheckApplicable,
5687
5730
  isPromptCacheRetention400Applicable,
5688
5731
  isSessionAffinity403Applicable,
5732
+ isOpenAISdkHeader403Applicable,
5689
5733
  hasPromptCacheRetentionUnsupportedSignal,
5690
5734
  // Non-GPT OpenAI-compatible model detection
5691
5735
  isKimiLikeModel,
@@ -5904,6 +5948,8 @@ export default function (pi: ExtensionAPI) {
5904
5948
  const warnedPromptCacheRetention400Models = new Set<string>();
5905
5949
  const sendSessionAffinityHeaders403Models = new Set<string>();
5906
5950
  const warnedSendSessionAffinityHeaders403Models = new Set<string>();
5951
+ const openAISdkHeader403Models = new Set<string>();
5952
+ const warnedOpenAISdkHeader403Models = new Set<string>();
5907
5953
  let cacheStatsByModel: Record<string, CacheStats> = {};
5908
5954
  let cacheStatsTotalsByModel: Record<string, CacheStats> = {};
5909
5955
  let cacheStatsLegacyFamily: Partial<Record<CacheProviderId, CacheStats>> = emptyAllCacheStats();
@@ -6570,28 +6616,42 @@ export default function (pi: ExtensionAPI) {
6570
6616
  return;
6571
6617
  }
6572
6618
 
6573
- // ── 403: sendSessionAffinityHeaders blocked by CDN/WAF ──
6574
- //
6575
- // Pi's openai-completions/responses adapters send three custom HTTP
6576
- // headers (session_id, x-client-request-id, x-session-affinity) when
6577
- // sendSessionAffinityHeaders is enabled. Some third-party proxies /
6578
- // CDNs / WAFs block these custom headers and return 403. We record a
6579
- // one-time per-session notification and surface it in doctor/fix so the
6580
- // user can disable the flag for the blocked model.
6619
+ // ── 403: proxy/CDN/WAF blocking ──
6581
6620
  if (event.status === 403) {
6582
- if (!isSessionAffinity403Applicable(model)) return;
6583
-
6584
6621
  const key403 = modelKey(model);
6585
- sendSessionAffinityHeaders403Models.add(key403);
6586
- if (warnedSendSessionAffinityHeaders403Models.has(key403)) return;
6587
- warnedSendSessionAffinityHeaders403Models.add(key403);
6588
- ctx.ui.notify(
6589
- `⚠️ ${LOG_PREFIX}: ${key403} returned HTTP 403 while sendSessionAffinityHeaders is enabled. ` +
6590
- `The proxy/CDN may be blocking Pi's custom session-affinity headers (session_id, x-client-request-id, x-session-affinity). ` +
6591
- `Run /cache-optimizer doctor for details and /cache-optimizer fix to set sendSessionAffinityHeaders: false.`,
6592
- "warning",
6593
- );
6594
- return;
6622
+
6623
+ // Case 1: Pi's custom session-affinity headers are enabled. Offer the
6624
+ // existing safe compat fix (`sendSessionAffinityHeaders: false`).
6625
+ if (isSessionAffinity403Applicable(model)) {
6626
+ sendSessionAffinityHeaders403Models.add(key403);
6627
+ if (warnedSendSessionAffinityHeaders403Models.has(key403)) return;
6628
+ warnedSendSessionAffinityHeaders403Models.add(key403);
6629
+ ctx.ui.notify(
6630
+ `⚠️ ${LOG_PREFIX}: ${key403} returned HTTP 403 while sendSessionAffinityHeaders is enabled. ` +
6631
+ `The proxy/CDN may be blocking Pi's custom session-affinity headers (session_id, x-client-request-id, x-session-affinity). ` +
6632
+ `Run /cache-optimizer doctor for details and /cache-optimizer fix to set sendSessionAffinityHeaders: false.`,
6633
+ "warning",
6634
+ );
6635
+ return;
6636
+ }
6637
+
6638
+ // Case 2: session-affinity headers are already absent/disabled, but the
6639
+ // provider still returns 403. Some CDNs/WAFs block the OpenAI JS SDK
6640
+ // default request fingerprint (User-Agent: OpenAI/JS ... or
6641
+ // X-Stainless-* headers). This is provider-specific; do NOT auto-fix by
6642
+ // writing a User-Agent because the right value depends on the endpoint.
6643
+ if (isOpenAISdkHeader403Applicable(model)) {
6644
+ openAISdkHeader403Models.add(key403);
6645
+ if (warnedOpenAISdkHeader403Models.has(key403)) return;
6646
+ warnedOpenAISdkHeader403Models.add(key403);
6647
+ ctx.ui.notify(
6648
+ `⚠️ ${LOG_PREFIX}: ${key403} returned HTTP 403 even though sendSessionAffinityHeaders is not enabled. ` +
6649
+ `The proxy/CDN may be blocking the OpenAI JS SDK User-Agent / X-Stainless-* headers. ` +
6650
+ `Run /cache-optimizer doctor for manual diagnostic guidance; /cache-optimizer fix will not auto-write User-Agent headers.`,
6651
+ "warning",
6652
+ );
6653
+ return;
6654
+ }
6595
6655
  }
6596
6656
  });
6597
6657
 
@@ -6714,7 +6774,7 @@ export default function (pi: ExtensionAPI) {
6714
6774
  cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
6715
6775
  return;
6716
6776
  }
6717
- const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)) });
6777
+ const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)), openAISdkHeader403: openAISdkHeader403Models.has(modelKey(model)) });
6718
6778
  const adapter = selectAdapterForModel(model);
6719
6779
  const sk = model ? sessionModelKey(model) : undefined;
6720
6780
  const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;
@@ -7135,7 +7195,7 @@ export default function (pi: ExtensionAPI) {
7135
7195
  if (!model) {
7136
7196
  cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
7137
7197
  } else {
7138
- const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)) });
7198
+ const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)), openAISdkHeader403: openAISdkHeader403Models.has(modelKey(model)) });
7139
7199
  const adapter = selectAdapterForModel(model);
7140
7200
  const sk = model ? sessionModelKey(model) : undefined;
7141
7201
  const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cache-optimizer",
3
- "version": "2.6.14",
3
+ "version": "2.6.16",
4
4
  "description": "Improve Pi prompt/KV cache hit rates with stable prompts, OpenAI-compatible cache keys, proxy compat warnings, and footer cache stats.",
5
5
  "keywords": [
6
6
  "pi-package",