@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 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
- It's possible to track usage costs by using Paid wrappers around you AI provider API.
92
- As of now, the following AI providers are supported: `OpenAI`, `Anthropic`, `Mistral` (OCR), and `Langchain`.
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
- Example usage:
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, PaidOpenAI } from "@paid-ai/paid-node";
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
- // initialize cost tracking
104
- await client.initializeTracing()
114
+ // Initialize cost tracking
115
+ await client.initializeTracing();
105
116
 
106
- // wrap openai in paid wrapper
107
- const openaiClient = new OpenAI({ apiKey: "<your_openai_api_key" });
108
- const paidOpenAiWrapper = new PaidOpenAI(openaiClient);
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
- // trace the call
121
+ // Trace the call
111
122
  await client.trace("<your_external_customer_id>", async () => {
112
- const response = await paidOpenAiWrapper.images.generate({
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
- When using `client.usage.recordUsage()` API, it's possible to create cost traces manually
127
- just by passing in the cost data.
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
- const additionalData = {
131
- costData: {
132
- vendor: "<vendor_name>", // can be anything
133
- cost : {
134
- amount: 0.0001,
135
- currency: "USD"
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
- ## Send signals over OTLP
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
- Besides sending signals over REST, it's also possible to send signals as part or tracing
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
- Example usage:
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
- // ... your app logic, cost tracking LLM wrapper calls
170
- client.signal("signal_name", { "data": { // ... additional data, e.g. costs } });
171
- }, "<optional_external_agent_id>");
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
 
@@ -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.0",
64
- "User-Agent": "@paid-ai/paid-node/0.2.0",
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) });
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.2.0";
1
+ export declare const SDK_VERSION = "0.2.1";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = "0.2.0";
4
+ exports.SDK_VERSION = "0.2.1";
@@ -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.0",
28
- "User-Agent": "@paid-ai/paid-node/0.2.0",
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) });
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.2.0";
1
+ export declare const SDK_VERSION = "0.2.1";
@@ -1 +1 @@
1
- export const SDK_VERSION = "0.2.0";
1
+ export const SDK_VERSION = "0.2.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paid-ai/paid-node",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "repository": "https://github.com/paid-ai/paid-node",
6
6
  "type": "commonjs",