@lensmcp/protocol-types 1.25.33 → 1.25.34
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.d.ts +6 -0
- package/index.js +1 -1
- package/lib/ai-call-groups.d.ts +56 -0
- package/lib/ai-call-groups.js +2 -0
- package/lib/ai-call-row.d.ts +80 -0
- package/lib/ai-call-row.js +3 -3
- package/lib/ai-call.d.ts +2 -0
- package/lib/ai-call.js +8 -8
- package/lib/ai-context.d.ts +113 -0
- package/lib/ai-context.js +6 -0
- package/lib/ai-origins.d.ts +72 -0
- package/lib/ai-origins.js +10 -0
- package/lib/dashboard.d.ts +10 -3
- package/lib/event-loop.d.ts +46 -0
- package/lib/event-loop.js +1 -1
- package/lib/team-design.d.ts +228 -0
- package/lib/team-design.js +1 -0
- package/lib/team-library.d.ts +283 -0
- package/lib/team-library.js +1 -0
- package/lib/team.d.ts +209 -0
- package/lib/team.js +1 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ export * from './lib/ai-content.js';
|
|
|
19
19
|
export * from './lib/http-exchange.js';
|
|
20
20
|
export * from './lib/ai-call.js';
|
|
21
21
|
export * from './lib/ai-call-row.js';
|
|
22
|
+
export * from './lib/ai-call-groups.js';
|
|
23
|
+
export * from './lib/ai-context.js';
|
|
24
|
+
export * from './lib/ai-origins.js';
|
|
22
25
|
export * from './lib/ai-playground.js';
|
|
23
26
|
export * from './lib/pod-memory.js';
|
|
24
27
|
export * from './lib/perf-rules.js';
|
|
@@ -27,3 +30,6 @@ export * from './lib/page-visit.js';
|
|
|
27
30
|
export * from './lib/replay.js';
|
|
28
31
|
export * from './lib/event-loop.js';
|
|
29
32
|
export * from './lib/workspace-actions.js';
|
|
33
|
+
export * from './lib/team.js';
|
|
34
|
+
export * from './lib/team-design.js';
|
|
35
|
+
export * from './lib/team-library.js';
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export*from"./lib/schema-version.js";export*from"./lib/source-location.js";export*from"./lib/context.js";export*from"./lib/event.js";export*from"./lib/node.js";export*from"./lib/edge.js";export*from"./lib/attributes.js";export*from"./lib/producer-health.js";export*from"./lib/resources.js";export*from"./lib/tokens.js";export*from"./lib/capture-demand.js";export*from"./lib/bounded-clone.js";export*from"./lib/bounded-map.js";export*from"./lib/flood-guard.js";export*from"./lib/dashboard.js";export*from"./lib/log-query.js";export*from"./lib/error-dismissal.js";export*from"./lib/ai-content.js";export*from"./lib/http-exchange.js";export*from"./lib/ai-call.js";export*from"./lib/ai-call-row.js";export*from"./lib/ai-playground.js";export*from"./lib/pod-memory.js";export*from"./lib/perf-rules.js";export*from"./lib/sources.js";export*from"./lib/page-visit.js";export*from"./lib/replay.js";export*from"./lib/event-loop.js";export*from"./lib/workspace-actions.js";
|
|
1
|
+
"use strict";export*from"./lib/schema-version.js";export*from"./lib/source-location.js";export*from"./lib/context.js";export*from"./lib/event.js";export*from"./lib/node.js";export*from"./lib/edge.js";export*from"./lib/attributes.js";export*from"./lib/producer-health.js";export*from"./lib/resources.js";export*from"./lib/tokens.js";export*from"./lib/capture-demand.js";export*from"./lib/bounded-clone.js";export*from"./lib/bounded-map.js";export*from"./lib/flood-guard.js";export*from"./lib/dashboard.js";export*from"./lib/log-query.js";export*from"./lib/error-dismissal.js";export*from"./lib/ai-content.js";export*from"./lib/http-exchange.js";export*from"./lib/ai-call.js";export*from"./lib/ai-call-row.js";export*from"./lib/ai-call-groups.js";export*from"./lib/ai-context.js";export*from"./lib/ai-origins.js";export*from"./lib/ai-playground.js";export*from"./lib/pod-memory.js";export*from"./lib/perf-rules.js";export*from"./lib/sources.js";export*from"./lib/page-visit.js";export*from"./lib/replay.js";export*from"./lib/event-loop.js";export*from"./lib/workspace-actions.js";export*from"./lib/team.js";export*from"./lib/team-design.js";export*from"./lib/team-library.js";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { AiCallRow, AiNames, AiNameSource } from './ai-call-row.js';
|
|
2
|
+
import type { AiPartKind } from './ai-context.js';
|
|
3
|
+
/**
|
|
4
|
+
* The kept AI calls in groups (planning/ai-calls) — by the agent that made them (the default), the action they were
|
|
5
|
+
* for, their run, their thread, their flow, the model, the service, or any label the app put on them (`meta:case`) —
|
|
6
|
+
* and inside a group, its runs: one turn of an agent's tool loop, its calls by step.
|
|
7
|
+
*
|
|
8
|
+
* Pure, on rows already named and in their runs (`/api/ai/calls`, the AI call index's view), and shared: the AI calls
|
|
9
|
+
* page groups by it, and so does the MCP server's `ai_calls`.
|
|
10
|
+
*/
|
|
11
|
+
export type AiGroupBy = 'agent' | 'action' | 'run' | 'thread' | 'flow' | 'model' | 'service' | `meta:${string}`;
|
|
12
|
+
export interface AiTotals {
|
|
13
|
+
calls: number;
|
|
14
|
+
runs: number;
|
|
15
|
+
failed: number;
|
|
16
|
+
input: number;
|
|
17
|
+
cached: number;
|
|
18
|
+
written: number;
|
|
19
|
+
output: number;
|
|
20
|
+
/** What the input went to, by kind — summed over the calls whose split was read. */
|
|
21
|
+
split: Partial<Record<AiPartKind, number>>;
|
|
22
|
+
/** Estimated; `priced` of the calls had a price. */
|
|
23
|
+
costUsd: number;
|
|
24
|
+
priced: number;
|
|
25
|
+
first: number;
|
|
26
|
+
last: number;
|
|
27
|
+
models: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface AiRun {
|
|
30
|
+
key: string;
|
|
31
|
+
from: AiCallRow['run']['from'];
|
|
32
|
+
/** Its calls by step. */
|
|
33
|
+
calls: AiCallRow[];
|
|
34
|
+
at: number;
|
|
35
|
+
last: number;
|
|
36
|
+
totals: AiTotals;
|
|
37
|
+
}
|
|
38
|
+
export interface AiGroup {
|
|
39
|
+
key: string;
|
|
40
|
+
name: string;
|
|
41
|
+
/** Where an agent's or an action's name came from. */
|
|
42
|
+
from?: AiNameSource;
|
|
43
|
+
/** Agent and action groups can be renamed and merged; the others are what they are. */
|
|
44
|
+
nameable: boolean;
|
|
45
|
+
/** The groups merged into this one (their keys), from the names file — each can be taken back out. */
|
|
46
|
+
merged: string[];
|
|
47
|
+
runs: AiRun[];
|
|
48
|
+
totals: AiTotals;
|
|
49
|
+
}
|
|
50
|
+
export declare function aiTotalsOf(rows: readonly AiCallRow[]): AiTotals;
|
|
51
|
+
/** The rows' runs, newest first — oldest first (`inOrder`) where the order is the story, as in one flow; each run's calls by step. */
|
|
52
|
+
export declare function aiRunsOf(rows: readonly AiCallRow[], inOrder?: boolean): AiRun[];
|
|
53
|
+
/** The rows in groups, the group with the latest call first; a group's runs newest first. */
|
|
54
|
+
export declare function groupAiCalls(rows: readonly AiCallRow[], by: AiGroupBy, names?: AiNames): AiGroup[];
|
|
55
|
+
/** A call matches a search when any word of it is in its agent, action, model, service, ids, labels or what was said. */
|
|
56
|
+
export declare function matchesAi(r: AiCallRow, q: string): boolean;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var u=Object.defineProperty;var i=(t,e)=>u(t,"name",{value:e,configurable:!0});var m=Object.defineProperty,c=i((t,e)=>m(t,"name",{value:e,configurable:!0}),"i");export function aiTotalsOf(t){const e={calls:t.length,runs:new Set(t.map(a=>a.run.key)).size,failed:0,input:0,cached:0,written:0,output:0,split:{},costUsd:0,priced:0,first:1/0,last:0,models:[]},o=new Map;for(const a of t){a.failed&&e.failed++,e.input+=a.tokens?.input??0,e.cached+=a.tokens?.cached??0,e.written+=a.tokens?.written??0,e.output+=a.tokens?.output??0;for(const[s,n]of Object.entries(a.split??{}))e.split[s]=(e.split[s]??0)+n;a.costUsd!==void 0&&(e.costUsd+=a.costUsd,e.priced++),e.first=Math.min(e.first,a.at),e.last=Math.max(e.last,a.at),a.model&&o.set(a.model,(o.get(a.model)??0)+1)}return e.models=[...o].sort((a,s)=>s[1]-a[1]).map(([a])=>a),t.length||(e.first=0),e}i(aiTotalsOf,"aiTotalsOf"),c(aiTotalsOf,"aiTotalsOf");export function aiRunsOf(t,e=!1){const o=new Map;for(const a of t){const s=o.get(a.run.key);s?s.push(a):o.set(a.run.key,[a])}return[...o.entries()].map(([a,s])=>{s.sort((r,l)=>(r.step??0)-(l.step??0)||r.at-l.at);const n=aiTotalsOf(s);return{key:a,from:s[0].run.from,calls:s,at:n.first,last:n.last,totals:n}}).sort((a,s)=>e?a.at-s.at:s.last-a.last)}i(aiRunsOf,"aiRunsOf"),c(aiRunsOf,"aiRunsOf");const d="(none)";function f(t,e){if(e==="agent")return t.agent;if(e==="action")return t.action??{key:"action:",name:"(no action)"};if(e==="run")return{key:t.run.key,name:t.agent.name};if(e==="thread")return t.thread?{key:t.thread.key,name:`thread ${t.thread.name}`}:{key:"thread:",name:"(no thread)"};if(e==="flow")return t.flowId?{key:`flow:${t.flowId}`,name:t.flowId}:t.requestId?{key:`request:${t.requestId}`,name:`request ${t.requestId}`}:{key:"flow:",name:"(no flow)"};if(e==="model")return{key:`model:${t.model??""}`,name:t.model??"(unknown model)"};if(e==="service")return{key:`service:${t.service}`,name:t.service};const o=e.slice(5),a=t.meta?.[o];return{key:`${e}=${a??""}`,name:a??d}}i(f,"m"),c(f,"keyOf");export function groupAiCalls(t,e,o){const a=new Map;for(const n of t){const r=f(n,e),l=a.get(r.key);l?l.rows.push(n):a.set(r.key,{...r,rows:[n]})}const s=e==="agent"||e==="action";return[...a.values()].map(n=>({key:n.key,name:n.name,...n.from?{from:n.from}:{},nameable:s&&/^(agent|action):./.test(n.key),merged:o?Object.entries(o.merge).filter(([,r])=>r===n.key).map(([r])=>r).sort():[],runs:aiRunsOf(n.rows,e==="flow"),totals:aiTotalsOf(n.rows)})).sort((n,r)=>r.totals.last-n.totals.last||n.name.localeCompare(r.name))}i(groupAiCalls,"groupAiCalls"),c(groupAiCalls,"groupAiCalls");export function matchesAi(t,e){const o=e.toLowerCase().split(/\s+/).filter(Boolean);if(!o.length)return!0;const a=[t.id,t.agent.name,t.action?.name,t.model,t.service,t.flowId,t.requestId,t.schema,t.asked,t.said,...t.called??[],...Object.entries(t.meta??{}).map(([s,n])=>`${s}:${n}`)].filter(Boolean).join(`
|
|
2
|
+
`).toLowerCase();return o.every(s=>a.includes(s))}i(matchesAi,"matchesAi"),c(matchesAi,"matchesAi");
|
package/lib/ai-call-row.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { HttpExchange } from './http-exchange.js';
|
|
2
2
|
import { type AiApi, type AiShape } from './ai-call.js';
|
|
3
|
+
import { type AiPartKind } from './ai-context.js';
|
|
4
|
+
import type { AiOrigins } from './ai-origins.js';
|
|
3
5
|
/**
|
|
4
6
|
* One AI provider's call as a line of the AI calls page — and what it belongs to: the agent that made it, the run
|
|
5
7
|
* (one turn of an agent's tool loop, its rounds) it is part of, the action it was for, its flow.
|
|
@@ -11,6 +13,10 @@ import { type AiApi, type AiShape } from './ai-call.js';
|
|
|
11
13
|
* app did not name is found from the calls themselves: one continuing another's answer (`previous_response_id`), or
|
|
12
14
|
* carrying the results of the tools another's answer called. What a person renames or merges ({@link AiNames}) wins.
|
|
13
15
|
*
|
|
16
|
+
* What joins runs across requests and services is the app's to say: its thread (`metadata.thread` / `conversation` /
|
|
17
|
+
* `case`, or the `x-lensmcp-thread` header — not `session`, a word the capture redacts as a credential) and the run that
|
|
18
|
+
* started this one (`metadata.parent_run`, `x-lensmcp-parent`).
|
|
19
|
+
*
|
|
14
20
|
* Pure, and shared: the server indexes the kept exchanges with it, the page groups by it.
|
|
15
21
|
*/
|
|
16
22
|
/** Where a name came from: renamed by a person, said by the app, or read off the call. */
|
|
@@ -43,9 +49,12 @@ export interface AiCallRow {
|
|
|
43
49
|
tokens?: {
|
|
44
50
|
input?: number;
|
|
45
51
|
cached?: number;
|
|
52
|
+
written?: number;
|
|
46
53
|
output?: number;
|
|
47
54
|
reasoning?: number;
|
|
48
55
|
};
|
|
56
|
+
/** What its input tokens went to, by kind — the provider's count split by estimate ({@link aiContextOf}). */
|
|
57
|
+
split?: Partial<Record<AiPartKind, number>>;
|
|
49
58
|
/** What it cost, estimated from its tokens by the lens's price table (the server's) — none for a model it has no price for. */
|
|
50
59
|
costUsd?: number;
|
|
51
60
|
agent: AiNamed;
|
|
@@ -53,6 +62,14 @@ export interface AiCallRow {
|
|
|
53
62
|
key: string;
|
|
54
63
|
from: AiRunSource;
|
|
55
64
|
};
|
|
65
|
+
/** The conversation, case or document its run serves: what joins runs across requests and services. */
|
|
66
|
+
thread?: {
|
|
67
|
+
key: string;
|
|
68
|
+
name: string;
|
|
69
|
+
from: 'metadata' | 'header';
|
|
70
|
+
};
|
|
71
|
+
/** The run that started its run (`run:<id>`): an agent called by another, a job a conversation started. */
|
|
72
|
+
parent?: string;
|
|
56
73
|
/** Its place in its run, from 1 — set when the rows are linked ({@link linkAiRuns}). */
|
|
57
74
|
step?: number;
|
|
58
75
|
/** The app's own round and attempt (`metadata.round`, `metadata.attempt`). */
|
|
@@ -111,11 +128,72 @@ export interface AiCallView {
|
|
|
111
128
|
row: AiCallRow;
|
|
112
129
|
/** Its run's calls by step, itself among them. */
|
|
113
130
|
run: AiCallRow[];
|
|
131
|
+
/**
|
|
132
|
+
* The other calls of its thread — the conversation, case or document it serves, across requests and services — by
|
|
133
|
+
* time, at most {@link MAX_THREAD_CALLS}; absent when it has no thread.
|
|
134
|
+
*/
|
|
135
|
+
thread?: AiCallRow[];
|
|
114
136
|
exchange?: HttpExchange;
|
|
115
137
|
/** Why the exchange is not here: a dashboard not bound to loopback, or a file no longer kept. */
|
|
116
138
|
withheld?: string;
|
|
117
139
|
names: AiNames;
|
|
140
|
+
/** Where each part of what the model was given came from — set beside the earlier calls of its run, thread and flow (`aiOriginsOf`); only with the exchange. */
|
|
141
|
+
origins?: AiOrigins;
|
|
142
|
+
}
|
|
143
|
+
/** One model's share of an AI provider's kept calls — Data › External APIs › its host › AI models, when the AI tap read none. */
|
|
144
|
+
export interface AiModelUse {
|
|
145
|
+
model: string;
|
|
146
|
+
calls: number;
|
|
147
|
+
failed: number;
|
|
148
|
+
/** Calls its provider reported no usage for. */
|
|
149
|
+
unmetered: number;
|
|
150
|
+
input: number;
|
|
151
|
+
cached: number;
|
|
152
|
+
output: number;
|
|
153
|
+
/** Over the `priced` calls — those the lens's price table has a price for. */
|
|
154
|
+
costUsd: number;
|
|
155
|
+
priced: number;
|
|
156
|
+
/** The agents that called it. */
|
|
157
|
+
agents: number;
|
|
158
|
+
first: number;
|
|
159
|
+
last: number;
|
|
160
|
+
}
|
|
161
|
+
/** One agent's share of an AI provider's kept calls — and what its input went to (its calls' `split`, summed). */
|
|
162
|
+
export interface AiAgentUse {
|
|
163
|
+
key: string;
|
|
164
|
+
name: string;
|
|
165
|
+
from: AiNameSource;
|
|
166
|
+
calls: number;
|
|
167
|
+
runs: number;
|
|
168
|
+
failed: number;
|
|
169
|
+
input: number;
|
|
170
|
+
cached: number;
|
|
171
|
+
written: number;
|
|
172
|
+
output: number;
|
|
173
|
+
costUsd: number;
|
|
174
|
+
priced: number;
|
|
175
|
+
split: Partial<Record<AiPartKind, number>>;
|
|
176
|
+
models: string[];
|
|
177
|
+
last: number;
|
|
178
|
+
}
|
|
179
|
+
/** `GET /api/ai/models?host=` — the host's kept AI calls, by model and by agent: the AI tap is off unless a service turns it on, the kept calls are not. */
|
|
180
|
+
export interface AiModelsView {
|
|
181
|
+
host: string;
|
|
182
|
+
at: number;
|
|
183
|
+
calls: number;
|
|
184
|
+
oldest?: number;
|
|
185
|
+
models: AiModelUse[];
|
|
186
|
+
agents?: AiAgentUse[];
|
|
187
|
+
/** The kept calls are still being read for the first time: how far. */
|
|
188
|
+
reading?: {
|
|
189
|
+
done: number;
|
|
190
|
+
total: number;
|
|
191
|
+
};
|
|
118
192
|
}
|
|
193
|
+
/** The calls to `host` by model, the most called first. */
|
|
194
|
+
export declare function aiModelUses(rows: readonly AiCallRow[], host: string): AiModelUse[];
|
|
195
|
+
/** The calls to `host` — to every host when none is named (`ai://agents`) — by agent (named: renames and merges applied), the one with the most input first. */
|
|
196
|
+
export declare function aiAgentUses(rows: readonly AiCallRow[], host?: string): AiAgentUse[];
|
|
119
197
|
/** `POST /api/ai/names` — rename a group (an empty name takes the rename back), merge one into another, or undo a merge. */
|
|
120
198
|
export type AiNamesChange = {
|
|
121
199
|
op: 'rename';
|
|
@@ -130,6 +208,8 @@ export type AiNamesChange = {
|
|
|
130
208
|
key: string;
|
|
131
209
|
};
|
|
132
210
|
export declare const MAX_TOOLS = 40;
|
|
211
|
+
/** A call's page shows at most this many calls of its thread — the latest. */
|
|
212
|
+
export declare const MAX_THREAD_CALLS = 300;
|
|
133
213
|
export declare const MAX_META = 24;
|
|
134
214
|
/** A key out of a name: lower case, words joined by `-` (`Critical Limits Analyst` → `critical-limits-analyst`). */
|
|
135
215
|
export declare const aiKeyOf: (kind: "agent" | "action", name: string) => string;
|
package/lib/ai-call-row.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`).map(
|
|
3
|
-
`).map(r=>r.trim()).find(Boolean);if(!
|
|
1
|
+
"use strict";var R=Object.defineProperty;var u=(e,s)=>R(e,"name",{value:s,configurable:!0});var B=Object.defineProperty,c=u((e,s)=>B(e,"name",{value:s,configurable:!0}),"d");import{aiCallOf as S}from"./ai-call.js";import{aiContextOf as T,aiSplitOf as z}from"./ai-context.js";export const EMPTY_AI_NAMES={version:1,names:{},merge:{}};export function aiModelUses(e,s){const n=s.toLowerCase(),r=new Map;for(const t of e){if(t.host.toLowerCase()!==n)continue;const o=t.model??"unknown";let a=r.get(o);a||(a={use:{model:o,calls:0,failed:0,unmetered:0,input:0,cached:0,output:0,costUsd:0,priced:0,agents:0,first:t.at,last:t.at},agents:new Set},r.set(o,a));const i=a.use;i.calls+=1,t.failed&&(i.failed+=1),t.tokens?(i.input+=t.tokens.input??0,i.cached+=t.tokens.cached??0,i.output+=t.tokens.output??0):i.unmetered+=1,t.costUsd!==void 0&&(i.costUsd+=t.costUsd,i.priced+=1),a.agents.add(t.agent.key),i.first=Math.min(i.first,t.at),i.last=Math.max(i.last,t.at)}return[...r.values()].map(({use:t,agents:o})=>({...t,agents:o.size})).sort((t,o)=>o.calls-t.calls||o.last-t.last)}u(aiModelUses,"aiModelUses"),c(aiModelUses,"aiModelUses");export function aiAgentUses(e,s){const n=s?.toLowerCase(),r=new Map;for(const t of e){if(n!==void 0&&t.host.toLowerCase()!==n)continue;let o=r.get(t.agent.key);o||(o={use:{key:t.agent.key,name:t.agent.name,from:t.agent.from,calls:0,runs:0,failed:0,input:0,cached:0,written:0,output:0,costUsd:0,priced:0,split:{},models:[],last:t.at},runs:new Set,models:new Map},r.set(t.agent.key,o));const a=o.use;a.calls+=1,o.runs.add(t.run.key),t.failed&&(a.failed+=1),a.input+=t.tokens?.input??0,a.cached+=t.tokens?.cached??0,a.written+=t.tokens?.written??0,a.output+=t.tokens?.output??0,t.costUsd!==void 0&&(a.costUsd+=t.costUsd,a.priced+=1);for(const[i,m]of Object.entries(t.split??{}))a.split[i]=(a.split[i]??0)+m;t.model&&o.models.set(t.model,(o.models.get(t.model)??0)+1),a.last=Math.max(a.last,t.at)}return[...r.values()].map(({use:t,runs:o,models:a})=>({...t,runs:o.size,models:[...a].sort((i,m)=>m[1]-i[1]).map(([i])=>i)})).sort((t,o)=>o.input-t.input||o.calls-t.calls)}u(aiAgentUses,"aiAgentUses"),c(aiAgentUses,"aiAgentUses");export const MAX_TOOLS=40,MAX_THREAD_CALLS=300,MAX_META=24;const E=160,C="unknown service";export const aiKeyOf=c((e,s)=>`${e}:${s.normalize("NFKD").toLowerCase().replace(/[^a-z0-9:.]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"unnamed"}`,"aiKeyOf");export function humanName(e){const s=e.trim();if(/[A-Z]/.test(s)||/ /.test(s))return s;const n=s.replace(/[-_.]+/g," ").trim();return n?n[0].toUpperCase()+n.slice(1):s}u(humanName,"humanName"),c(humanName,"humanName");const f=c((e,s=E)=>{if(!e)return;const n=e.replace(/\s+/g," ").trim();if(n)return n.length>s?`${n.slice(0,s-1)}\u2026`:n},"line");export function promptTitle(e){const s=e.split(`
|
|
2
|
+
`).map(t=>t.trim()).find(Boolean)??"",n=/^#{1,6}\s+(.+)$/.exec(s);if(n){const t=n[1].split(/\s+[—–]\s+|\s+-\s+|:\s/)[0].replace(/[*_`]/g,"").trim();return t?t.slice(0,60):void 0}const r=/^you are (?:an? |the )?(.+?)(?:[.,;:!]|\s+(?:who|that|which|for|in|on|whose|with|and|following|using|working|based|at|to)\b|$)/i.exec(s);if(r){const t=r[1].trim().split(/\s+/);if(t.length>6||/^(?:going|about|given|now|here|responsible|able|allowed|expected|tasked|asked|helping|working|being|not)$/i.test(t[0]))return;const o=t.join(" ");return o[0].toUpperCase()+o.slice(1)}}u(promptTitle,"promptTitle"),c(promptTitle,"promptTitle");export function promptOpening(e){const s=e.split(`
|
|
3
|
+
`).map(r=>r.trim()).find(Boolean);if(!s)return;const n=s.replace(/^[#>*\s-]+/,"").split(/\s+/).filter(Boolean);if(!(n.length<3))return n.length>7?`${n.slice(0,7).join(" ")} \u2026`:n.join(" ")}u(promptOpening,"promptOpening"),c(promptOpening,"promptOpening");export function actionName(e){const s=e.split("::")[0],n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{8,}|\d{4,})$/i;return s.split(":").map(r=>r.split(".").filter(t=>!n.test(t)).join(".")).filter((r,t,o)=>r||t<o.length-1).join(":").slice(0,120)}u(actionName,"actionName"),c(actionName,"actionName");const g=c((e,s)=>{const n=e.request.headers.find(([r])=>r.toLowerCase()===s)?.[1];if(n===void 0||!s.startsWith("x-lensmcp-")||!n.includes("%"))return n;try{return decodeURIComponent(n)}catch{return n}},"header"),A=c(e=>e!==void 0&&/^\d{1,6}$/.test(e.trim())?Number(e.trim()):void 0,"intOf"),h=c((e,...s)=>{for(const n of s)if(e[n]?.trim())return e[n].trim()},"first");function j(e){const s={};for(const n of e.settings)if(n.name.startsWith("metadata.")){if(Object.keys(s).length>=MAX_META)break;s[n.name.slice(9)]=n.value.slice(0,200)}return s}u(j,"S"),c(j,"metaOf");function U(e,s,n){const r=c((p,k)=>({key:aiKeyOf("agent",p),name:humanName(p),from:k}),"named"),t=h(n,"agent","agent_name","agentName","assistant");if(t)return r(t,"metadata");const o=g(e,"x-lensmcp-agent");if(o?.trim())return r(o.trim(),"header");const a=s.system.map(p=>promptTitle(p.text)).find(Boolean);if(a)return r(a,"prompt");const i=s.answer.format?.name;if(i)return r(i,"schema");const m=[...s.tools,...s.answer.toolCalls].map(p=>p.server).find(Boolean);if(m)return r(m,"tools");const l=s.system.map(p=>promptOpening(p.text)).find(Boolean);return l?{key:aiKeyOf("agent",`prompt ${l.replace(/ …$/,"")}`),name:l,from:"prompt"}:r(`${e.service??C} ${s.api==="embeddings"?"embeddings":s.model??s.api}`,"service")}u(U,"B"),c(U,"agentOf");function _(e,s,n){const r=c((a,i)=>({key:aiKeyOf("action",a),name:a,from:i}),"named"),t=h(n,"act","action","task","step","operation");if(t&&actionName(t))return r(actionName(t),"metadata");const o=g(e,"x-lensmcp-action");if(o?.trim())return r(actionName(o.trim()),"header");if(s.answer.format?.name)return r(s.answer.format.name,"schema")}u(_,"E"),c(_,"actionOf");function N(e){if(e.kind==="message")return f(e.text);if(e.kind==="tool")return f(e.output!==void 0?`${e.tool} \u2192 ${e.output}`:`${e.tool}(${e.arguments})`)}u(N,"z"),c(N,"turnLine");function v(e){const s=e.answer;return s.error?f(`error: ${s.error}`):s.refusal?f(`refused: ${s.refusal}`):s.toolCalls.length?f(s.toolCalls.map(n=>`${n.tool}(${n.arguments})`).join(" \xB7 ")):e.embedding?`${e.embedding.vectors??e.embedding.count} vector${(e.embedding.vectors??e.embedding.count)===1?"":"s"}${e.embedding.dimensions?` \xD7 ${e.embedding.dimensions}`:""}`:f(s.text)}u(v,"A"),c(v,"saidLine");export function aiCallRow(e,s){const n=S(e);if(!n||!e.ai)return;const r=j(n),t=new Map(n.settings.map(d=>[d.name,d.value])),o=h(r,"run","run_id","runId"),a=g(e,"x-lensmcp-run")?.trim(),i=n.turns.filter(d=>d.kind==="tool").filter(d=>d.output!==void 0&&d.callId).map(d=>d.callId),m=n.answer.toolCalls.map(d=>d.callId).filter(d=>!!d),l=n.usage,p=l?{...l.input!==void 0?{input:l.input}:{},...l.cached?{cached:l.cached}:{},...l.written?{written:l.written}:{},...l.output!==void 0?{output:l.output}:{},...l.reasoning?{reasoning:l.reasoning}:{}}:void 0,k=n.shape==="embedding"||l?.input===void 0?{}:z(T(n,{sections:!1})),w=h(r,"thread","thread_id","threadId","conversation","conversation_id","conversationId","case","case_id","caseId"),y=g(e,"x-lensmcp-thread")?.trim(),$=h(r,"parent_run","parentRun","parent")??g(e,"x-lensmcp-parent")?.trim(),x=_(e,n,r),b=A(r.round),I=A(r.attempt),O=t.get("continues response"),M=[...n.turns].reverse().map(N).find(Boolean),L=n.answer.status==="failed"||!!n.answer.error||(e.status??0)>=400||!!e.error;return{id:e.id,at:e.startedAt,...e.durationMs!==void 0?{durationMs:e.durationMs}:{},...e.status!==void 0?{status:e.status}:{},...L?{failed:!0}:{},service:e.service??C,...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},provider:e.ai.provider,host:e.host,api:n.api,shape:n.shape,...n.model?{model:n.model}:{},...e.response.stream?{streamed:!0}:{},...p&&Object.keys(p).length?{tokens:p}:{},...Object.keys(k).length?{split:k}:{},agent:U(e,n,r),run:o?{key:`run:${o}`,from:"metadata"}:a?{key:`run:${a}`,from:"header"}:{key:`run:${e.id}`,from:"alone"},...w?{thread:{key:`thread:${w}`,name:w,from:"metadata"}}:y?{thread:{key:`thread:${y}`,name:y,from:"header"}}:{},...$?{parent:`run:${$}`}:{},...b!==void 0?{round:b}:{},...I!==void 0?{attempt:I}:{},...x?{action:x}:{},...n.answer.format?.name?{schema:n.answer.format.name}:{},...n.tools.length?{tools:n.tools.slice(0,MAX_TOOLS).map(d=>d.name)}:{},...n.answer.toolCalls.length?{called:n.answer.toolCalls.map(d=>d.name)}:{},...m.length?{callIds:m}:{},...i.length?{answers:i}:{},...n.responseId?{responseId:n.responseId}:{},...O?{previousResponseId:O}:{},...Object.keys(r).length?{meta:r}:{},...n.gaps.some(d=>/kept to its first|cut/.test(d))?{cut:!0}:{},...s.content&&M?{asked:M}:{},...s.content&&v(n)?{said:v(n)}:{}}}u(aiCallRow,"aiCallRow"),c(aiCallRow,"aiCallRow");export function linkAiRuns(e){const s=[...e].sort((o,a)=>o.at-a.at||o.id.localeCompare(a.id)),n=new Map,r=new Map;for(const o of s){if(o.run.from==="alone"){const a=o.previousResponseId?n.get(o.previousResponseId):void 0,i=a?void 0:o.answers?.map(l=>r.get(l)).find(Boolean),m=a??i;if(m){const l=a?"chain":"tools";o.run={key:m.run.key,from:m.run.from==="alone"||m.run.from===l?l:m.run.from},m.run.from==="alone"&&(m.run={key:m.run.key,from:l})}}o.responseId&&n.set(o.responseId,o);for(const a of o.callIds??[])r.set(a,o)}const t=new Map;for(const o of s){const a=(t.get(o.run.key)??0)+1;t.set(o.run.key,a),o.step=a}return e}u(linkAiRuns,"linkAiRuns"),c(linkAiRuns,"linkAiRuns");export function mergedKey(e,s){let n=s;for(let r=0;r<8&&e.merge[n]&&e.merge[n]!==s;r++)n=e.merge[n];return n}u(mergedKey,"mergedKey"),c(mergedKey,"mergedKey");export function applyAiNames(e,s){if(!Object.keys(s.names).length&&!Object.keys(s.merge).length)return e;const n=new Map;for(const t of e)n.has(t.agent.key)||n.set(t.agent.key,t.agent),t.action&&!n.has(t.action.key)&&n.set(t.action.key,t.action);const r=c(t=>{const o=mergedKey(s,t.key),a=s.names[o];if(a)return{key:o,name:a,from:"renamed"};if(o===t.key)return t;const i=n.get(o);return{key:o,name:i?.name??humanName(o.replace(/^[a-z]+:/,"")),from:i?.from??t.from}},"resolve");for(const t of e)t.agent=r(t.agent),t.action&&(t.action=r(t.action));return e}u(applyAiNames,"applyAiNames"),c(applyAiNames,"applyAiNames");export function aiNamesOf(e){const s=e&&typeof e=="object"?e:{},n=c(r=>{const t={};if(r&&typeof r=="object"&&!Array.isArray(r))for(const[o,a]of Object.entries(r))/^(agent|action):/.test(o)&&typeof a=="string"&&a.trim()&&(t[o]=a.trim().slice(0,120));return t},"pick");return{version:1,names:n(s.names),merge:n(s.merge)}}u(aiNamesOf,"aiNamesOf"),c(aiNamesOf,"aiNamesOf");
|
package/lib/ai-call.d.ts
CHANGED
|
@@ -108,6 +108,8 @@ export interface AiAnswer {
|
|
|
108
108
|
export interface AiUsage {
|
|
109
109
|
input?: number;
|
|
110
110
|
cached?: number;
|
|
111
|
+
/** Written to the provider's prompt cache by this call — read at a discount by a later call that opens the same way. */
|
|
112
|
+
written?: number;
|
|
111
113
|
output?: number;
|
|
112
114
|
reasoning?: number;
|
|
113
115
|
total?: number;
|
package/lib/ai-call.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
"use strict";var se=Object.defineProperty;var v=(t,s)=>se(t,"name",{value:s,configurable:!0});var oe=Object.defineProperty,
|
|
2
|
-
`||d==="\r"||d===" ")continue;const f=s[s.length-1];if(d==='"'){l=!0,a=!!f&&f.c==="{"&&f.state==="key";continue}if(d==="{"||d==="["){s.push({c:d,state:d==="{"?"key":"value"}),n=m+1;continue}if(d==="}"||d==="]"){s.pop(),i(m+1);continue}if(d===":"){f&&(f.state="value");continue}if(d===","){f&&(f.state=f.c==="{"?"key":"value");continue}let
|
|
3
|
-
\r `.includes(t[
|
|
1
|
+
"use strict";var se=Object.defineProperty;var v=(t,s)=>se(t,"name",{value:s,configurable:!0});var oe=Object.defineProperty,_=v((t,s)=>oe(t,"name",{value:s,configurable:!0}),"d");const b=_(t=>!!t&&typeof t=="object"&&!Array.isArray(t),"isObj"),e=_(t=>typeof t=="string"?t:void 0,"str"),h=_(t=>typeof t=="number"&&Number.isFinite(t)?t:void 0,"num"),k=_(t=>Array.isArray(t)?t:[],"arr"),c=_(t=>b(t)?t:{},"obj"),A=_(t=>typeof t=="string"?t:t===void 0?"":JSON.stringify(t),"json"),U=_(t=>t>=1024*1024?`${(t/1024/1024).toFixed(1)} MB`:`${Math.round(t/1024)} KB`,"kb");export const CUT_MARK="\u2026";export function parseJsonPrefix(t){try{return{value:JSON.parse(t),cut:!1}}catch{}const s=[];let n=0,l=!1,a=!1,r=!1;const o=t.length,i=_(m=>{const d=s[s.length-1];d&&(d.state="after"),n=m},"ended");for(let m=0;m<o;m++){const d=t[m];if(l){r?r=!1:d==="\\"?r=!0:d==='"'&&(l=!1,a?s[s.length-1].state="colon":i(m+1));continue}if(d===" "||d===`
|
|
2
|
+
`||d==="\r"||d===" ")continue;const f=s[s.length-1];if(d==='"'){l=!0,a=!!f&&f.c==="{"&&f.state==="key";continue}if(d==="{"||d==="["){s.push({c:d,state:d==="{"?"key":"value"}),n=m+1;continue}if(d==="}"||d==="]"){s.pop(),i(m+1);continue}if(d===":"){f&&(f.state="value");continue}if(d===","){f&&(f.state=f.c==="{"?"key":"value");continue}let g=m;for(;g<o&&!`,}]
|
|
3
|
+
\r `.includes(t[g]);)g++;if(g>=o)break;i(g),m=g-1}const u=s.map(m=>m.c==="{"?"}":"]").reverse().join("");if(l&&!a)for(let m=0;m<=6;m++)try{return{value:JSON.parse(`${t.slice(0,o-m)}${CUT_MARK}"${u}`),cut:!0}}catch{}if(n)try{return{value:JSON.parse(`${t.slice(0,n)}${u}`),cut:!0}}catch{return}}v(parseJsonPrefix,"parseJsonPrefix"),_(parseJsonPrefix,"parseJsonPrefix");export function aiApiOf(t){const s=t.split("?")[0].replace(/\/+$/,"");if(/\/responses$/.test(s))return"responses";if(/\/chat\/completions$/.test(s))return"chat";if(/\/embeddings$/.test(s))return"embeddings";if(/\/messages$/.test(s))return"messages"}v(aiApiOf,"aiApiOf"),_(aiApiOf,"aiApiOf");export function splitToolName(t,s){if(s)return{server:s,tool:t};const n=/^(?:mcp__)?([A-Za-z0-9-]+(?:_[A-Za-z0-9-]+)*)__(.+)$/.exec(t);return n?{server:n[1],tool:n[2]}:{tool:t}}v(splitToolName,"splitToolName"),_(splitToolName,"splitToolName");const $=_((t,s,n={})=>({...n.callId?{callId:n.callId}:{},name:t,...splitToolName(t,n.server),arguments:s,...n.output!==void 0?{output:n.output}:{},...n.error?{error:n.error}:{}}),"toolCall");function M(t,s,n){if(!t)return;if(t.omitted==="ai"){n.push(`The ${s==="request"?"prompt":"answer"} was not kept \u2014 an AI provider\u2019s bodies are kept only when asked for: Settings \u203A Features \u203A Capture \u203A AI calls, for the calls made after.`);return}if(t.omitted||t.text===void 0)return;const l=parseJsonPrefix(t.text);if(l)return(l.cut||t.truncated)&&n.push(`The ${s} was kept to its first ${U(t.kept)} of ${U(t.size)} \u2014 read as far as it goes; what follows is not shown (the ${s==="request"?"end of the conversation":"rest of the answer"}, marked ${CUT_MARK}).`),l.value}v(M,"E"),_(M,"readBody");function J(t){if(t.data===void 0||t.data==="[DONE]")return;const s=parseJsonPrefix(t.data);return s&&b(s.value)?{v:s.value,cut:s.cut}:void 0}v(J,"M"),_(J,"eventJson");function I(t){if(typeof t=="string")return{text:t,attachments:[]};const s=[],n=[];for(const l of k(t)){if(typeof l=="string"){s.push(l);continue}const a=c(l),r=e(a.type)??"",o=e(a.text)??e(a.refusal);o!==void 0?s.push(o):/image/.test(r)?n.push(e(a.detail)?`image (${e(a.detail)})`:"image"):/file|document/.test(r)?n.push(`file ${e(a.filename)??e(c(a.source).media_type)??e(a.file_id)??""}`.trim()):/audio/.test(r)?n.push("audio"):r&&n.push(r)}return{text:s.join(`
|
|
4
4
|
|
|
5
|
-
`),attachments:n}}v(I,"I"),
|
|
5
|
+
`),attachments:n}}v(I,"I"),_(I,"contentOf");const N=_(t=>typeof t=="string"?t:Array.isArray(t)&&I(t).text||A(t),"outputOf");function C(t,s){const n=[];for(const[a,r]of s){let o=t;for(const u of a.split("."))o=c(o)[u];if(o==null)continue;const i=typeof o=="string"?o:typeof o=="number"||typeof o=="boolean"?String(o):b(o)&&e(o.name)?`${e(o.type)??"tool"} ${e(o.name)}`:b(o)&&e(o.type)?e(o.type):JSON.stringify(o);n.push({name:r,value:i})}const l=c(t.metadata);for(const[a,r]of Object.entries(l))n.push({name:`metadata.${a}`,value:typeof r=="string"?r:JSON.stringify(r)});return n}v(C,"O"),_(C,"settingsOf");function O(t,s,n,l){const a=s?t.byCallId.get(s):void 0;if(a){a.output=n,l&&(a.error=l);return}t.call.turns.push({kind:"tool",...$("(an earlier call)","",{callId:s,output:n,error:l})})}v(O,"T"),_(O,"addOutput");function j(t,s){const n={kind:"tool",...s};t.call.turns.push(n),s.callId&&t.byCallId.set(s.callId,n)}v(j,"S"),_(j,"addCall");const ne=[["reasoning.effort","reasoning effort"],["reasoning.summary","reasoning summary"],["max_output_tokens","max output tokens"],["temperature","temperature"],["top_p","top_p"],["tool_choice","tool choice"],["parallel_tool_calls","parallel tool calls"],["text.verbosity","verbosity"],["truncation","truncation"],["previous_response_id","continues response"],["store","store"],["service_tier","service tier"],["stream","stream"],["prompt_cache_key","prompt cache key"],["user","user"],["safety_identifier","safety identifier"]],W=_(t=>A(Object.fromEntries(Object.entries(t).filter(([s])=>s!=="type"&&s!=="id"&&s!=="status"))),"builtInCall");function Z(t,s){const n=e(s.type)??(s.role?"message":"");if(n==="message"){const l=e(s.role)??"user",{text:a,attachments:r}=I(s.content);l==="system"?t.call.system.push({source:"system message",text:a}):t.call.turns.push({kind:"message",role:l,text:a,...r.length?{attachments:r}:{}})}else if(n==="function_call"||n==="custom_tool_call")j(t,$(e(s.name)??"?",e(s.arguments)??A(s.input),{callId:e(s.call_id)}));else if(n==="function_call_output"||n==="custom_tool_call_output"||n==="computer_call_output"||n==="local_shell_call_output")O(t,e(s.call_id),N(s.output));else if(n==="mcp_call")j(t,$(e(s.name)??"?",e(s.arguments)??"",{callId:e(s.id),server:e(s.server_label),...s.output!==void 0&&s.output!==null?{output:N(s.output)}:{},error:e(s.error)}));else if(n==="mcp_list_tools"){const l=k(s.tools).map(a=>e(c(a).name)).filter(Boolean);j(t,$("list_tools","",{server:e(s.server_label),output:l.join(", ")}))}else if(n==="reasoning"){const l=k(s.summary).map(a=>e(c(a).text)).filter(Boolean).join(`
|
|
6
6
|
|
|
7
|
-
`);t.call.turns.push({kind:"reasoning",...l?{text:l}:{}})}else n&&j(t,$(n.replace(/_call$/,""),W(s),{callId:e(s.call_id)??e(s.id)}))}v(Z,"R"),
|
|
7
|
+
`);t.call.turns.push({kind:"reasoning",...l?{text:l}:{}})}else n&&j(t,$(n.replace(/_call$/,""),W(s),{callId:e(s.call_id)??e(s.id)}))}v(Z,"R"),_(Z,"responsesItem");function D(t){return k(t.tools).map(s=>{const n=c(s),l=e(n.type)??"function";if(l==="mcp"){const r=e(n.server_label)??"mcp",o=k(n.allowed_tools).map(i=>e(i)).filter(Boolean);return{name:r,server:r,tool:o.length?o.join(", "):"every tool",type:l,...e(n.server_description)??e(n.server_url)?{description:e(n.server_description)??e(n.server_url)}:{},called:0}}const a=e(n.name)??l;return{name:a,...splitToolName(a),type:l,...e(n.description)?{description:e(n.description)}:{},...n.parameters!==void 0?{parameters:n.parameters}:{},...typeof n.strict=="boolean"?{strict:n.strict}:{},called:0}})}v(D,"W"),_(D,"responsesTools");function q(t,s){const n=t.call.answer;e(s.status)&&(n.status=e(s.status));const l=e(c(s.incomplete_details).reason);l&&(n.finish=l);const a=c(s.error);e(a.message)&&(n.error=e(a.message)),e(s.model)&&(t.call.model??=e(s.model)),e(s.id)&&(t.call.responseId=e(s.id)),E(t,k(s.output).map(c));const r=c(s.usage);Object.keys(r).length&&(t.call.usage={...h(r.input_tokens)!==void 0?{input:h(r.input_tokens)}:{},...h(c(r.input_tokens_details).cached_tokens)!==void 0?{cached:h(c(r.input_tokens_details).cached_tokens)}:{},...h(c(r.input_tokens_details).cache_write_tokens)?{written:h(c(r.input_tokens_details).cache_write_tokens)}:{},...h(r.output_tokens)!==void 0?{output:h(r.output_tokens)}:{},...h(c(r.output_tokens_details).reasoning_tokens)!==void 0?{reasoning:h(c(r.output_tokens_details).reasoning_tokens)}:{},...h(r.total_tokens)!==void 0?{total:h(r.total_tokens)}:{}})}v(q,"z"),_(q,"responsesAnswer");function E(t,s){const n=t.call.answer,l=[],a=[];for(const r of s){const o=e(r.type);if(o==="message")for(const i of k(r.content).map(c))e(i.refusal)?n.refusal=e(i.refusal):e(i.text)!==void 0&&l.push(e(i.text));else if(o==="function_call"||o==="custom_tool_call")n.toolCalls.push($(e(r.name)??"?",e(r.arguments)??A(r.input),{callId:e(r.call_id)}));else if(o==="mcp_call")n.toolCalls.push($(e(r.name)??"?",e(r.arguments)??"",{callId:e(r.id),server:e(r.server_label),...r.output!==void 0&&r.output!==null?{output:N(r.output)}:{},error:e(r.error)}));else if(o==="reasoning"){const i=k(r.summary).map(u=>e(c(u).text)).filter(Boolean).join(`
|
|
8
8
|
|
|
9
9
|
`);i&&a.push(i)}else o&&o!=="mcp_list_tools"&&n.toolCalls.push($(o.replace(/_call$/,""),W(r),{callId:e(r.call_id)??e(r.id)}))}l.length&&(n.text=l.join(`
|
|
10
10
|
|
|
11
11
|
`)),a.length&&(n.reasoning=a.join(`
|
|
12
12
|
|
|
13
|
-
`))}v(E,"F"),
|
|
13
|
+
`))}v(E,"F"),_(E,"responsesOutput");function H(t,s,n){let l,a=0;const r=new Map,o=new Set,i=new Map,u=new Map,m=new Map;for(const f of s){const g=J(f);if(!g)continue;g.cut&&a++;const p=g.v,y=e(p.type)??f.event,w=h(p.output_index)??0;if(y==="response.completed"||y==="response.incomplete"||y==="response.failed"){if(!g.cut&&b(p.response))l=p.response;else if(b(p.response)){const x=p.response;e(x.status)&&(t.call.answer.status=e(x.status))}}else if(y==="response.created"||y==="response.in_progress"){const x=c(p.response);e(x.model)&&(t.call.model??=e(x.model)),e(x.id)&&(t.call.responseId??=e(x.id))}else y==="response.output_item.added"||y==="response.output_item.done"?b(p.item)&&(!o.has(w)||!g.cut)&&(r.set(w,{...r.get(w)??{},...p.item}),y.endsWith(".done")&&!g.cut&&o.add(w)):y==="response.output_text.delta"||y==="response.refusal.delta"?i.set(w,(i.get(w)??"")+(e(p.delta)??"")):y==="response.output_text.done"&&!g.cut&&e(p.text)!==void 0?i.set(w,e(p.text)):y==="response.function_call_arguments.delta"||y==="response.custom_tool_call_input.delta"||y==="response.mcp_call_arguments.delta"?u.set(w,(u.get(w)??"")+(e(p.delta)??"")):(y==="response.function_call_arguments.done"||y==="response.mcp_call_arguments.done")&&!g.cut&&e(p.arguments)!==void 0?u.set(w,e(p.arguments)):y==="response.reasoning_summary_text.delta"?m.set(w,(m.get(w)??"")+(e(p.delta)??"")):(y==="error"||y==="response.error")&&(t.call.answer.error=e(p.message)??e(c(p.error).message)??"the stream reported an error")}if(l){q(t,l);return}const d=[...r.entries()].sort(([f],[g])=>f-g).map(([f,g])=>{if(o.has(f))return g;const p=e(g.type);return p==="message"&&i.has(f)?{...g,content:[{type:"output_text",text:i.get(f)}]}:(p==="function_call"||p==="mcp_call")&&u.has(f)?{...g,arguments:u.get(f)}:p==="custom_tool_call"&&u.has(f)?{...g,input:u.get(f)}:p==="reasoning"&&m.has(f)?{...g,summary:[{type:"summary_text",text:m.get(f)}]}:g});for(const[f,g]of i)r.has(f)||d.push({type:"message",content:[{type:"output_text",text:g}]});E(t,d),t.call.answer.status||(t.call.answer.status=d.length?"completed":"unknown"),a&&n.push(`${a} of the stream\u2019s events were kept to their first 4 KB, so the answer was rebuilt from its pieces${t.call.usage?"":" \u2014 its token counts came in the last event, which was cut"}.`)}v(H,"Z"),_(H,"responsesStream");function R(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model)),e(s.instructions)&&a.system.push({source:"instructions",text:e(s.instructions)});const r=s.input;if(typeof r=="string")a.turns.push({kind:"message",role:"user",text:r});else for(const i of k(r))b(i)&&Z(t,i);a.tools=D(s),a.settings=C(s,ne);const o=c(c(s.text).format);e(o.type)&&o.type!=="text"&&(a.answer.format={type:e(o.type),...e(o.name)?{name:e(o.name)}:{},...o.schema!==void 0?{schema:o.schema}:{},...typeof o.strict=="boolean"?{strict:o.strict}:{}}),T(t,n,l,i=>q(t,i),i=>H(t,i,l))}v(R,"D"),_(R,"responsesCall");const re=[["reasoning_effort","reasoning effort"],["max_completion_tokens","max completion tokens"],["max_tokens","max tokens"],["temperature","temperature"],["top_p","top_p"],["tool_choice","tool choice"],["parallel_tool_calls","parallel tool calls"],["n","n"],["seed","seed"],["stop","stop"],["presence_penalty","presence penalty"],["frequency_penalty","frequency penalty"],["verbosity","verbosity"],["store","store"],["service_tier","service tier"],["stream","stream"],["user","user"]];function K(t){const s=k(t.tool_calls).map(c).map(l=>{const a=c(l.function);return $(e(a.name)??e(c(l.custom).name)??"?",e(a.arguments)??e(c(l.custom).input)??"",{callId:e(l.id)})}),n=c(t.function_call);return e(n.name)&&s.push($(e(n.name),e(n.arguments)??"",{})),s}v(K,"G"),_(K,"chatCalls");function z(t,s){const n=t.call.answer;e(s.model)&&(t.call.model??=e(s.model)),e(s.id)&&(t.call.responseId??=e(s.id));const l=c(k(s.choices)[0]),a=c(l.message),r=I(a.content).text;r&&(n.text=r),e(a.refusal)&&(n.refusal=e(a.refusal));const o=e(a.reasoning_content)??e(a.reasoning);o&&(n.reasoning=o),n.toolCalls.push(...K(a)),e(l.finish_reason)&&(n.finish=e(l.finish_reason)),n.status=n.finish?"completed":n.status??"unknown";const i=c(s.error);e(i.message)&&(n.error=e(i.message));const u=c(s.usage);Object.keys(u).length&&(t.call.usage={...h(u.prompt_tokens)!==void 0?{input:h(u.prompt_tokens)}:{},...h(c(u.prompt_tokens_details).cached_tokens)!==void 0?{cached:h(c(u.prompt_tokens_details).cached_tokens)}:{},...h(c(u.prompt_tokens_details).cache_write_tokens)?{written:h(c(u.prompt_tokens_details).cache_write_tokens)}:{},...h(u.completion_tokens)!==void 0?{output:h(u.completion_tokens)}:{},...h(c(u.completion_tokens_details).reasoning_tokens)!==void 0?{reasoning:h(c(u.completion_tokens_details).reasoning_tokens)}:{},...h(u.total_tokens)!==void 0?{total:h(u.total_tokens)}:{}})}v(z,"L"),_(z,"chatAnswer");function G(t,s,n){let l="",a="",r="",o,i,u,m,d=0;const f=new Map;for(const p of s){const y=J(p);if(!y)continue;y.cut&&d++,u??=e(y.v.model),m??=e(y.v.id),b(y.v.usage)&&(i=y.v.usage),b(y.v.error)&&(t.call.answer.error=e(y.v.error.message)??"the stream reported an error");const w=c(k(y.v.choices)[0]),x=c(w.delta);l+=e(x.content)??"",a+=e(x.reasoning_content)??e(x.reasoning)??"",r+=e(x.refusal)??"";for(const S of k(x.tool_calls).map(c)){const L=h(S.index)??0,B=f.get(L)??{name:"",args:""},P=c(S.function);e(S.id)&&(B.id=e(S.id)),B.name+=e(P.name)??"",B.args+=e(P.arguments)??"",f.set(L,B)}e(w.finish_reason)&&(o=e(w.finish_reason))}const g={content:l,...a?{reasoning_content:a}:{},...r?{refusal:r}:{},tool_calls:[...f.entries()].sort(([p],[y])=>p-y).map(([,p])=>({id:p.id,function:{name:p.name,arguments:p.args}}))};z(t,{...u?{model:u}:{},...m?{id:m}:{},choices:[{message:g,...o?{finish_reason:o}:{}}],...i?{usage:i}:{}}),d&&n.push(`${d} of the stream\u2019s chunks were kept to their first 4 KB \u2014 the answer was joined from what was kept of them.`)}v(G,"V"),_(G,"chatStream");function Q(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model));for(const o of k(s.messages).map(c)){const i=e(o.role)??"user",{text:u,attachments:m}=I(o.content);if(i==="system")a.system.push({source:"system message",text:u});else if(i==="tool")O(t,e(o.tool_call_id),u);else if(i==="function")O(t,void 0,u);else{(u||m.length||i!=="assistant")&&a.turns.push({kind:"message",role:i,text:u,...m.length?{attachments:m}:{}});for(const d of K(o))j(t,d)}}a.tools=[...k(s.tools).map(c),...k(s.functions).map(o=>({type:"function",function:o}))].map(o=>{const i=c(o.function),u=e(o.type)??"function",m=e(i.name)??e(c(o.custom).name)??u;return{name:m,...splitToolName(m),type:u,...e(i.description)??e(c(o.custom).description)?{description:e(i.description)??e(c(o.custom).description)}:{},...i.parameters!==void 0?{parameters:i.parameters}:{},...typeof i.strict=="boolean"?{strict:i.strict}:{},called:0}}),a.settings=C(s,re);const r=c(s.response_format);if(e(r.type)&&r.type!=="text"){const o=c(r.json_schema);a.answer.format={type:e(r.type),...e(o.name)?{name:e(o.name)}:{},...o.schema!==void 0?{schema:o.schema}:{},...typeof o.strict=="boolean"?{strict:o.strict}:{}}}T(t,n,l,o=>z(t,o),o=>G(t,o,l))}v(Q,"X"),_(Q,"chatCall");const ae=[["max_tokens","max tokens"],["thinking.budget_tokens","thinking budget"],["temperature","temperature"],["top_p","top_p"],["top_k","top_k"],["tool_choice","tool choice"],["stop_sequences","stop sequences"],["stream","stream"],["metadata.user_id","user"]];function V(t,s,n){if(typeof n=="string"){t.call.turns.push({kind:"message",role:s,text:n});return}const l=[],a=[],r=_(()=>{(l.length||a.length)&&t.call.turns.push({kind:"message",role:s,text:l.join(`
|
|
14
14
|
|
|
15
|
-
`),...a.length?{attachments:[...a]}:{}}),l.length=0,a.length=0},"flush");for(const o of k(n).map(c)){const i=e(o.type)??"";i==="text"?l.push(e(o.text)??""):i==="tool_use"||i==="server_tool_use"||i==="mcp_tool_use"?(r(),j(t,$(e(o.name)??"?",A(o.input),{callId:e(o.id),server:e(o.server_name)}))):i==="tool_result"||i==="mcp_tool_result"||/_tool_result$/.test(i)?(r(),O(t,e(o.tool_use_id),N(o.content),o.is_error===!0?"the tool reported an error":void 0)):i==="thinking"||i==="redacted_thinking"?(r(),t.call.turns.push({kind:"reasoning",...e(o.thinking)?{text:e(o.thinking)}:{}})):a.push(i==="image"?"image":i==="document"?`document ${e(o.title)??""}`.trim():i)}r()}v(V,"ee"),
|
|
15
|
+
`),...a.length?{attachments:[...a]}:{}}),l.length=0,a.length=0},"flush");for(const o of k(n).map(c)){const i=e(o.type)??"";i==="text"?l.push(e(o.text)??""):i==="tool_use"||i==="server_tool_use"||i==="mcp_tool_use"?(r(),j(t,$(e(o.name)??"?",A(o.input),{callId:e(o.id),server:e(o.server_name)}))):i==="tool_result"||i==="mcp_tool_result"||/_tool_result$/.test(i)?(r(),O(t,e(o.tool_use_id),N(o.content),o.is_error===!0?"the tool reported an error":void 0)):i==="thinking"||i==="redacted_thinking"?(r(),t.call.turns.push({kind:"reasoning",...e(o.thinking)?{text:e(o.thinking)}:{}})):a.push(i==="image"?"image":i==="document"?`document ${e(o.title)??""}`.trim():i)}r()}v(V,"ee"),_(V,"anthropicBlocks");function F(t,s){const n=t.call.answer;e(s.model)&&(t.call.model??=e(s.model)),e(s.id)&&(t.call.responseId??=e(s.id));const l=[],a=[];for(const i of k(s.content).map(c)){const u=e(i.type);u==="text"?l.push(e(i.text)??""):u==="tool_use"||u==="server_tool_use"||u==="mcp_tool_use"?n.toolCalls.push($(e(i.name)??"?",A(i.input),{callId:e(i.id),server:e(i.server_name)})):u==="thinking"&&a.push(e(i.thinking)??"")}l.length&&(n.text=l.join(`
|
|
16
16
|
|
|
17
17
|
`)),a.length&&(n.reasoning=a.join(`
|
|
18
18
|
|
|
19
|
-
`)),e(s.stop_reason)&&(n.finish=e(s.stop_reason)),n.status=s.type==="error"?"failed":n.finish?"completed":n.status??"unknown";const r=c(s.error);e(r.message)&&(n.error=e(r.message));const o=c(s.usage);if(Object.keys(o).length){const i=
|
|
19
|
+
`)),e(s.stop_reason)&&(n.finish=e(s.stop_reason)),n.status=s.type==="error"?"failed":n.finish?"completed":n.status??"unknown";const r=c(s.error);e(r.message)&&(n.error=e(r.message));const o=c(s.usage);if(Object.keys(o).length){const i=h(o.input_tokens),u=h(o.cache_read_input_tokens),m=h(o.output_tokens);t.call.usage={...i!==void 0?{input:i+(u??0)+(h(o.cache_creation_input_tokens)??0)}:{},...u!==void 0?{cached:u}:{},...h(o.cache_creation_input_tokens)?{written:h(o.cache_creation_input_tokens)}:{},...m!==void 0?{output:m}:{}}}}v(F,"P"),_(F,"anthropicAnswer");function X(t,s,n){let l={};const a=new Map,r=new Map;let o={},i=0;for(const u of s){const m=J(u);if(!m)continue;m.cut&&i++;const d=m.v,f=e(d.type)??u.event,g=h(d.index)??0;if(f==="message_start")l=c(d.message),o={...c(l.usage)};else if(f==="content_block_start")a.set(g,{...c(d.content_block)});else if(f==="content_block_delta"){const p=c(d.delta),y=a.get(g)??{};e(p.text)!==void 0&&(y.text=(e(y.text)??"")+e(p.text)),e(p.thinking)!==void 0&&(y.thinking=(e(y.thinking)??"")+e(p.thinking)),e(p.partial_json)!==void 0&&r.set(g,(r.get(g)??"")+e(p.partial_json)),a.set(g,y)}else f==="message_delta"?(l={...l,...c(d.delta)},o={...o,...c(d.usage)}):f==="error"&&(t.call.answer.error=e(c(d.error).message)??"the stream reported an error")}for(const[u,m]of r){const d=a.get(u);d&&(d.input=parseJsonPrefix(m)?.value??m)}F(t,{...l,content:[...a.entries()].sort(([u],[m])=>u-m).map(([,u])=>u),usage:o}),i&&n.push(`${i} of the stream\u2019s events were kept to their first 4 KB \u2014 the answer was joined from what was kept of them.`)}v(X,"te"),_(X,"anthropicStream");function Y(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model));const r=s.system;if(typeof r=="string"&&r)a.system.push({source:"system",text:r});else for(const o of k(r).map(c))e(o.text)&&a.system.push({source:"system",text:e(o.text)});for(const o of k(s.messages).map(c))V(t,e(o.role)??"user",o.content);a.tools=[...k(s.tools).map(c).map(o=>{const i=e(o.name)??e(o.type)??"tool";return{name:i,...splitToolName(i),type:e(o.type)??"custom",...e(o.description)?{description:e(o.description)}:{},...o.input_schema!==void 0?{parameters:o.input_schema}:{},called:0}}),...k(s.mcp_servers).map(c).map(o=>({name:e(o.name)??"mcp",server:e(o.name)??"mcp",tool:"every tool",type:"mcp",...e(o.url)?{description:e(o.url)}:{},called:0}))],a.settings=C(s,ae),T(t,n,l,o=>F(t,o),o=>X(t,o,l))}v(Y,"ne"),_(Y,"anthropicCall");function ee(t,s,n,l){const a=t.call;e(s.model)&&(a.model=e(s.model));const r=s.input,o=typeof r=="string"?[r]:k(r),i=o.slice(0,200).map(p=>typeof p=="string"?p:typeof p=="number"?`token ${p}`:Array.isArray(p)?`${p.length} tokens`:A(p)),u=o.length>0&&o.every(p=>typeof p=="number");a.embedding={inputs:u?[`${o.length} tokens`]:i,count:u?1:o.length,...h(s.dimensions)!==void 0?{dimensions:h(s.dimensions)}:{}},a.settings=C(s,[["dimensions","dimensions"],["encoding_format","encoding"],["user","user"]]);const m=c(M(n.response.body,"answer",[])),d=k(m.data);if(d.length){a.embedding.vectors=d.length;const p=c(d[0]).embedding;Array.isArray(p)&&a.embedding.dimensions===void 0&&(a.embedding.dimensions=p.length)}const f=c(m.usage);h(f.prompt_tokens)!==void 0&&(a.usage={input:h(f.prompt_tokens),...h(f.total_tokens)!==void 0?{total:h(f.total_tokens)}:{}}),a.answer.status=n.status!==void 0&&n.status<400?"completed":"failed";const g=c(m.error);e(g.message)&&(a.answer.error=e(g.message))}v(ee,"se"),_(ee,"embeddingsCall");function T(t,s,n,l,a){const r=s.response.stream;if(r){if(r.events.length&&r.events.every(i=>i.data===void 0)){n.push("The answer streamed, and only each event\u2019s name, time and size were kept \u2014 an AI provider\u2019s bodies are kept only when asked for: Settings \u203A Features \u203A Capture \u203A AI calls.");return}a(r.events),r.cut&&n.push(`The stream was cut after ${r.events.length.toLocaleString("en-US")} of its ${r.total.toLocaleString("en-US")} events \u2014 what came after is not shown.`);return}const o=M(s.response.body,"answer",n);b(o)?l(o):s.error&&(t.call.answer.error=s.error),t.call.answer.status||(t.call.answer.status=s.error||(s.status??0)>=400?"failed":"unknown")}v(T,"N"),_(T,"answerOf");function te(t){return t.api==="embeddings"?"embedding":t.answer.format&&/json/.test(t.answer.format.type)?"decision":t.tools.length||t.answer.toolCalls.length||t.turns.some(s=>s.kind==="tool")?"tools":t.turns.filter(s=>s.kind==="message"&&(s.role==="user"||s.role==="assistant")).length>1?"chat":"prompt"}v(te,"oe"),_(te,"shapeOf");export function aiCallOf(t){if(!t.ai)return;const s=aiApiOf(t.path);if(!s)return;const n=[],l={call:{provider:t.ai.provider,api:s,shape:"prompt",settings:[],system:[],turns:[],tools:[],answer:{toolCalls:[]},gaps:n},byCallId:new Map},a=c(M(t.request.body,"request",n));s==="responses"?R(l,a,t,n):s==="chat"?Q(l,a,t,n):s==="messages"?Y(l,a,t,n):ee(l,a,t,n);const r=l.call;if(r.answer.text!==void 0&&(r.answer.format?/json/.test(r.answer.format.type):/^\s*[{[]/.test(r.answer.text))){const i=parseJsonPrefix(r.answer.text);i&&(b(i.value)||Array.isArray(i.value))&&(r.answer.json=i.value)}!r.answer.finish&&r.answer.toolCalls.length&&(r.answer.finish="tool calls");const o=new Map;for(const i of r.turns)i.kind==="tool"&&o.set(i.name,(o.get(i.name)??0)+1);for(const i of r.answer.toolCalls)o.set(i.name,(o.get(i.name)??0)+1);for(const i of r.tools)i.called=o.get(i.name)??0;r.shape=te(r);for(const i of r.system)i.text.endsWith(CUT_MARK)&&(i.cut=!0);return r}v(aiCallOf,"aiCallOf"),_(aiCallOf,"aiCallOf");export const SHAPE_LABEL={prompt:"Single prompt",chat:"Conversation",tools:"Tool calling",decision:"Structured decision",embedding:"Embedding"};export function prettyJson(t){const s=t.trim()?parseJsonPrefix(t):void 0;return s&&(b(s.value)||Array.isArray(s.value))?{value:s.value,text:t}:{text:t}}v(prettyJson,"prettyJson"),_(prettyJson,"prettyJson");
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { AiCall, AiRole } from './ai-call.js';
|
|
2
|
+
/**
|
|
3
|
+
* What an AI call's tokens went to (the AI call's Context tab, and each kept call's row): everything the model was
|
|
4
|
+
* given, part by part in the order it got them — the system prompt, the tool definitions, the answer's schema, the
|
|
5
|
+
* context the app put in, the earlier turns, the tool calls and their results, this turn — each with its tokens, the
|
|
6
|
+
* sections it is made of, and how far the provider's prompt cache reached into it; then what the answer's tokens went
|
|
7
|
+
* to, and what stands out (`findings`).
|
|
8
|
+
*
|
|
9
|
+
* A provider counts a call's tokens, not its parts: each part is estimated from its text ({@link estimateTokens}), and
|
|
10
|
+
* the estimates are scaled so they add up to the provider's count. The estimate was fitted on FoodGuard's kept calls
|
|
11
|
+
* (OpenAI, English prompts with Hebrew conversations): its total is within 0.5 % of the provider's on the median call.
|
|
12
|
+
*
|
|
13
|
+
* Pure: run on the call the panel already read.
|
|
14
|
+
*/
|
|
15
|
+
export type AiPartKind = 'system' | 'tools' | 'schema' | 'context' | 'history' | 'tool' | 'turn' | 'carried' | 'unkept';
|
|
16
|
+
/** The bar's order — what a prompt is built from first. */
|
|
17
|
+
export declare const AI_PART_KINDS: readonly AiPartKind[];
|
|
18
|
+
export declare const AI_PART_LABEL: Record<AiPartKind, string>;
|
|
19
|
+
export declare const AI_PART_SAID: Record<AiPartKind, string>;
|
|
20
|
+
export interface AiSection {
|
|
21
|
+
title: string;
|
|
22
|
+
/** A tagged section's tag (`memory`, `rules`…) and attributes (`id`, `note`, `source`, `version`…). */
|
|
23
|
+
tag?: string;
|
|
24
|
+
attrs?: Record<string, string>;
|
|
25
|
+
chars: number;
|
|
26
|
+
/** Estimated, scaled to its part's tokens. */
|
|
27
|
+
tokens: number;
|
|
28
|
+
/** Its span in its part's text — none for a JSON key. */
|
|
29
|
+
at?: number;
|
|
30
|
+
end?: number;
|
|
31
|
+
children?: AiSection[];
|
|
32
|
+
}
|
|
33
|
+
export type AiPartRef = {
|
|
34
|
+
system: number;
|
|
35
|
+
} | {
|
|
36
|
+
turn: number;
|
|
37
|
+
} | {
|
|
38
|
+
tools: true;
|
|
39
|
+
} | {
|
|
40
|
+
schema: true;
|
|
41
|
+
} | {
|
|
42
|
+
carried: true;
|
|
43
|
+
} | {
|
|
44
|
+
unkept: true;
|
|
45
|
+
};
|
|
46
|
+
export interface AiPart {
|
|
47
|
+
/** Its place in what the model got, from 0 — the order the prompt cache reads in. */
|
|
48
|
+
i: number;
|
|
49
|
+
kind: AiPartKind;
|
|
50
|
+
/** «System prompt · instructions», «user», «assistant», «tool · fill_question», «Context · developer message»… */
|
|
51
|
+
label: string;
|
|
52
|
+
role?: AiRole | 'tool';
|
|
53
|
+
ref: AiPartRef;
|
|
54
|
+
chars: number;
|
|
55
|
+
tokens: number;
|
|
56
|
+
/** Of its tokens, those inside the cached prefix. */
|
|
57
|
+
cached: number;
|
|
58
|
+
sections: AiSection[];
|
|
59
|
+
}
|
|
60
|
+
export interface AiFinding {
|
|
61
|
+
tone: 'bad' | 'warn' | 'info';
|
|
62
|
+
text: string;
|
|
63
|
+
}
|
|
64
|
+
export interface AiContext {
|
|
65
|
+
parts: AiPart[];
|
|
66
|
+
byKind: Array<{
|
|
67
|
+
kind: AiPartKind;
|
|
68
|
+
tokens: number;
|
|
69
|
+
cached: number;
|
|
70
|
+
parts: number;
|
|
71
|
+
}>;
|
|
72
|
+
/** The input tokens: the provider's count (`counted`), else the estimate. */
|
|
73
|
+
input: number;
|
|
74
|
+
counted: boolean;
|
|
75
|
+
cached: number;
|
|
76
|
+
written?: number;
|
|
77
|
+
/** The estimate of what is in the request, before it was scaled to the provider's count. */
|
|
78
|
+
estimated: number;
|
|
79
|
+
output: {
|
|
80
|
+
total: number;
|
|
81
|
+
reasoning: number;
|
|
82
|
+
answer: number;
|
|
83
|
+
tools: number;
|
|
84
|
+
counted: boolean;
|
|
85
|
+
};
|
|
86
|
+
findings: AiFinding[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* About how many tokens a text is, for an o200k-style tokenizer: a word 1 (a long one 1 more per 4 letters), a run of
|
|
90
|
+
* digits 1 per 3, a letter of a non-Latin script 0.66, punctuation 0.75 a character, a line break or an indent 1.
|
|
91
|
+
*/
|
|
92
|
+
export declare function estimateTokens(text: string): number;
|
|
93
|
+
/**
|
|
94
|
+
* What a text is made of: its `--- TITLE ---` blocks, else its headings (the shallowest level that repeats), else its
|
|
95
|
+
* `<tag>` blocks, else a JSON object's keys — one level, and a block that is JSON split by its keys in turn.
|
|
96
|
+
*/
|
|
97
|
+
export declare function sectionsOf(text: string): AiSection[];
|
|
98
|
+
/** What a prompt's tags say: the kinds of section an app's prompt is made of, by how often they change. */
|
|
99
|
+
export declare const STABLE_TAGS: ReadonlySet<string>;
|
|
100
|
+
export declare const CHANGING_TAGS: ReadonlySet<string>;
|
|
101
|
+
/**
|
|
102
|
+
* A prompt built of tagged sections — `<memory id="case" note="…" source="db:case_memory">…</memory>` — split by
|
|
103
|
+
* them, each with its tag and attributes, a list's items as its children (one level), and the untagged text between
|
|
104
|
+
* them kept as its own sections (its tokens are the prompt's too). A tag counts at the start of a line, closed by
|
|
105
|
+
* its own closing tag; anything else is text.
|
|
106
|
+
*/
|
|
107
|
+
export declare function taggedSections(text: string, from?: number, to?: number, depth?: number): AiSection[];
|
|
108
|
+
/** What the call's tokens went to — see the file's comment. `sections: false`: the parts only (a row's split). */
|
|
109
|
+
export declare function aiContextOf(call: AiCall, opts?: {
|
|
110
|
+
sections?: boolean;
|
|
111
|
+
}): AiContext;
|
|
112
|
+
/** A row's share of each kind — what a kept call keeps of its split, to add up over an agent, a run, a session. */
|
|
113
|
+
export declare function aiSplitOf(ctx: AiContext): Partial<Record<AiPartKind, number>>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";var P=Object.defineProperty;var $=(t,e)=>P(t,"name",{value:e,configurable:!0});var G=Object.defineProperty,g=$((t,e)=>G(t,"name",{value:e,configurable:!0}),"g");export const AI_PART_KINDS=["system","tools","schema","context","history","tool","turn","carried","unkept"],AI_PART_LABEL={system:"System prompt",tools:"Tool definitions",schema:"Answer schema",context:"Context",history:"Earlier turns",tool:"Tool calls & results",turn:"This turn",carried:"Held by the provider",unkept:"Not kept"},AI_PART_SAID={system:"The instructions every call of this agent starts with",tools:"The tools it was offered, as defined \u2014 sent with every call, called or not",schema:"The JSON schema its answer had to fit",context:"What the app put in beside the conversation (developer messages)",history:"The conversation before this turn \u2014 what was said, and answered",tool:"The tools it called earlier in this run, what it sent them and what came back",turn:"What this call asks \u2014 the latest message",carried:"The conversation of the response it continues (previous_response_id): counted by the provider, not in this request",unkept:"The end of the request, which the capture did not keep (a lens before 1.25.32 kept its first 64 KB): counted by the provider, not known here"};const R=/[\p{L}\p{M}]/u,W=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u,D=/(\s*)([A-Za-z]+|[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+|[\p{L}\p{M}]+|[0-9]+|[^\s\p{L}\p{M}0-9]+)|(\s+)/gu,B=4,I=.7;export function estimateTokens(t){let e=0;for(const o of t.matchAll(D)){const s=o[1],n=o[2];if(o[3]!==void 0){/\n| {2,}/.test(o[3])&&(e+=1);continue}if(s&&/\n| {2,}/.test(s)&&(e+=1),!n)continue;const r=n.charCodeAt(0);r>=65&&r<=90||r>=97&&r<=122?e+=1+(n.length>8?(n.length-8)/4:0):r>=48&&r<=57?e+=Math.ceil(n.length/3):W.test(n)?e+=[...n].length*.9:R.test(n)?e+=[...n].length*.66:e+=n.length*.75}return e}$(estimateTokens,"estimateTokens"),g(estimateTokens,"estimateTokens");const Z=/^[ \t]*-{3,}[ \t]*(.+?)[ \t]*-{3,}[ \t]*$/gm,U=/^(#{1,4})[ \t]+(.+?)[ \t]*#*[ \t]*$/gm,_=60;function C(t,e){const o=[],s=t.slice(0,e[0].at);return s.trim().length>40&&o.push({title:"(opening)",chars:s.length,tokens:0,at:0,end:e[0].at}),e.forEach((n,r)=>{const h=r+1<e.length?e[r+1].at:t.length;o.push({title:n.title.slice(0,120),chars:h-n.at,tokens:0,at:n.at,end:h})}),o}$(C,"P"),g(C,"cutAt");function M(t){const e=t.trim();if(!e.startsWith("{")&&!e.startsWith("["))return[];let o;try{o=JSON.parse(e)}catch{return[]}const s=o&&typeof o=="object"&&!Array.isArray(o)?o.properties:void 0,n=s&&typeof s=="object"&&!Array.isArray(s)?s:o,r=Array.isArray(n)?n.map((h,u)=>[`[${u}]`,h]):n&&typeof n=="object"?Object.entries(n):[];return r.length<2?[]:r.slice(0,_).map(([h,u])=>{const c=JSON.stringify(u,null,2)??"";return{title:h,chars:c.length,tokens:estimateTokens(c)}})}$(M,"M"),g(M,"jsonSections");export function sectionsOf(t){const e=taggedSections(t);return e.length?e:j(t)}$(sectionsOf,"sectionsOf"),g(sectionsOf,"sectionsOf");function j(t){if(t.length<200)return[];let e=g((s,n,r=()=>!0)=>[...t.matchAll(s)].filter(r).map(h=>({at:h.index,title:n(h)})),"pick")(Z,s=>s[1]);if(e.length<2){const s=[...t.matchAll(U)],n=[1,2,3,4].find(r=>s.filter(h=>h[1].length===r).length>=2);e=n?s.filter(r=>r[1].length===n).map(r=>({at:r.index,title:r[2]})):[]}if(e.length<2)return M(t);const o=C(t,e.slice(0,_));for(const s of o){const n=t.slice(s.at,s.end);s.tokens=estimateTokens(n);const r=n.indexOf(`
|
|
2
|
+
`),h=r>0?M(n.slice(r+1)):[];h.length&&(s.children=h)}return o}$(j,"N"),g(j,"plainSections");export const STABLE_TAGS=new Set(["instructions","rules","output","examples"]),CHANGING_TAGS=new Set(["memory","status","data","task"]);const F=/<([a-z][a-z0-9_-]{0,40})((?:\s+[A-Za-z_][\w.:-]*\s*=\s*(?:"[^"]*"|'[^']*'))*)\s*>/g,Q=/([A-Za-z_][\w.:-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g,X=g(t=>t.replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"),"unescape");function H(t){const e={};for(const o of t.matchAll(Q))e[o[1]]=X(o[2]??o[3]??"");return e}$(H,"q"),g(H,"attrsOf");function L(t,e,o,s){const n=`<${e}`,r=`</${e}>`;let h=1,u=o;for(;u<s;){const c=t.indexOf(r,u);if(c<0||c>=s)return-1;let f=t.indexOf(n,u);for(;f>=0&&f<c&&!/[\s>]/.test(t[f+n.length]??"");)f=t.indexOf(n,f+1);if(f>=0&&f<c){h++,u=f+n.length;continue}if(--h===0)return c;u=c+r.length}return-1}$(L,"z"),g(L,"closeOf");const V=g((t,e)=>{for(let o=e-1;o>=0;o--){const s=t[o];if(s===`
|
|
3
|
+
`)return!0;if(s!==" "&&s!==" ")return!1}return!0},"atLineStart");export function taggedSections(t,e=0,o=t.length,s=0){const n=[],r=new RegExp(F.source,"g");let h=e;for(;h<o&&n.length<_;){r.lastIndex=h;const l=r.exec(t);if(!l||l.index>=o)break;const p=l[1],k=l.index+l[0].length,x=V(t,l.index)?L(t,p,k,o):-1;if(x<0){h=k;continue}const i=x+p.length+3,d=H(l[2]??""),b={title:p,tag:p,...Object.keys(d).length?{attrs:d}:{},chars:i-l.index,tokens:estimateTokens(t.slice(l.index,i)),at:l.index,end:i};if(s<1){const w=taggedSections(t,k,x,s+1).filter(a=>a.tag),v=w.length?w:j(t.slice(k,x)).map(a=>a.at===void 0?a:{...a,at:a.at+k,end:a.end+k});v.length&&(b.children=v)}n.push(b),h=i}if(!n.length)return[];const u=[];let c=e;const f=g((l,p)=>{const k=t.slice(l,p);k.trim().length>20&&u.push({title:"(untagged)",chars:p-l,tokens:estimateTokens(k),at:l,end:p})},"gap");for(const l of n)f(c,l.at),u.push(l),c=l.end;f(c,o);const y=new Map;for(const l of u){if(!l.tag)continue;const p=l.attrs?.id?`${l.tag} \xB7 ${l.attrs.id}`:l.tag,k=(y.get(p)??0)+1;y.set(p,k),l.title=k>1?`${p} #${k}`:p}return u}$(taggedSections,"taggedSections"),g(taggedSections,"taggedSections");function N(t,e){const o=t.reduce((s,n)=>s+n.tokens,0);for(const s of t)s.tokens=o>0?Math.round(s.tokens/o*e):0,s.children&&N(s.children,s.tokens)}$(N,"I"),g(N,"scaleSections");const Y={user:"user",assistant:"assistant",developer:"Context \xB7 developer message",system:"system message"};function E(t,e,o){if(t.kind==="reasoning")return t.text?{kind:"history",label:"reasoning \xB7 sent back",role:"assistant",ref:{turn:e},text:t.text,estimate:estimateTokens(t.text)+B}:void 0;if(t.kind==="tool"){const s=`${t.name}(${t.arguments})${t.output!==void 0?`
|
|
4
|
+
${t.output}`:""}`;return{kind:"tool",label:`tool \xB7 ${t.tool}${t.output!==void 0?" \u2014 called, and its result":" \u2014 called"}`,role:"tool",ref:{turn:e},text:s,estimate:estimateTokens(s)+B*(t.output!==void 0?2:1)}}return{kind:o,label:Y[t.role],role:t.role,ref:{turn:e},text:t.text,estimate:estimateTokens(t.text)+B}}$(E,"Z"),g(E,"turnDraft");function J(t){const e=t.turns;let o=e.length;for(;o>0&&e[o-1].kind==="message"&&e[o-1].role!=="assistant";)o--;return e.map((s,n)=>s.kind==="tool"?"tool":s.kind==="reasoning"?"history":s.role==="developer"||s.role==="system"?"context":n>=o&&s.role==="user"?"turn":"history")}$(J,"F"),g(J,"turnKinds");function K(t){const e=t.system.map((c,f)=>({kind:"system",label:c.source==="instructions"?"System prompt \xB7 instructions":`System prompt \xB7 ${c.source}`,ref:{system:f},text:c.text,estimate:estimateTokens(c.text)+B})),o=t.tools.map(c=>JSON.stringify({name:c.name,description:c.description,parameters:c.parameters})).join(`
|
|
5
|
+
`),s=t.tools.length?[{kind:"tools",label:`${t.tools.length} tool${t.tools.length===1?"":"s"} offered`,ref:{tools:!0},text:o,estimate:estimateTokens(o)*I}]:[],n=t.answer.format?.schema!==void 0?JSON.stringify(t.answer.format.schema):"",r=n?[{kind:"schema",label:`Answer schema${t.answer.format?.name?` \xB7 ${t.answer.format.name}`:""}`,ref:{schema:!0},text:n,estimate:estimateTokens(n)*I}]:[],h=J(t),u=t.turns.map((c,f)=>E(c,f,h[f])).filter(c=>!!c);for(const c of u){const f=t.turns[c.ref.turn];f.kind==="message"&&f.attachments?.length&&(c.label+=` \xB7 ${f.attachments.join(", ")}`)}return t.provider==="anthropic"?[...s,...e,...r,...u]:[...e,...s,...r,...u]}$(K,"U"),g(K,"draftsOf");export function aiContextOf(t,e={}){const o=e.sections!==!1,s=K(t),n=Math.round(s.reduce((a,m)=>a+m.estimate,0)),r=t.usage,h=r?.input!==void 0&&r.input>0,u=h?r.input:n,c=t.settings.some(a=>a.name==="continues response"),f=t.gaps.some(a=>/^The (request was kept to its first|prompt was not kept)/.test(a)),y=h&&(c||f)&&u>n*1.05?u-n:0,l=c?y:0,p=c?0:y,k=n>0?(u-y)/n:0,x=s.map((a,m)=>({i:m,kind:a.kind,label:a.label,...a.role?{role:a.role}:{},ref:a.ref,chars:a.text.length,tokens:Math.round(a.estimate*k),cached:0,sections:!o||a.kind==="tools"?[]:sectionsOf(a.text)}));l&&x.unshift({i:-1,kind:"carried",label:"The conversation it continues",ref:{carried:!0},chars:0,tokens:l,cached:0,sections:[]}),p&&x.push({i:-1,kind:"unkept",label:"The rest of the request \u2014 not kept",ref:{unkept:!0},chars:0,tokens:p,cached:0,sections:[]});const i=u-x.reduce((a,m)=>a+m.tokens,0);i&&x.length&&(x.reduce((a,m)=>m.tokens>a.tokens?m:a).tokens+=i);for(const a of x)a.kind==="tools"&&o&&(a.sections=t.tools.map(m=>({title:m.server?`${m.server} \u203A ${m.tool}`:m.tool,chars:0,tokens:estimateTokens(JSON.stringify({d:m.description,p:m.parameters}))}))),N(a.sections,a.tokens);x.forEach((a,m)=>a.i=m);const d=Math.min(r?.cached??0,u);let b=d;for(const a of x)a.cached=Math.min(a.tokens,b),b-=a.cached;const w=AI_PART_KINDS.map(a=>{const m=x.filter(A=>A.kind===a);return{kind:a,tokens:m.reduce((A,T)=>A+T.tokens,0),cached:m.reduce((A,T)=>A+T.cached,0),parts:m.length}}).filter(a=>a.parts),v=q(t);return{parts:x,byKind:w,input:u,counted:h,cached:d,...r?.written?{written:r.written}:{},estimated:n,output:v,findings:z(t,x,u,d,v)}}$(aiContextOf,"aiContextOf"),g(aiContextOf,"aiContextOf");function q(t){const e=t.answer,o=e.json!==void 0?JSON.stringify(e.json):e.text??e.refusal??"",s=e.toolCalls.map(p=>`${p.name}(${p.arguments})`).join(`
|
|
6
|
+
`),n=estimateTokens(o),r=estimateTokens(s)+e.toolCalls.length*B,h=t.usage,u=h?.output!==void 0,c=u?h.output:Math.round(n+r),f=Math.min(h?.reasoning??0,c),y=c-f,l=n+r>0?Math.round(r/(n+r)*y):0;return{total:c,reasoning:f,answer:y-l,tools:l,counted:u}}$(q,"X"),g(q,"outputOf");const S=g(t=>Math.round(t).toLocaleString("en-US"),"fmt"),O=g((t,e)=>`${e>0?Math.round(t/e*100):0}%`,"pct");function z(t,e,o,s,n){const r=[],h=t.answer;if(h.status==="incomplete"||/max_output_tokens|length|max_tokens/.test(h.finish??"")){const i=t.settings.find(d=>/max output|max_tokens|max_completion/.test(d.name));r.push({tone:"bad",text:`It stopped at its output cap${i?` (${i.name} ${i.value})`:""} \u2014 the answer is cut.`})}t.gaps.length&&r.push({tone:"warn",text:"Part of the call was not kept \u2014 the split covers what was."});const u=t.turns.reduce((i,d)=>i+(d.kind==="message"?d.attachments?.length??0:0),0);u&&r.push({tone:"info",text:`${u} image${u===1?"":"s"} or file${u===1?"":"s"} went with its messages: their tokens are counted by the provider but not read from the text, so the split spreads them over the parts.`});const c=t.usage;if(c?.input!==void 0&&o>=1024){if(!s)r.push({tone:"warn",text:`None of its ${S(o)} input tokens came from the prompt cache${c.written?` \u2014 ${S(c.written)} were written to it, for a later call that opens the same way`:""}.`});else if(s<o*.9){const w=e.find(v=>v.cached<v.tokens);w&&r.push({tone:"info",text:`${O(s,o)} cached \u2014 the cache stopped in \xAB${w.label}\xBB: from there on the prompt differed from the last call that opened the same way.`})}const i=e.findIndex(w=>w.kind==="context"),d=i>=0?e.slice(i+1).filter(w=>w.kind==="history"||w.kind==="tool"):[],b=d.reduce((w,v)=>w+v.tokens,0);i>=0&&d.length>=2&&e[i].cached<e[i].tokens&&b>0&&r.push({tone:"info",text:`The context the app put in sits before ${d.length} earlier turns (${S(b)} tokens): when it changes between calls, nothing after it can be read from the cache.`})}const f=e.flatMap(i=>i.sections.filter(d=>d.tag).map(d=>({p:i,x:d}))),y=f.findIndex(({x:i})=>CHANGING_TAGS.has(i.tag)),l=y>=0?f.slice(y+1).filter(({x:i})=>STABLE_TAGS.has(i.tag)):[];if(y>=0&&l.length){const i=f[y].x,d=l.reduce((b,{x:w})=>b+w.tokens,0);r.push({tone:"info",text:`\xAB${i.title}\xBB changes from call to call and comes before ${l.length} section${l.length===1?"":"s"} that should not (\xAB${l[0].x.title}\xBB\u2026, ${S(d)} tokens): put the stable ones first, or they cannot be read from the cache.`})}const p=e.filter(i=>i.kind!=="carried").reduce((i,d)=>!i||d.tokens>i.tokens?d:i,void 0);if(p&&o>2e3&&p.tokens>o*.5){const i=p.sections.reduce((d,b)=>!d||b.tokens>d.tokens?b:d,void 0);r.push({tone:"info",text:`\xAB${p.label}\xBB is ${O(p.tokens,o)} of the input${i&&i.tokens>p.tokens*.4?` \u2014 most of it \xAB${i.title}\xBB (${S(i.tokens)} tokens)`:""}.`})}const k=e.find(i=>i.kind==="tools"),x=h.toolCalls.length;return k&&k.tokens>o*.2&&!x&&!t.turns.some(i=>i.kind==="tool")&&r.push({tone:"info",text:`${t.tools.length} tool definitions are ${O(k.tokens,o)} of the input, and it called none of them.`}),n.total>=200&&n.reasoning>n.total*.7&&r.push({tone:"info",text:`${O(n.reasoning,n.total)} of its output was reasoning (${S(n.reasoning)} tokens).`}),r}$(z,"Q"),g(z,"findingsOf");export function aiSplitOf(t){return Object.fromEntries(t.byKind.filter(e=>e.tokens>0).map(e=>[e.kind,e.tokens]))}$(aiSplitOf,"aiSplitOf"),g(aiSplitOf,"aiSplitOf");
|