pi-cache-optimizer 2.6.15 → 2.6.17

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/README.md CHANGED
@@ -32,7 +32,7 @@ Pi extension for improving provider-side KV / prompt cache hit rates. It keeps s
32
32
  - Requests long cache retention when Pi/provider compat supports it.
33
33
  - Adds a session-id `prompt_cache_key` fallback for `openai-completions` / `openai-responses` payloads when no effective key exists.
34
34
  - Warns once for third-party OpenAI-compatible proxies missing cache/session-affinity compat flags.
35
- - Detects Anthropic adaptive thinking models (opus-4.6+, sonnet-4.6+, fable-5+) missing `forceAdaptiveThinking: true` compat.
35
+ - Detects Anthropic adaptive thinking models (opus-4.6+, sonnet-4.6+ including Sonnet 5, fable-5+) missing `forceAdaptiveThinking: true` compat.
36
36
  - Shows restart-persistent provider/model footer stats for supported model families.
37
37
  - Supports optional router-extension integration through versioned global protocols (`Symbol.for("pi.routing.registry.v1")` and `Symbol.for("pi.cache.hints.v1")`) without importing router packages.
38
38
 
@@ -111,7 +111,7 @@ Notes:
111
111
 
112
112
  ## Anthropic adaptive thinking models
113
113
 
114
- Claude models from opus-4.6 / sonnet-4.6 / fable-5 onwards require `forceAdaptiveThinking: true` in compat. Without it, Pi sends the legacy thinking format and Anthropic rejects the request.
114
+ Claude models from opus-4.6 / sonnet-4.6 (including Sonnet 5) / fable-5 onwards require `forceAdaptiveThinking: true` in compat. Without it, Pi sends the legacy thinking format and Anthropic rejects the request.
115
115
 
116
116
  Pi's built-in catalog already sets this flag for official models. Custom channels in `models.json` that override these models must include the flag:
117
117
 
@@ -126,7 +126,7 @@ Pi's built-in catalog already sets this flag for official models. Custom channel
126
126
  "forceAdaptiveThinking": true
127
127
  },
128
128
  "models": [
129
- { "id": "claude-opus-4-8", "name": "Claude Opus 4.8" }
129
+ { "id": "claude-sonnet-5", "name": "Claude Sonnet 5" }
130
130
  ]
131
131
  }
132
132
  }
@@ -140,7 +140,7 @@ Or use model-level override:
140
140
  "providers": {
141
141
  "your-claude-channel": {
142
142
  "modelOverrides": {
143
- "claude-opus-4-8": {
143
+ "claude-sonnet-5": {
144
144
  "compat": {
145
145
  "forceAdaptiveThinking": true
146
146
  }
package/README.zh-CN.md CHANGED
@@ -32,7 +32,7 @@
32
32
  - 在 Pi / provider compat 支持时请求长缓存保留。
33
33
  - 对 `openai-completions` / `openai-responses` 请求,在没有有效 key 时使用 Pi session id 补 `prompt_cache_key`。
34
34
  - 对缺少缓存 / session-affinity compat 的第三方 OpenAI-compatible 代理给出一次性提醒。
35
- - 检测 Anthropic adaptive thinking 模型(opus-4.6+、sonnet-4.6+、fable-5+)是否缺少 `forceAdaptiveThinking: true` compat。
35
+ - 检测 Anthropic adaptive thinking 模型(opus-4.6+、sonnet-4.6+ 含 Sonnet 5、fable-5+)是否缺少 `forceAdaptiveThinking: true` compat。
36
36
  - 为支持的模型家族显示可跨 Pi 进程 / 终端重启延续的 provider/model footer 缓存统计。
37
37
  - 通过版本化全局协议(`Symbol.for("pi.routing.registry.v1")` 与 `Symbol.for("pi.cache.hints.v1")`)支持可选的 router extension 集成,而不导入任何 router 包。
38
38
 
@@ -111,7 +111,7 @@ LiteLLM / OneAPI / NewAPI / 类 OpenRouter 渠道等第三方 `openai-completion
111
111
 
112
112
  ## Anthropic adaptive thinking 模型
113
113
 
114
- Claude 从 opus-4.6 / sonnet-4.6 / fable-5 开始需要在 compat 中设置 `forceAdaptiveThinking: true`。缺少此 flag 时,Pi 会发送旧版 thinking 格式,Anthropic 会拒绝请求。
114
+ Claude 从 opus-4.6 / sonnet-4.6(含 Sonnet 5)/ fable-5 开始需要在 compat 中设置 `forceAdaptiveThinking: true`。缺少此 flag 时,Pi 会发送旧版 thinking 格式,Anthropic 会拒绝请求。
115
115
 
116
116
  Pi 内置 catalog 已为官方模型设置此 flag。`models.json` 中覆盖这些模型的自定义渠道必须包含该 flag:
117
117
 
@@ -126,7 +126,7 @@ Pi 内置 catalog 已为官方模型设置此 flag。`models.json` 中覆盖这
126
126
  "forceAdaptiveThinking": true
127
127
  },
128
128
  "models": [
129
- { "id": "claude-opus-4-8", "name": "Claude Opus 4.8" }
129
+ { "id": "claude-sonnet-5", "name": "Claude Sonnet 5" }
130
130
  ]
131
131
  }
132
132
  }
@@ -140,7 +140,7 @@ Pi 内置 catalog 已为官方模型设置此 flag。`models.json` 中覆盖这
140
140
  "providers": {
141
141
  "your-claude-channel": {
142
142
  "modelOverrides": {
143
- "claude-opus-4-8": {
143
+ "claude-sonnet-5": {
144
144
  "compat": {
145
145
  "forceAdaptiveThinking": true
146
146
  }
package/index.ts CHANGED
@@ -1143,14 +1143,14 @@ function isGeminiLikeAssistantMessage(message: unknown, model: PiModel | undefin
1143
1143
  *
1144
1144
  * Adaptive-generation models (from pi-ai built-in catalog) include:
1145
1145
  * claude-opus-4-6, claude-opus-4-7, claude-opus-4-8 (also dotted 4.6/4.7/4.8)
1146
- * claude-sonnet-4-6
1146
+ * claude-sonnet-4-6, claude-sonnet-5
1147
1147
  * claude-fable-5
1148
1148
  *
1149
1149
  * We match broadly: opus >= 4-6, sonnet >= 4-6, fable >= 5.
1150
1150
  * Ids may carry date-stamp or size suffixes like "[1M]".
1151
1151
  */
1152
- const ADAPTIVE_OPUS_PATTERN = /(^|[\/\s:_-])(opus-4[.-][6-9]|opus-4-[1-9][0-9])($|[-_.:\/\s\[])/i;
1153
- const ADAPTIVE_SONNET_PATTERN = /(^|[\/\s:_-])(sonnet-4[.-][6-9]|sonnet-4-[1-9][0-9])($|[-_.:\/\s\[])/i;
1152
+ const ADAPTIVE_OPUS_PATTERN = /(^|[\/\s:_-])(opus-4[.-][6-9]|opus-4-[1-9][0-9]|opus-([5-9]|[1-9][0-9]))($|[-_.:\/\s\[])/i;
1153
+ const ADAPTIVE_SONNET_PATTERN = /(^|[\/\s:_-])(sonnet-4[.-][6-9]|sonnet-4-[1-9][0-9]|sonnet-([5-9]|[1-9][0-9]))($|[-_.:\/\s\[])/i;
1154
1154
  const ADAPTIVE_FABLE_PATTERN = /(^|[\/\s:_-])fable-([5-9]|[1-9][0-9])($|[-_.:\/\s\[])/i;
1155
1155
 
1156
1156
  function isAdaptiveGenerationModel(model: PiModel | undefined): boolean {
@@ -3943,6 +3943,22 @@ function isSessionAffinity403Applicable(model: PiModel): boolean {
3943
3943
  return getCompat(model).sendSessionAffinityHeaders === true;
3944
3944
  }
3945
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
+
3946
3962
  /**
3947
3963
  * Detect router / channel profiles from a PiModel and return diagnostic notes.
3948
3964
  *
@@ -4140,7 +4156,7 @@ function getCompatCheckNotApplicableLines(model: PiModel): string[] {
4140
4156
  return ["ℹ️ Compat check not applicable for this model."];
4141
4157
  }
4142
4158
 
4143
- function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean; sessionAffinity403?: boolean } = {}): string {
4159
+ function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean; sessionAffinity403?: boolean; openAISdkHeader403?: boolean } = {}): string {
4144
4160
  const lines: string[] = [];
4145
4161
  lines.push(`Provider: ${model.provider}`);
4146
4162
  lines.push(`Model: ${model.id}`);
@@ -4216,6 +4232,20 @@ function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400
4216
4232
  lines.push(" (session_id, x-client-request-id, x-session-affinity) and return 403. If you");
4217
4233
  lines.push(" see 403 errors, run /cache-optimizer fix to set sendSessionAffinityHeaders: false.");
4218
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
+ }
4219
4249
  }
4220
4250
 
4221
4251
  // ── Router/channel diagnostics ──
@@ -4408,12 +4438,19 @@ function buildCompatDiagnosis(model: PiModel): string | undefined {
4408
4438
  if (isPromptCacheRetention400Applicable(model)) {
4409
4439
  lines.push(getPromptCacheRetentionUnsupportedHint());
4410
4440
  }
4411
- // 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.
4412
4443
  if (isSessionAffinity403Applicable(model)) {
4413
4444
  lines.push(
4414
4445
  "ℹ️ Session affinity headers are enabled. If you see 403 \"blocked\" errors,",
4415
4446
  " the proxy/CDN may be blocking Pi's custom headers. Set sendSessionAffinityHeaders: false.",
4416
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
+ );
4417
4454
  }
4418
4455
  appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);
4419
4456
  } else {
@@ -5692,6 +5729,7 @@ export const __internals_for_tests = {
5692
5729
  isCompatCheckApplicable,
5693
5730
  isPromptCacheRetention400Applicable,
5694
5731
  isSessionAffinity403Applicable,
5732
+ isOpenAISdkHeader403Applicable,
5695
5733
  hasPromptCacheRetentionUnsupportedSignal,
5696
5734
  // Non-GPT OpenAI-compatible model detection
5697
5735
  isKimiLikeModel,
@@ -5910,6 +5948,8 @@ export default function (pi: ExtensionAPI) {
5910
5948
  const warnedPromptCacheRetention400Models = new Set<string>();
5911
5949
  const sendSessionAffinityHeaders403Models = new Set<string>();
5912
5950
  const warnedSendSessionAffinityHeaders403Models = new Set<string>();
5951
+ const openAISdkHeader403Models = new Set<string>();
5952
+ const warnedOpenAISdkHeader403Models = new Set<string>();
5913
5953
  let cacheStatsByModel: Record<string, CacheStats> = {};
5914
5954
  let cacheStatsTotalsByModel: Record<string, CacheStats> = {};
5915
5955
  let cacheStatsLegacyFamily: Partial<Record<CacheProviderId, CacheStats>> = emptyAllCacheStats();
@@ -6576,28 +6616,42 @@ export default function (pi: ExtensionAPI) {
6576
6616
  return;
6577
6617
  }
6578
6618
 
6579
- // ── 403: sendSessionAffinityHeaders blocked by CDN/WAF ──
6580
- //
6581
- // Pi's openai-completions/responses adapters send three custom HTTP
6582
- // headers (session_id, x-client-request-id, x-session-affinity) when
6583
- // sendSessionAffinityHeaders is enabled. Some third-party proxies /
6584
- // CDNs / WAFs block these custom headers and return 403. We record a
6585
- // one-time per-session notification and surface it in doctor/fix so the
6586
- // user can disable the flag for the blocked model.
6619
+ // ── 403: proxy/CDN/WAF blocking ──
6587
6620
  if (event.status === 403) {
6588
- if (!isSessionAffinity403Applicable(model)) return;
6589
-
6590
6621
  const key403 = modelKey(model);
6591
- sendSessionAffinityHeaders403Models.add(key403);
6592
- if (warnedSendSessionAffinityHeaders403Models.has(key403)) return;
6593
- warnedSendSessionAffinityHeaders403Models.add(key403);
6594
- ctx.ui.notify(
6595
- `⚠️ ${LOG_PREFIX}: ${key403} returned HTTP 403 while sendSessionAffinityHeaders is enabled. ` +
6596
- `The proxy/CDN may be blocking Pi's custom session-affinity headers (session_id, x-client-request-id, x-session-affinity). ` +
6597
- `Run /cache-optimizer doctor for details and /cache-optimizer fix to set sendSessionAffinityHeaders: false.`,
6598
- "warning",
6599
- );
6600
- 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
+ }
6601
6655
  }
6602
6656
  });
6603
6657
 
@@ -6720,7 +6774,7 @@ export default function (pi: ExtensionAPI) {
6720
6774
  cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
6721
6775
  return;
6722
6776
  }
6723
- 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)) });
6724
6778
  const adapter = selectAdapterForModel(model);
6725
6779
  const sk = model ? sessionModelKey(model) : undefined;
6726
6780
  const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;
@@ -7141,7 +7195,7 @@ export default function (pi: ExtensionAPI) {
7141
7195
  if (!model) {
7142
7196
  cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
7143
7197
  } else {
7144
- 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)) });
7145
7199
  const adapter = selectAdapterForModel(model);
7146
7200
  const sk = model ? sessionModelKey(model) : undefined;
7147
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.15",
3
+ "version": "2.6.17",
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",