@amplitude/ai 0.2.1 → 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/.claude/commands/instrument-with-amplitude-ai.md +12 -0
- package/.cursor/skills/instrument-with-amplitude-ai/SKILL.md +4 -42
- package/AGENTS.md +86 -28
- package/README.md +190 -111
- package/amplitude-ai.md +463 -0
- package/bin/amplitude-ai-doctor.mjs +0 -0
- package/bin/amplitude-ai-instrument.mjs +0 -0
- package/bin/amplitude-ai-mcp.mjs +0 -0
- package/bin/amplitude-ai-register-catalog.mjs +0 -0
- package/bin/amplitude-ai-status.mjs +0 -0
- package/bin/amplitude-ai.mjs +14 -5
- package/data/agent_event_catalog.json +52 -2
- package/dist/bound-agent.d.ts +18 -14
- package/dist/bound-agent.d.ts.map +1 -1
- package/dist/bound-agent.js +4 -1
- package/dist/bound-agent.js.map +1 -1
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +31 -19
- package/dist/cli/status.js.map +1 -1
- package/dist/client.d.ts +14 -14
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +38 -0
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +5 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +4 -0
- package/dist/context.js.map +1 -1
- package/dist/core/constants.d.ts +3 -1
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/constants.js +3 -1
- package/dist/core/constants.js.map +1 -1
- package/dist/core/tracking.d.ts +12 -2
- package/dist/core/tracking.d.ts.map +1 -1
- package/dist/core/tracking.js +13 -2
- package/dist/core/tracking.js.map +1 -1
- package/dist/decorators.d.ts +1 -1
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +4 -3
- package/dist/decorators.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -2
- package/dist/mcp/contract.d.ts +4 -0
- package/dist/mcp/contract.d.ts.map +1 -1
- package/dist/mcp/contract.js +6 -2
- package/dist/mcp/contract.js.map +1 -1
- package/dist/mcp/generate-verify-test.d.ts +7 -0
- package/dist/mcp/generate-verify-test.d.ts.map +1 -0
- package/dist/mcp/generate-verify-test.js +25 -0
- package/dist/mcp/generate-verify-test.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.js +2 -1
- package/dist/mcp/instrument-file.d.ts +14 -0
- package/dist/mcp/instrument-file.d.ts.map +1 -0
- package/dist/mcp/instrument-file.js +139 -0
- package/dist/mcp/instrument-file.js.map +1 -0
- package/dist/mcp/scan-project.d.ts +52 -0
- package/dist/mcp/scan-project.d.ts.map +1 -0
- package/dist/mcp/scan-project.js +309 -0
- package/dist/mcp/scan-project.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +79 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/validate-file.d.ts +4 -0
- package/dist/mcp/validate-file.d.ts.map +1 -1
- package/dist/mcp/validate-file.js +559 -11
- package/dist/mcp/validate-file.js.map +1 -1
- package/dist/middleware.js +2 -1
- package/dist/middleware.js.map +1 -1
- package/dist/node_modules/.pnpm/acorn-typescript@1.4.13_acorn@8.16.0/node_modules/acorn-typescript/lib/index.js +2389 -0
- package/dist/node_modules/.pnpm/acorn-typescript@1.4.13_acorn@8.16.0/node_modules/acorn-typescript/lib/index.js.map +1 -0
- package/dist/node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.js +5128 -0
- package/dist/node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js +1 -1
- package/dist/patching.d.ts.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +1 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/base.d.ts +2 -1
- package/dist/providers/base.d.ts.map +1 -1
- package/dist/providers/base.js +4 -0
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +2 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/serverless.d.ts +19 -0
- package/dist/serverless.d.ts.map +1 -0
- package/dist/serverless.js +35 -0
- package/dist/serverless.js.map +1 -0
- package/dist/session.d.ts +24 -8
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +20 -1
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/llms-full.txt +353 -69
- package/llms.txt +6 -2
- package/mcp.schema.json +7 -3
- package/package.json +10 -5
- package/bin/amplitude-ai-init.mjs +0 -27
- package/dist/cli/init.d.ts +0 -14
- package/dist/cli/init.d.ts.map +0 -1
- package/dist/cli/init.js +0 -40
- package/dist/cli/init.js.map +0 -1
package/llms-full.txt
CHANGED
|
@@ -1,73 +1,260 @@
|
|
|
1
1
|
# llms-full.txt
|
|
2
|
-
# @amplitude/ai 0.
|
|
3
|
-
#
|
|
2
|
+
# @amplitude/ai 0.3.0 — Complete API Reference for AI Coding Agents
|
|
3
|
+
#
|
|
4
|
+
# This file is the definitive guide for any coding agent instrumenting
|
|
5
|
+
# a JavaScript/TypeScript AI application with @amplitude/ai.
|
|
6
|
+
# For tool discovery only, see llms.txt.
|
|
7
|
+
|
|
8
|
+
## Quick Setup for AI Coding Agents
|
|
9
|
+
|
|
10
|
+
Install the SDK, then configure your coding agent's MCP server for
|
|
11
|
+
project analysis tools (scan_project, validate_file):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @amplitude/ai
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
MCP config (one line — works with any MCP-compatible agent):
|
|
18
|
+
```json
|
|
19
|
+
{ "amplitude-ai": { "command": "npx", "args": ["amplitude-ai", "mcp"] } }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Agent-specific setup:
|
|
23
|
+
|
|
24
|
+
Cursor: Add to .cursor/mcp.json:
|
|
25
|
+
{ "mcpServers": { "amplitude-ai": { "command": "npx", "args": ["amplitude-ai", "mcp"] } } }
|
|
26
|
+
Then point the agent at: node_modules/@amplitude/ai/amplitude-ai.md
|
|
27
|
+
|
|
28
|
+
Claude Code:
|
|
29
|
+
claude mcp add amplitude-ai -- npx amplitude-ai mcp
|
|
30
|
+
Then point the agent at: node_modules/@amplitude/ai/amplitude-ai.md
|
|
31
|
+
|
|
32
|
+
Codex CLI: Add to ~/.codex/config.toml:
|
|
33
|
+
[mcp_servers.amplitude-ai]
|
|
34
|
+
command = "npx"
|
|
35
|
+
args = ["amplitude-ai", "mcp"]
|
|
36
|
+
AGENTS.md in the npm package is auto-read by Codex.
|
|
37
|
+
|
|
38
|
+
Without MCP: Read amplitude-ai.md for the complete guided workflow, or this file for the API reference.
|
|
39
|
+
|
|
40
|
+
## Instrumentation Workflow (4 Phases)
|
|
41
|
+
|
|
42
|
+
Phase 1 — Detect: Run scan_project MCP tool (or read package.json + grep for providers manually).
|
|
43
|
+
Phase 2 — Discover: Identify agents, call sites, delegation patterns. Read source files.
|
|
44
|
+
Phase 3 — Instrument: Create bootstrap file, swap providers, add session wrapping, tool tracking.
|
|
45
|
+
Phase 4 — Verify: Run tsc --noEmit, validate_file on changed files, amplitude-ai doctor.
|
|
46
|
+
|
|
47
|
+
For the full guided workflow with code examples for every phase,
|
|
48
|
+
read the MCP resource: amplitude-ai://instrument-guide
|
|
49
|
+
|
|
50
|
+
---
|
|
4
51
|
|
|
5
52
|
## Core API
|
|
6
53
|
|
|
7
|
-
### AmplitudeAI(
|
|
54
|
+
### AmplitudeAI(options)
|
|
8
55
|
Initialize the SDK. Required entry point.
|
|
9
56
|
```
|
|
10
|
-
|
|
57
|
+
import { AmplitudeAI, AIConfig } from '@amplitude/ai';
|
|
58
|
+
const ai = new AmplitudeAI({
|
|
59
|
+
apiKey: string,
|
|
60
|
+
config?: new AIConfig({
|
|
61
|
+
contentMode?: 'full' | 'metadata_only' | 'customer_enriched',
|
|
62
|
+
redactPii?: boolean,
|
|
63
|
+
debug?: boolean,
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
11
66
|
```
|
|
12
67
|
|
|
13
|
-
### patch(options)
|
|
68
|
+
### patch(options) / unpatch()
|
|
14
69
|
Zero-code instrumentation. Monkey-patches all detected provider SDKs.
|
|
15
70
|
```
|
|
16
|
-
|
|
17
|
-
|
|
71
|
+
import { patch, unpatch } from '@amplitude/ai';
|
|
72
|
+
patch({ amplitudeAI: ai });
|
|
73
|
+
// All provider calls are now tracked. Call unpatch() to restore.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### wrap(client, ai, opts?)
|
|
77
|
+
Wrap an existing provider client without changing its construction.
|
|
78
|
+
```
|
|
79
|
+
import { wrap } from '@amplitude/ai';
|
|
80
|
+
const instrumented = wrap(existingOpenAIClient, ai);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### ai.agent(agentId, opts?) → BoundAgent
|
|
84
|
+
Create a bound agent for identity and session lineage.
|
|
85
|
+
```
|
|
86
|
+
const agent = ai.agent('shopping-agent', {
|
|
87
|
+
description?: string, // human-readable, appears in event streams
|
|
88
|
+
userId?: string,
|
|
89
|
+
parentAgentId?: string,
|
|
90
|
+
customerOrgId?: string,
|
|
91
|
+
agentVersion?: string,
|
|
92
|
+
env?: string,
|
|
93
|
+
context?: Record<string, unknown>,
|
|
94
|
+
sessionId?: string,
|
|
95
|
+
groups?: Record<string, unknown>,
|
|
96
|
+
deviceId?: string,
|
|
97
|
+
browserSessionId?: string,
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### agent.child(agentId, overrides?) → BoundAgent
|
|
102
|
+
Create a child agent that inherits parent identity.
|
|
103
|
+
```
|
|
104
|
+
const recipeAgent = shoppingAgent.child('recipe-agent', {
|
|
105
|
+
description: 'Finds recipes and checks ingredient availability',
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### agent.session(opts?) → Session
|
|
110
|
+
Create a session for multi-turn tracking.
|
|
111
|
+
```
|
|
112
|
+
const session = agent.session({
|
|
113
|
+
sessionId?: string, // defaults to random UUID
|
|
114
|
+
userId?: string,
|
|
115
|
+
deviceId?: string,
|
|
116
|
+
browserSessionId?: string, // links to Amplitude browser session
|
|
117
|
+
autoFlush?: boolean, // auto-flush on completion (default: auto-detect serverless)
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### session.run(fn) / session.runSync(fn)
|
|
122
|
+
Execute code within session context. Auto-ends session when fn completes.
|
|
123
|
+
Provider calls inside run() are automatically tagged with session/agent identity.
|
|
124
|
+
```
|
|
125
|
+
await session.run(async (s) => {
|
|
126
|
+
s.trackUserMessage(content);
|
|
127
|
+
// ... LLM calls are auto-tracked by provider wrappers ...
|
|
128
|
+
s.trackAiMessage(response, model, provider, latencyMs);
|
|
129
|
+
});
|
|
130
|
+
// Session automatically ends here, [Agent] Session End emitted.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### s.runAs(childAgent, fn) / s.runAsSync(childAgent, fn)
|
|
134
|
+
Delegate to a child agent within the same session.
|
|
135
|
+
Shares sessionId, traceId, turn counter. Does NOT emit Session End for child.
|
|
136
|
+
```
|
|
137
|
+
await s.runAs(recipeAgent, async (cs) => {
|
|
138
|
+
cs.trackUserMessage('Find me a pancake recipe');
|
|
139
|
+
// LLM calls here are tagged with recipe-agent's agentId
|
|
140
|
+
// parentAgentId is automatically set to shopping-agent
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### s.trackUserMessage(content, opts?) → eventId
|
|
145
|
+
Track a user message. Returns the event ID.
|
|
146
|
+
```
|
|
147
|
+
s.trackUserMessage('What recipes do you have for pancakes?');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### s.trackAiMessage(content, model, provider, latencyMs, opts?) → eventId
|
|
151
|
+
Track an AI response. Use when provider wrappers can't auto-capture (e.g. Assistants API).
|
|
152
|
+
```
|
|
153
|
+
s.trackAiMessage('Here are some pancake recipes...', 'gpt-4o-mini', 'openai', 1250, {
|
|
154
|
+
inputTokens: 150, outputTokens: 400, totalTokens: 550,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### s.trackToolCall(toolName, latencyMs, success, opts?) → eventId
|
|
159
|
+
Track a tool call explicitly (prefer tool() HOF instead).
|
|
160
|
+
```
|
|
161
|
+
s.trackToolCall('search_products', 45, true);
|
|
18
162
|
```
|
|
19
163
|
|
|
20
|
-
###
|
|
21
|
-
|
|
164
|
+
### s.score(name, value, targetId, opts?)
|
|
165
|
+
Track a quality score (user feedback, automated eval, reviewer).
|
|
22
166
|
```
|
|
23
|
-
|
|
24
|
-
wrap(anthropicClient, ai) → Anthropic wrapper
|
|
25
|
-
wrap(azureClient, ai) → AzureOpenAI wrapper
|
|
167
|
+
s.score('user-feedback', 1, 'msg-001', { targetType: 'message', source: 'user' });
|
|
26
168
|
```
|
|
27
169
|
|
|
28
|
-
### ai.
|
|
29
|
-
|
|
170
|
+
### ai.score(opts)
|
|
171
|
+
Track a score outside session context.
|
|
30
172
|
```
|
|
31
|
-
|
|
32
|
-
const child = agent.child('sub-agent')
|
|
33
|
-
const session = agent.session({ sessionId: 's1' })
|
|
34
|
-
await session.run(async (s) => { ... })
|
|
35
|
-
await s.runAs(child, async (cs) => { ... }) // delegate to child agent
|
|
173
|
+
ai.score({ userId, name: 'user-feedback', value: 1, targetId: 'msg-001', targetType: 'message', source: 'user' });
|
|
36
174
|
```
|
|
37
175
|
|
|
38
|
-
###
|
|
39
|
-
|
|
176
|
+
### ai.flush() / agent.flush()
|
|
177
|
+
Flush pending events. CRITICAL for serverless (Next.js, Vercel, Lambda):
|
|
178
|
+
```
|
|
179
|
+
await session.run(async (s) => { /* ... */ });
|
|
180
|
+
await ai.flush(); // Must call before returning response in serverless!
|
|
40
181
|
```
|
|
41
|
-
|
|
182
|
+
|
|
183
|
+
### tool(fn, opts?) / tool(opts)(fn)
|
|
184
|
+
Higher-order function that wraps a function to auto-emit [Agent] Tool Call events.
|
|
185
|
+
Works inside session.run() — automatically inherits session/agent context.
|
|
186
|
+
```
|
|
187
|
+
import { tool } from '@amplitude/ai';
|
|
188
|
+
const searchProducts = tool(async (args: { query: string }) => { /* ... */ }, {
|
|
42
189
|
name: 'search_products',
|
|
43
|
-
|
|
44
|
-
|
|
190
|
+
});
|
|
191
|
+
// Inside session.run(), just call the wrapped function:
|
|
192
|
+
const results = await searchProducts({ query: 'pancakes' });
|
|
193
|
+
// [Agent] Tool Call event is automatically emitted with duration, success, input/output.
|
|
45
194
|
```
|
|
46
195
|
|
|
47
|
-
### observe(fn,
|
|
48
|
-
|
|
196
|
+
### observe(fn, opts?) / observe(opts)(fn)
|
|
197
|
+
Higher-order function that wraps a function to auto-emit [Agent] Span events.
|
|
198
|
+
Creates a session boundary if none exists.
|
|
49
199
|
```
|
|
50
|
-
|
|
200
|
+
import { observe } from '@amplitude/ai';
|
|
201
|
+
const handleRequest = observe(async (req) => { /* ... */ }, { name: 'request-handler' });
|
|
51
202
|
```
|
|
52
203
|
|
|
53
|
-
###
|
|
54
|
-
|
|
204
|
+
### injectContext() / extractContext(headers)
|
|
205
|
+
Cross-service context propagation for message queues and microservices.
|
|
55
206
|
```
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
207
|
+
import { injectContext, extractContext } from '@amplitude/ai';
|
|
208
|
+
// Sender (inside session.run):
|
|
209
|
+
const headers = injectContext();
|
|
210
|
+
await queue.send({ payload, headers });
|
|
211
|
+
// Receiver:
|
|
212
|
+
const ctx = extractContext(message.headers);
|
|
213
|
+
const session = agent.session({ ...ctx });
|
|
59
214
|
```
|
|
60
215
|
|
|
216
|
+
### createAmplitudeAIMiddleware(opts)
|
|
217
|
+
Express/Fastify/Hono middleware for automatic session tracking.
|
|
218
|
+
```
|
|
219
|
+
import { createAmplitudeAIMiddleware } from '@amplitude/ai';
|
|
220
|
+
app.use(createAmplitudeAIMiddleware({
|
|
221
|
+
amplitudeAI: ai,
|
|
222
|
+
userIdResolver: (req) => req.headers['x-user-id'] ?? null,
|
|
223
|
+
}));
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### MockAmplitudeAI (testing)
|
|
227
|
+
Deterministic test double. Captures events in-memory.
|
|
228
|
+
```
|
|
229
|
+
import { AIConfig } from '@amplitude/ai';
|
|
230
|
+
import { MockAmplitudeAI } from '@amplitude/ai/testing';
|
|
231
|
+
const mock = new MockAmplitudeAI(new AIConfig({ contentMode: 'full' }));
|
|
232
|
+
const agent = mock.agent('test-agent', { userId: 'u1' });
|
|
233
|
+
await agent.session({ sessionId: 's1' }).run(async (s) => { /* ... */ });
|
|
234
|
+
mock.getEvents() // all events
|
|
235
|
+
mock.getEvents('[Agent] Tool Call') // filtered by type
|
|
236
|
+
mock.eventsForAgent('recipe-agent') // filtered by agent ID
|
|
237
|
+
mock.assertEventTracked('[Agent] User Message', { userId: 'u1' })
|
|
238
|
+
mock.assertSessionClosed('s1')
|
|
239
|
+
await mock.flush();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
61
244
|
## Provider Wrappers
|
|
62
245
|
|
|
63
|
-
| Provider | Import
|
|
64
|
-
|
|
65
|
-
| OpenAI | `new OpenAI({ amplitude })`
|
|
66
|
-
| Anthropic | `new Anthropic({ amplitude })
|
|
67
|
-
| Gemini | `new Gemini({ amplitude })`
|
|
68
|
-
| AzureOpenAI | `new AzureOpenAI({ amplitude })
|
|
69
|
-
| Bedrock | `new Bedrock({ amplitude })`
|
|
70
|
-
| Mistral | `new Mistral({ amplitude })`
|
|
246
|
+
| Provider | Import | Streaming | Tool Calls | TTFB | Cache |
|
|
247
|
+
|-------------|----------------------------------|-----------|------------|------|-------|
|
|
248
|
+
| OpenAI | `new OpenAI({ amplitude: ai })` | Yes | Yes | Yes | Yes |
|
|
249
|
+
| Anthropic | `new Anthropic({ amplitude: ai })`| Yes | Yes | Yes | Yes |
|
|
250
|
+
| Gemini | `new Gemini({ amplitude: ai })` | Yes | No | No | No |
|
|
251
|
+
| AzureOpenAI | `new AzureOpenAI({ amplitude: ai })`| Yes | Yes | Yes | No |
|
|
252
|
+
| Bedrock | `new Bedrock({ amplitude: ai })` | Yes | Yes | No | No |
|
|
253
|
+
| Mistral | `new Mistral({ amplitude: ai })` | Yes | No | No | No |
|
|
254
|
+
|
|
255
|
+
All wrappers are imported from '@amplitude/ai'.
|
|
256
|
+
|
|
257
|
+
---
|
|
71
258
|
|
|
72
259
|
## Canonical Patterns
|
|
73
260
|
|
|
@@ -85,59 +272,156 @@ const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
|
85
272
|
const instrumented = wrap(existingOpenAIClient, ai);
|
|
86
273
|
```
|
|
87
274
|
|
|
88
|
-
### 3. Provider wrapper
|
|
275
|
+
### 3. Provider wrapper (recommended)
|
|
89
276
|
```typescript
|
|
90
|
-
import { AmplitudeAI, OpenAI } from '@amplitude/ai';
|
|
91
|
-
const ai = new AmplitudeAI({
|
|
92
|
-
|
|
277
|
+
import { AmplitudeAI, AIConfig, OpenAI } from '@amplitude/ai';
|
|
278
|
+
const ai = new AmplitudeAI({
|
|
279
|
+
apiKey: process.env.AMPLITUDE_AI_API_KEY!,
|
|
280
|
+
config: new AIConfig({ contentMode: 'full', redactPii: true }),
|
|
281
|
+
});
|
|
282
|
+
const openai = new OpenAI({ amplitude: ai, apiKey: process.env.OPENAI_API_KEY! });
|
|
93
283
|
```
|
|
94
284
|
|
|
95
|
-
### 4.
|
|
285
|
+
### 4. Multi-agent with session + runAs (recommended for multi-agent apps)
|
|
96
286
|
```typescript
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
287
|
+
import { AmplitudeAI, AIConfig, OpenAI, tool } from '@amplitude/ai';
|
|
288
|
+
|
|
289
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY!, config: new AIConfig({ contentMode: 'full', redactPii: true }) });
|
|
290
|
+
const openai = new OpenAI({ amplitude: ai, apiKey: process.env.OPENAI_API_KEY! });
|
|
291
|
+
|
|
292
|
+
const orchestrator = ai.agent('shopping-agent', { description: 'Orchestrates shopping requests' });
|
|
293
|
+
const recipeAgent = orchestrator.child('recipe-agent', { description: 'Finds recipes' });
|
|
294
|
+
|
|
295
|
+
const askRecipeAgent = tool(async (args: { query: string }) => {
|
|
296
|
+
// ... calls openai inside s.runAs(recipeAgent, ...)
|
|
297
|
+
}, { name: 'ask_recipe_agent' });
|
|
298
|
+
|
|
299
|
+
export async function POST(req: Request) {
|
|
300
|
+
const { messages, userId } = await req.json();
|
|
301
|
+
const browserSessionId = req.headers.get('x-amplitude-session-id');
|
|
302
|
+
const deviceId = req.headers.get('x-amplitude-device-id');
|
|
303
|
+
|
|
304
|
+
return orchestrator.session({ userId, browserSessionId, deviceId }).run(async (s) => {
|
|
305
|
+
s.trackUserMessage(messages[messages.length - 1].content);
|
|
306
|
+
|
|
307
|
+
// Provider calls auto-tracked. tool() calls auto-tracked.
|
|
308
|
+
const response = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages });
|
|
309
|
+
|
|
310
|
+
s.trackAiMessage(response.choices[0].message.content, 'gpt-4o-mini', 'openai', latencyMs);
|
|
311
|
+
await ai.flush(); // serverless: flush before returning
|
|
312
|
+
return Response.json({ content: response.choices[0].message.content });
|
|
313
|
+
});
|
|
314
|
+
}
|
|
100
315
|
```
|
|
101
316
|
|
|
102
317
|
### 5. Express middleware
|
|
103
318
|
```typescript
|
|
104
|
-
import { createAmplitudeAIMiddleware } from '@amplitude/ai';
|
|
105
|
-
|
|
319
|
+
import { AmplitudeAI, createAmplitudeAIMiddleware } from '@amplitude/ai';
|
|
320
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
321
|
+
app.use(createAmplitudeAIMiddleware({
|
|
322
|
+
amplitudeAI: ai,
|
|
323
|
+
userIdResolver: (req) => req.headers['x-user-id'] ?? null,
|
|
324
|
+
}));
|
|
106
325
|
```
|
|
107
326
|
|
|
108
|
-
### 6.
|
|
327
|
+
### 6. Testing with MockAmplitudeAI
|
|
109
328
|
```typescript
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
329
|
+
import { AIConfig, tool } from '@amplitude/ai';
|
|
330
|
+
import { MockAmplitudeAI } from '@amplitude/ai/testing';
|
|
331
|
+
const mock = new MockAmplitudeAI(new AIConfig({ contentMode: 'full' }));
|
|
332
|
+
const agent = mock.agent('test-agent');
|
|
333
|
+
const session = agent.session({ userId: 'test-user', sessionId: 'test-session' });
|
|
113
334
|
await session.run(async (s) => {
|
|
114
|
-
|
|
115
|
-
const result = await s.runAs(researcher, async (cs) => {
|
|
116
|
-
return openai.chat.completions.create({ model: 'gpt-4o', messages: [...] });
|
|
117
|
-
});
|
|
335
|
+
s.trackUserMessage('hello');
|
|
118
336
|
});
|
|
119
|
-
|
|
337
|
+
expect(mock.getEvents('[Agent] User Message').length).toBe(1);
|
|
338
|
+
mock.assertSessionClosed('test-session');
|
|
120
339
|
```
|
|
121
340
|
|
|
122
|
-
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## MCP Tools (via amplitude-ai mcp)
|
|
344
|
+
|
|
345
|
+
All tools are available when the MCP server is connected.
|
|
346
|
+
|
|
347
|
+
### scan_project(root_path) → ScanResult
|
|
348
|
+
Detect framework, providers, agents, call sites, and multi-agent signals.
|
|
349
|
+
Returns structured JSON with: framework, providers[], agents[] (each with inferred_id,
|
|
350
|
+
file, call_site_details, tool_definitions), is_multi_agent, multi_agent_signals[],
|
|
351
|
+
has_streaming, has_edge_runtime, has_frontend_deps, recommendations[].
|
|
352
|
+
Use this as the first step of instrumentation.
|
|
353
|
+
|
|
354
|
+
### validate_file(source, language?) → FileAnalysis
|
|
355
|
+
AST-based analysis of a single file for uninstrumented LLM call sites.
|
|
356
|
+
Returns: call_sites[] with instrumented (bool), has_patch, has_amplitude_import,
|
|
357
|
+
has_session_context. Run before and after instrumentation to verify coverage.
|
|
358
|
+
|
|
359
|
+
### instrument_file(source, language?, scan_result?) → InstrumentedSource
|
|
360
|
+
Apply text-based provider swap and session transforms.
|
|
361
|
+
Best for simple import swaps. For multi-agent patterns, prefer direct editing.
|
|
362
|
+
|
|
363
|
+
### generate_verify_test(scan_result) → TestSource
|
|
364
|
+
Generate a vitest verification test using MockAmplitudeAI.
|
|
365
|
+
Tests each discovered agent and multi-agent delegation.
|
|
366
|
+
|
|
367
|
+
### validate_setup() → SetupStatus
|
|
368
|
+
Check required environment variables (AMPLITUDE_AI_API_KEY, etc.).
|
|
123
369
|
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
370
|
+
### suggest_instrumentation(framework?, provider?, content_tier?) → Guidance
|
|
371
|
+
Framework-specific instrumentation guidance with content-tier and privacy defaults.
|
|
372
|
+
|
|
373
|
+
### get_event_schema(event_type?) → EventSchema
|
|
374
|
+
Return the event property catalog for all or specific event types.
|
|
375
|
+
|
|
376
|
+
### get_integration_pattern(id?) → Pattern
|
|
377
|
+
Return canonical instrumentation patterns (zero-code, wrap, bound-agent, etc.).
|
|
378
|
+
|
|
379
|
+
### search_docs(query, max_results?) → SearchResults
|
|
380
|
+
Keyword search over README and this file.
|
|
381
|
+
|
|
382
|
+
## MCP Resources
|
|
383
|
+
|
|
384
|
+
### amplitude-ai://event-schema
|
|
385
|
+
Full event property catalog (all [Agent] event types and their properties).
|
|
386
|
+
|
|
387
|
+
### amplitude-ai://integration-patterns
|
|
388
|
+
Canonical instrumentation patterns as structured JSON.
|
|
389
|
+
|
|
390
|
+
### amplitude-ai://instrument-guide
|
|
391
|
+
The complete 4-phase instrumentation workflow (Detect → Discover → Instrument → Verify)
|
|
392
|
+
with code examples for every step. Read this for guided instrumentation.
|
|
393
|
+
|
|
394
|
+
---
|
|
130
395
|
|
|
131
396
|
## CLI
|
|
132
397
|
|
|
133
|
-
- `amplitude-ai
|
|
398
|
+
- `amplitude-ai mcp` — Start the MCP server for AI coding agents
|
|
134
399
|
- `amplitude-ai doctor [--json] [--no-mock-check]` — Validate environment and event pipeline
|
|
135
400
|
- `amplitude-ai status [--json]` — Show SDK version, installed providers, and env config
|
|
136
|
-
- `amplitude-ai mcp` — Start MCP server over stdio
|
|
137
401
|
- `amplitude-ai --help` / `amplitude-ai --version`
|
|
138
402
|
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Events Emitted
|
|
406
|
+
|
|
407
|
+
| Event Type | Emitted By |
|
|
408
|
+
|---|---|
|
|
409
|
+
| [Agent] User Message | s.trackUserMessage() |
|
|
410
|
+
| [Agent] AI Response | Provider wrappers (auto) or s.trackAiMessage() |
|
|
411
|
+
| [Agent] Tool Call | tool() HOF (auto) or s.trackToolCall() |
|
|
412
|
+
| [Agent] Span | observe() HOF (auto) or s.trackSpan() |
|
|
413
|
+
| [Agent] Session End | session.run() completion (auto) or s.trackSessionEnd() |
|
|
414
|
+
| [Agent] Score | ai.score() or s.score() |
|
|
415
|
+
| [Agent] Embedding | provider wrappers (auto) |
|
|
416
|
+
| [Agent] Session Enrichment | s.trackSessionEnrichment() |
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
139
420
|
## Common Errors
|
|
140
421
|
|
|
141
422
|
- "No events captured" → Ensure session.run() wraps your LLM calls
|
|
142
423
|
- "patch() drops events silently" → patch() requires active SessionContext; use session.run()
|
|
143
|
-
- "flush() timeout" → Call ai.flush() before process exit in serverless
|
|
424
|
+
- "flush() timeout" → Call ai.flush() before process exit in serverless (Next.js, Lambda)
|
|
425
|
+
- "tool() not tracking" → tool() must be called inside session.run() to inherit context
|
|
426
|
+
- "child agent events missing parentAgentId" → Use s.runAs(child, fn), not direct child calls
|
|
427
|
+
- "observe() not emitting spans" → observe() must be called inside session.run() for session context
|
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<!-- GENERATED FILE: do not edit manually -->
|
|
1
|
+
<!-- GENERATED FILE: do not edit manually. Update scripts/generate-agent-docs.mjs instead. -->
|
|
2
2
|
# llms.txt
|
|
3
3
|
package=@amplitude/ai
|
|
4
|
-
version=0.
|
|
4
|
+
version=0.3.0
|
|
5
5
|
|
|
6
6
|
[mcp.tools]
|
|
7
7
|
get_event_schema
|
|
@@ -10,10 +10,14 @@ validate_setup
|
|
|
10
10
|
suggest_instrumentation
|
|
11
11
|
validate_file
|
|
12
12
|
search_docs
|
|
13
|
+
scan_project
|
|
14
|
+
generate_verify_test
|
|
15
|
+
instrument_file
|
|
13
16
|
|
|
14
17
|
[mcp.resources]
|
|
15
18
|
amplitude-ai://event-schema
|
|
16
19
|
amplitude-ai://integration-patterns
|
|
20
|
+
amplitude-ai://instrument-guide
|
|
17
21
|
|
|
18
22
|
[events]
|
|
19
23
|
[Agent] AI Response
|
package/mcp.schema.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generated": true,
|
|
3
3
|
"package": "@amplitude/ai",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"prompt": "instrument_app",
|
|
6
6
|
"tools": [
|
|
7
7
|
"get_event_schema",
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
"validate_setup",
|
|
10
10
|
"suggest_instrumentation",
|
|
11
11
|
"validate_file",
|
|
12
|
-
"search_docs"
|
|
12
|
+
"search_docs",
|
|
13
|
+
"scan_project",
|
|
14
|
+
"generate_verify_test",
|
|
15
|
+
"instrument_file"
|
|
13
16
|
],
|
|
14
17
|
"resources": [
|
|
15
18
|
"amplitude-ai://event-schema",
|
|
16
|
-
"amplitude-ai://integration-patterns"
|
|
19
|
+
"amplitude-ai://integration-patterns",
|
|
20
|
+
"amplitude-ai://instrument-guide"
|
|
17
21
|
]
|
|
18
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amplitude/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Amplitude AI SDK - LLM usage tracking for Amplitude Analytics",
|
|
6
6
|
"keywords": [
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
".": "./dist/index.js",
|
|
31
31
|
"./bound-agent": "./dist/bound-agent.js",
|
|
32
32
|
"./cli/doctor": "./dist/cli/doctor.js",
|
|
33
|
-
"./cli/init": "./dist/cli/init.js",
|
|
34
33
|
"./cli/providers": "./dist/cli/providers.js",
|
|
35
34
|
"./cli/status": "./dist/cli/status.js",
|
|
36
35
|
"./client": "./dist/client.js",
|
|
@@ -51,7 +50,10 @@
|
|
|
51
50
|
"./internals": "./dist/internals.js",
|
|
52
51
|
"./mcp": "./dist/mcp/index.js",
|
|
53
52
|
"./mcp/contract": "./dist/mcp/contract.js",
|
|
53
|
+
"./mcp/generate-verify-test": "./dist/mcp/generate-verify-test.js",
|
|
54
|
+
"./mcp/instrument-file": "./dist/mcp/instrument-file.js",
|
|
54
55
|
"./mcp/patterns": "./dist/mcp/patterns.js",
|
|
56
|
+
"./mcp/scan-project": "./dist/mcp/scan-project.js",
|
|
55
57
|
"./mcp/server": "./dist/mcp/server.js",
|
|
56
58
|
"./mcp/validate-file": "./dist/mcp/validate-file.js",
|
|
57
59
|
"./middleware": "./dist/middleware.js",
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
"./providers/mistral": "./dist/providers/mistral.js",
|
|
66
68
|
"./providers/openai": "./dist/providers/openai.js",
|
|
67
69
|
"./register": "./dist/register.js",
|
|
70
|
+
"./serverless": "./dist/serverless.js",
|
|
68
71
|
"./session": "./dist/session.js",
|
|
69
72
|
"./tenant": "./dist/tenant.js",
|
|
70
73
|
"./testing": "./dist/testing.js",
|
|
@@ -87,13 +90,13 @@
|
|
|
87
90
|
"amplitude-ai": "./bin/amplitude-ai.mjs",
|
|
88
91
|
"amplitude-ai-completions": "./bin/amplitude-ai-completions.mjs",
|
|
89
92
|
"amplitude-ai-doctor": "./bin/amplitude-ai-doctor.mjs",
|
|
90
|
-
"amplitude-ai-init": "./bin/amplitude-ai-init.mjs",
|
|
91
93
|
"amplitude-ai-instrument": "./bin/amplitude-ai-instrument.mjs",
|
|
92
94
|
"amplitude-ai-mcp": "./bin/amplitude-ai-mcp.mjs",
|
|
93
95
|
"amplitude-ai-register-catalog": "./bin/amplitude-ai-register-catalog.mjs",
|
|
94
96
|
"amplitude-ai-status": "./bin/amplitude-ai-status.mjs"
|
|
95
97
|
},
|
|
96
98
|
"files": [
|
|
99
|
+
"amplitude-ai.md",
|
|
97
100
|
"AGENTS.md",
|
|
98
101
|
"dist",
|
|
99
102
|
"bin",
|
|
@@ -101,7 +104,8 @@
|
|
|
101
104
|
"llms.txt",
|
|
102
105
|
"llms-full.txt",
|
|
103
106
|
"mcp.schema.json",
|
|
104
|
-
".cursor/skills/instrument-with-amplitude-ai/SKILL.md"
|
|
107
|
+
".cursor/skills/instrument-with-amplitude-ai/SKILL.md",
|
|
108
|
+
".claude/commands/instrument-with-amplitude-ai.md"
|
|
105
109
|
],
|
|
106
110
|
"scripts": {
|
|
107
111
|
"build": "tsdown",
|
|
@@ -117,7 +121,6 @@
|
|
|
117
121
|
"test:typescript": "tsc -p tsconfig.json --noEmit",
|
|
118
122
|
"prepublishOnly": "pnpm run build"
|
|
119
123
|
},
|
|
120
|
-
"dependencies": {},
|
|
121
124
|
"devDependencies": {
|
|
122
125
|
"@amplitude/analytics-node": "1.3.5",
|
|
123
126
|
"@biomejs/biome": "1.9.4",
|
|
@@ -174,6 +177,8 @@
|
|
|
174
177
|
},
|
|
175
178
|
"optionalDependencies": {
|
|
176
179
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
180
|
+
"acorn": "^8.16.0",
|
|
181
|
+
"acorn-typescript": "^1.4.13",
|
|
177
182
|
"zod": "3.25.76"
|
|
178
183
|
},
|
|
179
184
|
"pnpm": {
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { runInit } from '../dist/cli/init.js';
|
|
4
|
-
|
|
5
|
-
const args = process.argv.slice(2);
|
|
6
|
-
const dryRun = args.includes('--dry-run');
|
|
7
|
-
const force = args.includes('--force');
|
|
8
|
-
|
|
9
|
-
const result = runInit({
|
|
10
|
-
cwd: process.cwd(),
|
|
11
|
-
dryRun,
|
|
12
|
-
force,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
process.stdout.write(
|
|
16
|
-
JSON.stringify(
|
|
17
|
-
{
|
|
18
|
-
command: 'init',
|
|
19
|
-
dryRun,
|
|
20
|
-
force,
|
|
21
|
-
created: result.created,
|
|
22
|
-
skipped: result.skipped,
|
|
23
|
-
},
|
|
24
|
-
null,
|
|
25
|
-
2,
|
|
26
|
-
) + '\n',
|
|
27
|
-
);
|
package/dist/cli/init.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
//#region src/cli/init.d.ts
|
|
2
|
-
type InitOptions = {
|
|
3
|
-
cwd: string;
|
|
4
|
-
force: boolean;
|
|
5
|
-
dryRun: boolean;
|
|
6
|
-
};
|
|
7
|
-
type InitResult = {
|
|
8
|
-
created: string[];
|
|
9
|
-
skipped: string[];
|
|
10
|
-
};
|
|
11
|
-
declare const runInit: (options: InitOptions) => InitResult;
|
|
12
|
-
//#endregion
|
|
13
|
-
export { type InitOptions, type InitResult, runInit };
|
|
14
|
-
//# sourceMappingURL=init.d.ts.map
|
package/dist/cli/init.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","names":[],"sources":["../../src/cli/init.ts"],"sourcesContent":[],"mappings":";KAGK,WAAA;EAAA,GAAA,EAAA,MAAA;EAMA,KAAA,EAAA,OAAU;EAqBT,MAAA,EAAA,OAmBL;;KAxCI,UAAA;;;;cAqBC,mBAAoB,gBAAc"}
|