pi-cache-optimizer 2.6.13 → 2.6.14
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/index.ts +111 -16
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -3921,6 +3921,22 @@ function isPromptCacheRetention400Applicable(model: PiModel): boolean {
|
|
|
3921
3921
|
getCompat(model).supportsLongCacheRetention === true;
|
|
3922
3922
|
}
|
|
3923
3923
|
|
|
3924
|
+
/**
|
|
3925
|
+
* Whether the 403 sendSessionAffinityHeaders diagnostic applies to a model.
|
|
3926
|
+
*
|
|
3927
|
+
* Pi's openai-completions / openai-responses adapters send three custom HTTP
|
|
3928
|
+
* headers (session_id, x-client-request-id, x-session-affinity) when
|
|
3929
|
+
* `sendSessionAffinityHeaders` is enabled. Some third-party proxies / CDNs /
|
|
3930
|
+
* WAFs block these custom headers and return HTTP 403 "Your request was
|
|
3931
|
+
* blocked". This guard returns true only for OpenAI-compatible APIs whose
|
|
3932
|
+
* merged compat currently enables session-affinity headers, so the 403
|
|
3933
|
+
* monitoring and doctor/fix paths can advise disabling the flag.
|
|
3934
|
+
*/
|
|
3935
|
+
function isSessionAffinity403Applicable(model: PiModel): boolean {
|
|
3936
|
+
if (!isOpenAICompatibleApi(model.api)) return false;
|
|
3937
|
+
return getCompat(model).sendSessionAffinityHeaders === true;
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3924
3940
|
/**
|
|
3925
3941
|
* Detect router / channel profiles from a PiModel and return diagnostic notes.
|
|
3926
3942
|
*
|
|
@@ -4118,7 +4134,7 @@ function getCompatCheckNotApplicableLines(model: PiModel): string[] {
|
|
|
4118
4134
|
return ["ℹ️ Compat check not applicable for this model."];
|
|
4119
4135
|
}
|
|
4120
4136
|
|
|
4121
|
-
function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean } = {}): string {
|
|
4137
|
+
function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400?: boolean; sessionAffinity403?: boolean } = {}): string {
|
|
4122
4138
|
const lines: string[] = [];
|
|
4123
4139
|
lines.push(`Provider: ${model.provider}`);
|
|
4124
4140
|
lines.push(`Model: ${model.id}`);
|
|
@@ -4177,6 +4193,25 @@ function buildDoctorDiagnosis(model: PiModel, options: { promptCacheRetention400
|
|
|
4177
4193
|
}
|
|
4178
4194
|
}
|
|
4179
4195
|
|
|
4196
|
+
// ── Session affinity 403 diagnostics ──
|
|
4197
|
+
// Advises the user when sendSessionAffinityHeaders is enabled, since some
|
|
4198
|
+
// third-party proxies / CDNs / WAFs block Pi's custom session-affinity HTTP
|
|
4199
|
+
// headers (session_id, x-client-request-id, x-session-affinity) and return
|
|
4200
|
+
// 403. If a 403 was already observed for this model, show a stronger hint.
|
|
4201
|
+
if (isSessionAffinity403Applicable(model)) {
|
|
4202
|
+
lines.push("");
|
|
4203
|
+
if (options.sessionAffinity403) {
|
|
4204
|
+
lines.push("⚠️ A 403 response was observed while sendSessionAffinityHeaders is enabled.");
|
|
4205
|
+
lines.push(" The proxy/CDN likely blocks Pi's custom session-affinity headers (session_id,");
|
|
4206
|
+
lines.push(" x-client-request-id, x-session-affinity). Run /cache-optimizer fix to");
|
|
4207
|
+
lines.push(` set sendSessionAffinityHeaders: false in ${getModelsJsonDisplayPath()}.`);
|
|
4208
|
+
} else {
|
|
4209
|
+
lines.push("ℹ️ Session affinity headers are enabled. Some CDNs/WAFs block custom headers");
|
|
4210
|
+
lines.push(" (session_id, x-client-request-id, x-session-affinity) and return 403. If you");
|
|
4211
|
+
lines.push(" see 403 errors, run /cache-optimizer fix to set sendSessionAffinityHeaders: false.");
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4180
4215
|
// ── Router/channel diagnostics ──
|
|
4181
4216
|
const routerNotes = describeRouterChannelDiagnostics(model);
|
|
4182
4217
|
if (routerNotes.length > 0) {
|
|
@@ -4367,6 +4402,13 @@ function buildCompatDiagnosis(model: PiModel): string | undefined {
|
|
|
4367
4402
|
if (isPromptCacheRetention400Applicable(model)) {
|
|
4368
4403
|
lines.push(getPromptCacheRetentionUnsupportedHint());
|
|
4369
4404
|
}
|
|
4405
|
+
// Advisory for 403 session-affinity header blocking (only when enabled).
|
|
4406
|
+
if (isSessionAffinity403Applicable(model)) {
|
|
4407
|
+
lines.push(
|
|
4408
|
+
"ℹ️ Session affinity headers are enabled. If you see 403 \"blocked\" errors,",
|
|
4409
|
+
" the proxy/CDN may be blocking Pi's custom headers. Set sendSessionAffinityHeaders: false.",
|
|
4410
|
+
);
|
|
4411
|
+
}
|
|
4370
4412
|
appendOptionalOpenAIProxyCompatAdviceLines(lines, optionalOpenAIProxyCompat);
|
|
4371
4413
|
} else {
|
|
4372
4414
|
lines.push(...getCompatCheckNotApplicableLines(model));
|
|
@@ -5643,6 +5685,7 @@ export const __internals_for_tests = {
|
|
|
5643
5685
|
isOfficialOpenAIBaseUrl,
|
|
5644
5686
|
isCompatCheckApplicable,
|
|
5645
5687
|
isPromptCacheRetention400Applicable,
|
|
5688
|
+
isSessionAffinity403Applicable,
|
|
5646
5689
|
hasPromptCacheRetentionUnsupportedSignal,
|
|
5647
5690
|
// Non-GPT OpenAI-compatible model detection
|
|
5648
5691
|
isKimiLikeModel,
|
|
@@ -5859,6 +5902,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
5859
5902
|
const warnedModels = new Set<string>();
|
|
5860
5903
|
const promptCacheRetention400Models = new Set<string>();
|
|
5861
5904
|
const warnedPromptCacheRetention400Models = new Set<string>();
|
|
5905
|
+
const sendSessionAffinityHeaders403Models = new Set<string>();
|
|
5906
|
+
const warnedSendSessionAffinityHeaders403Models = new Set<string>();
|
|
5862
5907
|
let cacheStatsByModel: Record<string, CacheStats> = {};
|
|
5863
5908
|
let cacheStatsTotalsByModel: Record<string, CacheStats> = {};
|
|
5864
5909
|
let cacheStatsLegacyFamily: Partial<Record<CacheProviderId, CacheStats>> = emptyAllCacheStats();
|
|
@@ -6506,20 +6551,48 @@ export default function (pi: ExtensionAPI) {
|
|
|
6506
6551
|
pi.on("after_provider_response", (event, ctx) => {
|
|
6507
6552
|
const model = resolveRouteModel(ctx.model, ctx) ?? ctx.model;
|
|
6508
6553
|
if (!runtimeOptimizerEnabled || !model) return;
|
|
6509
|
-
if (event.status !== 400) return;
|
|
6510
|
-
if (!isPromptCacheRetention400Applicable(model)) return;
|
|
6511
|
-
if (!hasPromptCacheRetentionUnsupportedSignal(event.headers)) return;
|
|
6512
6554
|
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6555
|
+
// ── 400: prompt_cache_retention unsupported ──
|
|
6556
|
+
if (event.status === 400) {
|
|
6557
|
+
if (!isPromptCacheRetention400Applicable(model)) return;
|
|
6558
|
+
if (!hasPromptCacheRetentionUnsupportedSignal(event.headers)) return;
|
|
6559
|
+
|
|
6560
|
+
const key = modelKey(model);
|
|
6561
|
+
promptCacheRetention400Models.add(key);
|
|
6562
|
+
if (warnedPromptCacheRetention400Models.has(key)) return;
|
|
6563
|
+
warnedPromptCacheRetention400Models.add(key);
|
|
6564
|
+
ctx.ui.notify(
|
|
6565
|
+
`⚠️ ${LOG_PREFIX}: ${key} returned HTTP 400 while supportsLongCacheRetention is enabled. ` +
|
|
6566
|
+
getPromptCacheRetentionUnsupportedHint() +
|
|
6567
|
+
` Run /cache-optimizer doctor for the exact edit location.`,
|
|
6568
|
+
"warning",
|
|
6569
|
+
);
|
|
6570
|
+
return;
|
|
6571
|
+
}
|
|
6572
|
+
|
|
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.
|
|
6581
|
+
if (event.status === 403) {
|
|
6582
|
+
if (!isSessionAffinity403Applicable(model)) return;
|
|
6583
|
+
|
|
6584
|
+
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;
|
|
6595
|
+
}
|
|
6523
6596
|
});
|
|
6524
6597
|
|
|
6525
6598
|
pi.on("message_end", async (event, ctx) => {
|
|
@@ -6641,7 +6714,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6641
6714
|
cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
|
|
6642
6715
|
return;
|
|
6643
6716
|
}
|
|
6644
|
-
const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)) });
|
|
6717
|
+
const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)) });
|
|
6645
6718
|
const adapter = selectAdapterForModel(model);
|
|
6646
6719
|
const sk = model ? sessionModelKey(model) : undefined;
|
|
6647
6720
|
const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;
|
|
@@ -6731,6 +6804,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
6731
6804
|
};
|
|
6732
6805
|
}
|
|
6733
6806
|
|
|
6807
|
+
// If no regular missing compat flags but the model has a recorded
|
|
6808
|
+
// 403 while sendSessionAffinityHeaders was enabled (Pi sent custom
|
|
6809
|
+
// session-affinity HTTP headers and the proxy/CDN blocked them), offer
|
|
6810
|
+
// to override `sendSessionAffinityHeaders` to false in models.json.
|
|
6811
|
+
if (!suggestion && isSessionAffinity403Applicable(model) && sendSessionAffinityHeaders403Models.has(modelKey(model))) {
|
|
6812
|
+
const key = modelKey(model);
|
|
6813
|
+
const slashIdx = key.indexOf("/");
|
|
6814
|
+
const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;
|
|
6815
|
+
suggestion = {
|
|
6816
|
+
providerLabel,
|
|
6817
|
+
modelId: model.id,
|
|
6818
|
+
compatKeys: { sendSessionAffinityHeaders: false },
|
|
6819
|
+
};
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6734
6822
|
if (!suggestion) {
|
|
6735
6823
|
const key = modelKey(model);
|
|
6736
6824
|
cmdCtx.ui.notify(`✅ Nothing to fix for "${key}". Compat already configured.`, "info");
|
|
@@ -6755,6 +6843,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
6755
6843
|
"Create or edit the entry below to override supportsLongCacheRetention to false.",
|
|
6756
6844
|
);
|
|
6757
6845
|
}
|
|
6846
|
+
if (sendSessionAffinityHeaders403Models.has(modelKey(model))) {
|
|
6847
|
+
manualLines.push(
|
|
6848
|
+
"",
|
|
6849
|
+
"💡 This model returned HTTP 403 while sendSessionAffinityHeaders was enabled.",
|
|
6850
|
+
"Create or edit the entry below to override sendSessionAffinityHeaders to false.",
|
|
6851
|
+
);
|
|
6852
|
+
}
|
|
6758
6853
|
manualLines.push(
|
|
6759
6854
|
"",
|
|
6760
6855
|
"If the provider/model already exists in models.json, add these compat keys under",
|
|
@@ -7040,7 +7135,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7040
7135
|
if (!model) {
|
|
7041
7136
|
cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
|
|
7042
7137
|
} else {
|
|
7043
|
-
const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)) });
|
|
7138
|
+
const diagnosis = buildDoctorDiagnosis(model, { promptCacheRetention400: promptCacheRetention400Models.has(modelKey(model)), sessionAffinity403: sendSessionAffinityHeaders403Models.has(modelKey(model)) });
|
|
7044
7139
|
const adapter = selectAdapterForModel(model);
|
|
7045
7140
|
const sk = model ? sessionModelKey(model) : undefined;
|
|
7046
7141
|
const statsState = model ? cacheStatsTotalsByModel[modelKey(model)] : undefined;
|
package/package.json
CHANGED