@pasko70/pibo 3.4.1 → 3.4.3
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/dist/agent-runtime/routed-session.js +6 -1
- package/dist/agent-runtimes/pi/adapter.js +4 -1
- package/dist/agent-runtimes/pi/history.js +55 -1
- package/dist/apps/chat/chat-settings-routes.js +10 -0
- package/dist/apps/chat/data/chat-data-mappers.js +1 -1
- package/dist/apps/chat/stream.js +1 -1
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +2 -1
- package/dist/apps/chat-ui/assets/{dist-DnZL1rRp.js → dist-B4YhOxh0.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D0md0xIR.js → dist-BkUu8WPA.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-ERKABhp3.js → dist-C8GzUMPk.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DHnaUMlq.js → dist-Cyb4rVa6.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Abz605MV.js → dist-DeMKnZR8.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BOceJ0jM.css +1 -0
- package/dist/apps/chat-ui/assets/index-Dk4mbXAB.js +228 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-0oTGFHni.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/auth/openai-codex-usage.js +34 -5
- package/dist/core/session-router.js +95 -11
- package/dist/core/user-settings.js +11 -0
- package/dist/data/ingest-service.js +1 -1
- package/dist/data/schema.js +10 -1
- package/dist/debug/agents.js +5 -3
- package/dist/gateway/server.js +1 -1
- package/dist/session-ui/terminalRows.js +3 -1
- package/dist/sessions/pibo-data-store.js +38 -1
- package/dist/sessions/store.js +38 -0
- package/dist/shared/tool-call-metrics.js +138 -0
- package/dist/shared/tool-call-token-settings.js +45 -0
- package/dist/shared/trace-event-projection.js +2 -0
- package/dist/shared/trace-live-reducer.js +1 -0
- package/dist/shared/trace-patch-nodes.js +4 -0
- package/dist/subagents/context.js +2 -2
- package/dist/subagents/observation-query.js +28 -1
- package/dist/subagents/observations.js +8 -0
- package/dist/subagents/tool.js +12 -8
- package/npm-shrinkwrap.json +22 -13
- package/package.json +2 -1
- package/dist/apps/chat-ui/assets/index-BWVPNFjU.js +0 -228
- package/dist/apps/chat-ui/assets/index-CywOD6EF.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-CZmxeSn3.js +0 -43
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="theme-color" content="#101d22" />
|
|
7
7
|
<title>Pibo</title>
|
|
8
|
-
<script type="module" crossorigin src="/apps/chat-vscode/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/apps/chat-vscode/assets/index-0oTGFHni.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-b18ZkEo0.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { readPiCredential, resolvePiProviderAuth, } from "../agent-runtimes/pi/credentials.js";
|
|
2
3
|
const OPENAI_CODEX_PROVIDER = "openai-codex";
|
|
3
4
|
const OPENAI_CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
4
5
|
const OPENAI_JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
6
|
+
const USAGE_REFRESH_MS = 30_000;
|
|
7
|
+
const USAGE_MAX_STALE_MS = 5 * 60_000;
|
|
8
|
+
const USAGE_REQUEST_TIMEOUT_MS = 2_000;
|
|
9
|
+
// One bounded, credential-scoped snapshot shared by sessions. Never retain raw credentials.
|
|
10
|
+
let usageCache;
|
|
5
11
|
function isRecord(value) {
|
|
6
12
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7
13
|
}
|
|
@@ -125,8 +131,31 @@ export async function getOpenAiCodexProviderUsageForActiveModel(activeModel) {
|
|
|
125
131
|
if (activeModel?.provider !== OPENAI_CODEX_PROVIDER)
|
|
126
132
|
return undefined;
|
|
127
133
|
const credential = await readPiCredential(OPENAI_CODEX_PROVIDER);
|
|
128
|
-
if (credential?.type !== "oauth")
|
|
134
|
+
if (credential?.type !== "oauth") {
|
|
135
|
+
usageCache = undefined;
|
|
129
136
|
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const key = createHash("sha256").update(JSON.stringify(credential)).digest("hex");
|
|
139
|
+
if (usageCache?.key !== key)
|
|
140
|
+
usageCache = { key, refreshAfter: 0 };
|
|
141
|
+
const cache = usageCache;
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
const usable = () => cache.value && Date.now() - Date.parse(cache.value.fetchedAt) < USAGE_MAX_STALE_MS
|
|
144
|
+
? cache.value
|
|
145
|
+
: undefined;
|
|
146
|
+
if (now < cache.refreshAfter)
|
|
147
|
+
return usable();
|
|
148
|
+
cache.pending ??= fetchOpenAiCodexProviderUsage(credential.accountId)
|
|
149
|
+
.then((value) => { cache.value = value; return value; })
|
|
150
|
+
.catch(() => usable())
|
|
151
|
+
.finally(() => {
|
|
152
|
+
cache.refreshAfter = Date.now() + USAGE_REFRESH_MS;
|
|
153
|
+
cache.pending = undefined;
|
|
154
|
+
});
|
|
155
|
+
// Quota is advisory: stale-while-revalidate keeps repeat status commands local.
|
|
156
|
+
return usable() ?? await cache.pending;
|
|
157
|
+
}
|
|
158
|
+
async function fetchOpenAiCodexProviderUsage(storedAccountId) {
|
|
130
159
|
const resolvedAuth = await resolvePiProviderAuth(OPENAI_CODEX_PROVIDER);
|
|
131
160
|
const accessToken = resolvedAuth?.auth.apiKey;
|
|
132
161
|
if (!accessToken)
|
|
@@ -135,13 +164,13 @@ export async function getOpenAiCodexProviderUsageForActiveModel(activeModel) {
|
|
|
135
164
|
Authorization: `Bearer ${accessToken}`,
|
|
136
165
|
"User-Agent": "codex-cli",
|
|
137
166
|
};
|
|
138
|
-
const accountId = getOpenAiAccountId(accessToken,
|
|
167
|
+
const accountId = getOpenAiAccountId(accessToken, storedAccountId);
|
|
139
168
|
if (accountId)
|
|
140
169
|
headers["ChatGPT-Account-Id"] = accountId;
|
|
141
|
-
const response = await fetch(OPENAI_CODEX_USAGE_URL, { headers });
|
|
170
|
+
const response = await fetch(OPENAI_CODEX_USAGE_URL, { headers, signal: AbortSignal.timeout(USAGE_REQUEST_TIMEOUT_MS) });
|
|
142
171
|
if (!response.ok) {
|
|
143
|
-
|
|
144
|
-
throw new Error(`OpenAI Codex usage request failed: ${response.status}
|
|
172
|
+
await response.body?.cancel();
|
|
173
|
+
throw new Error(`OpenAI Codex usage request failed: ${response.status}`);
|
|
145
174
|
}
|
|
146
175
|
return normalizeUsagePayload(await response.json());
|
|
147
176
|
}
|
|
@@ -7,13 +7,13 @@ import { runtimeSessionErrorDetails } from "./session-errors.js";
|
|
|
7
7
|
import { OutputRenderSequencer, outputRenderHighWaterStore } from "./output-render-sequence.js";
|
|
8
8
|
import { normalizePiboAgentSessionName, } from "../subagents/tool.js";
|
|
9
9
|
import { piboAgentObservationDetails, piboAgentObservationKind, piboAgentObservationRole, piboAgentObservationSourceFromEvent, piboAgentObservationText, } from "../subagents/observations.js";
|
|
10
|
-
import { preparePiboAgentObservationQuery, selectPiboAgentObservationPage, } from "../subagents/observation-query.js";
|
|
10
|
+
import { piboAgentObservationCursorScopeKey, preparePiboAgentObservationQuery, selectPiboAgentObservationPage, } from "../subagents/observation-query.js";
|
|
11
11
|
import { PiboRunRegistry } from "../runs/registry.js";
|
|
12
12
|
import { PiboRunCancellationError, PiboRunCancelledError, PiboRunExecutionTimeoutError, waitForRunCancellationSettlement } from "../runs/lifecycle.js";
|
|
13
13
|
import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
|
|
14
14
|
import { createPiboSignalRegistry } from "../signals/registry.js";
|
|
15
15
|
import { createDefaultPiboReliabilityStore } from "../reliability/store.js";
|
|
16
|
-
import { InMemoryPiboSessionStore, createPiboSessionId, } from "../sessions/store.js";
|
|
16
|
+
import { PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES, InMemoryPiboSessionStore, createPiboSessionId, } from "../sessions/store.js";
|
|
17
17
|
import { createAgentRuntimeBindingPersistence } from "../sessions/runtime-binding-persistence.js";
|
|
18
18
|
import { createLegacyPiRuntimeSessionBinding, RuntimeSessionBindingConflictError, } from "../sessions/runtime-binding.js";
|
|
19
19
|
import { AgentRuntimeBindingMissingError, AgentRuntimeUnavailableError } from "../agent-runtime/errors.js";
|
|
@@ -349,7 +349,9 @@ export class PiboSessionRouter {
|
|
|
349
349
|
activeSubagentRequests = new Map();
|
|
350
350
|
subagentRequestIdsByEvent = new Map();
|
|
351
351
|
agentObservations = [];
|
|
352
|
+
agentObservationHighWaterByParent = new Map();
|
|
352
353
|
agentObservationEvictedThroughByParent = new Map();
|
|
354
|
+
agentObservationAutoCursorFallback = new Map();
|
|
353
355
|
nextAgentObservationSequence = 1;
|
|
354
356
|
scheduledRunReminders = new Map();
|
|
355
357
|
runReminderDeliveries = new Map();
|
|
@@ -945,16 +947,41 @@ export class PiboSessionRouter {
|
|
|
945
947
|
const status = this.sessions.get(piboSessionId)?.getStatus();
|
|
946
948
|
return status ? this.withPersistedRuntimeBinding(status) : undefined;
|
|
947
949
|
}
|
|
948
|
-
async getSessionStatusSnapshot(piboSessionId) {
|
|
949
|
-
const session = await this.getOrCreateSession(piboSessionId);
|
|
950
|
+
async getSessionStatusSnapshot(piboSessionId, options) {
|
|
951
|
+
const session = options?.activate === false ? this.sessions.get(piboSessionId) : await this.getOrCreateSession(piboSessionId);
|
|
952
|
+
if (!session) {
|
|
953
|
+
this.resolvePiboSession(piboSessionId);
|
|
954
|
+
return undefined;
|
|
955
|
+
}
|
|
950
956
|
try {
|
|
951
957
|
return this.withPersistedRuntimeBinding(await session.getStatusSnapshot());
|
|
952
958
|
}
|
|
953
959
|
finally {
|
|
954
|
-
|
|
960
|
+
// Passive header polling must neither create nor indefinitely retain a runtime.
|
|
961
|
+
if (options?.activate !== false)
|
|
962
|
+
this.scheduleIdleSessionEvictionIfIdle(piboSessionId);
|
|
955
963
|
}
|
|
956
964
|
}
|
|
957
965
|
async getSessionForkCandidates(piboSessionId) {
|
|
966
|
+
const canReadPersisted = () => !this.closing
|
|
967
|
+
&& !this.quiescingSessions.has(piboSessionId)
|
|
968
|
+
&& !this.disposingSessions.has(piboSessionId)
|
|
969
|
+
&& !this.sessions.has(piboSessionId)
|
|
970
|
+
&& !this.pendingSessions.has(piboSessionId);
|
|
971
|
+
if (canReadPersisted()) {
|
|
972
|
+
const record = this.resolvePiboSession(piboSessionId);
|
|
973
|
+
const binding = this.resolveSessionRuntimeBinding(record);
|
|
974
|
+
const adapter = this.resolveAgentRuntimeRegistry(binding.runtimeInstanceId).requireAgentRuntimeAdapter(binding.runtimeInstanceId);
|
|
975
|
+
if (binding.state === "bound" && adapter.descriptor.id === binding.adapterId
|
|
976
|
+
&& adapter.descriptor.capabilities.lifecycle.fork && adapter.readForkCandidates) {
|
|
977
|
+
const workspace = record.workspace ?? this.options.cwd ?? getDefaultPiboWorkspace();
|
|
978
|
+
const candidates = await adapter.readForkCandidates({ binding, workspace });
|
|
979
|
+
const current = this.resolvePiboSession(piboSessionId);
|
|
980
|
+
if (candidates !== undefined && canReadPersisted() && current.workspace === record.workspace
|
|
981
|
+
&& runtimeBindingsEqual(binding, this.resolveSessionRuntimeBinding(current)))
|
|
982
|
+
return candidates;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
958
985
|
const session = await this.getOrCreateSession(piboSessionId);
|
|
959
986
|
try {
|
|
960
987
|
return await session.getForkCandidates();
|
|
@@ -1146,7 +1173,9 @@ export class PiboSessionRouter {
|
|
|
1146
1173
|
this.subagentRequestIdsByEvent.clear();
|
|
1147
1174
|
this.outputRenderSequencer.disposeAll();
|
|
1148
1175
|
this.agentObservations.length = 0;
|
|
1176
|
+
this.agentObservationHighWaterByParent.clear();
|
|
1149
1177
|
this.agentObservationEvictedThroughByParent.clear();
|
|
1178
|
+
this.agentObservationAutoCursorFallback.clear();
|
|
1150
1179
|
await this.telemetryWriter?.dispose();
|
|
1151
1180
|
const lifecycleFailures = [...authDisposals, ...webAppDisposals]
|
|
1152
1181
|
.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
@@ -1523,6 +1552,7 @@ export class PiboSessionRouter {
|
|
|
1523
1552
|
return result;
|
|
1524
1553
|
},
|
|
1525
1554
|
statusResources,
|
|
1555
|
+
getToolMetricTokenCalculation: () => loadPiboUserSettings().toolMetrics.tokenCalculation,
|
|
1526
1556
|
});
|
|
1527
1557
|
this.sessions.set(piboSession.id, session);
|
|
1528
1558
|
return session;
|
|
@@ -2098,8 +2128,18 @@ export class PiboSessionRouter {
|
|
|
2098
2128
|
observeManagedAgents(parentPiboSessionId, input) {
|
|
2099
2129
|
for (const agentId of input.agentIds ?? [])
|
|
2100
2130
|
this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
2101
|
-
const
|
|
2131
|
+
const baseQuery = preparePiboAgentObservationQuery(input);
|
|
2132
|
+
const cursorScope = piboAgentObservationCursorScopeKey(baseQuery.filters);
|
|
2133
|
+
const explicitAfterSequence = input.afterSequence !== undefined;
|
|
2134
|
+
const savedAfterSequence = baseQuery.cursorMode === "auto" && !explicitAfterSequence
|
|
2135
|
+
? this.getAgentObservationAutoCursor(parentPiboSessionId, cursorScope)
|
|
2136
|
+
: undefined;
|
|
2137
|
+
const query = savedAfterSequence === undefined
|
|
2138
|
+
? baseQuery
|
|
2139
|
+
: preparePiboAgentObservationQuery({ ...input, afterSequence: savedAfterSequence });
|
|
2102
2140
|
const observations = this.agentObservations;
|
|
2141
|
+
const evictedThrough = this.agentObservationEvictedThroughByParent.get(parentPiboSessionId) ?? 0;
|
|
2142
|
+
const sourceHighWater = Math.max(evictedThrough, this.agentObservationHighWaterByParent.get(parentPiboSessionId) ?? 0);
|
|
2103
2143
|
function* ordered() {
|
|
2104
2144
|
const start = query.scanOrder === "asc" ? 0 : observations.length - 1;
|
|
2105
2145
|
const end = query.scanOrder === "asc" ? observations.length : -1;
|
|
@@ -2110,7 +2150,48 @@ export class PiboSessionRouter {
|
|
|
2110
2150
|
yield observation;
|
|
2111
2151
|
}
|
|
2112
2152
|
}
|
|
2113
|
-
|
|
2153
|
+
const page = selectPiboAgentObservationPage(ordered(), query, { evictedThrough });
|
|
2154
|
+
if (query.cursorMode === "history")
|
|
2155
|
+
return page;
|
|
2156
|
+
const initialSnapshot = !explicitAfterSequence && savedAfterSequence === undefined;
|
|
2157
|
+
const nextAfterSequence = initialSnapshot || !page.truncated
|
|
2158
|
+
? Math.max(page.nextAfterSequence, sourceHighWater)
|
|
2159
|
+
: page.nextAfterSequence;
|
|
2160
|
+
const advancedAfterSequence = this.advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, nextAfterSequence);
|
|
2161
|
+
return {
|
|
2162
|
+
...page,
|
|
2163
|
+
autoCursorSequence: advancedAfterSequence,
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) {
|
|
2167
|
+
if (this.sessionStore.getAgentObservationAutoCursor) {
|
|
2168
|
+
return this.sessionStore.getAgentObservationAutoCursor(parentPiboSessionId, cursorScope);
|
|
2169
|
+
}
|
|
2170
|
+
return this.agentObservationAutoCursorFallback.get(JSON.stringify([parentPiboSessionId, cursorScope]));
|
|
2171
|
+
}
|
|
2172
|
+
advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence) {
|
|
2173
|
+
if (this.sessionStore.advanceAgentObservationAutoCursor) {
|
|
2174
|
+
return this.sessionStore.advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence);
|
|
2175
|
+
}
|
|
2176
|
+
const key = JSON.stringify([parentPiboSessionId, cursorScope]);
|
|
2177
|
+
const advanced = Math.max(this.agentObservationAutoCursorFallback.get(key) ?? 0, sequence);
|
|
2178
|
+
this.agentObservationAutoCursorFallback.delete(key);
|
|
2179
|
+
this.agentObservationAutoCursorFallback.set(key, advanced);
|
|
2180
|
+
const prefix = `${JSON.stringify([parentPiboSessionId]).slice(0, -1)},`;
|
|
2181
|
+
let scopeCount = 0;
|
|
2182
|
+
for (const existingKey of this.agentObservationAutoCursorFallback.keys()) {
|
|
2183
|
+
if (existingKey.startsWith(prefix))
|
|
2184
|
+
scopeCount += 1;
|
|
2185
|
+
}
|
|
2186
|
+
for (const existingKey of this.agentObservationAutoCursorFallback.keys()) {
|
|
2187
|
+
if (scopeCount <= PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES)
|
|
2188
|
+
break;
|
|
2189
|
+
if (!existingKey.startsWith(prefix))
|
|
2190
|
+
continue;
|
|
2191
|
+
this.agentObservationAutoCursorFallback.delete(existingKey);
|
|
2192
|
+
scopeCount -= 1;
|
|
2193
|
+
}
|
|
2194
|
+
return advanced;
|
|
2114
2195
|
}
|
|
2115
2196
|
async killManagedAgent(parentPiboSessionId, agentId) {
|
|
2116
2197
|
const child = this.requireManagedAgent(parentPiboSessionId, agentId);
|
|
@@ -2300,16 +2381,16 @@ export class PiboSessionRouter {
|
|
|
2300
2381
|
throw new Error(`Subagent "${subagent.name}" exceeded max depth ${maxDepth} from Pibo session "${parentPiboSessionId}"`);
|
|
2301
2382
|
}
|
|
2302
2383
|
}
|
|
2303
|
-
getSubagentDepth(piboSessionId) {
|
|
2384
|
+
getSubagentDepth(piboSessionId, sessionsById) {
|
|
2304
2385
|
let depth = 0;
|
|
2305
|
-
let current = this.sessionStore.get(piboSessionId);
|
|
2386
|
+
let current = sessionsById ? sessionsById.get(piboSessionId) : this.sessionStore.get(piboSessionId);
|
|
2306
2387
|
const seen = new Set();
|
|
2307
2388
|
while (current?.parentId) {
|
|
2308
2389
|
if (seen.has(current.parentId))
|
|
2309
2390
|
break;
|
|
2310
2391
|
seen.add(current.parentId);
|
|
2311
2392
|
depth += 1;
|
|
2312
|
-
current = this.sessionStore.get(current.parentId);
|
|
2393
|
+
current = sessionsById ? sessionsById.get(current.parentId) : this.sessionStore.get(current.parentId);
|
|
2313
2394
|
}
|
|
2314
2395
|
return depth;
|
|
2315
2396
|
}
|
|
@@ -2412,6 +2493,7 @@ export class PiboSessionRouter {
|
|
|
2412
2493
|
details: piboAgentObservationDetails(event),
|
|
2413
2494
|
};
|
|
2414
2495
|
this.agentObservations.push(observation);
|
|
2496
|
+
this.agentObservationHighWaterByParent.set(session.parentId, Math.max(this.agentObservationHighWaterByParent.get(session.parentId) ?? 0, sequence));
|
|
2415
2497
|
if (this.agentObservations.length > MAX_AGENT_OBSERVATIONS) {
|
|
2416
2498
|
const evicted = this.agentObservations.splice(0, this.agentObservations.length - MAX_AGENT_OBSERVATIONS);
|
|
2417
2499
|
for (const item of evicted) {
|
|
@@ -2567,7 +2649,9 @@ export class PiboSessionRouter {
|
|
|
2567
2649
|
}
|
|
2568
2650
|
projectKnownSessionSignals() {
|
|
2569
2651
|
const sessions = this.sessionStore.list?.() ?? [];
|
|
2570
|
-
|
|
2652
|
+
// The complete list is already loaded; avoid an additional store query for every ancestor.
|
|
2653
|
+
const sessionsById = new Map(sessions.map((session) => [session.id, session]));
|
|
2654
|
+
const depthBySessionId = new Map(sessions.map((session) => [session.id, this.getSubagentDepth(session.id, sessionsById)]));
|
|
2571
2655
|
sessions.sort((left, right) => (depthBySessionId.get(left.id) ?? 0) - (depthBySessionId.get(right.id) ?? 0));
|
|
2572
2656
|
for (const session of sessions) {
|
|
2573
2657
|
this.signalRegistry.project({ type: "session_created", session });
|
|
@@ -4,6 +4,7 @@ import { piboHomePath } from "./pibo-home.js";
|
|
|
4
4
|
import { sanitizePreviewServerSettings } from "./preview-server-settings.js";
|
|
5
5
|
import { sanitizeTelemetryRetentionSettings } from "./telemetry-retention-settings.js";
|
|
6
6
|
import { sanitizeTelemetryStaleThresholdSettings } from "./telemetry-staleness.js";
|
|
7
|
+
import { DEFAULT_TOOL_METRIC_TOKEN_CALCULATION, parseToolMetricTokenCalculation, } from "../shared/tool-call-token-settings.js";
|
|
7
8
|
export const DEFAULT_USER_TIMEZONE = "UTC";
|
|
8
9
|
export const DEFAULT_WEB_ANNOTATIONS_TOGGLE_SHORTCUT = "Alt+Shift+A";
|
|
9
10
|
export const DEFAULT_TRANSCRIPTION_PROVIDER_ID = "openai-chatgpt";
|
|
@@ -64,6 +65,15 @@ export function sanitizeSpeechSettings(value) {
|
|
|
64
65
|
: {};
|
|
65
66
|
return { providerId: sanitizeTranscriptionProviderId(raw.providerId) ?? DEFAULT_SPEECH_PROVIDER_ID };
|
|
66
67
|
}
|
|
68
|
+
export function sanitizeToolMetricSettings(value) {
|
|
69
|
+
const raw = value && typeof value === "object" && !Array.isArray(value)
|
|
70
|
+
? value
|
|
71
|
+
: {};
|
|
72
|
+
return {
|
|
73
|
+
tokenCalculation: parseToolMetricTokenCalculation(raw.tokenCalculation)
|
|
74
|
+
?? DEFAULT_TOOL_METRIC_TOKEN_CALCULATION,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
67
77
|
export function sanitizeTimezone(value) {
|
|
68
78
|
if (typeof value !== "string")
|
|
69
79
|
return undefined;
|
|
@@ -87,6 +97,7 @@ function sanitizeUserSettings(value) {
|
|
|
87
97
|
shortcuts: sanitizeShortcutSettings(raw.shortcuts),
|
|
88
98
|
transcription: sanitizeTranscriptionSettings(raw.transcription),
|
|
89
99
|
speech: sanitizeSpeechSettings(raw.speech),
|
|
100
|
+
toolMetrics: sanitizeToolMetricSettings(raw.toolMetrics),
|
|
90
101
|
previewServers: sanitizePreviewServerSettings(raw.previewServers),
|
|
91
102
|
telemetryStaleThresholds: sanitizeTelemetryStaleThresholdSettings(raw.telemetryStaleThresholds),
|
|
92
103
|
telemetryRetention: sanitizeTelemetryRetentionSettings(raw.telemetryRetention),
|
|
@@ -427,7 +427,7 @@ function specificAttributesForOutputEvent(event) {
|
|
|
427
427
|
if (event.type === "tool_call")
|
|
428
428
|
return { toolCallId: event.toolCallId, toolName: event.toolName, argsComplete: event.argsComplete, intent: event.intent };
|
|
429
429
|
if (event.type === "tool_execution_started" || event.type === "tool_execution_updated" || event.type === "tool_execution_finished")
|
|
430
|
-
return { toolCallId: event.toolCallId, toolName: event.toolName, isError: "isError" in event ? event.isError : undefined, intent: event.intent };
|
|
430
|
+
return { toolCallId: event.toolCallId, toolName: event.toolName, isError: "isError" in event ? event.isError : undefined, intent: event.intent, toolMetrics: event.type === "tool_execution_finished" ? event.toolMetrics : undefined };
|
|
431
431
|
if (event.type === "subagent_session")
|
|
432
432
|
return { toolCallId: event.toolCallId, toolName: event.toolName, subagentName: event.subagentName, childPiboSessionId: event.childPiboSessionId, threadKey: event.threadKey };
|
|
433
433
|
if (event.type === "execution_result")
|
package/dist/data/schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const PIBO_DATA_SCHEMA_VERSION =
|
|
1
|
+
export const PIBO_DATA_SCHEMA_VERSION = 9;
|
|
2
2
|
const NATIVE_HISTORY_FALLBACK_SCHEMA_VERSION = 5;
|
|
3
3
|
const retiredScopeColumn = ["owner", "scope"].join("_");
|
|
4
4
|
const payloadTableDefinition = `
|
|
@@ -254,6 +254,15 @@ function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRe
|
|
|
254
254
|
FOREIGN KEY (parent_pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
255
255
|
);
|
|
256
256
|
|
|
257
|
+
CREATE TABLE IF NOT EXISTS session_agent_observation_auto_cursors (
|
|
258
|
+
parent_pibo_session_id TEXT NOT NULL,
|
|
259
|
+
cursor_scope TEXT NOT NULL,
|
|
260
|
+
sequence INTEGER NOT NULL CHECK(sequence >= 0),
|
|
261
|
+
updated_at TEXT NOT NULL,
|
|
262
|
+
PRIMARY KEY (parent_pibo_session_id, cursor_scope),
|
|
263
|
+
FOREIGN KEY (parent_pibo_session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
264
|
+
);
|
|
265
|
+
|
|
257
266
|
CREATE TABLE IF NOT EXISTS session_output_part_counters (
|
|
258
267
|
pibo_session_id TEXT NOT NULL,
|
|
259
268
|
event_id TEXT NOT NULL,
|
package/dist/debug/agents.js
CHANGED
|
@@ -81,7 +81,7 @@ export function inspectDebugAgentObservations(parentPiboSessionId, store, input
|
|
|
81
81
|
throw new Error(`Debug store "pibo-data" not found at ${store.path}`);
|
|
82
82
|
const db = openReadOnlyDebugDatabase(store);
|
|
83
83
|
try {
|
|
84
|
-
const query = preparePiboAgentObservationQuery(input);
|
|
84
|
+
const query = preparePiboAgentObservationQuery({ ...input, cursorMode: "history" });
|
|
85
85
|
const owned = readOwnedAgents(db, parentPiboSessionId);
|
|
86
86
|
const ownedById = new Map(owned.map((agent) => [agent.agentId, agent]));
|
|
87
87
|
for (const agentId of input.agentIds ?? []) {
|
|
@@ -287,8 +287,10 @@ Usage:
|
|
|
287
287
|
[--order asc|desc] [--limit 1..200] [--include-tools]
|
|
288
288
|
[--tool-detail summary|full] [--details] [--json]
|
|
289
289
|
|
|
290
|
-
Default: the newest 20 completed assistant messages
|
|
291
|
-
|
|
290
|
+
Default: the newest 20 completed assistant messages; streaming deltas and tools are hidden.
|
|
291
|
+
The CLI is stateless history inspection. Use --after-sequence for explicit incremental polling.
|
|
292
|
+
Use --include-tools only for stalls, errors, or targeted diagnosis; prefer --tool-call-id when known.
|
|
293
|
+
Explicit --event-type or --kind
|
|
292
294
|
filters retain access to progress events. Repeat plural filters for OR within that field.
|
|
293
295
|
Different fields combine with AND. With --after-sequence, pages always consume the oldest unseen rows;
|
|
294
296
|
--order desc reverses only the returned page, so nextAfterSequence remains safe for polling.
|
package/dist/gateway/server.js
CHANGED
|
@@ -421,7 +421,7 @@ export class PiboGatewayServer {
|
|
|
421
421
|
},
|
|
422
422
|
rebindSessionRuntime: (piboSessionId, input) => this.requireRouter().rebindSessionRuntime(piboSessionId, input),
|
|
423
423
|
getSessionRuntimeStatus: (piboSessionId) => this.requireRouter().getSessionRuntimeStatus(piboSessionId),
|
|
424
|
-
getSessionStatusSnapshot: (piboSessionId) => this.requireRouter().getSessionStatusSnapshot(piboSessionId),
|
|
424
|
+
getSessionStatusSnapshot: (piboSessionId, options) => this.requireRouter().getSessionStatusSnapshot(piboSessionId, options),
|
|
425
425
|
getSessionForkCandidates: (piboSessionId) => this.requireRouter().getSessionForkCandidates(piboSessionId),
|
|
426
426
|
listSessionRuntimeStatuses: () => this.requireRouter().listSessionRuntimeStatuses(),
|
|
427
427
|
listRuns: (options) => this.requireRouter().listRuns(options),
|
|
@@ -22,7 +22,7 @@ export function buildCompactTerminalRows(traceView, options) {
|
|
|
22
22
|
const candidates = syncThinkingToolRows(flatNodes.map((item) => createRowCandidate(item.node, item.turnId)));
|
|
23
23
|
applyCompletedTurnTiming(candidates, turnById);
|
|
24
24
|
const reconciled = reconcileConceptualRowCandidates(candidates);
|
|
25
|
-
const rows = (options.toolDisplayMode ?? "default") === "default"
|
|
25
|
+
const rows = !options.debugMode && (options.toolDisplayMode ?? "default") === "default"
|
|
26
26
|
? groupRelatedToolCandidates(reconciled).map((candidate) => candidate.row)
|
|
27
27
|
: reconciled.map((candidate) => candidate.row);
|
|
28
28
|
return applyToolDisplayMode(rows, options.toolDisplayMode ?? "default");
|
|
@@ -165,6 +165,8 @@ function createRowCandidate(node, turnId) {
|
|
|
165
165
|
...candidate.row,
|
|
166
166
|
id: compactTerminalRowIdentity(node),
|
|
167
167
|
intent: node.intent,
|
|
168
|
+
isToolCall: node.type === "tool.call" || node.type === "tool.result" || (node.type === "agent.delegation" && Boolean(node.toolCallId)),
|
|
169
|
+
toolMetrics: node.toolMetrics,
|
|
168
170
|
...debugFields(node),
|
|
169
171
|
},
|
|
170
172
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ChatDataIngestService } from "../data/ingest-service.js";
|
|
2
2
|
import { PiboDataStore } from "../data/pibo-store.js";
|
|
3
|
-
import { createPiboSession, matchesFindInput, } from "./store.js";
|
|
3
|
+
import { PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES, createPiboSession, matchesFindInput, } from "./store.js";
|
|
4
4
|
import { createLegacyPiRuntimeSessionBinding, nextRuntimeSessionBinding, RuntimeSessionBindingConflictError, } from "./runtime-binding.js";
|
|
5
5
|
const SESSION_SELECT = `
|
|
6
6
|
SELECT
|
|
@@ -165,6 +165,43 @@ export class PiboDataSessionStore {
|
|
|
165
165
|
return sequence;
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
|
+
getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) {
|
|
169
|
+
const row = this.db.prepare(`
|
|
170
|
+
SELECT sequence
|
|
171
|
+
FROM session_agent_observation_auto_cursors
|
|
172
|
+
WHERE parent_pibo_session_id = ? AND cursor_scope = ?
|
|
173
|
+
`).get(parentPiboSessionId, cursorScope);
|
|
174
|
+
return row?.sequence;
|
|
175
|
+
}
|
|
176
|
+
advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence) {
|
|
177
|
+
return this.dataStore.transaction(() => {
|
|
178
|
+
this.db.prepare(`
|
|
179
|
+
INSERT INTO session_agent_observation_auto_cursors (
|
|
180
|
+
parent_pibo_session_id, cursor_scope, sequence, updated_at
|
|
181
|
+
)
|
|
182
|
+
SELECT id, ?, ?, ? FROM sessions WHERE id = ? AND deleted_at IS NULL
|
|
183
|
+
ON CONFLICT(parent_pibo_session_id, cursor_scope) DO UPDATE SET
|
|
184
|
+
sequence = MAX(session_agent_observation_auto_cursors.sequence, excluded.sequence),
|
|
185
|
+
updated_at = CASE
|
|
186
|
+
WHEN excluded.sequence > session_agent_observation_auto_cursors.sequence THEN excluded.updated_at
|
|
187
|
+
ELSE session_agent_observation_auto_cursors.updated_at
|
|
188
|
+
END
|
|
189
|
+
`).run(cursorScope, sequence, new Date().toISOString(), parentPiboSessionId);
|
|
190
|
+
this.db.prepare(`
|
|
191
|
+
DELETE FROM session_agent_observation_auto_cursors
|
|
192
|
+
WHERE parent_pibo_session_id = ?
|
|
193
|
+
AND cursor_scope <> ?
|
|
194
|
+
AND cursor_scope NOT IN (
|
|
195
|
+
SELECT cursor_scope
|
|
196
|
+
FROM session_agent_observation_auto_cursors
|
|
197
|
+
WHERE parent_pibo_session_id = ? AND cursor_scope <> ?
|
|
198
|
+
ORDER BY updated_at DESC, cursor_scope DESC
|
|
199
|
+
LIMIT ?
|
|
200
|
+
)
|
|
201
|
+
`).run(parentPiboSessionId, cursorScope, parentPiboSessionId, cursorScope, PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES - 1);
|
|
202
|
+
return this.getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) ?? sequence;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
168
205
|
claimOrAttachOutputPart(input) {
|
|
169
206
|
return this.dataStore.transaction(() => {
|
|
170
207
|
const indexAttribute = outputPartIndexAttribute(input.kind);
|
package/dist/sessions/store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { createInitialRuntimeSessionBinding, createLegacyPiRuntimeSessionBinding, nextRuntimeSessionBinding, } from "./runtime-binding.js";
|
|
3
|
+
export const PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES = 128;
|
|
3
4
|
export function createPiboSessionId() {
|
|
4
5
|
return `ps_${randomUUID()}`;
|
|
5
6
|
}
|
|
@@ -50,6 +51,7 @@ export class InMemoryPiboSessionStore {
|
|
|
50
51
|
byNativeSession = new Map();
|
|
51
52
|
outputRenderHighWater = new Map();
|
|
52
53
|
agentObservationNextSequence = new Map();
|
|
54
|
+
agentObservationAutoCursors = new Map();
|
|
53
55
|
outputToolInvocationNextOrdinal = new Map();
|
|
54
56
|
get(id) {
|
|
55
57
|
return this.byId.get(id);
|
|
@@ -128,6 +130,34 @@ export class InMemoryPiboSessionStore {
|
|
|
128
130
|
this.agentObservationNextSequence.set(parentPiboSessionId, sequence + 1);
|
|
129
131
|
return sequence;
|
|
130
132
|
}
|
|
133
|
+
getAgentObservationAutoCursor(parentPiboSessionId, cursorScope) {
|
|
134
|
+
if (!this.byId.has(parentPiboSessionId))
|
|
135
|
+
return undefined;
|
|
136
|
+
return this.agentObservationAutoCursors.get(agentObservationAutoCursorKey(parentPiboSessionId, cursorScope));
|
|
137
|
+
}
|
|
138
|
+
advanceAgentObservationAutoCursor(parentPiboSessionId, cursorScope, sequence) {
|
|
139
|
+
if (!this.byId.has(parentPiboSessionId))
|
|
140
|
+
return sequence;
|
|
141
|
+
const key = agentObservationAutoCursorKey(parentPiboSessionId, cursorScope);
|
|
142
|
+
const advanced = Math.max(this.agentObservationAutoCursors.get(key) ?? 0, sequence);
|
|
143
|
+
this.agentObservationAutoCursors.delete(key);
|
|
144
|
+
this.agentObservationAutoCursors.set(key, advanced);
|
|
145
|
+
const prefix = `${JSON.stringify([parentPiboSessionId]).slice(0, -1)},`;
|
|
146
|
+
let scopeCount = 0;
|
|
147
|
+
for (const existingKey of this.agentObservationAutoCursors.keys()) {
|
|
148
|
+
if (existingKey.startsWith(prefix))
|
|
149
|
+
scopeCount += 1;
|
|
150
|
+
}
|
|
151
|
+
for (const existingKey of this.agentObservationAutoCursors.keys()) {
|
|
152
|
+
if (scopeCount <= PIBO_AGENT_OBSERVATION_AUTO_CURSOR_MAX_SCOPES)
|
|
153
|
+
break;
|
|
154
|
+
if (!existingKey.startsWith(prefix))
|
|
155
|
+
continue;
|
|
156
|
+
this.agentObservationAutoCursors.delete(existingKey);
|
|
157
|
+
scopeCount -= 1;
|
|
158
|
+
}
|
|
159
|
+
return advanced;
|
|
160
|
+
}
|
|
131
161
|
claimOutputToolInvocationOrdinal(piboSessionId, eventId, toolCallId) {
|
|
132
162
|
const key = outputToolInvocationCounterKey(piboSessionId, eventId, toolCallId);
|
|
133
163
|
const ordinal = this.outputToolInvocationNextOrdinal.get(key) ?? 0;
|
|
@@ -172,6 +202,11 @@ export class InMemoryPiboSessionStore {
|
|
|
172
202
|
this.byNativeSession.delete(nativeKey);
|
|
173
203
|
this.outputRenderHighWater.delete(id);
|
|
174
204
|
this.agentObservationNextSequence.delete(id);
|
|
205
|
+
const observationCursorPrefix = `${JSON.stringify([id]).slice(0, -1)},`;
|
|
206
|
+
for (const key of this.agentObservationAutoCursors.keys()) {
|
|
207
|
+
if (key.startsWith(observationCursorPrefix))
|
|
208
|
+
this.agentObservationAutoCursors.delete(key);
|
|
209
|
+
}
|
|
175
210
|
const counterPrefix = `${JSON.stringify([id]).slice(0, -1)},`;
|
|
176
211
|
for (const key of this.outputToolInvocationNextOrdinal.keys()) {
|
|
177
212
|
if (key.startsWith(counterPrefix))
|
|
@@ -213,6 +248,9 @@ function outputRenderSequenceHighWater(metadata) {
|
|
|
213
248
|
const value = metadata?.outputRenderSequenceHighWater;
|
|
214
249
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
215
250
|
}
|
|
251
|
+
function agentObservationAutoCursorKey(parentPiboSessionId, cursorScope) {
|
|
252
|
+
return JSON.stringify([parentPiboSessionId, cursorScope]);
|
|
253
|
+
}
|
|
216
254
|
function outputToolInvocationCounterKey(piboSessionId, eventId, toolCallId) {
|
|
217
255
|
return JSON.stringify([piboSessionId, eventId, toolCallId]);
|
|
218
256
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { DEFAULT_TOOL_METRIC_TOKEN_CALCULATION, sanitizeToolMetricTokenCalculation, toolMetricTokenBasis, } from "./tool-call-token-settings.js";
|
|
3
|
+
export const MAX_TIKTOKEN_PAYLOAD_CHARACTERS = 4_000_000;
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
let activeTokenizer;
|
|
6
|
+
/** Bounded structural walk; character mode never tokenizes, copies, or scans large strings. */
|
|
7
|
+
export function measureToolPayloadCharacters(payload) {
|
|
8
|
+
let budget = 10_000;
|
|
9
|
+
const seen = new WeakSet();
|
|
10
|
+
function size(value, depth) {
|
|
11
|
+
if (--budget < 0 || depth > 64)
|
|
12
|
+
return NaN;
|
|
13
|
+
if (typeof value === "string")
|
|
14
|
+
return value.length;
|
|
15
|
+
if (value === null)
|
|
16
|
+
return 4;
|
|
17
|
+
if (typeof value === "boolean")
|
|
18
|
+
return value ? 4 : 5;
|
|
19
|
+
if (typeof value === "number")
|
|
20
|
+
return String(value).length;
|
|
21
|
+
if (typeof value !== "object" || seen.has(value))
|
|
22
|
+
return NaN;
|
|
23
|
+
seen.add(value);
|
|
24
|
+
let chars = 2;
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
for (const item of value) {
|
|
27
|
+
chars += size(item, depth + 1) + 1;
|
|
28
|
+
if (!Number.isFinite(chars))
|
|
29
|
+
return NaN;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const record = value;
|
|
34
|
+
if (["image", "audio", "document", "resource", "image_url"].includes(String(record.type)))
|
|
35
|
+
return NaN;
|
|
36
|
+
for (const key in record) {
|
|
37
|
+
if (--budget < 0)
|
|
38
|
+
return NaN;
|
|
39
|
+
if (!Object.hasOwn(record, key) || record[key] === undefined)
|
|
40
|
+
continue;
|
|
41
|
+
chars += key.length + 3 + size(record[key], depth + 1) + 1;
|
|
42
|
+
if (!Number.isFinite(chars))
|
|
43
|
+
return NaN;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
seen.delete(value);
|
|
47
|
+
return chars;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const chars = size(payload, 0);
|
|
51
|
+
return Number.isFinite(chars) ? chars : undefined;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function estimateToolPayloadTokens(payload, calculation = DEFAULT_TOOL_METRIC_TOKEN_CALCULATION) {
|
|
58
|
+
try {
|
|
59
|
+
const normalized = sanitizeToolMetricTokenCalculation(calculation);
|
|
60
|
+
if (normalized.method === "characters") {
|
|
61
|
+
const chars = measureToolPayloadCharacters(payload);
|
|
62
|
+
if (chars === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
const tokens = Math.ceil(chars / normalized.factor);
|
|
65
|
+
return Number.isSafeInteger(tokens) ? tokens : undefined;
|
|
66
|
+
}
|
|
67
|
+
const text = serializableToolPayloadText(payload);
|
|
68
|
+
if (text === undefined)
|
|
69
|
+
return undefined;
|
|
70
|
+
return tiktokenPayloadTokens(text, normalized.encoding);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Diagnostics must not turn an otherwise successful tool into a failure.
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export class ToolCallMetricsCollector {
|
|
78
|
+
getCalculation;
|
|
79
|
+
active = new Map();
|
|
80
|
+
constructor(getCalculation = () => DEFAULT_TOOL_METRIC_TOKEN_CALCULATION) {
|
|
81
|
+
this.getCalculation = getCalculation;
|
|
82
|
+
}
|
|
83
|
+
start(id, args, now = performance.now()) {
|
|
84
|
+
if (this.active.has(id))
|
|
85
|
+
return;
|
|
86
|
+
const calculation = this.currentCalculation();
|
|
87
|
+
this.active.set(id, {
|
|
88
|
+
startedAt: now,
|
|
89
|
+
inputTokens: estimateToolPayloadTokens(args, calculation),
|
|
90
|
+
calculation,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
finish(id, result, now = performance.now()) {
|
|
94
|
+
const started = this.active.get(id);
|
|
95
|
+
this.active.delete(id);
|
|
96
|
+
const calculation = started?.calculation ?? this.currentCalculation();
|
|
97
|
+
// Harness result metadata is not model-visible tool output.
|
|
98
|
+
const output = result && typeof result === "object" && "content" in result
|
|
99
|
+
? result.content : result;
|
|
100
|
+
return {
|
|
101
|
+
tokenBasis: toolMetricTokenBasis(calculation),
|
|
102
|
+
durationMs: started ? Math.max(0, now - started.startedAt) : undefined,
|
|
103
|
+
inputTokens: started?.inputTokens,
|
|
104
|
+
outputTokens: estimateToolPayloadTokens(output, calculation),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
clear() {
|
|
108
|
+
this.active.clear();
|
|
109
|
+
}
|
|
110
|
+
currentCalculation() {
|
|
111
|
+
try {
|
|
112
|
+
return sanitizeToolMetricTokenCalculation(this.getCalculation());
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return DEFAULT_TOOL_METRIC_TOKEN_CALCULATION;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function serializableToolPayloadText(payload) {
|
|
120
|
+
const measuredCharacters = measureToolPayloadCharacters(payload);
|
|
121
|
+
if (measuredCharacters === undefined || measuredCharacters > MAX_TIKTOKEN_PAYLOAD_CHARACTERS)
|
|
122
|
+
return undefined;
|
|
123
|
+
if (typeof payload === "string")
|
|
124
|
+
return payload;
|
|
125
|
+
const serialized = JSON.stringify(payload);
|
|
126
|
+
return typeof serialized === "string" && serialized.length <= MAX_TIKTOKEN_PAYLOAD_CHARACTERS
|
|
127
|
+
? serialized
|
|
128
|
+
: undefined;
|
|
129
|
+
}
|
|
130
|
+
function tiktokenPayloadTokens(text, encoding) {
|
|
131
|
+
if (activeTokenizer?.encoding !== encoding) {
|
|
132
|
+
activeTokenizer?.tokenizer.free();
|
|
133
|
+
const tiktoken = require("tiktoken");
|
|
134
|
+
activeTokenizer = { encoding, tokenizer: tiktoken.get_encoding(encoding) };
|
|
135
|
+
}
|
|
136
|
+
const tokens = activeTokenizer.tokenizer.encode_ordinary(text).length;
|
|
137
|
+
return Number.isSafeInteger(tokens) ? tokens : undefined;
|
|
138
|
+
}
|