@zhushanwen/pi-cache-probe 0.3.0 → 0.3.1
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/package.json +4 -1
- package/src/__tests__/fingerprint.test.ts +1 -1
- package/src/fingerprint.ts +8 -5
- package/src/index.ts +9 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-cache-probe",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Zero-impact Pi probe extension — records system prompt / tools fingerprint hashes into session custom entries (only on change) to attribute LLM prompt-cache miss to prefix drift vs server-side eviction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"index.ts",
|
|
26
26
|
"analyze.py"
|
|
27
27
|
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@zhushanwen/pi-ext-guards": "0.4.0"
|
|
30
|
+
},
|
|
28
31
|
"devDependencies": {
|
|
29
32
|
"@vitest/coverage-v8": "^4.1.9",
|
|
30
33
|
"vitest": "^4.1.8"
|
|
@@ -74,7 +74,7 @@ describe("extractSystem(payload 四口径)", () => {
|
|
|
74
74
|
]);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
it("
|
|
77
|
+
it("伪造对象形态:顶层 systemInstruction(实装恒传 string,仅测试构造可达)", () => {
|
|
78
78
|
expect(extractSystem({ systemInstruction: { role: "system", parts: [] } })).toEqual({
|
|
79
79
|
role: "system",
|
|
80
80
|
parts: [],
|
package/src/fingerprint.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
12
12
|
|
|
13
|
+
import { isRecord } from "@zhushanwen/pi-ext-guards";
|
|
14
|
+
|
|
13
15
|
/** 指纹 hash 长度(hex 字符数)。 */
|
|
14
16
|
const HASH_LEN = 16;
|
|
15
17
|
|
|
@@ -41,10 +43,6 @@ export interface ProbeEntryData {
|
|
|
41
43
|
error?: string;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
45
|
-
return value !== null && typeof value === "object";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
46
|
/** 递归 sort keys 的稳定序列化(undefined 归一为 null,防 key 顺序抖动产生假变化)。 */
|
|
49
47
|
function stableStringify(value: unknown): string {
|
|
50
48
|
if (value === undefined) return "null";
|
|
@@ -63,7 +61,12 @@ export function hashOf(value: unknown): string {
|
|
|
63
61
|
* - OpenAI 兼容:messages 内 role 为 system 或 developer
|
|
64
62
|
* (pi 源码 ai/src/api/openai-completions.ts 的 useDeveloperRole 分支)
|
|
65
63
|
* - Anthropic:顶层 system(数组)
|
|
66
|
-
* - Google
|
|
64
|
+
* - Google:顶层字段 systemInstruction 为对象形态——仅手工伪造的输入可达:
|
|
65
|
+
* pi-ai 0.84.4 实装(google-generative-ai.js:294 / google-vertex.js:368)恒传
|
|
66
|
+
* string(sanitizeSurrogates(context.systemPrompt)),isRecord 分支对 string
|
|
67
|
+
* 恒不命中,故实装流量下 Google 侧返回 null(spFull 观测恒 no-system)。
|
|
68
|
+
* 归因是「提取只认对象形态」而非「Google 请求没有 system」,勿把下方
|
|
69
|
+
* isRecord(systemInstruction) 分支误读为 Google 口径主通路。
|
|
67
70
|
*/
|
|
68
71
|
export function extractSystem(payload: unknown): unknown | null {
|
|
69
72
|
if (!isRecord(payload)) return null;
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* - before_agent_start(每 turn 1 次):算 7 个输入侧 hash 暂存 pending
|
|
10
10
|
* (systemPromptOptions 各字段 + getAllTools 注册表)
|
|
11
11
|
* - before_provider_request(每笔 LLM 请求,仅消费 turn 首笔):从最终 payload
|
|
12
|
-
* 提取 spFull(system 消息,兼容 system/developer/anthropic/google
|
|
12
|
+
* 提取 spFull(system 消息,兼容 system/developer/anthropic/google 口径;
|
|
13
|
+
* google 口径实装流量恒 no-system——pi-ai 恒传 string,该分支仅手工伪造
|
|
14
|
+
* 对象输入可达,详见 fingerprint.ts extractSystem 注释)与
|
|
13
15
|
* toolsSent(tools 数组),与 pending 合并对比,变化/基线时 appendEntry
|
|
14
16
|
* - agent_end:turn 内无 provider 请求则丢弃 pending(无请求即无归因价值)
|
|
15
17
|
*
|
|
@@ -21,6 +23,7 @@
|
|
|
21
23
|
*/
|
|
22
24
|
|
|
23
25
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
26
|
+
import { toErrorMessage } from "@zhushanwen/pi-ext-guards";
|
|
24
27
|
|
|
25
28
|
import {
|
|
26
29
|
buildProbeEntry,
|
|
@@ -60,12 +63,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
60
63
|
// 输入侧 7 hash 计算在 fingerprint.ts 纯函数层(handler 只做编排与 fail-safe)
|
|
61
64
|
pending = computePendingFingerprint(event?.systemPromptOptions, pi.getAllTools());
|
|
62
65
|
} catch (err) {
|
|
63
|
-
stderr(`before_agent_start failed: ${
|
|
66
|
+
stderr(`before_agent_start failed: ${toErrorMessage(err)}`);
|
|
64
67
|
pending = null;
|
|
65
68
|
try {
|
|
66
|
-
pi.appendEntry(CUSTOM_TYPE, { v: SCHEMA_VERSION, seq, error:
|
|
69
|
+
pi.appendEntry(CUSTOM_TYPE, { v: SCHEMA_VERSION, seq, error: toErrorMessage(err) });
|
|
67
70
|
} catch (appendErr) {
|
|
68
|
-
stderr(`appendEntry failed: ${
|
|
71
|
+
stderr(`appendEntry failed: ${toErrorMessage(appendErr)}`);
|
|
69
72
|
}
|
|
70
73
|
needsBaseline = true;
|
|
71
74
|
}
|
|
@@ -88,12 +91,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
88
91
|
last = cur;
|
|
89
92
|
needsBaseline = false;
|
|
90
93
|
} catch (err) {
|
|
91
|
-
const msg =
|
|
94
|
+
const msg = toErrorMessage(err);
|
|
92
95
|
stderr(`before_provider_request failed: ${msg}`);
|
|
93
96
|
try {
|
|
94
97
|
pi.appendEntry(CUSTOM_TYPE, { v: SCHEMA_VERSION, seq, error: msg });
|
|
95
98
|
} catch (appendErr) {
|
|
96
|
-
stderr(`appendEntry failed: ${
|
|
99
|
+
stderr(`appendEntry failed: ${toErrorMessage(appendErr)}`);
|
|
97
100
|
}
|
|
98
101
|
needsBaseline = true;
|
|
99
102
|
}
|