vibezcheck 0.5.5 β 0.5.7
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 -50
- package/dist/ai-sdk/index.d.mts +41 -1
- package/dist/ai-sdk/index.d.ts +41 -1
- package/dist/ai-sdk/index.js +157 -2
- package/dist/ai-sdk/index.js.map +1 -1
- package/dist/ai-sdk/index.mjs +156 -2
- package/dist/ai-sdk/index.mjs.map +1 -1
- package/dist/ai-sdk/middleware.js +17 -1
- package/dist/ai-sdk/middleware.js.map +1 -1
- package/dist/ai-sdk/middleware.mjs +17 -1
- package/dist/ai-sdk/middleware.mjs.map +1 -1
- package/dist/billing/index.js +17 -1
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +17 -1
- package/dist/billing/index.mjs.map +1 -1
- package/dist/compat/stripe-meter.js +17 -1
- package/dist/compat/stripe-meter.js.map +1 -1
- package/dist/compat/stripe-meter.mjs +17 -1
- package/dist/compat/stripe-meter.mjs.map +1 -1
- package/dist/compat/stripe-provider.js +155 -2
- package/dist/compat/stripe-provider.js.map +1 -1
- package/dist/compat/stripe-provider.mjs +155 -2
- package/dist/compat/stripe-provider.mjs.map +1 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +158 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +157 -2
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +32 -9
- package/dist/react/index.d.ts +32 -9
- package/dist/react/index.js +851 -242
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +852 -243
- package/dist/react/index.mjs.map +1 -1
- package/examples/nextjs-app-router-openai/README.md +8 -6
- package/examples/nextjs-app-router-openai/app/api/chat/route.ts +14 -6
- package/examples/nextjs-app-router-openai/app/page.tsx +68 -22
- package/examples/nextjs-app-router-openai/package.json +1 -1
- package/package.json +189 -188
package/README.md
CHANGED
|
@@ -1,37 +1,49 @@
|
|
|
1
1
|
# β¦ VibezCheck
|
|
2
2
|
|
|
3
3
|
> **The 1-Line Token Meter & Real-Time Billing Engine for AI.**
|
|
4
|
-
> Track tokens, compute real-time
|
|
4
|
+
> Track tokens, compute real-time micro-costs, set profit margins, and bill customers with **0ms added latency** across Vercel AI SDK (v4, v5, v6, v7) and native LLM SDKs.
|
|
5
5
|
|
|
6
|
-
[](https://npmjs.org/package/vibezcheck)
|
|
6
|
+
[](https://npmjs.org/package/vibezcheck)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
[](https://www.typescriptlang.org/)
|
|
9
|
-
[]()
|
|
10
|
+
[]()
|
|
10
11
|
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
### The 2-Line Promise
|
|
14
15
|
|
|
15
16
|
```typescript
|
|
16
|
-
// 1. Server: Wrap your model with
|
|
17
|
-
const model = vibezcheck('openai/gpt-4o', { customer: 'cus_alex' });
|
|
17
|
+
// 1. Server: Wrap your model with billing & inject telemetry into stream
|
|
18
|
+
const model = vibezcheck('openai/gpt-4o-mini', { customer: 'cus_alex', pricing: { margin: 1.3 } });
|
|
19
|
+
return vibezcheck.toResponse(result);
|
|
18
20
|
|
|
19
|
-
// 2. Client: Drop in
|
|
21
|
+
// 2. Client: Drop in zero-prop financial HUD & micro-receipts
|
|
22
|
+
<VibezReceipt message={message} />
|
|
20
23
|
<VibezCheck messages={messages} />
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
25
|
-
##
|
|
28
|
+
## β‘ What's New in v0.5.6
|
|
26
29
|
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
30
|
+
* **πͺ Zero-Prop Client UIs**: `<VibezReceipt message={message} />` and `<VibezCheck messages={messages} />` auto-derive model slugs, exact tokens, dollar micro-costs, profit margins (`+30% Margin`), and latency straight from the telemetry stream. No manual `model`, `margin`, or `cost` props required.
|
|
31
|
+
* **π‘ Universal Stream Injector (`vibezcheck.toResponse`)**: Intercepts AI SDK stream completion chunks to attach verified telemetry (`data-vibezcheck` & `message-metadata` SSE for v5/v6/v7, or `2:[{...}]` annotations for v4) with **0ms TTFB overhead**.
|
|
32
|
+
* **π‘οΈ Zero Context Window Pollution**: Telemetry travels out-of-band. When AI SDK's `convertToModelMessages(messages)` prepares future conversation turns, metadata parts are automatically omittedβkeeping your LLM context pure and preventing runaway token loops.
|
|
33
|
+
* **π¨ Scoped Zero-Config Styling**: Built-in inline fallback styles guarantee that receipts and HUD components look polished out-of-the-box, even if your Tailwind configuration ignores `node_modules`.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## π Key Features
|
|
38
|
+
|
|
39
|
+
* **β‘ 0ms Added Latency**: Direct transparent proxy stream; no external server redirects, no pre-buffering, and zero impact on first-token response time.
|
|
40
|
+
* **π‘οΈ Built-in $0.50 Circuit Breaker**: Auto-trips if an agent or query exceeds the safety fuse box, preventing infinite loops and surprise $1,000 bills.
|
|
41
|
+
* **π In-Flight Abort Trapper**: Accurately meters and debits tokens even if the client closes their laptop lid or navigates away mid-stream.
|
|
42
|
+
* **πͺ BigInt Nano-USD Precision**: Sub-cent financial math ($1 = $10^9$ Nano-USD) eliminating IEEE 754 floating-point drift.
|
|
43
|
+
* **π° 1-Line Profit Margins**: Turn wholesale API costs into retail revenue with `pricing: { margin: 1.3 }` (+30% markup).
|
|
32
44
|
* **π Zero-DB Dev Mode**: Run locally on `localhost:3000` with NO database and NO Stripe keys required.
|
|
33
|
-
* **π³ Pluggable SaaS Billing**:
|
|
34
|
-
* **π€ Multi-Tool Agent Sessions**: Cumulative session budgets across
|
|
45
|
+
* **π³ Pluggable SaaS Billing**: Credit wallets, top-ups, checkout sessions, customer portal, and user API keys (`vz_live_...`).
|
|
46
|
+
* **π€ Multi-Tool Agent Sessions**: Cumulative session budgets across multiple LLM steps, Python sandboxes, and web scrapers.
|
|
35
47
|
* **π 100% Drop-In Polyfills**: Direct replacement for `@stripe/ai-sdk/meter`, `@stripe/ai-sdk/provider`, and `@stripe/token-meter`.
|
|
36
48
|
|
|
37
49
|
---
|
|
@@ -44,93 +56,211 @@ npm install vibezcheck ai @ai-sdk/openai @ai-sdk/react stripe
|
|
|
44
56
|
|
|
45
57
|
---
|
|
46
58
|
|
|
47
|
-
## β‘ Quickstart:
|
|
59
|
+
## β‘ 1-Minute Quickstart: Next.js App Router
|
|
60
|
+
|
|
61
|
+
### 1. Server Route (`app/api/chat/route.ts`)
|
|
48
62
|
|
|
49
|
-
|
|
63
|
+
Wrap any model with `vibezcheck()`, and return the response using `vibezcheck.toResponse()`:
|
|
50
64
|
|
|
51
65
|
```typescript
|
|
52
66
|
import { streamText } from 'ai';
|
|
53
67
|
import { openai } from '@ai-sdk/openai';
|
|
54
68
|
import { vibezcheck } from 'vibezcheck';
|
|
55
69
|
|
|
70
|
+
export const maxDuration = 30;
|
|
71
|
+
|
|
56
72
|
export async function POST(req: Request) {
|
|
57
73
|
const { messages } = await req.json();
|
|
58
74
|
|
|
59
75
|
const result = streamText({
|
|
60
|
-
// Wrap any
|
|
76
|
+
// Wrap any provider (OpenAI, Anthropic, Google, DeepSeek, etc.)
|
|
61
77
|
model: vibezcheck(openai('gpt-4o-mini'), {
|
|
62
|
-
customer: '
|
|
63
|
-
pricing: { margin: 1.3 },
|
|
64
|
-
maxCostPerCallUSD: 0.50,
|
|
78
|
+
customer: 'cus_alex', // Stripe customer ID or user email
|
|
79
|
+
pricing: { margin: 1.3 }, // +30% profit margin
|
|
80
|
+
maxCostPerCallUSD: 0.50, // Safety fuse box ($0.50 cap)
|
|
65
81
|
}),
|
|
66
82
|
messages,
|
|
67
83
|
});
|
|
68
84
|
|
|
69
|
-
|
|
85
|
+
// β¦ Universal telemetry stream injector (works with v4, v5, v6, and v7)
|
|
86
|
+
return vibezcheck.toResponse(result);
|
|
70
87
|
}
|
|
71
88
|
```
|
|
72
89
|
|
|
73
|
-
### 2. Frontend
|
|
90
|
+
### 2. Frontend Client (`app/page.tsx`)
|
|
91
|
+
|
|
92
|
+
Render message bubbles with `<VibezReceipt />` and float the aggregated `<VibezCheck />` HUD:
|
|
74
93
|
|
|
75
94
|
```tsx
|
|
76
95
|
'use client';
|
|
77
96
|
|
|
78
97
|
import { useChat } from '@ai-sdk/react';
|
|
79
98
|
import { useState } from 'react';
|
|
80
|
-
import { VibezCheck } from 'vibezcheck/ui';
|
|
99
|
+
import { VibezReceipt, VibezCheck } from 'vibezcheck/ui';
|
|
81
100
|
|
|
82
|
-
export default function
|
|
101
|
+
export default function ChatPage() {
|
|
83
102
|
const [input, setInput] = useState('');
|
|
84
103
|
const { messages, sendMessage } = useChat();
|
|
85
104
|
|
|
86
105
|
return (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
{message.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
<main className="flex flex-col w-full max-w-lg py-20 mx-auto px-4 min-h-screen">
|
|
107
|
+
<div className="flex-1 space-y-4 mb-24">
|
|
108
|
+
{messages.map((message) => (
|
|
109
|
+
<div key={message.id} className="p-3 rounded-xl border border-zinc-200 dark:border-zinc-800">
|
|
110
|
+
<div className="text-xs font-semibold uppercase tracking-wider text-zinc-400 mb-1">
|
|
111
|
+
{message.role === 'user' ? 'You' : 'Assistant'}
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{/* Display message content */}
|
|
115
|
+
<div className="text-sm whitespace-pre-wrap">
|
|
116
|
+
{message.parts
|
|
117
|
+
? message.parts.map((part, i) => (part.type === 'text' ? part.text : null))
|
|
118
|
+
: message.content}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{/* β¦ Zero-Prop Micro-Receipt: auto-derives tokens, micro-cost, model, & latency */}
|
|
122
|
+
{message.role === 'assistant' && (
|
|
123
|
+
<div className="mt-2 flex justify-end">
|
|
124
|
+
<VibezReceipt message={message} />
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
))}
|
|
129
|
+
</div>
|
|
99
130
|
|
|
100
131
|
<form
|
|
101
|
-
onSubmit={e => {
|
|
132
|
+
onSubmit={(e) => {
|
|
102
133
|
e.preventDefault();
|
|
134
|
+
if (!input.trim()) return;
|
|
103
135
|
sendMessage({ text: input });
|
|
104
136
|
setInput('');
|
|
105
137
|
}}
|
|
138
|
+
className="fixed bottom-0 left-0 right-0 p-4 bg-background/80 backdrop-blur-md"
|
|
106
139
|
>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
140
|
+
<div className="max-w-lg mx-auto">
|
|
141
|
+
<input
|
|
142
|
+
className="w-full p-3 rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 shadow-sm"
|
|
143
|
+
value={input}
|
|
144
|
+
placeholder="Ask something..."
|
|
145
|
+
onChange={(e) => setInput(e.target.value)}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
113
148
|
</form>
|
|
114
149
|
|
|
115
|
-
{/* β¦
|
|
150
|
+
{/* β¦ Zero-Prop Financial HUD: aggregated conversation tokens, cost, & active model */}
|
|
116
151
|
<VibezCheck messages={messages} />
|
|
117
|
-
</
|
|
152
|
+
</main>
|
|
118
153
|
);
|
|
119
154
|
}
|
|
120
155
|
```
|
|
121
156
|
|
|
122
157
|
---
|
|
123
158
|
|
|
124
|
-
##
|
|
159
|
+
## π§© Zero-Prop Client Components
|
|
160
|
+
|
|
161
|
+
Import from either `vibezcheck/ui` or `vibezcheck/react`:
|
|
162
|
+
|
|
163
|
+
### 1. `<VibezReceipt />` (Per-Message Micro-Badge)
|
|
164
|
+
|
|
165
|
+
Displays verified tokens, cost in USD, model slug, and latency under individual assistant messages.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<VibezReceipt message={message} />
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
* **Zero Required Props**: Reads telemetry from `message.parts` (`data-vibezcheck`), `message.annotations`, and `message.metadata`.
|
|
172
|
+
* **Variants**: `variant="pill"` (default), `variant="minimal"`, or `variant="card"`.
|
|
173
|
+
* **Scoped Styling**: Ships with inline fallback styling so it never breaks even without Tailwind CSS.
|
|
174
|
+
|
|
175
|
+
### 2. `<VibezCheck />` (Global Conversation HUD)
|
|
176
|
+
|
|
177
|
+
Floating pill and expandable popover showing real-time conversation metrics.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<VibezCheck messages={messages} />
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
* **Aggregated Metrics**: Automatically sums input, output, and reasoning tokens across all messages.
|
|
184
|
+
* **Auto-Derived Model & Margin**: Displays the active model badge (e.g. `gpt-4o-mini`) and configured markup (e.g. `+30% Margin`).
|
|
185
|
+
* **Optional Top-Up**: Pass `onTopUp={() => openBillingModal()}` to display an integrated credit top-up button.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## π Multi-Version Vercel AI SDK Compatibility
|
|
190
|
+
|
|
191
|
+
`vibezcheck` provides seamless compatibility across every major generation of the Vercel AI SDK:
|
|
192
|
+
|
|
193
|
+
### AI SDK v5, v6 & v7 (UI Message Stream)
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { createUIMessageStreamResponse, toUIMessageStream, streamText } from 'ai';
|
|
197
|
+
import { openai } from '@ai-sdk/openai';
|
|
198
|
+
import { vibezcheck } from 'vibezcheck';
|
|
199
|
+
|
|
200
|
+
export async function POST(req: Request) {
|
|
201
|
+
const { messages } = await req.json();
|
|
202
|
+
|
|
203
|
+
const result = streamText({
|
|
204
|
+
model: vibezcheck(openai('gpt-4o-mini'), { customer: 'cus_123' }),
|
|
205
|
+
messages,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Recommended: pass result directly
|
|
209
|
+
return vibezcheck.toResponse(result);
|
|
210
|
+
|
|
211
|
+
// Or wrap custom UI message stream responses:
|
|
212
|
+
// return vibezcheck.toResponse(
|
|
213
|
+
// createUIMessageStreamResponse({
|
|
214
|
+
// stream: toUIMessageStream({ stream: result.stream }),
|
|
215
|
+
// })
|
|
216
|
+
// );
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### AI SDK v4 (Data Stream Protocol)
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { streamText } from 'ai';
|
|
224
|
+
import { openai } from '@ai-sdk/openai';
|
|
225
|
+
import { vibezcheck } from 'vibezcheck';
|
|
226
|
+
|
|
227
|
+
export async function POST(req: Request) {
|
|
228
|
+
const { messages } = await req.json();
|
|
229
|
+
|
|
230
|
+
const result = streamText({
|
|
231
|
+
model: vibezcheck(openai('gpt-4o'), { customer: 'cus_123' }),
|
|
232
|
+
messages,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Automatically emits 2:[{"type":"vibezcheck", ...}] data stream annotations
|
|
236
|
+
return vibezcheck.toResponse(result);
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## π‘οΈ Context Window Hygiene: How It Works
|
|
243
|
+
|
|
244
|
+
A frequent hazard in LLM streaming is "telemetry leakage"βwhere usage data sent to the client gets accidentally included in subsequent conversation turns, causing runaway context growth and unnecessary token costs.
|
|
245
|
+
|
|
246
|
+
VibezCheck prevents this by design:
|
|
247
|
+
1. **Server Injection**: `vibezcheck.toResponse` injects telemetry out-of-band right before stream completion (`data-vibezcheck` and `message-metadata` SSE events).
|
|
248
|
+
2. **Client Ingestion**: `<VibezReceipt />` and `<VibezCheck />` extract telemetry from message parts without mutating textual content.
|
|
249
|
+
3. **Next Request**: When `convertToModelMessages(messages)` runs on subsequent requests, the AI SDK automatically discards transient metadata and data parts. **Zero telemetry tokens are sent to the LLM.**
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## π³ Commercial SaaS Billing
|
|
254
|
+
|
|
255
|
+
### 1. Stripe Top-Up Sessions (`BillingHelper`)
|
|
125
256
|
|
|
126
|
-
|
|
257
|
+
Allow users to purchase pre-paid AI credits with Stripe Checkout:
|
|
127
258
|
|
|
128
259
|
```typescript
|
|
129
260
|
import { BillingHelper } from 'vibezcheck';
|
|
130
261
|
|
|
131
262
|
const billing = new BillingHelper({ apiKey: process.env.STRIPE_SECRET_KEY });
|
|
132
263
|
|
|
133
|
-
// Customer purchases $20 in AI usage credits
|
|
134
264
|
export async function POST(req: Request) {
|
|
135
265
|
const { customerId } = await req.json();
|
|
136
266
|
|
|
@@ -158,7 +288,7 @@ const auth = new ApiKeyAuth(stripe);
|
|
|
158
288
|
// 1. Generate key for customer
|
|
159
289
|
const { apiKey } = await auth.createKey({
|
|
160
290
|
customerId: 'cus_123',
|
|
161
|
-
name: 'Production
|
|
291
|
+
name: 'Production Key',
|
|
162
292
|
});
|
|
163
293
|
// => "vz_live_9f83b2..."
|
|
164
294
|
|
|
@@ -236,7 +366,7 @@ npx vibezcheck audit --ci
|
|
|
236
366
|
|
|
237
367
|
## π Examples
|
|
238
368
|
|
|
239
|
-
* **[Next.js App Router + OpenAI](examples/nextjs-app-router-openai)**:
|
|
369
|
+
* **[Next.js App Router + OpenAI](examples/nextjs-app-router-openai)**: Modern AI SDK App Router implementation with 1-line token metering and `<VibezCheck />`.
|
|
240
370
|
* **[Next.js 15 AI SaaS Starter](examples/nextjs-saas-starter)**: Complete SaaS template with Stripe Checkout, Top-Up Modal, and `<VibezReceipt />`.
|
|
241
371
|
* **[Local Cost Meter](examples/01-local-cost-meter.ts)**: Offline micro-transaction calculations with BigInt Nano-USD precision.
|
|
242
372
|
* **[Native OpenAI Streaming](examples/03-native-openai-stream.ts)**: Zero-latency stream wrapper for the official `openai` Node SDK.
|
package/dist/ai-sdk/index.d.mts
CHANGED
|
@@ -4,6 +4,45 @@ import { C as CustomerParam } from '../types-Btf2OhIr.mjs';
|
|
|
4
4
|
import { V as VibezMeter } from '../client-BzwEHQlC.mjs';
|
|
5
5
|
import 'stripe';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* β¦ VibezCheck Universal Telemetry Response Stream Injector
|
|
9
|
+
*
|
|
10
|
+
* Transmits real-time verified token counts, exact dollar costs, profit margins,
|
|
11
|
+
* and model telemetry from the server into AI SDK useChat() across all versions:
|
|
12
|
+
* - AI SDK v5/v6/v7 (UI Message Stream): Injects `data-vibezcheck` and `message-metadata` SSE events
|
|
13
|
+
* - AI SDK v4 (Data Stream Protocol): Injects `2:[{"type":"vibezcheck", ...}]` message annotation lines
|
|
14
|
+
* - Custom / Text Streams: Injects clean telemetry without breaking stream parsers
|
|
15
|
+
*/
|
|
16
|
+
interface ToResponseOptions {
|
|
17
|
+
/** Custom ResponseInit (status, statusText, headers) */
|
|
18
|
+
init?: ResponseInit;
|
|
19
|
+
/** Additional HTTP headers */
|
|
20
|
+
headers?: HeadersInit;
|
|
21
|
+
/** Explicit telemetry payload override */
|
|
22
|
+
telemetry?: any;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Transforms an AI SDK stream result (or standard Response) into a client-ready HTTP Response
|
|
26
|
+
* that transmits both AI completion chunks and verified VibezCheck telemetry.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* import { streamText } from 'ai';
|
|
31
|
+
* import { openai } from '@ai-sdk/openai';
|
|
32
|
+
* import { vibezcheck } from 'vibezcheck';
|
|
33
|
+
*
|
|
34
|
+
* export async function POST(req: Request) {
|
|
35
|
+
* const { messages } = await req.json();
|
|
36
|
+
* const result = streamText({
|
|
37
|
+
* model: vibezcheck(openai('gpt-4o'), { customer: 'cus_123' }),
|
|
38
|
+
* messages,
|
|
39
|
+
* });
|
|
40
|
+
* return vibezcheck.toResponse(result);
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function toResponse(result: any, options?: ToResponseOptions): Response;
|
|
45
|
+
|
|
7
46
|
interface VibezCheckModelOptions extends WithBillingOptions {
|
|
8
47
|
/** OpenAI / Anthropic / AI Gateway API Key override */
|
|
9
48
|
apiKey?: string;
|
|
@@ -69,6 +108,7 @@ declare function createVibezModel(modelOrId: any, options?: VibezCheckModelOptio
|
|
|
69
108
|
declare function createVibezSession(sessionOptions?: VibezSessionOptions): VibezSession;
|
|
70
109
|
declare const vibezcheck: typeof createVibezModel & {
|
|
71
110
|
session: typeof createVibezSession;
|
|
111
|
+
toResponse: typeof toResponse;
|
|
72
112
|
};
|
|
73
113
|
|
|
74
|
-
export { type VibezCheckModelOptions, type VibezSession, type VibezSessionOptions, WithBillingOptions, createVibezModel, createVibezSession, vibezcheck };
|
|
114
|
+
export { type ToResponseOptions, type VibezCheckModelOptions, type VibezSession, type VibezSessionOptions, WithBillingOptions, createVibezModel, createVibezSession, toResponse, vibezcheck };
|
package/dist/ai-sdk/index.d.ts
CHANGED
|
@@ -4,6 +4,45 @@ import { C as CustomerParam } from '../types-Btf2OhIr.js';
|
|
|
4
4
|
import { V as VibezMeter } from '../client-CbO6vLUe.js';
|
|
5
5
|
import 'stripe';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* β¦ VibezCheck Universal Telemetry Response Stream Injector
|
|
9
|
+
*
|
|
10
|
+
* Transmits real-time verified token counts, exact dollar costs, profit margins,
|
|
11
|
+
* and model telemetry from the server into AI SDK useChat() across all versions:
|
|
12
|
+
* - AI SDK v5/v6/v7 (UI Message Stream): Injects `data-vibezcheck` and `message-metadata` SSE events
|
|
13
|
+
* - AI SDK v4 (Data Stream Protocol): Injects `2:[{"type":"vibezcheck", ...}]` message annotation lines
|
|
14
|
+
* - Custom / Text Streams: Injects clean telemetry without breaking stream parsers
|
|
15
|
+
*/
|
|
16
|
+
interface ToResponseOptions {
|
|
17
|
+
/** Custom ResponseInit (status, statusText, headers) */
|
|
18
|
+
init?: ResponseInit;
|
|
19
|
+
/** Additional HTTP headers */
|
|
20
|
+
headers?: HeadersInit;
|
|
21
|
+
/** Explicit telemetry payload override */
|
|
22
|
+
telemetry?: any;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Transforms an AI SDK stream result (or standard Response) into a client-ready HTTP Response
|
|
26
|
+
* that transmits both AI completion chunks and verified VibezCheck telemetry.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* import { streamText } from 'ai';
|
|
31
|
+
* import { openai } from '@ai-sdk/openai';
|
|
32
|
+
* import { vibezcheck } from 'vibezcheck';
|
|
33
|
+
*
|
|
34
|
+
* export async function POST(req: Request) {
|
|
35
|
+
* const { messages } = await req.json();
|
|
36
|
+
* const result = streamText({
|
|
37
|
+
* model: vibezcheck(openai('gpt-4o'), { customer: 'cus_123' }),
|
|
38
|
+
* messages,
|
|
39
|
+
* });
|
|
40
|
+
* return vibezcheck.toResponse(result);
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function toResponse(result: any, options?: ToResponseOptions): Response;
|
|
45
|
+
|
|
7
46
|
interface VibezCheckModelOptions extends WithBillingOptions {
|
|
8
47
|
/** OpenAI / Anthropic / AI Gateway API Key override */
|
|
9
48
|
apiKey?: string;
|
|
@@ -69,6 +108,7 @@ declare function createVibezModel(modelOrId: any, options?: VibezCheckModelOptio
|
|
|
69
108
|
declare function createVibezSession(sessionOptions?: VibezSessionOptions): VibezSession;
|
|
70
109
|
declare const vibezcheck: typeof createVibezModel & {
|
|
71
110
|
session: typeof createVibezSession;
|
|
111
|
+
toResponse: typeof toResponse;
|
|
72
112
|
};
|
|
73
113
|
|
|
74
|
-
export { type VibezCheckModelOptions, type VibezSession, type VibezSessionOptions, WithBillingOptions, createVibezModel, createVibezSession, vibezcheck };
|
|
114
|
+
export { type ToResponseOptions, type VibezCheckModelOptions, type VibezSession, type VibezSessionOptions, WithBillingOptions, createVibezModel, createVibezSession, toResponse, vibezcheck };
|
package/dist/ai-sdk/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(ai_sdk_exports, {
|
|
|
33
33
|
createVibezModel: () => createVibezModel,
|
|
34
34
|
createVibezSession: () => createVibezSession,
|
|
35
35
|
meteredModel: () => meteredModel,
|
|
36
|
+
toResponse: () => toResponse,
|
|
36
37
|
vibezcheck: () => vibezcheck,
|
|
37
38
|
withBilling: () => withBilling
|
|
38
39
|
});
|
|
@@ -1417,6 +1418,7 @@ function withBilling(model, optionsOrCustomer, extraOptions = {}) {
|
|
|
1417
1418
|
}
|
|
1418
1419
|
}
|
|
1419
1420
|
scheduleFlush();
|
|
1421
|
+
return event;
|
|
1420
1422
|
};
|
|
1421
1423
|
const handler = {
|
|
1422
1424
|
get(target, prop, receiver) {
|
|
@@ -1479,7 +1481,22 @@ function withBilling(model, optionsOrCustomer, extraOptions = {}) {
|
|
|
1479
1481
|
const chunkUsage = (chunk.type === "finish" ? chunk.usage : null) || chunk.usage || chunk.token_usage || chunk.usageMetadata;
|
|
1480
1482
|
if (chunkUsage) {
|
|
1481
1483
|
streamCompleted = true;
|
|
1482
|
-
handleUsage(chunkUsage);
|
|
1484
|
+
const event = handleUsage(chunkUsage);
|
|
1485
|
+
if (chunk.type === "finish" && event) {
|
|
1486
|
+
chunk.providerMetadata = {
|
|
1487
|
+
...chunk.providerMetadata,
|
|
1488
|
+
vibezcheck: {
|
|
1489
|
+
type: "vibezcheck",
|
|
1490
|
+
cost: event.cost,
|
|
1491
|
+
usage: event.usage,
|
|
1492
|
+
model: modelId,
|
|
1493
|
+
provider,
|
|
1494
|
+
customerId,
|
|
1495
|
+
customerEmail,
|
|
1496
|
+
timestamp: event.timestamp
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1483
1500
|
}
|
|
1484
1501
|
},
|
|
1485
1502
|
flush() {
|
|
@@ -1534,6 +1551,142 @@ function withBilling(model, optionsOrCustomer, extraOptions = {}) {
|
|
|
1534
1551
|
}
|
|
1535
1552
|
var meteredModel = withBilling;
|
|
1536
1553
|
|
|
1554
|
+
// src/ai-sdk/to-response.ts
|
|
1555
|
+
function toResponse(result, options) {
|
|
1556
|
+
let baseResponse;
|
|
1557
|
+
if (typeof result?.toUIMessageStreamResponse === "function") {
|
|
1558
|
+
baseResponse = result.toUIMessageStreamResponse(options?.init);
|
|
1559
|
+
} else if (typeof result?.toDataStreamResponse === "function") {
|
|
1560
|
+
baseResponse = result.toDataStreamResponse(options?.init);
|
|
1561
|
+
} else if (typeof result?.toTextStreamResponse === "function") {
|
|
1562
|
+
baseResponse = result.toTextStreamResponse(options?.init);
|
|
1563
|
+
} else if (result instanceof Response) {
|
|
1564
|
+
baseResponse = result;
|
|
1565
|
+
} else if (result && typeof result === "object" && result.body && result.headers) {
|
|
1566
|
+
baseResponse = result;
|
|
1567
|
+
} else {
|
|
1568
|
+
baseResponse = new Response(result, options?.init);
|
|
1569
|
+
}
|
|
1570
|
+
if (!baseResponse.body) {
|
|
1571
|
+
return baseResponse;
|
|
1572
|
+
}
|
|
1573
|
+
const contentType = baseResponse.headers.get("content-type") || "";
|
|
1574
|
+
const isSSE = contentType.includes("text/event-stream") || baseResponse.headers.has("x-vercel-ai-ui-message-stream");
|
|
1575
|
+
const isDataStream = baseResponse.headers.has("x-vercel-ai-data-stream");
|
|
1576
|
+
const encoder = new TextEncoder();
|
|
1577
|
+
const decoder = new TextDecoder();
|
|
1578
|
+
let injected = false;
|
|
1579
|
+
const resolveTelemetry = async () => {
|
|
1580
|
+
if (options?.telemetry) return options.telemetry;
|
|
1581
|
+
if (result?.providerMetadata) {
|
|
1582
|
+
try {
|
|
1583
|
+
const meta = await Promise.race([
|
|
1584
|
+
result.providerMetadata,
|
|
1585
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 800))
|
|
1586
|
+
]);
|
|
1587
|
+
if (meta?.vibezcheck) {
|
|
1588
|
+
return meta.vibezcheck;
|
|
1589
|
+
}
|
|
1590
|
+
} catch {
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
if (result?.usage) {
|
|
1594
|
+
try {
|
|
1595
|
+
const u = await Promise.race([
|
|
1596
|
+
result.usage,
|
|
1597
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 800))
|
|
1598
|
+
]);
|
|
1599
|
+
if (u) {
|
|
1600
|
+
const inputTokens = u.promptTokens ?? u.inputTokens ?? 0;
|
|
1601
|
+
const outputTokens = u.completionTokens ?? u.outputTokens ?? 0;
|
|
1602
|
+
const reasoningTokens = u.reasoningTokens ?? u.completionTokensDetails?.reasoningTokens;
|
|
1603
|
+
return {
|
|
1604
|
+
type: "vibezcheck",
|
|
1605
|
+
usage: {
|
|
1606
|
+
inputTokens,
|
|
1607
|
+
outputTokens,
|
|
1608
|
+
totalTokens: inputTokens + outputTokens,
|
|
1609
|
+
reasoningTokens
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
} catch {
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
return null;
|
|
1617
|
+
};
|
|
1618
|
+
const transformStream = new TransformStream({
|
|
1619
|
+
async transform(chunk, controller) {
|
|
1620
|
+
const text = decoder.decode(chunk, { stream: true });
|
|
1621
|
+
if (isSSE && text.includes("[DONE]") && !injected) {
|
|
1622
|
+
injected = true;
|
|
1623
|
+
const telemetry = await resolveTelemetry();
|
|
1624
|
+
if (telemetry) {
|
|
1625
|
+
const ssePayload = `data: ${JSON.stringify({ type: "data-vibezcheck", data: telemetry })}
|
|
1626
|
+
|
|
1627
|
+
data: ${JSON.stringify({ type: "message-metadata", messageMetadata: { vibezcheck: telemetry } })}
|
|
1628
|
+
|
|
1629
|
+
`;
|
|
1630
|
+
const doneIndex = text.indexOf("data: [DONE]");
|
|
1631
|
+
if (doneIndex !== -1) {
|
|
1632
|
+
const before = text.slice(0, doneIndex);
|
|
1633
|
+
const after = text.slice(doneIndex);
|
|
1634
|
+
if (before) controller.enqueue(encoder.encode(before));
|
|
1635
|
+
controller.enqueue(encoder.encode(ssePayload));
|
|
1636
|
+
controller.enqueue(encoder.encode(after));
|
|
1637
|
+
return;
|
|
1638
|
+
} else {
|
|
1639
|
+
controller.enqueue(encoder.encode(ssePayload));
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
} else if (isDataStream && (text.includes("d:{") || text.includes("d: {")) && !injected) {
|
|
1643
|
+
injected = true;
|
|
1644
|
+
const telemetry = await resolveTelemetry();
|
|
1645
|
+
if (telemetry) {
|
|
1646
|
+
const annotationLine = `2:[${JSON.stringify(telemetry)}]
|
|
1647
|
+
`;
|
|
1648
|
+
controller.enqueue(encoder.encode(annotationLine));
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
controller.enqueue(chunk);
|
|
1652
|
+
},
|
|
1653
|
+
async flush(controller) {
|
|
1654
|
+
if (!injected) {
|
|
1655
|
+
injected = true;
|
|
1656
|
+
const telemetry = await resolveTelemetry();
|
|
1657
|
+
if (telemetry) {
|
|
1658
|
+
if (isSSE) {
|
|
1659
|
+
const ssePayload = `data: ${JSON.stringify({ type: "data-vibezcheck", data: telemetry })}
|
|
1660
|
+
|
|
1661
|
+
data: ${JSON.stringify({ type: "message-metadata", messageMetadata: { vibezcheck: telemetry } })}
|
|
1662
|
+
|
|
1663
|
+
`;
|
|
1664
|
+
controller.enqueue(encoder.encode(ssePayload));
|
|
1665
|
+
} else {
|
|
1666
|
+
const annotationLine = `2:[${JSON.stringify(telemetry)}]
|
|
1667
|
+
`;
|
|
1668
|
+
controller.enqueue(encoder.encode(annotationLine));
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
const mergedHeaders = new Headers(baseResponse.headers);
|
|
1675
|
+
if (options?.headers) {
|
|
1676
|
+
const extraHeaders = new Headers(options.headers);
|
|
1677
|
+
extraHeaders.forEach((value, key) => {
|
|
1678
|
+
mergedHeaders.set(key, value);
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
const status = options?.init?.status ?? baseResponse.status;
|
|
1682
|
+
const statusText = options?.init?.statusText ?? baseResponse.statusText;
|
|
1683
|
+
return new Response(baseResponse.body.pipeThrough(transformStream), {
|
|
1684
|
+
status,
|
|
1685
|
+
statusText,
|
|
1686
|
+
headers: mergedHeaders
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1537
1690
|
// src/ai-sdk/declarative.ts
|
|
1538
1691
|
function createVibezModel(modelOrId, options = {}) {
|
|
1539
1692
|
if (typeof modelOrId === "object" && modelOrId !== null) {
|
|
@@ -1708,13 +1861,15 @@ function createVibezSession(sessionOptions = {}) {
|
|
|
1708
1861
|
};
|
|
1709
1862
|
}
|
|
1710
1863
|
var vibezcheck = Object.assign(createVibezModel, {
|
|
1711
|
-
session: createVibezSession
|
|
1864
|
+
session: createVibezSession,
|
|
1865
|
+
toResponse
|
|
1712
1866
|
});
|
|
1713
1867
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1714
1868
|
0 && (module.exports = {
|
|
1715
1869
|
createVibezModel,
|
|
1716
1870
|
createVibezSession,
|
|
1717
1871
|
meteredModel,
|
|
1872
|
+
toResponse,
|
|
1718
1873
|
vibezcheck,
|
|
1719
1874
|
withBilling
|
|
1720
1875
|
});
|