@paid-ai/paid-node 0.2.0 → 0.2.1
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 +180 -39
- package/dist/cjs/Client.js +2 -2
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Client.mjs +2 -2
- package/dist/esm/version.d.mts +1 -1
- package/dist/esm/version.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,30 +86,41 @@ To enable logs, set the `PAID_LOG_LEVEL` environment variable. Available levels
|
|
|
86
86
|
|
|
87
87
|
Example: `PAID_LOG_LEVEL=debug node your-app.js`
|
|
88
88
|
|
|
89
|
-
## Cost Tracking
|
|
89
|
+
## Cost Tracking via OTEL tracing
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
As of now, the following
|
|
91
|
+
You can track usage costs by using Paid wrappers around your AI provider's SDK.
|
|
92
|
+
As of now, the following SDKs' APIs are wrapped:
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
```
|
|
95
|
+
openai
|
|
96
|
+
anthropic
|
|
97
|
+
mistral
|
|
98
|
+
langchain (as a callback)
|
|
99
|
+
vercel (Vercel AI SDK)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Using the Paid wrappers
|
|
103
|
+
|
|
104
|
+
Example usage with OpenAI:
|
|
95
105
|
|
|
96
106
|
```typescript
|
|
97
|
-
import { PaidClient
|
|
107
|
+
import { PaidClient } from "@paid-ai/paid-node";
|
|
108
|
+
import { PaidOpenAI } from "@paid-ai/paid-node/openai";
|
|
98
109
|
import OpenAI from "openai";
|
|
99
110
|
|
|
100
111
|
async function main() {
|
|
101
112
|
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
102
113
|
|
|
103
|
-
//
|
|
104
|
-
await client.initializeTracing()
|
|
114
|
+
// Initialize cost tracking
|
|
115
|
+
await client.initializeTracing();
|
|
105
116
|
|
|
106
|
-
//
|
|
107
|
-
const openaiClient = new OpenAI({ apiKey: "<your_openai_api_key" });
|
|
108
|
-
const
|
|
117
|
+
// Wrap OpenAI in paid wrapper
|
|
118
|
+
const openaiClient = new OpenAI({ apiKey: "<your_openai_api_key>" });
|
|
119
|
+
const paidOpenAI = new PaidOpenAI(openaiClient);
|
|
109
120
|
|
|
110
|
-
//
|
|
121
|
+
// Trace the call
|
|
111
122
|
await client.trace("<your_external_customer_id>", async () => {
|
|
112
|
-
const response = await
|
|
123
|
+
const response = await paidOpenAI.images.generate({
|
|
113
124
|
prompt: "A beautiful sunset over the mountains",
|
|
114
125
|
n: 1,
|
|
115
126
|
size: "256x256"
|
|
@@ -121,54 +132,184 @@ async function main() {
|
|
|
121
132
|
}
|
|
122
133
|
```
|
|
123
134
|
|
|
135
|
+
## Signaling via OTEL tracing
|
|
136
|
+
|
|
137
|
+
A more reliable and user-friendly way to send signals is to send them via OTEL tracing.
|
|
138
|
+
This allows you to send signals with less arguments and boilerplate as the information is available in the tracing context `Paid.trace()`.
|
|
139
|
+
The interface is `Paid.signal()`, which takes in signal name, optional data, and a flag that attaches costs from the same trace.
|
|
140
|
+
`Paid.signal()` has to be called within a trace - meaning inside of a callback to `Paid.trace()`.
|
|
141
|
+
In contrast to `Paid.usage.recordBulk()`, `Paid.signal()` is using OpenTelemetry to provide reliable delivery.
|
|
142
|
+
|
|
143
|
+
Here's an example of how to use it:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { PaidClient } from "@paid-ai/paid-node";
|
|
147
|
+
|
|
148
|
+
async function main() {
|
|
149
|
+
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
150
|
+
|
|
151
|
+
// Initialize tracing
|
|
152
|
+
await client.initializeTracing();
|
|
153
|
+
|
|
154
|
+
// Trace the call
|
|
155
|
+
await client.trace("<your_external_customer_id>", async () => {
|
|
156
|
+
// ...do some work...
|
|
157
|
+
client.signal("<your_signal_name>", { /* optional data */ });
|
|
158
|
+
}, "<your_external_agent_id>"); // external_agent_id is required for signals
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Signal-costs - Attaching cost traces to a signal
|
|
163
|
+
|
|
164
|
+
If you want a signal to carry information about costs,
|
|
165
|
+
then the signal should be sent from the same tracing context
|
|
166
|
+
as the wrappers that recorded those costs.
|
|
167
|
+
|
|
168
|
+
This will look something like this:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { PaidClient } from "@paid-ai/paid-node";
|
|
172
|
+
import { PaidOpenAI } from "@paid-ai/paid-node/openai";
|
|
173
|
+
import OpenAI from "openai";
|
|
174
|
+
|
|
175
|
+
async function main() {
|
|
176
|
+
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
177
|
+
await client.initializeTracing();
|
|
178
|
+
|
|
179
|
+
const openaiClient = new OpenAI({ apiKey: "<your_openai_api_key>" });
|
|
180
|
+
const paidOpenAI = new PaidOpenAI(openaiClient);
|
|
181
|
+
|
|
182
|
+
await client.trace("<your_external_customer_id>", async () => {
|
|
183
|
+
// ... your workflow logic
|
|
184
|
+
// ... your AI calls made through Paid wrappers
|
|
185
|
+
const response = await paidOpenAI.chat.completions.create({
|
|
186
|
+
model: "gpt-4",
|
|
187
|
+
messages: [{ role: "user", content: "Hello!" }]
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Send signal with cost tracing enabled
|
|
191
|
+
client.signal(
|
|
192
|
+
"<your_signal_name>",
|
|
193
|
+
true, // enableCostTracing - set this flag to associate it with costs
|
|
194
|
+
{ /* optional data */ }
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// ... your workflow logic
|
|
198
|
+
// ... your AI calls made through Paid wrappers (can be sent after the signal too)
|
|
199
|
+
}, "<your_external_agent_id>");
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Then, all of the costs traced in `client.trace()` context are related to that signal.
|
|
204
|
+
|
|
124
205
|
## Manual Cost Tracking
|
|
125
206
|
|
|
126
|
-
|
|
127
|
-
|
|
207
|
+
If you would prefer to not use Paid to track your costs automatically but you want to send us the costs yourself,
|
|
208
|
+
then you can use manual cost tracking mechanism. Just attach the cost information in the following format to a signal payload:
|
|
128
209
|
|
|
129
210
|
```typescript
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
211
|
+
import { PaidClient, Paid } from "@paid-ai/paid-node";
|
|
212
|
+
|
|
213
|
+
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
214
|
+
|
|
215
|
+
const signal: Paid.Signal = {
|
|
216
|
+
event_name: "<your_signal_name>",
|
|
217
|
+
agent_id: "<your_agent_id>",
|
|
218
|
+
customer_id: "<your_external_customer_id>",
|
|
219
|
+
data: {
|
|
220
|
+
costData: {
|
|
221
|
+
vendor: "<any_vendor_name>", // can be anything, traces are grouped by vendors in the UI
|
|
222
|
+
cost: {
|
|
223
|
+
amount: 0.002,
|
|
224
|
+
currency: "USD"
|
|
225
|
+
},
|
|
226
|
+
"gen_ai.response.model": "<ai_model_name>",
|
|
136
227
|
}
|
|
137
228
|
}
|
|
138
229
|
};
|
|
139
|
-
await client.usage.recordBulk({
|
|
140
|
-
signals: [{
|
|
141
|
-
agent_id: "<your_agent_id>",
|
|
142
|
-
event_name: "<your_signal_name>",
|
|
143
|
-
customer_id: "<your_customer_id>",
|
|
144
|
-
data: additionalData,
|
|
145
|
-
}]
|
|
146
|
-
})
|
|
147
230
|
|
|
231
|
+
await client.usage.recordBulk({ signals: [signal] });
|
|
148
232
|
await client.usage.flush(); // need to flush to send usage immediately
|
|
149
233
|
```
|
|
150
234
|
|
|
151
|
-
|
|
235
|
+
Alternatively the same `costData` payload can be passed to OTLP signaling mechanism:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { PaidClient } from "@paid-ai/paid-node";
|
|
239
|
+
|
|
240
|
+
async function main() {
|
|
241
|
+
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
242
|
+
await client.initializeTracing();
|
|
243
|
+
|
|
244
|
+
await client.trace("<your_external_customer_id>", async () => {
|
|
245
|
+
// ...do some work...
|
|
246
|
+
client.signal("<your_signal_name>", {
|
|
247
|
+
costData: {
|
|
248
|
+
vendor: "<any_vendor_name>", // can be anything, traces are grouped by vendors in the UI
|
|
249
|
+
cost: {
|
|
250
|
+
amount: 0.002,
|
|
251
|
+
currency: "USD"
|
|
252
|
+
},
|
|
253
|
+
"gen_ai.response.model": "<ai_model_name>",
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}, "<your_external_agent_id>");
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Manual Usage Tracking
|
|
152
261
|
|
|
153
|
-
|
|
154
|
-
context, just like with cost tracking.
|
|
262
|
+
If you would prefer to send us raw usage manually (without wrappers) and have us compute the cost, you can attach usage data in the following format:
|
|
155
263
|
|
|
156
|
-
|
|
264
|
+
```typescript
|
|
265
|
+
import { PaidClient, Paid } from "@paid-ai/paid-node";
|
|
266
|
+
|
|
267
|
+
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
268
|
+
|
|
269
|
+
const signal: Paid.Signal = {
|
|
270
|
+
event_name: "<your_signal_name>",
|
|
271
|
+
agent_id: "<your_agent_id>",
|
|
272
|
+
customer_id: "<your_external_customer_id>",
|
|
273
|
+
data: {
|
|
274
|
+
costData: {
|
|
275
|
+
vendor: "<any_vendor_name>", // can be anything, traces are grouped by vendors in the UI
|
|
276
|
+
attributes: {
|
|
277
|
+
"gen_ai.response.model": "gpt-4-turbo",
|
|
278
|
+
"gen_ai.usage.input_tokens": 100,
|
|
279
|
+
"gen_ai.usage.output_tokens": 300,
|
|
280
|
+
"gen_ai.usage.cached_input_tokens": 600,
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
await client.usage.recordBulk({ signals: [signal] });
|
|
287
|
+
await client.usage.flush();
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Same but via OTEL signaling:
|
|
157
291
|
|
|
158
292
|
```typescript
|
|
159
293
|
import { PaidClient } from "@paid-ai/paid-node";
|
|
160
294
|
|
|
161
295
|
async function main() {
|
|
162
296
|
const client = new PaidClient({ token: "<your_paid_api_key>" });
|
|
297
|
+
await client.initializeTracing();
|
|
163
298
|
|
|
164
|
-
// initialize cost tracking
|
|
165
|
-
await client.initializeTracing()
|
|
166
|
-
|
|
167
|
-
// trace the call
|
|
168
299
|
await client.trace("<your_external_customer_id>", async () => {
|
|
169
|
-
// ...
|
|
170
|
-
client.signal("
|
|
171
|
-
|
|
300
|
+
// ...do some work...
|
|
301
|
+
client.signal("<your_signal_name>", {
|
|
302
|
+
costData: {
|
|
303
|
+
vendor: "<any_vendor_name>", // can be anything, traces are grouped by vendors in the UI
|
|
304
|
+
attributes: {
|
|
305
|
+
"gen_ai.response.model": "gpt-4-turbo",
|
|
306
|
+
"gen_ai.usage.input_tokens": 100,
|
|
307
|
+
"gen_ai.usage.output_tokens": 300,
|
|
308
|
+
"gen_ai.usage.cached_input_tokens": 600,
|
|
309
|
+
},
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}, "<your_external_agent_id>");
|
|
172
313
|
}
|
|
173
314
|
```
|
|
174
315
|
|
package/dist/cjs/Client.js
CHANGED
|
@@ -60,8 +60,8 @@ class PaidClient {
|
|
|
60
60
|
this._options = Object.assign(Object.assign({}, _options), { headers: (0, headers_js_1.mergeHeaders)({
|
|
61
61
|
"X-Fern-Language": "JavaScript",
|
|
62
62
|
"X-Fern-SDK-Name": "@paid-ai/paid-node",
|
|
63
|
-
"X-Fern-SDK-Version": "0.2.
|
|
64
|
-
"User-Agent": "@paid-ai/paid-node/0.2.
|
|
63
|
+
"X-Fern-SDK-Version": "0.2.1",
|
|
64
|
+
"User-Agent": "@paid-ai/paid-node/0.2.1",
|
|
65
65
|
"X-Fern-Runtime": core.RUNTIME.type,
|
|
66
66
|
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
|
67
67
|
}, _options === null || _options === void 0 ? void 0 : _options.headers) });
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.2.
|
|
1
|
+
export declare const SDK_VERSION = "0.2.1";
|
package/dist/cjs/version.js
CHANGED
package/dist/esm/Client.mjs
CHANGED
|
@@ -24,8 +24,8 @@ export class PaidClient {
|
|
|
24
24
|
this._options = Object.assign(Object.assign({}, _options), { headers: mergeHeaders({
|
|
25
25
|
"X-Fern-Language": "JavaScript",
|
|
26
26
|
"X-Fern-SDK-Name": "@paid-ai/paid-node",
|
|
27
|
-
"X-Fern-SDK-Version": "0.2.
|
|
28
|
-
"User-Agent": "@paid-ai/paid-node/0.2.
|
|
27
|
+
"X-Fern-SDK-Version": "0.2.1",
|
|
28
|
+
"User-Agent": "@paid-ai/paid-node/0.2.1",
|
|
29
29
|
"X-Fern-Runtime": core.RUNTIME.type,
|
|
30
30
|
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
|
31
31
|
}, _options === null || _options === void 0 ? void 0 : _options.headers) });
|
package/dist/esm/version.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.2.
|
|
1
|
+
export declare const SDK_VERSION = "0.2.1";
|
package/dist/esm/version.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.2.
|
|
1
|
+
export const SDK_VERSION = "0.2.1";
|