generalcontext 0.2.0 → 0.3.0
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/README.md +32 -87
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +175 -0
- package/dist/index.d.ts +52 -9
- package/dist/index.js +1013 -37
- package/package.json +27 -4
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# generalcontext
|
|
2
2
|
|
|
3
|
-
Record your agent's model calls for General Context.
|
|
3
|
+
Record your AI agent's model calls for [General Context](https://generalcontextlabs.com/docs). We read your agent's production conversations and find where it goes wrong, and each failure you confirm becomes an RL and eval task built from the conversation. Your tasks together are your benchmark.
|
|
4
|
+
|
|
5
|
+
Recording never gets in your agent's way: calls are sent in the background, a failure on our side is logged and dropped, never thrown, and there are no runtime dependencies.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -10,110 +12,53 @@ npm install generalcontext
|
|
|
10
12
|
|
|
11
13
|
Set `GENERALCONTEXT_API_KEY` to the key from your workspace's Connect page.
|
|
12
14
|
|
|
13
|
-
##
|
|
15
|
+
## Record
|
|
14
16
|
|
|
15
|
-
Call once per process, before your agent makes model calls.
|
|
17
|
+
Call `initialize()` once per process, before your agent makes model calls. Pass the client modules you use: it is required whenever a bundler is involved.
|
|
16
18
|
|
|
17
19
|
```ts
|
|
18
20
|
import OpenAI from 'openai';
|
|
19
21
|
import Anthropic from '@anthropic-ai/sdk';
|
|
20
|
-
import { initialize } from 'generalcontext';
|
|
22
|
+
import { initialize, withSession, flush } from 'generalcontext';
|
|
21
23
|
|
|
22
24
|
initialize({ appName: 'support-agent', instrumentModules: { openAI: OpenAI, anthropic: Anthropic } });
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Configuration
|
|
26
|
-
|
|
27
|
-
| Option | Default | |
|
|
28
|
-
| - | - | - |
|
|
29
|
-
| `apiKey` | `GENERALCONTEXT_API_KEY` | |
|
|
30
|
-
| `baseUrl` | `GENERALCONTEXT_BASE_URL`, else `https://generalcontextlabs.com` | The option wins over the env var. Must be `https://`, except `http://localhost`, `127.0.0.1` or `[::1]`. Other URLs are refused and nothing is sent. |
|
|
31
|
-
| `appName` | none | Sent with every batch. |
|
|
32
|
-
| `redact` | `false` | See [Redaction](#redaction). |
|
|
33
|
-
| `beforeSend` | none | `(call) => call \| null`. Runs at send time on each call, before redaction. Return the call (edited or not) to send it, or `null` to drop it. A throw drops the call. |
|
|
34
|
-
| `instrumentModules` | installed `openai` and `@anthropic-ai/sdk` | Pass these whenever a bundler is involved. |
|
|
35
|
-
| `instrumentFetch` | `false` | SDK clients are always recorded. Set `true` to also record raw `fetch` POSTs to any `/chat/completions` URL. |
|
|
36
|
-
| `disableBatch` | `false` | Send each call right away. |
|
|
37
|
-
|
|
38
|
-
| Env var | |
|
|
39
|
-
| - | - |
|
|
40
|
-
| `GENERALCONTEXT_API_KEY` | API key, if `apiKey` is not passed. |
|
|
41
|
-
| `GENERALCONTEXT_BASE_URL` | Collector URL, if `baseUrl` is not passed. |
|
|
42
|
-
| `GENERALCONTEXT_REDACT_SALT` | Secret that keeps redaction placeholders stable across processes. |
|
|
43
|
-
| `GENERALCONTEXT_DISABLED` | `1`, `true` or `yes` makes `initialize()` do nothing. |
|
|
44
|
-
|
|
45
|
-
Requests never follow redirects. A call over 4 MB is dropped, and the queue holds at most 5000 calls or 64 MB. Pending calls are flushed when the process is about to exit, for at most 2 seconds.
|
|
46
|
-
|
|
47
|
-
## Proxies
|
|
48
|
-
|
|
49
|
-
Node's built-in `fetch` ignores `HTTPS_PROXY` by default. On Node 24+, set `NODE_USE_ENV_PROXY=1`. On older versions, install a global dispatcher (for example undici's `setGlobalDispatcher(new EnvHttpProxyAgent())`). If your proxy inspects TLS, point `NODE_EXTRA_CA_CERTS` at its CA certificate.
|
|
50
25
|
|
|
51
|
-
|
|
26
|
+
// Group every call in one conversation under the id you already use for it
|
|
27
|
+
await withSession({ sessionId: conversationId, userId: customerId, channel: 'chat' }, () => handle(message));
|
|
52
28
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
export async function register() {
|
|
57
|
-
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
58
|
-
const { initialize } = await import('generalcontext');
|
|
59
|
-
const { OpenAI } = await import('openai');
|
|
60
|
-
const anthropic = await import('@anthropic-ai/sdk');
|
|
61
|
-
initialize({ appName: 'my-agent', instrumentModules: { openAI: OpenAI, anthropic } });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Keep the SDKs out of the bundle so routes use the instance we patched:
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
// next.config.ts
|
|
70
|
-
export default { serverExternalPackages: ['openai', '@anthropic-ai/sdk'] };
|
|
29
|
+
await flush(); // before a serverless function, job or script exits
|
|
71
30
|
```
|
|
72
31
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## Sessions and tasks
|
|
76
|
-
|
|
77
|
-
Tag calls with your own ids. Tags carry across `await`.
|
|
32
|
+
If your app serves several companies of its own, pass `account` to `withSession` (the id or name you already use for that company) so conversations are grouped by account.
|
|
78
33
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
`withSession(conversationId, fn)` also works. `conversation({ id, ... }, fn)` is the older name.
|
|
88
|
-
|
|
89
|
-
## Serverless and scripts
|
|
90
|
-
|
|
91
|
-
Calls send in the background. Before a function or script exits:
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
await flush(); // never throws
|
|
95
|
-
```
|
|
34
|
+
| Provider | Package | `instrumentModules` key |
|
|
35
|
+
| - | - | - |
|
|
36
|
+
| OpenAI, including Azure OpenAI | `openai` | `openAI` |
|
|
37
|
+
| Anthropic, including Bedrock, Vertex and beta | `@anthropic-ai/sdk` | `anthropic` |
|
|
38
|
+
| Google Gemini | `@google/genai` | `googleGenAI` |
|
|
39
|
+
| AWS Bedrock Converse | `@aws-sdk/client-bedrock-runtime` | `bedrockRuntime` |
|
|
40
|
+
| Anything else | Send [OpenTelemetry](https://generalcontextlabs.com/docs/opentelemetry) | |
|
|
96
41
|
|
|
97
|
-
|
|
42
|
+
**Voice (OpenAI Realtime).** `OpenAIRealtimeWS` (`openai/realtime/ws`) and `OpenAIRealtimeWebSocket`
|
|
43
|
+
(`openai/realtime/websocket`) connections are recorded too. Create the connection inside `withSession(...)` and
|
|
44
|
+
the whole call lands in that conversation. Each `response.done` is recorded as one `openai-realtime` call shaped
|
|
45
|
+
like a Responses call: the instructions and tools, the conversation so far as text (caller turns from the input
|
|
46
|
+
transcription, agent turns from the output transcript, tool calls and their results), and a `voice` field with
|
|
47
|
+
`speech_stopped_at`, `first_audio_at`, `interrupted` and `truncated_at_ms`. Turn on input audio transcription in
|
|
48
|
+
the session to record what the caller said. Audio is never recorded. Pass a connection to `wrap()` to record only
|
|
49
|
+
that one.
|
|
98
50
|
|
|
99
|
-
|
|
51
|
+
Node 18 to 24, Node.js runtime only (not Edge). Supported client versions, Next.js, proxies, redaction, outcomes and events, and every option: [SDK reference](https://generalcontextlabs.com/docs/sdk).
|
|
100
52
|
|
|
101
|
-
|
|
102
|
-
await reportOutcome(conversationId, 'fail', 'customer reopened the ticket');
|
|
53
|
+
## Command line
|
|
103
54
|
|
|
104
|
-
|
|
105
|
-
|
|
55
|
+
```sh
|
|
56
|
+
npx generalcontext verify # the key works, and when the last call arrived
|
|
57
|
+
npx generalcontext verify --session conv-123 # that one conversation arrived
|
|
58
|
+
npx generalcontext benchmark pull --out benchmark.json
|
|
106
59
|
```
|
|
107
60
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
## Redaction
|
|
111
|
-
|
|
112
|
-
`initialize({ redact: true })` replaces emails, phone numbers and card or SSN-like numbers with placeholders such as `<email:3f9a1c>` before anything leaves your process. Set `GENERALCONTEXT_REDACT_SALT` to one secret in every process so the same value gets the same placeholder. Or pass your own `(text) => text`. Redaction also applies to `reportOutcome` and `logEvent`, so a redacted session id still matches its calls.
|
|
113
|
-
|
|
114
|
-
## One client only
|
|
115
|
-
|
|
116
|
-
`wrap(client, { redact })` records a single client instance without patching the SDK. Its `redact` applies to that client only. If `initialize()` already patched the SDK, the client is recorded with the `initialize()` settings.
|
|
61
|
+
Running the benchmark is in the Python package. See the [command line docs](https://generalcontextlabs.com/docs/cli).
|
|
117
62
|
|
|
118
63
|
## License
|
|
119
64
|
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright 2026 General Context Labs, Inc.
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// generalcontext verify [--session ID] [--json]
|
|
5
|
+
// generalcontext benchmark pull [--out FILE]
|
|
6
|
+
// Exit 0 on success, 1 on a failed check, 2 on a usage or configuration error. No runtime dependencies.
|
|
7
|
+
import { writeFileSync } from 'node:fs';
|
|
8
|
+
const DEFAULT_BASE_URL = 'https://generalcontextlabs.com';
|
|
9
|
+
const TIMEOUT_MS = 30_000;
|
|
10
|
+
const USAGE = `Usage:
|
|
11
|
+
generalcontext verify [--session ID] [--json]
|
|
12
|
+
generalcontext benchmark pull [--out FILE]
|
|
13
|
+
Uses GENERALCONTEXT_API_KEY and GENERALCONTEXT_BASE_URL.`;
|
|
14
|
+
const PYTHON_HINT = 'Run the benchmark with Python: pip install generalcontext && generalcontext evals run ...';
|
|
15
|
+
class Exit extends Error {
|
|
16
|
+
code;
|
|
17
|
+
constructor(code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.code = code;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const trimSlashes = (url) => { let end = url.length; while (end > 0 && url[end - 1] === '/')
|
|
23
|
+
end--; return url.slice(0, end); };
|
|
24
|
+
// Same rule as the SDK: https, or plain http to this machine only.
|
|
25
|
+
function secureUrl(url) {
|
|
26
|
+
try {
|
|
27
|
+
const u = new URL(url);
|
|
28
|
+
return u.protocol === 'https:' || (u.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function settings() {
|
|
35
|
+
const key = (process.env.GENERALCONTEXT_API_KEY ?? '').trim();
|
|
36
|
+
if (!key)
|
|
37
|
+
throw new Exit(2, 'GENERALCONTEXT_API_KEY is not set. Create a key on the API keys page and export it.');
|
|
38
|
+
const base = trimSlashes((process.env.GENERALCONTEXT_BASE_URL ?? '').trim() || DEFAULT_BASE_URL);
|
|
39
|
+
if (!secureUrl(base))
|
|
40
|
+
throw new Exit(2, `GENERALCONTEXT_BASE_URL ${base} is not https. Use https, or http://localhost for local development.`);
|
|
41
|
+
return { key, base };
|
|
42
|
+
}
|
|
43
|
+
async function get(path) {
|
|
44
|
+
const { key, base } = settings();
|
|
45
|
+
let response;
|
|
46
|
+
try {
|
|
47
|
+
response = await fetch(`${base}/api/platform-collect${path}`, {
|
|
48
|
+
headers: { Authorization: `Bearer ${key}`, Accept: 'application/json' },
|
|
49
|
+
redirect: 'error', // never forward the key to another host
|
|
50
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new Exit(1, `Could not reach ${base}: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
const text = await response.text();
|
|
57
|
+
let body = null;
|
|
58
|
+
try {
|
|
59
|
+
body = text ? JSON.parse(text) : null;
|
|
60
|
+
}
|
|
61
|
+
catch { /* not JSON */ }
|
|
62
|
+
return { status: response.status, body };
|
|
63
|
+
}
|
|
64
|
+
const errorText = (status, body) => typeof body?.error === 'string' ? body.error : `HTTP ${status}`;
|
|
65
|
+
function failed(status, body) {
|
|
66
|
+
if (status === 401)
|
|
67
|
+
throw new Exit(1, 'The API key was not accepted. Check GENERALCONTEXT_API_KEY.');
|
|
68
|
+
throw new Exit(1, `Request failed: ${errorText(status, body)}`);
|
|
69
|
+
}
|
|
70
|
+
function parseFlags(args, known) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (let i = 0; i < args.length; i++) {
|
|
73
|
+
let arg = args[i], value;
|
|
74
|
+
const eq = arg.indexOf('=');
|
|
75
|
+
if (arg.startsWith('--') && eq > 0) {
|
|
76
|
+
value = arg.slice(eq + 1);
|
|
77
|
+
arg = arg.slice(0, eq);
|
|
78
|
+
}
|
|
79
|
+
const kind = known[arg];
|
|
80
|
+
if (!kind)
|
|
81
|
+
throw new Exit(2, `Unknown argument ${arg}.\n${USAGE}`);
|
|
82
|
+
if (kind === 'bool') {
|
|
83
|
+
out[arg] = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
value ??= args[++i];
|
|
87
|
+
if (value === undefined || value === '')
|
|
88
|
+
throw new Exit(2, `${arg} needs a value.\n${USAGE}`);
|
|
89
|
+
out[arg] = value;
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
const scopeWords = { ingest: 'sends traffic', evals: 'runs the benchmark' };
|
|
94
|
+
async function verify(args) {
|
|
95
|
+
const flags = parseFlags(args, { '--session': 'value', '--json': 'bool' });
|
|
96
|
+
const session = flags['--session'];
|
|
97
|
+
const { status, body } = await get(`/status${session ? `?session=${encodeURIComponent(session)}` : ''}`);
|
|
98
|
+
if (status !== 200 || !body)
|
|
99
|
+
failed(status, body);
|
|
100
|
+
const found = session ? Boolean(body.session?.found) : true;
|
|
101
|
+
if (flags['--json']) {
|
|
102
|
+
console.log(JSON.stringify(body, null, 2));
|
|
103
|
+
return found ? 0 : 1;
|
|
104
|
+
}
|
|
105
|
+
const scopes = Array.isArray(body.key?.scopes) ? body.key.scopes : [];
|
|
106
|
+
const label = body.key?.label ? ` "${body.key.label}"` : '';
|
|
107
|
+
console.log(`Workspace ${body.workspace}. Key${label} ${scopes.length ? scopes.map(s => scopeWords[s] ?? s).join(' and ') : 'has no scopes'}.`);
|
|
108
|
+
console.log(body.last_call_at ? `Last call arrived ${body.last_call_at} from ${body.last_source ?? 'unknown'}.` : 'No calls have arrived yet.');
|
|
109
|
+
if (session) {
|
|
110
|
+
const s = body.session ?? {};
|
|
111
|
+
if (!found) {
|
|
112
|
+
console.log(`Conversation ${session}: not found yet.`);
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
const models = Array.isArray(s.models) ? s.models : [];
|
|
116
|
+
console.log(`Conversation ${session}: arrived, ${s.calls ?? 0} call${s.calls === 1 ? '' : 's'}${models.length ? ` (${models.join(', ')})` : ''}.`);
|
|
117
|
+
}
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
async function benchmark(args) {
|
|
121
|
+
const [sub, ...rest] = args;
|
|
122
|
+
if (sub !== 'pull')
|
|
123
|
+
throw new Exit(2, USAGE);
|
|
124
|
+
const flags = parseFlags(rest, { '--out': 'value' });
|
|
125
|
+
const out = flags['--out'] ?? 'benchmark.json';
|
|
126
|
+
const { status, body } = await get('/benchmark');
|
|
127
|
+
if (status === 403)
|
|
128
|
+
throw new Exit(1, errorText(status, body) === `HTTP 403`
|
|
129
|
+
? 'This key cannot download the benchmark. On API keys, create a key that can run the benchmark.' : errorText(status, body));
|
|
130
|
+
if (status !== 200 || !body)
|
|
131
|
+
failed(status, body);
|
|
132
|
+
try {
|
|
133
|
+
writeFileSync(out, JSON.stringify(body, null, 2) + '\n');
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throw new Exit(1, `Could not write ${out}: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
const tasks = Array.isArray(body.tasks) ? body.tasks : [];
|
|
139
|
+
const splits = new Map();
|
|
140
|
+
for (const t of tasks) {
|
|
141
|
+
const s = typeof t?.split === 'string' && t.split ? t.split : 'unsplit';
|
|
142
|
+
splits.set(s, (splits.get(s) ?? 0) + 1);
|
|
143
|
+
}
|
|
144
|
+
const bySplit = [...splits].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([s, n]) => `${n} ${s}`).join(', ');
|
|
145
|
+
console.log(`Wrote ${out}: ${tasks.length} task${tasks.length === 1 ? '' : 's'}${bySplit ? ` (${bySplit})` : ''}.`);
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
async function main(argv) {
|
|
149
|
+
const [command, ...args] = argv;
|
|
150
|
+
try {
|
|
151
|
+
if (command === 'verify')
|
|
152
|
+
return await verify(args);
|
|
153
|
+
if (command === 'benchmark')
|
|
154
|
+
return await benchmark(args);
|
|
155
|
+
if (command === 'evals') {
|
|
156
|
+
console.error(PYTHON_HINT);
|
|
157
|
+
return 2;
|
|
158
|
+
}
|
|
159
|
+
if (command === '-h' || command === '--help' || command === 'help') {
|
|
160
|
+
console.log(USAGE);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
console.error(USAGE);
|
|
164
|
+
return 2;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (error instanceof Exit) {
|
|
168
|
+
(error.code === 0 ? console.log : console.error)(error.message);
|
|
169
|
+
return error.code;
|
|
170
|
+
}
|
|
171
|
+
console.error(`generalcontext: ${error?.message ?? error}`);
|
|
172
|
+
return 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
main(process.argv.slice(2)).then(code => { process.exitCode = code; });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.3.0";
|
|
2
2
|
type Metadata = Record<string, string | number | boolean>;
|
|
3
3
|
export type Session = {
|
|
4
4
|
sessionId: string;
|
|
5
5
|
userId?: string;
|
|
6
6
|
channel?: string;
|
|
7
7
|
product?: string;
|
|
8
|
+
account?: string;
|
|
8
9
|
metadata?: Metadata;
|
|
9
10
|
};
|
|
10
11
|
type Tags = {
|
|
@@ -12,9 +13,17 @@ type Tags = {
|
|
|
12
13
|
user_id?: string;
|
|
13
14
|
channel?: string;
|
|
14
15
|
product?: string;
|
|
16
|
+
account?: string;
|
|
15
17
|
metadata?: Metadata;
|
|
16
18
|
};
|
|
17
|
-
type Api = 'openai-chat' | 'openai-responses' | 'anthropic';
|
|
19
|
+
type Api = 'openai-chat' | 'openai-responses' | 'anthropic' | 'gemini' | 'bedrock-converse' | 'openai-realtime';
|
|
20
|
+
/** Voice timing on 'openai-realtime' calls. Timestamps are ISO strings, null when not seen. */
|
|
21
|
+
export type VoiceTiming = {
|
|
22
|
+
speech_stopped_at: string | null;
|
|
23
|
+
first_audio_at: string | null;
|
|
24
|
+
interrupted: boolean;
|
|
25
|
+
truncated_at_ms?: number;
|
|
26
|
+
};
|
|
18
27
|
export type Call = {
|
|
19
28
|
api: Api;
|
|
20
29
|
conversation: Tags | null;
|
|
@@ -24,6 +33,7 @@ export type Call = {
|
|
|
24
33
|
started_at: string;
|
|
25
34
|
ended_at: string;
|
|
26
35
|
error: string | null;
|
|
36
|
+
voice?: VoiceTiming;
|
|
27
37
|
};
|
|
28
38
|
type Scrub = (text: string) => string;
|
|
29
39
|
type BeforeSend = (call: Call) => Call | null | undefined;
|
|
@@ -36,18 +46,29 @@ export type InitializeOptions = {
|
|
|
36
46
|
redact?: boolean | Scrub;
|
|
37
47
|
/** Runs at send time on each call, before redaction. Return the call to send, or null to drop it. */
|
|
38
48
|
beforeSend?: BeforeSend;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
/**
|
|
50
|
+
* SDK classes or modules to patch, e.g. { openAI: OpenAI, anthropic: Anthropic }. Also accepts
|
|
51
|
+
* googleGenAI (the '@google/genai' module) and bedrockRuntime (the '@aws-sdk/client-bedrock-runtime' module).
|
|
52
|
+
*/
|
|
53
|
+
instrumentModules?: Modules;
|
|
44
54
|
/** Also record raw fetch POSTs to any URL ending /chat/completions. Default false. */
|
|
45
55
|
instrumentFetch?: boolean;
|
|
46
56
|
/** Send each call right away instead of batching. */
|
|
47
57
|
disableBatch?: boolean;
|
|
48
58
|
};
|
|
59
|
+
type Modules = {
|
|
60
|
+
openAI?: unknown;
|
|
61
|
+
anthropic?: unknown;
|
|
62
|
+
googleGenAI?: unknown;
|
|
63
|
+
bedrockRuntime?: unknown;
|
|
64
|
+
};
|
|
49
65
|
/** Configure recording once per process, before your agent makes model calls. */
|
|
50
66
|
export declare function initialize(options?: InitializeOptions): void;
|
|
67
|
+
/**
|
|
68
|
+
* Resolves once initialize() has patched every SDK build it found, including the ES module builds it loads
|
|
69
|
+
* asynchronously. Await it when a model call may run before the app's next await after initialize(). Never rejects.
|
|
70
|
+
*/
|
|
71
|
+
export declare function ready(): Promise<void>;
|
|
51
72
|
/** Tag every call made inside `run` with one session. `conversation` is the same function. */
|
|
52
73
|
export declare function withSession<T>(session: Session | string, run: () => T): T;
|
|
53
74
|
/** Older name for withSession. Accepts { id } as well as { sessionId }. */
|
|
@@ -55,12 +76,34 @@ export declare function conversation<T>(tags: Session | {
|
|
|
55
76
|
id: string;
|
|
56
77
|
channel?: string;
|
|
57
78
|
product?: string;
|
|
79
|
+
account?: string;
|
|
58
80
|
metadata?: Metadata;
|
|
59
81
|
} | string, run: () => T): T;
|
|
60
82
|
/** Tag every call made inside `run` with a task name, such as one step of your agent loop. */
|
|
61
83
|
export declare function task<T>(name: string, run: () => T): T;
|
|
62
|
-
/**
|
|
63
|
-
|
|
84
|
+
/**
|
|
85
|
+
* The copy of a call that is recorded. Never changes `value`, and never touches what the caller sends to the provider.
|
|
86
|
+
* Agents resend the whole run on every call, so without this, later calls pass the per-call limit and are lost.
|
|
87
|
+
*
|
|
88
|
+
* - Inline binary becomes a placeholder that keeps the shape: a base64 data URL keeps its prefix
|
|
89
|
+
* (`data:image/png;base64,[omitted N bytes sha256:ab12cd34ef56]`), and a base64 `data`, `result` or
|
|
90
|
+
* `b64_json` field (Anthropic `source.data`, Gemini `inlineData.data`, OpenAI `input_audio.data`, Responses
|
|
91
|
+
* `image_generation_call.result`) becomes `[omitted N bytes sha256:...]`. N is the length of the base64 text
|
|
92
|
+
* and the hash is the first 12 hex digits of the sha256 of that text, so the same image gets the same placeholder
|
|
93
|
+
* in every call. Media types stay. Binary is never recorded: it does not help review and it is a privacy risk.
|
|
94
|
+
* - Any string over 200,000 characters keeps its first 100,000 and last 20,000 characters around
|
|
95
|
+
* `\n[... clipped N characters ...]\n`.
|
|
96
|
+
*
|
|
97
|
+
* Runs before beforeSend and redaction. A call still over the limit afterwards is dropped.
|
|
98
|
+
*/
|
|
99
|
+
export declare function compactForRecording(value: unknown, key?: string, depth?: number): unknown;
|
|
100
|
+
export declare function maskSecrets(text: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* Wait until recorded calls are sent, for at most `timeoutMs` (default 10 s). Call before a serverless
|
|
103
|
+
* function or script exits. Resolves true when the queue was sent, false at the deadline. Never throws.
|
|
104
|
+
* Sending continues in the background after the deadline.
|
|
105
|
+
*/
|
|
106
|
+
export declare function flush(timeoutMs?: number): Promise<boolean>;
|
|
64
107
|
/** Mark how a session ended. Returns true when the platform accepted it. Never throws. */
|
|
65
108
|
export declare function reportOutcome(sessionId: string, outcome: 'success' | 'fail', note?: string): Promise<boolean>;
|
|
66
109
|
export type AppEvent = {
|