pi-plus 0.1.8 → 0.1.11
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/CHANGELOG.md +25 -0
- package/README.md +72 -151
- package/anthropic.js +82 -0
- package/bedrock-converse-stream.js +90 -0
- package/core/export-html/template.css +1076 -0
- package/core/export-html/template.html +55 -0
- package/core/export-html/template.js +1902 -0
- package/core/export-html/vendor/highlight.min.js +1213 -0
- package/core/export-html/vendor/marked.min.js +78 -0
- package/github-copilot.js +9 -0
- package/image-resize-worker.js +9 -0
- package/kimi-coding.js +9 -0
- package/modes/interactive/assets/clankolas.png +0 -0
- package/modes/interactive/theme/dark.json +91 -0
- package/modes/interactive/theme/light.json +90 -0
- package/modes/interactive/theme/theme-schema.json +361 -0
- package/openai-codex.js +82 -0
- package/openrouter.js +82 -0
- package/package.json +23 -43
- package/pipi.js +2322 -0
- package/radius.js +82 -0
- package/xai.js +9 -0
- package/LICENSE +0 -21
- package/src/auto-compact.ts +0 -43
- package/src/context-usage.ts +0 -91
- package/src/footer-render.ts +0 -248
- package/src/footer.ts +0 -139
- package/src/index.ts +0 -172
- package/src/logger.ts +0 -56
- package/src/model-config.ts +0 -184
- package/src/percentages.ts +0 -99
- package/src/settings.ts +0 -101
- package/src/usage.ts +0 -201
package/src/usage.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ports of Claude Code's usage-based context measurement and rough tail
|
|
3
|
-
* estimation.
|
|
4
|
-
*
|
|
5
|
-
* Sources (claude-code repo):
|
|
6
|
-
* - src/utils/tokens.ts — tokenCountWithEstimation, getTokenCountFromUsage
|
|
7
|
-
* - src/services/tokenEstimation.ts — roughTokenCountEstimationForBlock
|
|
8
|
-
* - src/services/compact/microCompact.ts — IMAGE_MAX_TOKEN_SIZE = 2000
|
|
9
|
-
*
|
|
10
|
-
* pi's message model differs from CC's: tool results are separate
|
|
11
|
-
* `toolResult` messages (CC: `user` messages containing tool_result blocks)
|
|
12
|
-
* and assistant tool calls use the `toolCall` block type (CC: `tool_use`).
|
|
13
|
-
* The estimation below maps those 1:1 so the arithmetic matches CC.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/** Structural shape of pi's Usage we rely on (pi-ai Usage). */
|
|
17
|
-
export interface UsageSnapshot {
|
|
18
|
-
input: number;
|
|
19
|
-
output: number;
|
|
20
|
-
cacheRead: number;
|
|
21
|
-
cacheWrite: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Structural minimal message shape — accepts pi AgentMessage without a
|
|
25
|
-
* hard runtime dependency on pi-ai. */
|
|
26
|
-
export interface RoughMessage {
|
|
27
|
-
role?: string;
|
|
28
|
-
content?: unknown;
|
|
29
|
-
usage?: Partial<UsageSnapshot> | null;
|
|
30
|
-
stopReason?: string;
|
|
31
|
-
timestamp?: number;
|
|
32
|
-
toolsAdded?: unknown[];
|
|
33
|
-
toolsRemoved?: unknown[];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** CC: images and PDFs are billed ~2000 tokens after client-side resizing;
|
|
37
|
-
* a 1MB base64 PDF must NOT fall through to the stringify catch-all. */
|
|
38
|
-
const IMAGE_DOCUMENT_TOKEN_ESTIMATE = 2000;
|
|
39
|
-
const CHARS_PER_TOKEN = 4;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* CC: getTokenCountFromUsage — the full context size at the time of that API
|
|
43
|
-
* call: input + cache creation (5m and 1h) + cache read + output.
|
|
44
|
-
* pi's `cacheWrite` already includes the 1h subset, and `reasoning` is a
|
|
45
|
-
* subset of `output`, so summing the four fields is exactly CC's formula.
|
|
46
|
-
*/
|
|
47
|
-
export function getTokenCountFromUsage(usage: UsageSnapshot): number {
|
|
48
|
-
return usage.input + usage.cacheWrite + usage.cacheRead + usage.output;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function hasPositiveTokenCount(usage: Partial<UsageSnapshot> | null | undefined): usage is UsageSnapshot {
|
|
52
|
-
if (!usage) return false;
|
|
53
|
-
const total = getTokenCountFromUsage({
|
|
54
|
-
input: usage.input ?? 0,
|
|
55
|
-
output: usage.output ?? 0,
|
|
56
|
-
cacheRead: usage.cacheRead ?? 0,
|
|
57
|
-
cacheWrite: usage.cacheWrite ?? 0,
|
|
58
|
-
});
|
|
59
|
-
return total > 0;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function safeJsonStringify(value: unknown): string {
|
|
63
|
-
try {
|
|
64
|
-
return JSON.stringify(value) ?? "undefined";
|
|
65
|
-
} catch {
|
|
66
|
-
return "[unserializable]";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** CC: roughTokenCountEstimation — characters / 4, rounded. */
|
|
71
|
-
export function roughTokenCountEstimation(content: string, bytesPerToken: number = CHARS_PER_TOKEN): number {
|
|
72
|
-
return Math.round(content.length / bytesPerToken);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Find the last assistant message whose usage validly describes the current
|
|
77
|
-
* context prefix. Port of pi's getLastAssistantUsageInfo (estimate.ts), which
|
|
78
|
-
* matches CC's getTokenUsage behavior: skip aborted/error responses, skip
|
|
79
|
-
* responses older than a newer prefix message (e.g. a compaction summary
|
|
80
|
-
* inserted afterwards), skip zero-usage records.
|
|
81
|
-
*/
|
|
82
|
-
export function findLastUsageInfo(
|
|
83
|
-
messages: readonly RoughMessage[],
|
|
84
|
-
): { usage: UsageSnapshot; index: number } | undefined {
|
|
85
|
-
let latestPrefixTimestamp = Number.NEGATIVE_INFINITY;
|
|
86
|
-
let usageInfo: { usage: UsageSnapshot; index: number } | undefined;
|
|
87
|
-
|
|
88
|
-
for (let i = 0; i < messages.length; i++) {
|
|
89
|
-
const message = messages[i];
|
|
90
|
-
if (message.role === "assistant" && hasPositiveTokenCount(message.usage)) {
|
|
91
|
-
const usageAppliesToPrefix = (message.timestamp ?? 0) >= latestPrefixTimestamp;
|
|
92
|
-
if (usageAppliesToPrefix && message.stopReason !== "aborted" && message.stopReason !== "error") {
|
|
93
|
-
usageInfo = {
|
|
94
|
-
usage: {
|
|
95
|
-
input: message.usage.input ?? 0,
|
|
96
|
-
output: message.usage.output ?? 0,
|
|
97
|
-
cacheRead: message.usage.cacheRead ?? 0,
|
|
98
|
-
cacheWrite: message.usage.cacheWrite ?? 0,
|
|
99
|
-
},
|
|
100
|
-
index: i,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (message.timestamp !== undefined) {
|
|
105
|
-
latestPrefixTimestamp = Math.max(latestPrefixTimestamp, message.timestamp);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return usageInfo;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** CC: roughTokenCountEstimationForBlock — per content block. */
|
|
112
|
-
function estimateBlockTokens(block: unknown): number {
|
|
113
|
-
if (typeof block === "string") return roughTokenCountEstimation(block);
|
|
114
|
-
if (!block || typeof block !== "object") return 0;
|
|
115
|
-
const b = block as Record<string, unknown>;
|
|
116
|
-
switch (b.type) {
|
|
117
|
-
case "text":
|
|
118
|
-
return roughTokenCountEstimation(String(b.text ?? ""));
|
|
119
|
-
case "thinking":
|
|
120
|
-
return roughTokenCountEstimation(String(b.thinking ?? ""));
|
|
121
|
-
case "image":
|
|
122
|
-
case "document":
|
|
123
|
-
return IMAGE_DOCUMENT_TOKEN_ESTIMATE;
|
|
124
|
-
// pi toolResult message content (CC: tool_result block inside a user message)
|
|
125
|
-
case "toolResult":
|
|
126
|
-
return estimateContentTokens(b.content);
|
|
127
|
-
// pi assistant tool call (CC: tool_use block — name + serialized input)
|
|
128
|
-
case "toolCall":
|
|
129
|
-
return roughTokenCountEstimation(String(b.name ?? "") + safeJsonStringify(b.arguments ?? {}));
|
|
130
|
-
default:
|
|
131
|
-
// server_tool_use, web_search_tool_result, mcp_tool_use, etc. — text-like
|
|
132
|
-
// payloads where the stringify length tracks the serialized form.
|
|
133
|
-
return roughTokenCountEstimation(safeJsonStringify(block));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function estimateContentTokens(content: unknown): number {
|
|
138
|
-
if (!content) return 0;
|
|
139
|
-
if (typeof content === "string") return roughTokenCountEstimation(content);
|
|
140
|
-
if (!Array.isArray(content)) return roughTokenCountEstimation(safeJsonStringify(content));
|
|
141
|
-
let total = 0;
|
|
142
|
-
for (const block of content) total += estimateBlockTokens(block);
|
|
143
|
-
return total;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* CC: roughTokenCountEstimationForMessage — CC counts only assistant and user
|
|
148
|
-
* message content (tool results ride inside user messages there). pi carries
|
|
149
|
-
* tool results as their own `toolResult` messages, estimated via their content
|
|
150
|
-
* blocks. pi also keeps `system` messages in the branch (CC has none in the
|
|
151
|
-
* tail), so those are counted as chars/4 plus their tool definitions.
|
|
152
|
-
*/
|
|
153
|
-
export function estimateMessageTokens(message: RoughMessage): number {
|
|
154
|
-
if (message.role === "system") {
|
|
155
|
-
let chars = 0;
|
|
156
|
-
const content = message.content;
|
|
157
|
-
if (typeof content === "string") {
|
|
158
|
-
chars += content.length;
|
|
159
|
-
} else if (Array.isArray(content)) {
|
|
160
|
-
for (const block of content) {
|
|
161
|
-
if (block && typeof block === "object" && "text" in block) {
|
|
162
|
-
chars += String((block as { text: unknown }).text ?? "").length;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const toolsAdded = message.toolsAdded?.length ? safeJsonStringify(message.toolsAdded).length : 0;
|
|
167
|
-
const toolsRemoved = message.toolsRemoved?.length ? safeJsonStringify(message.toolsRemoved).length : 0;
|
|
168
|
-
chars += toolsAdded + toolsRemoved;
|
|
169
|
-
return Math.round(chars / CHARS_PER_TOKEN);
|
|
170
|
-
}
|
|
171
|
-
if (
|
|
172
|
-
(message.role === "assistant" || message.role === "user" || message.role === "toolResult") &&
|
|
173
|
-
message.content != null
|
|
174
|
-
) {
|
|
175
|
-
return estimateContentTokens(message.content);
|
|
176
|
-
}
|
|
177
|
-
return 0;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* CC: tokenCountWithEstimation — THE canonical context measurement for
|
|
182
|
-
* threshold checks. Last valid API usage (exact tokens for everything up to
|
|
183
|
-
* that response) plus a rough estimate of everything appended since.
|
|
184
|
-
*
|
|
185
|
-
* Note: CC walks back past sibling assistant records split from the same API
|
|
186
|
-
* response (shared message.id, interleaved tool_results). pi does not split
|
|
187
|
-
* assistant records per content block, so no walk-back is needed.
|
|
188
|
-
*/
|
|
189
|
-
export function tokenCountWithEstimation(messages: readonly RoughMessage[]): number {
|
|
190
|
-
const usageInfo = findLastUsageInfo(messages);
|
|
191
|
-
if (usageInfo) {
|
|
192
|
-
let trailingTokens = 0;
|
|
193
|
-
for (let i = usageInfo.index + 1; i < messages.length; i++) {
|
|
194
|
-
trailingTokens += estimateMessageTokens(messages[i]);
|
|
195
|
-
}
|
|
196
|
-
return getTokenCountFromUsage(usageInfo.usage) + trailingTokens;
|
|
197
|
-
}
|
|
198
|
-
let tokens = 0;
|
|
199
|
-
for (const message of messages) tokens += estimateMessageTokens(message);
|
|
200
|
-
return tokens;
|
|
201
|
-
}
|