@providerprotocol/ai 0.0.19 → 0.0.20
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 +82 -9
- package/dist/anthropic/index.js +6 -6
- package/dist/chunk-P5IRTEM5.js +120 -0
- package/dist/chunk-P5IRTEM5.js.map +1 -0
- package/dist/{chunk-5FEAOEXV.js → chunk-U3FZWV4U.js} +53 -102
- package/dist/chunk-U3FZWV4U.js.map +1 -0
- package/dist/google/index.js +6 -6
- package/dist/http/index.js +4 -3
- package/dist/index.js +2 -2
- package/dist/ollama/index.js +6 -6
- package/dist/openai/index.js +6 -6
- package/dist/openrouter/index.js +6 -6
- package/dist/proxy/index.d.ts +192 -9
- package/dist/proxy/index.js +33 -59
- package/dist/proxy/index.js.map +1 -1
- package/dist/xai/index.js +6 -6
- package/package.json +1 -1
- package/dist/chunk-5FEAOEXV.js.map +0 -1
- package/dist/chunk-DZQHVGNV.js +0 -71
- package/dist/chunk-DZQHVGNV.js.map +0 -1
package/dist/proxy/index.d.ts
CHANGED
|
@@ -149,10 +149,11 @@ declare function sendError$2(message: string, status: number, event: H3Event): {
|
|
|
149
149
|
/**
|
|
150
150
|
* H3/Nitro/Nuxt adapter utilities.
|
|
151
151
|
*
|
|
152
|
-
* @example
|
|
152
|
+
* @example Basic usage
|
|
153
153
|
* ```typescript
|
|
154
154
|
* // Nuxt server route: server/api/ai.post.ts
|
|
155
|
-
* import { llm
|
|
155
|
+
* import { llm } from '@providerprotocol/ai';
|
|
156
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
156
157
|
* import { parseBody } from '@providerprotocol/ai/proxy';
|
|
157
158
|
* import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';
|
|
158
159
|
*
|
|
@@ -170,6 +171,49 @@ declare function sendError$2(message: string, status: number, event: H3Event): {
|
|
|
170
171
|
* }
|
|
171
172
|
* });
|
|
172
173
|
* ```
|
|
174
|
+
*
|
|
175
|
+
* @example API Gateway with authentication (Nuxt)
|
|
176
|
+
* ```typescript
|
|
177
|
+
* // server/api/ai.post.ts
|
|
178
|
+
* import { llm } from '@providerprotocol/ai';
|
|
179
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
180
|
+
* import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';
|
|
181
|
+
* import { parseBody } from '@providerprotocol/ai/proxy';
|
|
182
|
+
* import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';
|
|
183
|
+
*
|
|
184
|
+
* // Server manages AI provider keys - users never see them
|
|
185
|
+
* const claude = llm({
|
|
186
|
+
* model: anthropic('claude-sonnet-4-20250514'),
|
|
187
|
+
* config: {
|
|
188
|
+
* apiKey: new RoundRobinKeys([
|
|
189
|
+
* process.env.ANTHROPIC_KEY_1!,
|
|
190
|
+
* process.env.ANTHROPIC_KEY_2!,
|
|
191
|
+
* ]),
|
|
192
|
+
* retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),
|
|
193
|
+
* },
|
|
194
|
+
* });
|
|
195
|
+
*
|
|
196
|
+
* export default defineEventHandler(async (event) => {
|
|
197
|
+
* // Authenticate with your platform credentials
|
|
198
|
+
* const token = getHeader(event, 'authorization')?.replace('Bearer ', '');
|
|
199
|
+
* const user = await validatePlatformToken(token);
|
|
200
|
+
* if (!user) {
|
|
201
|
+
* throw createError({ statusCode: 401, message: 'Unauthorized' });
|
|
202
|
+
* }
|
|
203
|
+
*
|
|
204
|
+
* // Track usage per user
|
|
205
|
+
* // await trackUsage(user.id);
|
|
206
|
+
*
|
|
207
|
+
* const body = await readBody(event);
|
|
208
|
+
* const { messages, system, params } = parseBody(body);
|
|
209
|
+
*
|
|
210
|
+
* if (params?.stream) {
|
|
211
|
+
* return h3Adapter.streamSSE(claude.stream(messages, { system }), event);
|
|
212
|
+
* }
|
|
213
|
+
* const turn = await claude.generate(messages, { system });
|
|
214
|
+
* return h3Adapter.sendJSON(turn, event);
|
|
215
|
+
* });
|
|
216
|
+
* ```
|
|
173
217
|
*/
|
|
174
218
|
declare const h3: {
|
|
175
219
|
sendJSON: typeof sendJSON$2;
|
|
@@ -236,10 +280,11 @@ declare function sendError$1(message: string, status: number, reply: FastifyRepl
|
|
|
236
280
|
/**
|
|
237
281
|
* Fastify adapter utilities.
|
|
238
282
|
*
|
|
239
|
-
* @example
|
|
283
|
+
* @example Basic usage
|
|
240
284
|
* ```typescript
|
|
241
285
|
* import Fastify from 'fastify';
|
|
242
|
-
* import { llm
|
|
286
|
+
* import { llm } from '@providerprotocol/ai';
|
|
287
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
243
288
|
* import { parseBody } from '@providerprotocol/ai/proxy';
|
|
244
289
|
* import { fastify as fastifyAdapter } from '@providerprotocol/ai/proxy/server';
|
|
245
290
|
*
|
|
@@ -257,6 +302,51 @@ declare function sendError$1(message: string, status: number, reply: FastifyRepl
|
|
|
257
302
|
* }
|
|
258
303
|
* });
|
|
259
304
|
* ```
|
|
305
|
+
*
|
|
306
|
+
* @example API Gateway with authentication
|
|
307
|
+
* ```typescript
|
|
308
|
+
* import Fastify from 'fastify';
|
|
309
|
+
* import { llm } from '@providerprotocol/ai';
|
|
310
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
311
|
+
* import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';
|
|
312
|
+
* import { parseBody } from '@providerprotocol/ai/proxy';
|
|
313
|
+
* import { fastify as fastifyAdapter } from '@providerprotocol/ai/proxy/server';
|
|
314
|
+
*
|
|
315
|
+
* const app = Fastify();
|
|
316
|
+
*
|
|
317
|
+
* // Server manages AI provider keys - users never see them
|
|
318
|
+
* const claude = llm({
|
|
319
|
+
* model: anthropic('claude-sonnet-4-20250514'),
|
|
320
|
+
* config: {
|
|
321
|
+
* apiKey: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),
|
|
322
|
+
* retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),
|
|
323
|
+
* },
|
|
324
|
+
* });
|
|
325
|
+
*
|
|
326
|
+
* // Auth hook for your platform
|
|
327
|
+
* app.addHook('preHandler', async (request, reply) => {
|
|
328
|
+
* const token = request.headers.authorization?.replace('Bearer ', '');
|
|
329
|
+
* const user = await validatePlatformToken(token);
|
|
330
|
+
* if (!user) {
|
|
331
|
+
* reply.status(401).send({ error: 'Unauthorized' });
|
|
332
|
+
* return;
|
|
333
|
+
* }
|
|
334
|
+
* request.user = user;
|
|
335
|
+
* });
|
|
336
|
+
*
|
|
337
|
+
* app.post('/api/ai', async (request, reply) => {
|
|
338
|
+
* // Track usage per user
|
|
339
|
+
* // await trackUsage(request.user.id);
|
|
340
|
+
*
|
|
341
|
+
* const { messages, system, params } = parseBody(request.body);
|
|
342
|
+
*
|
|
343
|
+
* if (params?.stream) {
|
|
344
|
+
* return fastifyAdapter.streamSSE(claude.stream(messages, { system }), reply);
|
|
345
|
+
* }
|
|
346
|
+
* const turn = await claude.generate(messages, { system });
|
|
347
|
+
* return fastifyAdapter.sendJSON(turn, reply);
|
|
348
|
+
* });
|
|
349
|
+
* ```
|
|
260
350
|
*/
|
|
261
351
|
declare const fastify: {
|
|
262
352
|
sendJSON: typeof sendJSON$1;
|
|
@@ -320,11 +410,12 @@ declare function sendError(message: string, status: number, res: ExpressResponse
|
|
|
320
410
|
/**
|
|
321
411
|
* Express adapter utilities.
|
|
322
412
|
*
|
|
323
|
-
* @example
|
|
413
|
+
* @example Basic usage
|
|
324
414
|
* ```typescript
|
|
325
415
|
* import express from 'express';
|
|
326
|
-
* import { llm
|
|
327
|
-
* import {
|
|
416
|
+
* import { llm } from '@providerprotocol/ai';
|
|
417
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
418
|
+
* import { parseBody } from '@providerprotocol/ai/proxy';
|
|
328
419
|
* import { express as expressAdapter } from '@providerprotocol/ai/proxy/server';
|
|
329
420
|
*
|
|
330
421
|
* const app = express();
|
|
@@ -342,6 +433,51 @@ declare function sendError(message: string, status: number, res: ExpressResponse
|
|
|
342
433
|
* }
|
|
343
434
|
* });
|
|
344
435
|
* ```
|
|
436
|
+
*
|
|
437
|
+
* @example API Gateway with authentication
|
|
438
|
+
* ```typescript
|
|
439
|
+
* import express from 'express';
|
|
440
|
+
* import { llm } from '@providerprotocol/ai';
|
|
441
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
442
|
+
* import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';
|
|
443
|
+
* import { parseBody } from '@providerprotocol/ai/proxy';
|
|
444
|
+
* import { express as expressAdapter } from '@providerprotocol/ai/proxy/server';
|
|
445
|
+
*
|
|
446
|
+
* const app = express();
|
|
447
|
+
* app.use(express.json());
|
|
448
|
+
*
|
|
449
|
+
* // Your platform's auth middleware
|
|
450
|
+
* async function authMiddleware(req, res, next) {
|
|
451
|
+
* const token = req.headers.authorization?.replace('Bearer ', '');
|
|
452
|
+
* const user = await validatePlatformToken(token);
|
|
453
|
+
* if (!user) return res.status(401).json({ error: 'Unauthorized' });
|
|
454
|
+
* req.user = user;
|
|
455
|
+
* next();
|
|
456
|
+
* }
|
|
457
|
+
*
|
|
458
|
+
* // Server manages AI provider keys - users never see them
|
|
459
|
+
* const claude = llm({
|
|
460
|
+
* model: anthropic('claude-sonnet-4-20250514'),
|
|
461
|
+
* config: {
|
|
462
|
+
* apiKey: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),
|
|
463
|
+
* retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),
|
|
464
|
+
* },
|
|
465
|
+
* });
|
|
466
|
+
*
|
|
467
|
+
* app.post('/api/ai', authMiddleware, async (req, res) => {
|
|
468
|
+
* // Track usage per user
|
|
469
|
+
* // await trackUsage(req.user.id);
|
|
470
|
+
*
|
|
471
|
+
* const { messages, system, params } = parseBody(req.body);
|
|
472
|
+
*
|
|
473
|
+
* if (params?.stream) {
|
|
474
|
+
* expressAdapter.streamSSE(claude.stream(messages, { system }), res);
|
|
475
|
+
* } else {
|
|
476
|
+
* const turn = await claude.generate(messages, { system });
|
|
477
|
+
* expressAdapter.sendJSON(turn, res);
|
|
478
|
+
* }
|
|
479
|
+
* });
|
|
480
|
+
* ```
|
|
345
481
|
*/
|
|
346
482
|
declare const express: {
|
|
347
483
|
sendJSON: typeof sendJSON;
|
|
@@ -458,9 +594,13 @@ declare function bindTools(schemas: ParsedRequest['tools'], implementations: Rec
|
|
|
458
594
|
* For use with Bun, Deno, Next.js App Router, Cloudflare Workers,
|
|
459
595
|
* and other frameworks that support Web API Response.
|
|
460
596
|
*
|
|
461
|
-
*
|
|
597
|
+
* **Security Note:** The proxy works without configuration, meaning no
|
|
598
|
+
* authentication by default. Always add your own auth layer in production.
|
|
599
|
+
*
|
|
600
|
+
* @example Basic usage
|
|
462
601
|
* ```typescript
|
|
463
|
-
* import { llm
|
|
602
|
+
* import { llm } from '@providerprotocol/ai';
|
|
603
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
464
604
|
* import { parseBody, toJSON, toSSE } from '@providerprotocol/ai/proxy';
|
|
465
605
|
*
|
|
466
606
|
* // Bun.serve / Deno.serve / Next.js App Router
|
|
@@ -474,6 +614,49 @@ declare function bindTools(schemas: ParsedRequest['tools'], implementations: Rec
|
|
|
474
614
|
* return toJSON(await instance.generate(messages));
|
|
475
615
|
* }
|
|
476
616
|
* ```
|
|
617
|
+
*
|
|
618
|
+
* @example API Gateway with authentication
|
|
619
|
+
* ```typescript
|
|
620
|
+
* import { llm } from '@providerprotocol/ai';
|
|
621
|
+
* import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
622
|
+
* import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';
|
|
623
|
+
* import { parseBody, toJSON, toSSE, toError } from '@providerprotocol/ai/proxy';
|
|
624
|
+
*
|
|
625
|
+
* // Your platform's user validation
|
|
626
|
+
* async function validateToken(token: string): Promise<{ id: string } | null> {
|
|
627
|
+
* // Verify JWT, check database, etc.
|
|
628
|
+
* return token ? { id: 'user-123' } : null;
|
|
629
|
+
* }
|
|
630
|
+
*
|
|
631
|
+
* // Server manages AI provider keys - users never see them
|
|
632
|
+
* const claude = llm({
|
|
633
|
+
* model: anthropic('claude-sonnet-4-20250514'),
|
|
634
|
+
* config: {
|
|
635
|
+
* apiKey: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),
|
|
636
|
+
* retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),
|
|
637
|
+
* },
|
|
638
|
+
* });
|
|
639
|
+
*
|
|
640
|
+
* Bun.serve({
|
|
641
|
+
* port: 3000,
|
|
642
|
+
* async fetch(req) {
|
|
643
|
+
* // Authenticate with YOUR platform credentials
|
|
644
|
+
* const token = req.headers.get('Authorization')?.replace('Bearer ', '');
|
|
645
|
+
* const user = await validateToken(token ?? '');
|
|
646
|
+
* if (!user) return toError('Unauthorized', 401);
|
|
647
|
+
*
|
|
648
|
+
* // Rate limit, track usage, bill user, etc.
|
|
649
|
+
* // await trackUsage(user.id);
|
|
650
|
+
*
|
|
651
|
+
* const { messages, system, params } = parseBody(await req.json());
|
|
652
|
+
*
|
|
653
|
+
* if (params?.stream) {
|
|
654
|
+
* return toSSE(claude.stream(messages, { system }));
|
|
655
|
+
* }
|
|
656
|
+
* return toJSON(await claude.generate(messages, { system }));
|
|
657
|
+
* },
|
|
658
|
+
* });
|
|
659
|
+
* ```
|
|
477
660
|
*/
|
|
478
661
|
declare const webapi: {
|
|
479
662
|
parseBody: typeof parseBody;
|
package/dist/proxy/index.js
CHANGED
|
@@ -10,8 +10,11 @@ import {
|
|
|
10
10
|
UserMessage
|
|
11
11
|
} from "../chunk-UMKWXGO3.js";
|
|
12
12
|
import {
|
|
13
|
-
UPPError
|
|
14
|
-
|
|
13
|
+
UPPError,
|
|
14
|
+
doFetch,
|
|
15
|
+
doStreamFetch,
|
|
16
|
+
normalizeHttpError
|
|
17
|
+
} from "../chunk-U3FZWV4U.js";
|
|
15
18
|
|
|
16
19
|
// src/providers/proxy/serialization.ts
|
|
17
20
|
function serializeMessage(m) {
|
|
@@ -98,12 +101,7 @@ var PROXY_CAPABILITIES = {
|
|
|
98
101
|
audioInput: true
|
|
99
102
|
};
|
|
100
103
|
function createLLMHandler(options) {
|
|
101
|
-
const {
|
|
102
|
-
endpoint,
|
|
103
|
-
headers: defaultHeaders = {},
|
|
104
|
-
fetch: customFetch = fetch,
|
|
105
|
-
timeout = 12e4
|
|
106
|
-
} = options;
|
|
104
|
+
const { endpoint, headers: defaultHeaders = {} } = options;
|
|
107
105
|
let providerRef = null;
|
|
108
106
|
return {
|
|
109
107
|
_setProvider(provider) {
|
|
@@ -127,10 +125,9 @@ function createLLMHandler(options) {
|
|
|
127
125
|
async complete(request) {
|
|
128
126
|
const body = serializeRequest(request);
|
|
129
127
|
const headers = mergeHeaders(request.config.headers, defaultHeaders);
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const response = await customFetch(endpoint, {
|
|
128
|
+
const response = await doFetch(
|
|
129
|
+
endpoint,
|
|
130
|
+
{
|
|
134
131
|
method: "POST",
|
|
135
132
|
headers: {
|
|
136
133
|
...headers,
|
|
@@ -138,30 +135,18 @@ function createLLMHandler(options) {
|
|
|
138
135
|
Accept: "application/json"
|
|
139
136
|
},
|
|
140
137
|
body: JSON.stringify(body),
|
|
141
|
-
signal: request.signal
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"proxy",
|
|
150
|
-
"llm"
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
const data = await response.json();
|
|
154
|
-
return turnJSONToLLMResponse(data);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
clearTimeout(timeoutId);
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
138
|
+
signal: request.signal
|
|
139
|
+
},
|
|
140
|
+
request.config,
|
|
141
|
+
"proxy",
|
|
142
|
+
"llm"
|
|
143
|
+
);
|
|
144
|
+
const data = await response.json();
|
|
145
|
+
return turnJSONToLLMResponse(data);
|
|
159
146
|
},
|
|
160
147
|
stream(request) {
|
|
161
148
|
const body = serializeRequest(request);
|
|
162
149
|
const headers = mergeHeaders(request.config.headers, defaultHeaders);
|
|
163
|
-
const controller = new AbortController();
|
|
164
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
165
150
|
let resolveResponse;
|
|
166
151
|
let rejectResponse;
|
|
167
152
|
const responsePromise = new Promise((resolve, reject) => {
|
|
@@ -170,25 +155,24 @@ function createLLMHandler(options) {
|
|
|
170
155
|
});
|
|
171
156
|
const generator = async function* () {
|
|
172
157
|
try {
|
|
173
|
-
const response = await
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
158
|
+
const response = await doStreamFetch(
|
|
159
|
+
endpoint,
|
|
160
|
+
{
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: {
|
|
163
|
+
...headers,
|
|
164
|
+
"Content-Type": "application/json",
|
|
165
|
+
Accept: "text/event-stream"
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify(body),
|
|
168
|
+
signal: request.signal
|
|
179
169
|
},
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
170
|
+
request.config,
|
|
171
|
+
"proxy",
|
|
172
|
+
"llm"
|
|
173
|
+
);
|
|
184
174
|
if (!response.ok) {
|
|
185
|
-
|
|
186
|
-
throw new UPPError(
|
|
187
|
-
text || `HTTP ${response.status}`,
|
|
188
|
-
"PROVIDER_ERROR",
|
|
189
|
-
"proxy",
|
|
190
|
-
"llm"
|
|
191
|
-
);
|
|
175
|
+
throw await normalizeHttpError(response, "proxy", "llm");
|
|
192
176
|
}
|
|
193
177
|
if (!response.body) {
|
|
194
178
|
throw new UPPError(
|
|
@@ -274,16 +258,6 @@ function turnJSONToLLMResponse(data) {
|
|
|
274
258
|
data: data.data
|
|
275
259
|
};
|
|
276
260
|
}
|
|
277
|
-
function combineSignals(signal1, signal2) {
|
|
278
|
-
const controller = new AbortController();
|
|
279
|
-
const onAbort = () => controller.abort();
|
|
280
|
-
signal1.addEventListener("abort", onAbort);
|
|
281
|
-
signal2.addEventListener("abort", onAbort);
|
|
282
|
-
if (signal1.aborted || signal2.aborted) {
|
|
283
|
-
controller.abort();
|
|
284
|
-
}
|
|
285
|
-
return controller.signal;
|
|
286
|
-
}
|
|
287
261
|
|
|
288
262
|
// src/providers/proxy/server/express.ts
|
|
289
263
|
function sendJSON(turn, res) {
|
package/dist/proxy/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/proxy/serialization.ts","../../src/providers/proxy/llm.ts","../../src/providers/proxy/server/express.ts","../../src/providers/proxy/server/fastify.ts","../../src/providers/proxy/server/h3.ts","../../src/providers/proxy/server/webapi.ts","../../src/providers/proxy/server/index.ts","../../src/providers/proxy/index.ts"],"sourcesContent":["/**\n * @fileoverview Serialization utilities for proxy transport.\n *\n * Handles converting PP types to/from JSON for HTTP transport.\n * These are pure functions with no side effects.\n *\n * @module providers/proxy/serialization\n */\n\nimport {\n UserMessage,\n AssistantMessage,\n ToolResultMessage,\n type Message,\n type MessageJSON,\n} from '../../types/messages.ts';\nimport type { UserContent, AssistantContent } from '../../types/content.ts';\nimport type { StreamEvent, EventDelta } from '../../types/stream.ts';\nimport type { Turn, TurnJSON } from '../../types/turn.ts';\n\n/**\n * Convert a Message to MessageJSON format.\n */\nexport function serializeMessage(m: Message): MessageJSON {\n const base: MessageJSON = {\n id: m.id,\n type: m.type,\n content: [],\n metadata: m.metadata,\n timestamp: m.timestamp.toISOString(),\n };\n\n if (m instanceof UserMessage) {\n base.content = m.content;\n } else if (m instanceof AssistantMessage) {\n base.content = m.content;\n base.toolCalls = m.toolCalls;\n } else if (m instanceof ToolResultMessage) {\n base.results = m.results;\n }\n\n return base;\n}\n\n/**\n * Reconstruct a Message from MessageJSON format.\n */\nexport function deserializeMessage(json: MessageJSON): Message {\n const options = {\n id: json.id,\n metadata: json.metadata,\n };\n\n switch (json.type) {\n case 'user':\n return new UserMessage(json.content as UserContent[], options);\n case 'assistant':\n return new AssistantMessage(\n json.content as AssistantContent[],\n json.toolCalls,\n options\n );\n case 'tool_result':\n return new ToolResultMessage(json.results ?? [], options);\n default:\n throw new Error(`Unknown message type: ${json.type}`);\n }\n}\n\n/**\n * Serialize a Turn to JSON-transportable format.\n */\nexport function serializeTurn(turn: Turn): TurnJSON {\n return {\n messages: turn.messages.map(serializeMessage),\n toolExecutions: turn.toolExecutions,\n usage: turn.usage,\n cycles: turn.cycles,\n data: turn.data,\n };\n}\n\n/**\n * Serialize a StreamEvent for JSON transport.\n * Converts Uint8Array data to base64 string.\n */\nexport function serializeStreamEvent(event: StreamEvent): StreamEvent {\n if (event.delta.data instanceof Uint8Array) {\n const { data, ...rest } = event.delta;\n const bytes = Array.from(data);\n const base64 = btoa(bytes.map((b) => String.fromCharCode(b)).join(''));\n return {\n type: event.type,\n index: event.index,\n delta: { ...rest, data: base64 as unknown as Uint8Array },\n };\n }\n return event;\n}\n\n/**\n * Deserialize a StreamEvent from JSON transport.\n * Converts base64 string data back to Uint8Array.\n */\nexport function deserializeStreamEvent(event: StreamEvent): StreamEvent {\n const delta = event.delta as EventDelta & { data?: string | Uint8Array };\n if (typeof delta.data === 'string') {\n const binaryString = atob(delta.data);\n const bytes = Uint8Array.from(binaryString, (c) => c.charCodeAt(0));\n return {\n type: event.type,\n index: event.index,\n delta: { ...delta, data: bytes },\n };\n }\n return event;\n}\n","/**\n * @fileoverview Proxy LLM handler implementation.\n *\n * Transports PP LLM requests over HTTP to a backend server.\n * Supports both synchronous completion and streaming via SSE.\n *\n * @module providers/proxy/llm\n */\n\nimport type {\n LLMHandler,\n BoundLLMModel,\n LLMRequest,\n LLMResponse,\n LLMStreamResult,\n LLMCapabilities,\n} from '../../types/llm.ts';\nimport type { LLMProvider } from '../../types/provider.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { TurnJSON } from '../../types/turn.ts';\nimport { AssistantMessage } from '../../types/messages.ts';\nimport { emptyUsage } from '../../types/turn.ts';\nimport { UPPError } from '../../types/errors.ts';\nimport type { ProxyLLMParams, ProxyProviderOptions } from './types.ts';\nimport {\n serializeMessage,\n deserializeMessage,\n deserializeStreamEvent,\n} from './serialization.ts';\n\n/**\n * Capability flags for proxy provider.\n * All capabilities are enabled since the backend determines actual support.\n */\nconst PROXY_CAPABILITIES: LLMCapabilities = {\n streaming: true,\n tools: true,\n structuredOutput: true,\n imageInput: true,\n videoInput: true,\n audioInput: true,\n};\n\n/**\n * Creates a proxy LLM handler.\n *\n * @param options - Proxy configuration options\n * @returns An LLM handler that transports requests over HTTP\n */\nexport function createLLMHandler(options: ProxyProviderOptions): LLMHandler<ProxyLLMParams> {\n const {\n endpoint,\n headers: defaultHeaders = {},\n fetch: customFetch = fetch,\n timeout = 120000,\n } = options;\n\n let providerRef: LLMProvider<ProxyLLMParams> | null = null;\n\n return {\n _setProvider(provider: LLMProvider<ProxyLLMParams>) {\n providerRef = provider;\n },\n\n bind(modelId: string): BoundLLMModel<ProxyLLMParams> {\n if (!providerRef) {\n throw new UPPError(\n 'Provider reference not set. Handler must be used with createProvider().',\n 'INVALID_REQUEST',\n 'proxy',\n 'llm'\n );\n }\n\n const model: BoundLLMModel<ProxyLLMParams> = {\n modelId,\n capabilities: PROXY_CAPABILITIES,\n\n get provider(): LLMProvider<ProxyLLMParams> {\n return providerRef!;\n },\n\n async complete(request: LLMRequest<ProxyLLMParams>): Promise<LLMResponse> {\n const body = serializeRequest(request);\n const headers = mergeHeaders(request.config.headers, defaultHeaders);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await customFetch(endpoint, {\n method: 'POST',\n headers: {\n ...headers,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal: request.signal\n ? combineSignals(request.signal, controller.signal)\n : controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const text = await response.text();\n throw new UPPError(\n text || `HTTP ${response.status}`,\n 'PROVIDER_ERROR',\n 'proxy',\n 'llm'\n );\n }\n\n const data = (await response.json()) as TurnJSON;\n return turnJSONToLLMResponse(data);\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n },\n\n stream(request: LLMRequest<ProxyLLMParams>): LLMStreamResult {\n const body = serializeRequest(request);\n const headers = mergeHeaders(request.config.headers, defaultHeaders);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n let resolveResponse: (value: LLMResponse) => void;\n let rejectResponse: (error: Error) => void;\n const responsePromise = new Promise<LLMResponse>((resolve, reject) => {\n resolveResponse = resolve;\n rejectResponse = reject;\n });\n\n const generator = async function* (): AsyncGenerator<StreamEvent> {\n try {\n const response = await customFetch(endpoint, {\n method: 'POST',\n headers: {\n ...headers,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n },\n body: JSON.stringify(body),\n signal: request.signal\n ? combineSignals(request.signal, controller.signal)\n : controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const text = await response.text();\n throw new UPPError(\n text || `HTTP ${response.status}`,\n 'PROVIDER_ERROR',\n 'proxy',\n 'llm'\n );\n }\n\n if (!response.body) {\n throw new UPPError(\n 'Response body is null',\n 'PROVIDER_ERROR',\n 'proxy',\n 'llm'\n );\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim() || line.startsWith(':')) continue;\n\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') continue;\n\n try {\n const parsed = JSON.parse(data);\n\n // Check if this is the final turn data\n if ('messages' in parsed && 'usage' in parsed && 'cycles' in parsed) {\n resolveResponse(turnJSONToLLMResponse(parsed as TurnJSON));\n } else {\n // It's a StreamEvent\n yield deserializeStreamEvent(parsed as StreamEvent);\n }\n } catch {\n // Skip malformed JSON\n }\n }\n }\n }\n } catch (error) {\n rejectResponse(error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n };\n\n return {\n [Symbol.asyncIterator]: generator,\n response: responsePromise,\n };\n },\n };\n\n return model;\n },\n };\n}\n\n/**\n * Serialize an LLMRequest for HTTP transport.\n */\nfunction serializeRequest(request: LLMRequest<ProxyLLMParams>): Record<string, unknown> {\n return {\n messages: request.messages.map(serializeMessage),\n system: request.system,\n params: request.params,\n tools: request.tools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n metadata: t.metadata,\n })),\n structure: request.structure,\n };\n}\n\n/**\n * Merge request headers with default headers.\n */\nfunction mergeHeaders(\n requestHeaders: Record<string, string | undefined> | undefined,\n defaultHeaders: Record<string, string>\n): Record<string, string> {\n const headers: Record<string, string> = { ...defaultHeaders };\n if (requestHeaders) {\n for (const [key, value] of Object.entries(requestHeaders)) {\n if (value !== undefined) {\n headers[key] = value;\n }\n }\n }\n return headers;\n}\n\n/**\n * Convert TurnJSON to LLMResponse.\n */\nfunction turnJSONToLLMResponse(data: TurnJSON): LLMResponse {\n const messages = data.messages.map(deserializeMessage);\n const lastAssistant = messages\n .filter((m): m is AssistantMessage => m.type === 'assistant')\n .pop();\n\n return {\n message: lastAssistant ?? new AssistantMessage(''),\n usage: data.usage ?? emptyUsage(),\n stopReason: 'stop',\n data: data.data,\n };\n}\n\n/**\n * Combine two AbortSignals into one.\n */\nfunction combineSignals(signal1: AbortSignal, signal2: AbortSignal): AbortSignal {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal1.addEventListener('abort', onAbort);\n signal2.addEventListener('abort', onAbort);\n if (signal1.aborted || signal2.aborted) {\n controller.abort();\n }\n return controller.signal;\n}\n","/**\n * @fileoverview Express/Connect adapter for proxy server.\n *\n * Provides utilities for using PP proxy with Express.js or Connect-based servers.\n * These adapters convert PP types to Express-compatible responses.\n *\n * @module providers/proxy/server/express\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\n\n/**\n * Express Response interface (minimal type to avoid dependency).\n */\ninterface ExpressResponse {\n setHeader(name: string, value: string): void;\n status(code: number): ExpressResponse;\n write(chunk: string): boolean;\n end(): void;\n json(body: unknown): void;\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param res - Express response object\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * expressAdapter.sendJSON(turn, res);\n * ```\n */\nexport function sendJSON(turn: Turn, res: ExpressResponse): void {\n res.setHeader('Content-Type', 'application/json');\n res.json(serializeTurn(turn));\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param res - Express response object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * expressAdapter.streamSSE(stream, res);\n * ```\n */\nexport function streamSSE(stream: StreamResult, res: ExpressResponse): void {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n (async () => {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n res.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n res.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n res.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n res.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param res - Express response object\n */\nexport function sendError(message: string, status: number, res: ExpressResponse): void {\n res.status(status).json({ error: message });\n}\n\n/**\n * Express adapter utilities.\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { llm, anthropic } from '@providerprotocol/ai';\n * import { parseBody, bindTools } from '@providerprotocol/ai/proxy';\n * import { express as expressAdapter } from '@providerprotocol/ai/proxy/server';\n *\n * const app = express();\n * app.use(express.json());\n *\n * app.post('/api/ai', async (req, res) => {\n * const { messages, system, params } = parseBody(req.body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * if (req.headers.accept?.includes('text/event-stream')) {\n * expressAdapter.streamSSE(instance.stream(messages), res);\n * } else {\n * const turn = await instance.generate(messages);\n * expressAdapter.sendJSON(turn, res);\n * }\n * });\n * ```\n */\nexport const express = {\n sendJSON,\n streamSSE,\n sendError,\n};\n","/**\n * @fileoverview Fastify adapter for proxy server.\n *\n * Provides utilities for using PP proxy with Fastify servers.\n * These adapters convert PP types to Fastify-compatible responses.\n *\n * @module providers/proxy/server/fastify\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\n\n/**\n * Fastify Reply interface (minimal type to avoid dependency).\n */\ninterface FastifyReply {\n header(name: string, value: string): FastifyReply;\n status(code: number): FastifyReply;\n send(payload: unknown): FastifyReply;\n raw: {\n write(chunk: string): boolean;\n end(): void;\n };\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param reply - Fastify reply object\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return fastifyAdapter.sendJSON(turn, reply);\n * ```\n */\nexport function sendJSON(turn: Turn, reply: FastifyReply): FastifyReply {\n return reply\n .header('Content-Type', 'application/json')\n .send(serializeTurn(turn));\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param reply - Fastify reply object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return fastifyAdapter.streamSSE(stream, reply);\n * ```\n */\nexport function streamSSE(stream: StreamResult, reply: FastifyReply): FastifyReply {\n reply\n .header('Content-Type', 'text/event-stream')\n .header('Cache-Control', 'no-cache')\n .header('Connection', 'keep-alive');\n\n const raw = reply.raw;\n\n (async () => {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n raw.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n raw.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n raw.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n raw.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n raw.end();\n }\n })();\n\n return reply;\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param reply - Fastify reply object\n */\nexport function sendError(message: string, status: number, reply: FastifyReply): FastifyReply {\n return reply.status(status).send({ error: message });\n}\n\n/**\n * Fastify adapter utilities.\n *\n * @example\n * ```typescript\n * import Fastify from 'fastify';\n * import { llm, anthropic } from '@providerprotocol/ai';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { fastify as fastifyAdapter } from '@providerprotocol/ai/proxy/server';\n *\n * const app = Fastify();\n *\n * app.post('/api/ai', async (request, reply) => {\n * const { messages, system, params } = parseBody(request.body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * if (request.headers.accept?.includes('text/event-stream')) {\n * return fastifyAdapter.streamSSE(instance.stream(messages), reply);\n * } else {\n * const turn = await instance.generate(messages);\n * return fastifyAdapter.sendJSON(turn, reply);\n * }\n * });\n * ```\n */\nexport const fastify = {\n sendJSON,\n streamSSE,\n sendError,\n};\n","/**\n * @fileoverview H3/Nitro/Nuxt adapter for proxy server.\n *\n * Provides utilities for using PP proxy with H3-based servers\n * (Nuxt, Nitro, or standalone H3).\n *\n * @module providers/proxy/server/h3\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\n\n/**\n * H3 Event interface (minimal type to avoid dependency).\n */\ninterface H3Event {\n node: {\n res: {\n setHeader(name: string, value: string): void;\n write(chunk: string): boolean;\n end(): void;\n };\n };\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param event - H3 event object\n * @returns Serialized turn data\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return h3Adapter.sendJSON(turn, event);\n * ```\n */\nexport function sendJSON(turn: Turn, event: H3Event): unknown {\n event.node.res.setHeader('Content-Type', 'application/json');\n return serializeTurn(turn);\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param event - H3 event object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return h3Adapter.streamSSE(stream, event);\n * ```\n */\nexport function streamSSE(stream: StreamResult, event: H3Event): void {\n const res = event.node.res;\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n (async () => {\n try {\n for await (const evt of stream) {\n const serialized = serializeStreamEvent(evt);\n res.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n res.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n res.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n res.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n}\n\n/**\n * Create a ReadableStream for H3's sendStream utility.\n *\n * Use this with H3's sendStream for better integration:\n * ```typescript\n * import { sendStream } from 'h3';\n * return sendStream(event, h3Adapter.createSSEStream(stream));\n * ```\n *\n * @param stream - The StreamResult from instance.stream()\n * @returns A ReadableStream of SSE data\n */\nexport function createSSEStream(stream: StreamResult): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serialized)}\\n\\n`));\n }\n\n const turn = await stream.turn;\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`));\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: message })}\\n\\n`));\n } finally {\n controller.close();\n }\n },\n });\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param event - H3 event object\n * @returns Error object for H3 to serialize\n */\nexport function sendError(message: string, status: number, event: H3Event): { error: string; statusCode: number } {\n return { error: message, statusCode: status };\n}\n\n/**\n * H3/Nitro/Nuxt adapter utilities.\n *\n * @example\n * ```typescript\n * // Nuxt server route: server/api/ai.post.ts\n * import { llm, anthropic } from '@providerprotocol/ai';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';\n *\n * export default defineEventHandler(async (event) => {\n * const body = await readBody(event);\n * const { messages, system, params } = parseBody(body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * const wantsStream = getHeader(event, 'accept')?.includes('text/event-stream');\n * if (wantsStream) {\n * return h3Adapter.streamSSE(instance.stream(messages), event);\n * } else {\n * const turn = await instance.generate(messages);\n * return h3Adapter.sendJSON(turn, event);\n * }\n * });\n * ```\n */\nexport const h3 = {\n sendJSON,\n streamSSE,\n createSSEStream,\n sendError,\n};\n","/**\n * @fileoverview Web API adapter for proxy server.\n *\n * Provides utilities for using PP proxy with Web API native frameworks\n * (Bun, Deno, Next.js App Router, Cloudflare Workers).\n *\n * These utilities return standard Web API Response objects that work\n * directly with modern runtimes.\n *\n * @module providers/proxy/server/webapi\n */\n\nimport type { Message } from '../../../types/messages.ts';\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport type { MessageJSON } from '../../../types/thread.ts';\nimport type { JSONSchema } from '../../../types/schema.ts';\nimport type { Tool, ToolMetadata } from '../../../types/tool.ts';\nimport {\n deserializeMessage,\n serializeTurn,\n serializeStreamEvent,\n} from '../serialization.ts';\n\n/**\n * Parsed request body from a proxy HTTP request.\n * This is just the deserialized PP data from the request body.\n */\nexport interface ParsedRequest {\n messages: Message[];\n system?: string | unknown[];\n params?: Record<string, unknown>;\n tools?: Array<{\n name: string;\n description: string;\n parameters: JSONSchema;\n metadata?: ToolMetadata;\n }>;\n structure?: JSONSchema;\n}\n\n/**\n * Parse an HTTP request body into PP types.\n *\n * @param body - The JSON-parsed request body\n * @returns Deserialized PP data\n *\n * @example\n * ```typescript\n * const body = await req.json();\n * const { messages, system, params } = parseBody(body);\n *\n * const instance = llm({ model: anthropic('...'), system, params });\n * const turn = await instance.generate(messages);\n * ```\n */\nexport function parseBody(body: unknown): ParsedRequest {\n if (!body || typeof body !== 'object') {\n throw new Error('Request body must be an object');\n }\n\n const data = body as Record<string, unknown>;\n\n if (!Array.isArray(data.messages)) {\n throw new Error('Request body must have a messages array');\n }\n\n return {\n messages: (data.messages as MessageJSON[]).map(deserializeMessage),\n system: data.system as string | unknown[] | undefined,\n params: data.params as Record<string, unknown> | undefined,\n tools: data.tools as ParsedRequest['tools'],\n structure: data.structure as JSONSchema | undefined,\n };\n}\n\n/**\n * Create a JSON Response from a Turn.\n *\n * @param turn - The completed inference turn\n * @returns HTTP Response with JSON body\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return toJSON(turn);\n * ```\n */\nexport function toJSON(turn: Turn): Response {\n return new Response(JSON.stringify(serializeTurn(turn)), {\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n/**\n * Create an SSE Response from a StreamResult.\n *\n * Streams PP StreamEvents as SSE, then sends the final Turn data.\n *\n * @param stream - The StreamResult from instance.stream()\n * @returns HTTP Response with SSE body\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return toSSE(stream);\n * ```\n */\nexport function toSSE(stream: StreamResult): Response {\n const encoder = new TextEncoder();\n\n const readable = new ReadableStream({\n async start(controller) {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n const data = `data: ${JSON.stringify(serialized)}\\n\\n`;\n controller.enqueue(encoder.encode(data));\n }\n\n // Send the final turn data\n const turn = await stream.turn;\n const turnData = serializeTurn(turn);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(turnData)}\\n\\n`));\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n controller.close();\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n controller.enqueue(encoder.encode(`data: {\"error\":\"${errorMsg}\"}\\n\\n`));\n controller.close();\n }\n },\n });\n\n return new Response(readable, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Create an error Response.\n *\n * @param message - Error message\n * @param status - HTTP status code (default: 500)\n * @returns HTTP Response with error body\n */\nexport function toError(message: string, status = 500): Response {\n return new Response(JSON.stringify({ error: message }), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n/**\n * Bind tool schemas to implementation functions.\n *\n * Takes tool schemas from the request and binds them to your\n * server-side implementations.\n *\n * @param schemas - Tool schemas from the request\n * @param implementations - Map of tool name to implementation\n * @returns Array of complete Tool objects\n *\n * @example\n * ```typescript\n * const { tools: schemas } = parseBody(body);\n *\n * const tools = bindTools(schemas, {\n * get_weather: async ({ location }) => fetchWeather(location),\n * search: async ({ query }) => searchDB(query),\n * });\n *\n * const instance = llm({ model, tools });\n * ```\n */\nexport function bindTools(\n schemas: ParsedRequest['tools'],\n implementations: Record<string, (params: unknown) => unknown | Promise<unknown>>\n): Tool[] {\n if (!schemas) return [];\n\n return schemas.map((schema) => {\n const run = implementations[schema.name];\n if (!run) {\n throw new Error(`No implementation for tool: ${schema.name}`);\n }\n return { ...schema, run };\n });\n}\n\n/**\n * Web API adapter utilities.\n *\n * For use with Bun, Deno, Next.js App Router, Cloudflare Workers,\n * and other frameworks that support Web API Response.\n *\n * @example\n * ```typescript\n * import { llm, anthropic } from '@providerprotocol/ai';\n * import { parseBody, toJSON, toSSE } from '@providerprotocol/ai/proxy';\n *\n * // Bun.serve / Deno.serve / Next.js App Router\n * export async function POST(req: Request) {\n * const { messages, system } = parseBody(await req.json());\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * if (req.headers.get('accept')?.includes('text/event-stream')) {\n * return toSSE(instance.stream(messages));\n * }\n * return toJSON(await instance.generate(messages));\n * }\n * ```\n */\nexport const webapi = {\n parseBody,\n toJSON,\n toSSE,\n toError,\n bindTools,\n};\n","/**\n * @fileoverview Framework adapters for proxy server utilities.\n *\n * Provides framework-specific adapters for using PP proxy with various\n * server frameworks. The base Web API utilities (toJSON, toSSE) work with\n * modern frameworks like Bun, Deno, and Next.js App Router. These adapters\n * provide native integration for Express, Fastify, and H3/Nuxt.\n *\n * @module providers/proxy/server\n */\n\nimport { express } from './express.ts';\nimport { fastify } from './fastify.ts';\nimport { h3 } from './h3.ts';\nimport { webapi, parseBody, toJSON, toSSE, toError, bindTools } from './webapi.ts';\n\nexport { express, fastify, h3, webapi };\nexport { parseBody, toJSON, toSSE, toError, bindTools };\nexport type { ParsedRequest } from './webapi.ts';\n\nexport type {\n ParsedBody,\n ProxyHandler,\n RequestMeta,\n AdapterOptions,\n} from './types.ts';\n\n/**\n * Server adapters namespace.\n *\n * Contains framework-specific adapters for Web API, Express, Fastify, and H3.\n *\n * @example Express\n * ```typescript\n * import { express } from '@providerprotocol/ai/proxy/server';\n *\n * app.post('/api/ai', async (req, res) => {\n * const { messages } = parseBody(req.body);\n * if (req.headers.accept?.includes('text/event-stream')) {\n * express.streamSSE(instance.stream(messages), res);\n * } else {\n * express.sendJSON(await instance.generate(messages), res);\n * }\n * });\n * ```\n *\n * @example Fastify\n * ```typescript\n * import { fastify } from '@providerprotocol/ai/proxy/server';\n *\n * app.post('/api/ai', async (request, reply) => {\n * const { messages } = parseBody(request.body);\n * if (request.headers.accept?.includes('text/event-stream')) {\n * return fastify.streamSSE(instance.stream(messages), reply);\n * }\n * return fastify.sendJSON(await instance.generate(messages), reply);\n * });\n * ```\n *\n * @example H3/Nuxt\n * ```typescript\n * import { h3 } from '@providerprotocol/ai/proxy/server';\n *\n * export default defineEventHandler(async (event) => {\n * const { messages } = parseBody(await readBody(event));\n * if (getHeader(event, 'accept')?.includes('text/event-stream')) {\n * return h3.streamSSE(instance.stream(messages), event);\n * }\n * return h3.sendJSON(await instance.generate(messages), event);\n * });\n * ```\n */\nexport const server = {\n /** Web API adapter (Bun, Deno, Next.js, Workers) */\n webapi,\n /** Express/Connect adapter */\n express,\n /** Fastify adapter */\n fastify,\n /** H3/Nitro/Nuxt adapter */\n h3,\n};\n","import { createProvider } from '../../core/provider.ts';\nimport type { ModelReference } from '../../types/provider.ts';\nimport { createLLMHandler } from './llm.ts';\nimport type { ProxyProviderOptions, ProxyRequestOptions } from './types.ts';\n\n/**\n * Creates a proxy provider that transports PP requests over HTTP to a backend server.\n *\n * The proxy acts as a pure transport layer - PP types go in, PP types come out.\n * The modelId is passed through to the backend, which decides which actual model to use.\n *\n * @param options - Configuration for the proxy endpoint\n * @returns A provider that can be used with llm()\n *\n * @example\n * ```typescript\n * import { proxy } from './providers/proxy';\n * import { llm } from './core/llm';\n *\n * const backend = proxy({ endpoint: '/api/ai' });\n *\n * const model = llm({\n * model: backend('gpt-4o'),\n * system: 'You are a helpful assistant.',\n * });\n *\n * const turn = await model.generate('Hello!');\n * ```\n */\nexport function proxy(options: ProxyProviderOptions) {\n return createProvider<ProxyRequestOptions>({\n name: 'proxy',\n version: '1.0.0',\n modalities: {\n llm: createLLMHandler(options),\n },\n });\n}\n\n/**\n * Shorthand for creating a proxy model reference with default model ID.\n *\n * Creates a proxy provider and immediately returns a model reference using\n * 'default' as the model identifier. Useful for simple single-endpoint setups.\n *\n * @param endpoint - The URL to proxy requests to\n * @returns A model reference for use with llm()\n *\n * @example\n * ```typescript\n * import { proxyModel } from './providers/proxy';\n * import { llm } from './core/llm';\n *\n * const model = llm({ model: proxyModel('/api/ai') });\n * const turn = await model.generate('Hello!');\n * ```\n */\nexport function proxyModel(endpoint: string): ModelReference<ProxyRequestOptions> {\n return proxy({ endpoint })('default');\n}\n\n// Re-export types\nexport type {\n ProxyLLMParams,\n ProxyProviderOptions,\n ProxyRequestOptions,\n} from './types.ts';\n\n// Re-export serialization utilities\nexport {\n serializeMessage,\n deserializeMessage,\n serializeTurn,\n serializeStreamEvent,\n deserializeStreamEvent,\n} from './serialization.ts';\n\n// Re-export server adapters\nexport { server, express, fastify, h3 } from './server/index.ts';\nexport type { ParsedBody, ProxyHandler, RequestMeta, AdapterOptions } from './server/index.ts';\n"],"mappings":"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","names":["sendJSON","streamSSE","sendError","sendJSON","streamSSE","sendError"]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/proxy/serialization.ts","../../src/providers/proxy/llm.ts","../../src/providers/proxy/server/express.ts","../../src/providers/proxy/server/fastify.ts","../../src/providers/proxy/server/h3.ts","../../src/providers/proxy/server/webapi.ts","../../src/providers/proxy/server/index.ts","../../src/providers/proxy/index.ts"],"sourcesContent":["/**\n * @fileoverview Serialization utilities for proxy transport.\n *\n * Handles converting PP types to/from JSON for HTTP transport.\n * These are pure functions with no side effects.\n *\n * @module providers/proxy/serialization\n */\n\nimport {\n UserMessage,\n AssistantMessage,\n ToolResultMessage,\n type Message,\n type MessageJSON,\n} from '../../types/messages.ts';\nimport type { UserContent, AssistantContent } from '../../types/content.ts';\nimport type { StreamEvent, EventDelta } from '../../types/stream.ts';\nimport type { Turn, TurnJSON } from '../../types/turn.ts';\n\n/**\n * Convert a Message to MessageJSON format.\n */\nexport function serializeMessage(m: Message): MessageJSON {\n const base: MessageJSON = {\n id: m.id,\n type: m.type,\n content: [],\n metadata: m.metadata,\n timestamp: m.timestamp.toISOString(),\n };\n\n if (m instanceof UserMessage) {\n base.content = m.content;\n } else if (m instanceof AssistantMessage) {\n base.content = m.content;\n base.toolCalls = m.toolCalls;\n } else if (m instanceof ToolResultMessage) {\n base.results = m.results;\n }\n\n return base;\n}\n\n/**\n * Reconstruct a Message from MessageJSON format.\n */\nexport function deserializeMessage(json: MessageJSON): Message {\n const options = {\n id: json.id,\n metadata: json.metadata,\n };\n\n switch (json.type) {\n case 'user':\n return new UserMessage(json.content as UserContent[], options);\n case 'assistant':\n return new AssistantMessage(\n json.content as AssistantContent[],\n json.toolCalls,\n options\n );\n case 'tool_result':\n return new ToolResultMessage(json.results ?? [], options);\n default:\n throw new Error(`Unknown message type: ${json.type}`);\n }\n}\n\n/**\n * Serialize a Turn to JSON-transportable format.\n */\nexport function serializeTurn(turn: Turn): TurnJSON {\n return {\n messages: turn.messages.map(serializeMessage),\n toolExecutions: turn.toolExecutions,\n usage: turn.usage,\n cycles: turn.cycles,\n data: turn.data,\n };\n}\n\n/**\n * Serialize a StreamEvent for JSON transport.\n * Converts Uint8Array data to base64 string.\n */\nexport function serializeStreamEvent(event: StreamEvent): StreamEvent {\n if (event.delta.data instanceof Uint8Array) {\n const { data, ...rest } = event.delta;\n const bytes = Array.from(data);\n const base64 = btoa(bytes.map((b) => String.fromCharCode(b)).join(''));\n return {\n type: event.type,\n index: event.index,\n delta: { ...rest, data: base64 as unknown as Uint8Array },\n };\n }\n return event;\n}\n\n/**\n * Deserialize a StreamEvent from JSON transport.\n * Converts base64 string data back to Uint8Array.\n */\nexport function deserializeStreamEvent(event: StreamEvent): StreamEvent {\n const delta = event.delta as EventDelta & { data?: string | Uint8Array };\n if (typeof delta.data === 'string') {\n const binaryString = atob(delta.data);\n const bytes = Uint8Array.from(binaryString, (c) => c.charCodeAt(0));\n return {\n type: event.type,\n index: event.index,\n delta: { ...delta, data: bytes },\n };\n }\n return event;\n}\n","/**\n * @fileoverview Proxy LLM handler implementation.\n *\n * Transports PP LLM requests over HTTP to a backend server.\n * Supports both synchronous completion and streaming via SSE.\n * Full support for retry strategies, timeouts, and custom headers.\n *\n * @module providers/proxy/llm\n */\n\nimport type {\n LLMHandler,\n BoundLLMModel,\n LLMRequest,\n LLMResponse,\n LLMStreamResult,\n LLMCapabilities,\n} from '../../types/llm.ts';\nimport type { LLMProvider } from '../../types/provider.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { TurnJSON } from '../../types/turn.ts';\nimport { AssistantMessage } from '../../types/messages.ts';\nimport { emptyUsage } from '../../types/turn.ts';\nimport { UPPError } from '../../types/errors.ts';\nimport { doFetch, doStreamFetch } from '../../http/fetch.ts';\nimport { normalizeHttpError } from '../../http/errors.ts';\nimport type { ProxyLLMParams, ProxyProviderOptions } from './types.ts';\nimport {\n serializeMessage,\n deserializeMessage,\n deserializeStreamEvent,\n} from './serialization.ts';\n\n/**\n * Capability flags for proxy provider.\n * All capabilities are enabled since the backend determines actual support.\n */\nconst PROXY_CAPABILITIES: LLMCapabilities = {\n streaming: true,\n tools: true,\n structuredOutput: true,\n imageInput: true,\n videoInput: true,\n audioInput: true,\n};\n\n/**\n * Creates a proxy LLM handler.\n *\n * Supports full ProviderConfig options including retry strategies, timeouts,\n * custom headers, and custom fetch implementations. This allows client-side\n * retry logic for network failures to the proxy server.\n *\n * @param options - Proxy configuration options\n * @returns An LLM handler that transports requests over HTTP\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { proxy } from '@providerprotocol/ai/proxy';\n * import { ExponentialBackoff } from '@providerprotocol/ai/http';\n *\n * const claude = llm({\n * model: proxy('https://api.myplatform.com/ai'),\n * config: {\n * headers: { 'Authorization': 'Bearer user-token' },\n * retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),\n * timeout: 30000,\n * },\n * });\n * ```\n */\nexport function createLLMHandler(options: ProxyProviderOptions): LLMHandler<ProxyLLMParams> {\n const { endpoint, headers: defaultHeaders = {} } = options;\n\n let providerRef: LLMProvider<ProxyLLMParams> | null = null;\n\n return {\n _setProvider(provider: LLMProvider<ProxyLLMParams>) {\n providerRef = provider;\n },\n\n bind(modelId: string): BoundLLMModel<ProxyLLMParams> {\n if (!providerRef) {\n throw new UPPError(\n 'Provider reference not set. Handler must be used with createProvider().',\n 'INVALID_REQUEST',\n 'proxy',\n 'llm'\n );\n }\n\n const model: BoundLLMModel<ProxyLLMParams> = {\n modelId,\n capabilities: PROXY_CAPABILITIES,\n\n get provider(): LLMProvider<ProxyLLMParams> {\n return providerRef!;\n },\n\n async complete(request: LLMRequest<ProxyLLMParams>): Promise<LLMResponse> {\n const body = serializeRequest(request);\n const headers = mergeHeaders(request.config.headers, defaultHeaders);\n\n const response = await doFetch(\n endpoint,\n {\n method: 'POST',\n headers: {\n ...headers,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'proxy',\n 'llm'\n );\n\n const data = (await response.json()) as TurnJSON;\n return turnJSONToLLMResponse(data);\n },\n\n stream(request: LLMRequest<ProxyLLMParams>): LLMStreamResult {\n const body = serializeRequest(request);\n const headers = mergeHeaders(request.config.headers, defaultHeaders);\n\n let resolveResponse: (value: LLMResponse) => void;\n let rejectResponse: (error: Error) => void;\n const responsePromise = new Promise<LLMResponse>((resolve, reject) => {\n resolveResponse = resolve;\n rejectResponse = reject;\n });\n\n const generator = async function* (): AsyncGenerator<StreamEvent> {\n try {\n const response = await doStreamFetch(\n endpoint,\n {\n method: 'POST',\n headers: {\n ...headers,\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n },\n body: JSON.stringify(body),\n signal: request.signal,\n },\n request.config,\n 'proxy',\n 'llm'\n );\n\n if (!response.ok) {\n throw await normalizeHttpError(response, 'proxy', 'llm');\n }\n\n if (!response.body) {\n throw new UPPError(\n 'Response body is null',\n 'PROVIDER_ERROR',\n 'proxy',\n 'llm'\n );\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim() || line.startsWith(':')) continue;\n\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') continue;\n\n try {\n const parsed = JSON.parse(data);\n\n // Check if this is the final turn data\n if ('messages' in parsed && 'usage' in parsed && 'cycles' in parsed) {\n resolveResponse(turnJSONToLLMResponse(parsed as TurnJSON));\n } else {\n // It's a StreamEvent\n yield deserializeStreamEvent(parsed as StreamEvent);\n }\n } catch {\n // Skip malformed JSON\n }\n }\n }\n }\n } catch (error) {\n rejectResponse(error instanceof Error ? error : new Error(String(error)));\n throw error;\n }\n };\n\n return {\n [Symbol.asyncIterator]: generator,\n response: responsePromise,\n };\n },\n };\n\n return model;\n },\n };\n}\n\n/**\n * Serialize an LLMRequest for HTTP transport.\n */\nfunction serializeRequest(request: LLMRequest<ProxyLLMParams>): Record<string, unknown> {\n return {\n messages: request.messages.map(serializeMessage),\n system: request.system,\n params: request.params,\n tools: request.tools?.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n metadata: t.metadata,\n })),\n structure: request.structure,\n };\n}\n\n/**\n * Merge request headers with default headers.\n */\nfunction mergeHeaders(\n requestHeaders: Record<string, string | undefined> | undefined,\n defaultHeaders: Record<string, string>\n): Record<string, string> {\n const headers: Record<string, string> = { ...defaultHeaders };\n if (requestHeaders) {\n for (const [key, value] of Object.entries(requestHeaders)) {\n if (value !== undefined) {\n headers[key] = value;\n }\n }\n }\n return headers;\n}\n\n/**\n * Convert TurnJSON to LLMResponse.\n */\nfunction turnJSONToLLMResponse(data: TurnJSON): LLMResponse {\n const messages = data.messages.map(deserializeMessage);\n const lastAssistant = messages\n .filter((m): m is AssistantMessage => m.type === 'assistant')\n .pop();\n\n return {\n message: lastAssistant ?? new AssistantMessage(''),\n usage: data.usage ?? emptyUsage(),\n stopReason: 'stop',\n data: data.data,\n };\n}\n\n","/**\n * @fileoverview Express/Connect adapter for proxy server.\n *\n * Provides utilities for using PP proxy with Express.js or Connect-based servers.\n * These adapters convert PP types to Express-compatible responses.\n *\n * @module providers/proxy/server/express\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\n\n/**\n * Express Response interface (minimal type to avoid dependency).\n */\ninterface ExpressResponse {\n setHeader(name: string, value: string): void;\n status(code: number): ExpressResponse;\n write(chunk: string): boolean;\n end(): void;\n json(body: unknown): void;\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param res - Express response object\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * expressAdapter.sendJSON(turn, res);\n * ```\n */\nexport function sendJSON(turn: Turn, res: ExpressResponse): void {\n res.setHeader('Content-Type', 'application/json');\n res.json(serializeTurn(turn));\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param res - Express response object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * expressAdapter.streamSSE(stream, res);\n * ```\n */\nexport function streamSSE(stream: StreamResult, res: ExpressResponse): void {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n (async () => {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n res.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n res.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n res.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n res.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param res - Express response object\n */\nexport function sendError(message: string, status: number, res: ExpressResponse): void {\n res.status(status).json({ error: message });\n}\n\n/**\n * Express adapter utilities.\n *\n * @example Basic usage\n * ```typescript\n * import express from 'express';\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { express as expressAdapter } from '@providerprotocol/ai/proxy/server';\n *\n * const app = express();\n * app.use(express.json());\n *\n * app.post('/api/ai', async (req, res) => {\n * const { messages, system, params } = parseBody(req.body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * if (req.headers.accept?.includes('text/event-stream')) {\n * expressAdapter.streamSSE(instance.stream(messages), res);\n * } else {\n * const turn = await instance.generate(messages);\n * expressAdapter.sendJSON(turn, res);\n * }\n * });\n * ```\n *\n * @example API Gateway with authentication\n * ```typescript\n * import express from 'express';\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { express as expressAdapter } from '@providerprotocol/ai/proxy/server';\n *\n * const app = express();\n * app.use(express.json());\n *\n * // Your platform's auth middleware\n * async function authMiddleware(req, res, next) {\n * const token = req.headers.authorization?.replace('Bearer ', '');\n * const user = await validatePlatformToken(token);\n * if (!user) return res.status(401).json({ error: 'Unauthorized' });\n * req.user = user;\n * next();\n * }\n *\n * // Server manages AI provider keys - users never see them\n * const claude = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * config: {\n * apiKey: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),\n * retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),\n * },\n * });\n *\n * app.post('/api/ai', authMiddleware, async (req, res) => {\n * // Track usage per user\n * // await trackUsage(req.user.id);\n *\n * const { messages, system, params } = parseBody(req.body);\n *\n * if (params?.stream) {\n * expressAdapter.streamSSE(claude.stream(messages, { system }), res);\n * } else {\n * const turn = await claude.generate(messages, { system });\n * expressAdapter.sendJSON(turn, res);\n * }\n * });\n * ```\n */\nexport const express = {\n sendJSON,\n streamSSE,\n sendError,\n};\n","/**\n * @fileoverview Fastify adapter for proxy server.\n *\n * Provides utilities for using PP proxy with Fastify servers.\n * These adapters convert PP types to Fastify-compatible responses.\n *\n * @module providers/proxy/server/fastify\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\n\n/**\n * Fastify Reply interface (minimal type to avoid dependency).\n */\ninterface FastifyReply {\n header(name: string, value: string): FastifyReply;\n status(code: number): FastifyReply;\n send(payload: unknown): FastifyReply;\n raw: {\n write(chunk: string): boolean;\n end(): void;\n };\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param reply - Fastify reply object\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return fastifyAdapter.sendJSON(turn, reply);\n * ```\n */\nexport function sendJSON(turn: Turn, reply: FastifyReply): FastifyReply {\n return reply\n .header('Content-Type', 'application/json')\n .send(serializeTurn(turn));\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param reply - Fastify reply object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return fastifyAdapter.streamSSE(stream, reply);\n * ```\n */\nexport function streamSSE(stream: StreamResult, reply: FastifyReply): FastifyReply {\n reply\n .header('Content-Type', 'text/event-stream')\n .header('Cache-Control', 'no-cache')\n .header('Connection', 'keep-alive');\n\n const raw = reply.raw;\n\n (async () => {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n raw.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n raw.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n raw.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n raw.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n raw.end();\n }\n })();\n\n return reply;\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param reply - Fastify reply object\n */\nexport function sendError(message: string, status: number, reply: FastifyReply): FastifyReply {\n return reply.status(status).send({ error: message });\n}\n\n/**\n * Fastify adapter utilities.\n *\n * @example Basic usage\n * ```typescript\n * import Fastify from 'fastify';\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { fastify as fastifyAdapter } from '@providerprotocol/ai/proxy/server';\n *\n * const app = Fastify();\n *\n * app.post('/api/ai', async (request, reply) => {\n * const { messages, system, params } = parseBody(request.body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * if (request.headers.accept?.includes('text/event-stream')) {\n * return fastifyAdapter.streamSSE(instance.stream(messages), reply);\n * } else {\n * const turn = await instance.generate(messages);\n * return fastifyAdapter.sendJSON(turn, reply);\n * }\n * });\n * ```\n *\n * @example API Gateway with authentication\n * ```typescript\n * import Fastify from 'fastify';\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { fastify as fastifyAdapter } from '@providerprotocol/ai/proxy/server';\n *\n * const app = Fastify();\n *\n * // Server manages AI provider keys - users never see them\n * const claude = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * config: {\n * apiKey: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),\n * retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),\n * },\n * });\n *\n * // Auth hook for your platform\n * app.addHook('preHandler', async (request, reply) => {\n * const token = request.headers.authorization?.replace('Bearer ', '');\n * const user = await validatePlatformToken(token);\n * if (!user) {\n * reply.status(401).send({ error: 'Unauthorized' });\n * return;\n * }\n * request.user = user;\n * });\n *\n * app.post('/api/ai', async (request, reply) => {\n * // Track usage per user\n * // await trackUsage(request.user.id);\n *\n * const { messages, system, params } = parseBody(request.body);\n *\n * if (params?.stream) {\n * return fastifyAdapter.streamSSE(claude.stream(messages, { system }), reply);\n * }\n * const turn = await claude.generate(messages, { system });\n * return fastifyAdapter.sendJSON(turn, reply);\n * });\n * ```\n */\nexport const fastify = {\n sendJSON,\n streamSSE,\n sendError,\n};\n","/**\n * @fileoverview H3/Nitro/Nuxt adapter for proxy server.\n *\n * Provides utilities for using PP proxy with H3-based servers\n * (Nuxt, Nitro, or standalone H3).\n *\n * @module providers/proxy/server/h3\n */\n\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport { serializeTurn, serializeStreamEvent } from '../serialization.ts';\n\n/**\n * H3 Event interface (minimal type to avoid dependency).\n */\ninterface H3Event {\n node: {\n res: {\n setHeader(name: string, value: string): void;\n write(chunk: string): boolean;\n end(): void;\n };\n };\n}\n\n/**\n * Send a Turn as JSON response.\n *\n * @param turn - The completed inference turn\n * @param event - H3 event object\n * @returns Serialized turn data\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return h3Adapter.sendJSON(turn, event);\n * ```\n */\nexport function sendJSON(turn: Turn, event: H3Event): unknown {\n event.node.res.setHeader('Content-Type', 'application/json');\n return serializeTurn(turn);\n}\n\n/**\n * Stream a StreamResult as Server-Sent Events.\n *\n * @param stream - The StreamResult from instance.stream()\n * @param event - H3 event object\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return h3Adapter.streamSSE(stream, event);\n * ```\n */\nexport function streamSSE(stream: StreamResult, event: H3Event): void {\n const res = event.node.res;\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n (async () => {\n try {\n for await (const evt of stream) {\n const serialized = serializeStreamEvent(evt);\n res.write(`data: ${JSON.stringify(serialized)}\\n\\n`);\n }\n\n const turn = await stream.turn;\n res.write(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`);\n res.write('data: [DONE]\\n\\n');\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n res.write(`data: ${JSON.stringify({ error: message })}\\n\\n`);\n } finally {\n res.end();\n }\n })();\n}\n\n/**\n * Create a ReadableStream for H3's sendStream utility.\n *\n * Use this with H3's sendStream for better integration:\n * ```typescript\n * import { sendStream } from 'h3';\n * return sendStream(event, h3Adapter.createSSEStream(stream));\n * ```\n *\n * @param stream - The StreamResult from instance.stream()\n * @returns A ReadableStream of SSE data\n */\nexport function createSSEStream(stream: StreamResult): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serialized)}\\n\\n`));\n }\n\n const turn = await stream.turn;\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(serializeTurn(turn))}\\n\\n`));\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ error: message })}\\n\\n`));\n } finally {\n controller.close();\n }\n },\n });\n}\n\n/**\n * Send an error response.\n *\n * @param message - Error message\n * @param status - HTTP status code\n * @param event - H3 event object\n * @returns Error object for H3 to serialize\n */\nexport function sendError(message: string, status: number, event: H3Event): { error: string; statusCode: number } {\n return { error: message, statusCode: status };\n}\n\n/**\n * H3/Nitro/Nuxt adapter utilities.\n *\n * @example Basic usage\n * ```typescript\n * // Nuxt server route: server/api/ai.post.ts\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';\n *\n * export default defineEventHandler(async (event) => {\n * const body = await readBody(event);\n * const { messages, system, params } = parseBody(body);\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * const wantsStream = getHeader(event, 'accept')?.includes('text/event-stream');\n * if (wantsStream) {\n * return h3Adapter.streamSSE(instance.stream(messages), event);\n * } else {\n * const turn = await instance.generate(messages);\n * return h3Adapter.sendJSON(turn, event);\n * }\n * });\n * ```\n *\n * @example API Gateway with authentication (Nuxt)\n * ```typescript\n * // server/api/ai.post.ts\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';\n * import { parseBody } from '@providerprotocol/ai/proxy';\n * import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';\n *\n * // Server manages AI provider keys - users never see them\n * const claude = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * config: {\n * apiKey: new RoundRobinKeys([\n * process.env.ANTHROPIC_KEY_1!,\n * process.env.ANTHROPIC_KEY_2!,\n * ]),\n * retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),\n * },\n * });\n *\n * export default defineEventHandler(async (event) => {\n * // Authenticate with your platform credentials\n * const token = getHeader(event, 'authorization')?.replace('Bearer ', '');\n * const user = await validatePlatformToken(token);\n * if (!user) {\n * throw createError({ statusCode: 401, message: 'Unauthorized' });\n * }\n *\n * // Track usage per user\n * // await trackUsage(user.id);\n *\n * const body = await readBody(event);\n * const { messages, system, params } = parseBody(body);\n *\n * if (params?.stream) {\n * return h3Adapter.streamSSE(claude.stream(messages, { system }), event);\n * }\n * const turn = await claude.generate(messages, { system });\n * return h3Adapter.sendJSON(turn, event);\n * });\n * ```\n */\nexport const h3 = {\n sendJSON,\n streamSSE,\n createSSEStream,\n sendError,\n};\n","/**\n * @fileoverview Web API adapter for proxy server.\n *\n * Provides utilities for using PP proxy with Web API native frameworks\n * (Bun, Deno, Next.js App Router, Cloudflare Workers).\n *\n * These utilities return standard Web API Response objects that work\n * directly with modern runtimes.\n *\n * @module providers/proxy/server/webapi\n */\n\nimport type { Message } from '../../../types/messages.ts';\nimport type { Turn } from '../../../types/turn.ts';\nimport type { StreamResult } from '../../../types/stream.ts';\nimport type { MessageJSON } from '../../../types/thread.ts';\nimport type { JSONSchema } from '../../../types/schema.ts';\nimport type { Tool, ToolMetadata } from '../../../types/tool.ts';\nimport {\n deserializeMessage,\n serializeTurn,\n serializeStreamEvent,\n} from '../serialization.ts';\n\n/**\n * Parsed request body from a proxy HTTP request.\n * This is just the deserialized PP data from the request body.\n */\nexport interface ParsedRequest {\n messages: Message[];\n system?: string | unknown[];\n params?: Record<string, unknown>;\n tools?: Array<{\n name: string;\n description: string;\n parameters: JSONSchema;\n metadata?: ToolMetadata;\n }>;\n structure?: JSONSchema;\n}\n\n/**\n * Parse an HTTP request body into PP types.\n *\n * @param body - The JSON-parsed request body\n * @returns Deserialized PP data\n *\n * @example\n * ```typescript\n * const body = await req.json();\n * const { messages, system, params } = parseBody(body);\n *\n * const instance = llm({ model: anthropic('...'), system, params });\n * const turn = await instance.generate(messages);\n * ```\n */\nexport function parseBody(body: unknown): ParsedRequest {\n if (!body || typeof body !== 'object') {\n throw new Error('Request body must be an object');\n }\n\n const data = body as Record<string, unknown>;\n\n if (!Array.isArray(data.messages)) {\n throw new Error('Request body must have a messages array');\n }\n\n return {\n messages: (data.messages as MessageJSON[]).map(deserializeMessage),\n system: data.system as string | unknown[] | undefined,\n params: data.params as Record<string, unknown> | undefined,\n tools: data.tools as ParsedRequest['tools'],\n structure: data.structure as JSONSchema | undefined,\n };\n}\n\n/**\n * Create a JSON Response from a Turn.\n *\n * @param turn - The completed inference turn\n * @returns HTTP Response with JSON body\n *\n * @example\n * ```typescript\n * const turn = await instance.generate(messages);\n * return toJSON(turn);\n * ```\n */\nexport function toJSON(turn: Turn): Response {\n return new Response(JSON.stringify(serializeTurn(turn)), {\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n/**\n * Create an SSE Response from a StreamResult.\n *\n * Streams PP StreamEvents as SSE, then sends the final Turn data.\n *\n * @param stream - The StreamResult from instance.stream()\n * @returns HTTP Response with SSE body\n *\n * @example\n * ```typescript\n * const stream = instance.stream(messages);\n * return toSSE(stream);\n * ```\n */\nexport function toSSE(stream: StreamResult): Response {\n const encoder = new TextEncoder();\n\n const readable = new ReadableStream({\n async start(controller) {\n try {\n for await (const event of stream) {\n const serialized = serializeStreamEvent(event);\n const data = `data: ${JSON.stringify(serialized)}\\n\\n`;\n controller.enqueue(encoder.encode(data));\n }\n\n // Send the final turn data\n const turn = await stream.turn;\n const turnData = serializeTurn(turn);\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(turnData)}\\n\\n`));\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n controller.close();\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n controller.enqueue(encoder.encode(`data: {\"error\":\"${errorMsg}\"}\\n\\n`));\n controller.close();\n }\n },\n });\n\n return new Response(readable, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\n/**\n * Create an error Response.\n *\n * @param message - Error message\n * @param status - HTTP status code (default: 500)\n * @returns HTTP Response with error body\n */\nexport function toError(message: string, status = 500): Response {\n return new Response(JSON.stringify({ error: message }), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n/**\n * Bind tool schemas to implementation functions.\n *\n * Takes tool schemas from the request and binds them to your\n * server-side implementations.\n *\n * @param schemas - Tool schemas from the request\n * @param implementations - Map of tool name to implementation\n * @returns Array of complete Tool objects\n *\n * @example\n * ```typescript\n * const { tools: schemas } = parseBody(body);\n *\n * const tools = bindTools(schemas, {\n * get_weather: async ({ location }) => fetchWeather(location),\n * search: async ({ query }) => searchDB(query),\n * });\n *\n * const instance = llm({ model, tools });\n * ```\n */\nexport function bindTools(\n schemas: ParsedRequest['tools'],\n implementations: Record<string, (params: unknown) => unknown | Promise<unknown>>\n): Tool[] {\n if (!schemas) return [];\n\n return schemas.map((schema) => {\n const run = implementations[schema.name];\n if (!run) {\n throw new Error(`No implementation for tool: ${schema.name}`);\n }\n return { ...schema, run };\n });\n}\n\n/**\n * Web API adapter utilities.\n *\n * For use with Bun, Deno, Next.js App Router, Cloudflare Workers,\n * and other frameworks that support Web API Response.\n *\n * **Security Note:** The proxy works without configuration, meaning no\n * authentication by default. Always add your own auth layer in production.\n *\n * @example Basic usage\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { parseBody, toJSON, toSSE } from '@providerprotocol/ai/proxy';\n *\n * // Bun.serve / Deno.serve / Next.js App Router\n * export async function POST(req: Request) {\n * const { messages, system } = parseBody(await req.json());\n * const instance = llm({ model: anthropic('claude-sonnet-4-20250514'), system });\n *\n * if (req.headers.get('accept')?.includes('text/event-stream')) {\n * return toSSE(instance.stream(messages));\n * }\n * return toJSON(await instance.generate(messages));\n * }\n * ```\n *\n * @example API Gateway with authentication\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';\n * import { parseBody, toJSON, toSSE, toError } from '@providerprotocol/ai/proxy';\n *\n * // Your platform's user validation\n * async function validateToken(token: string): Promise<{ id: string } | null> {\n * // Verify JWT, check database, etc.\n * return token ? { id: 'user-123' } : null;\n * }\n *\n * // Server manages AI provider keys - users never see them\n * const claude = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * config: {\n * apiKey: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),\n * retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),\n * },\n * });\n *\n * Bun.serve({\n * port: 3000,\n * async fetch(req) {\n * // Authenticate with YOUR platform credentials\n * const token = req.headers.get('Authorization')?.replace('Bearer ', '');\n * const user = await validateToken(token ?? '');\n * if (!user) return toError('Unauthorized', 401);\n *\n * // Rate limit, track usage, bill user, etc.\n * // await trackUsage(user.id);\n *\n * const { messages, system, params } = parseBody(await req.json());\n *\n * if (params?.stream) {\n * return toSSE(claude.stream(messages, { system }));\n * }\n * return toJSON(await claude.generate(messages, { system }));\n * },\n * });\n * ```\n */\nexport const webapi = {\n parseBody,\n toJSON,\n toSSE,\n toError,\n bindTools,\n};\n","/**\n * @fileoverview Framework adapters for proxy server utilities.\n *\n * Provides framework-specific adapters for using PP proxy with various\n * server frameworks. The base Web API utilities (toJSON, toSSE) work with\n * modern frameworks like Bun, Deno, and Next.js App Router. These adapters\n * provide native integration for Express, Fastify, and H3/Nuxt.\n *\n * @module providers/proxy/server\n */\n\nimport { express } from './express.ts';\nimport { fastify } from './fastify.ts';\nimport { h3 } from './h3.ts';\nimport { webapi, parseBody, toJSON, toSSE, toError, bindTools } from './webapi.ts';\n\nexport { express, fastify, h3, webapi };\nexport { parseBody, toJSON, toSSE, toError, bindTools };\nexport type { ParsedRequest } from './webapi.ts';\n\nexport type {\n ParsedBody,\n ProxyHandler,\n RequestMeta,\n AdapterOptions,\n} from './types.ts';\n\n/**\n * Server adapters namespace.\n *\n * Contains framework-specific adapters for Web API, Express, Fastify, and H3.\n *\n * @example Express\n * ```typescript\n * import { express } from '@providerprotocol/ai/proxy/server';\n *\n * app.post('/api/ai', async (req, res) => {\n * const { messages } = parseBody(req.body);\n * if (req.headers.accept?.includes('text/event-stream')) {\n * express.streamSSE(instance.stream(messages), res);\n * } else {\n * express.sendJSON(await instance.generate(messages), res);\n * }\n * });\n * ```\n *\n * @example Fastify\n * ```typescript\n * import { fastify } from '@providerprotocol/ai/proxy/server';\n *\n * app.post('/api/ai', async (request, reply) => {\n * const { messages } = parseBody(request.body);\n * if (request.headers.accept?.includes('text/event-stream')) {\n * return fastify.streamSSE(instance.stream(messages), reply);\n * }\n * return fastify.sendJSON(await instance.generate(messages), reply);\n * });\n * ```\n *\n * @example H3/Nuxt\n * ```typescript\n * import { h3 } from '@providerprotocol/ai/proxy/server';\n *\n * export default defineEventHandler(async (event) => {\n * const { messages } = parseBody(await readBody(event));\n * if (getHeader(event, 'accept')?.includes('text/event-stream')) {\n * return h3.streamSSE(instance.stream(messages), event);\n * }\n * return h3.sendJSON(await instance.generate(messages), event);\n * });\n * ```\n */\nexport const server = {\n /** Web API adapter (Bun, Deno, Next.js, Workers) */\n webapi,\n /** Express/Connect adapter */\n express,\n /** Fastify adapter */\n fastify,\n /** H3/Nitro/Nuxt adapter */\n h3,\n};\n","import { createProvider } from '../../core/provider.ts';\nimport type { ModelReference } from '../../types/provider.ts';\nimport { createLLMHandler } from './llm.ts';\nimport type { ProxyProviderOptions, ProxyRequestOptions } from './types.ts';\n\n/**\n * Creates a proxy provider that transports PP requests over HTTP to a backend server.\n *\n * The proxy acts as a pure transport layer - PP types go in, PP types come out.\n * The modelId is passed through to the backend, which decides which actual model to use.\n *\n * @param options - Configuration for the proxy endpoint\n * @returns A provider that can be used with llm()\n *\n * @example\n * ```typescript\n * import { proxy } from './providers/proxy';\n * import { llm } from './core/llm';\n *\n * const backend = proxy({ endpoint: '/api/ai' });\n *\n * const model = llm({\n * model: backend('gpt-4o'),\n * system: 'You are a helpful assistant.',\n * });\n *\n * const turn = await model.generate('Hello!');\n * ```\n */\nexport function proxy(options: ProxyProviderOptions) {\n return createProvider<ProxyRequestOptions>({\n name: 'proxy',\n version: '1.0.0',\n modalities: {\n llm: createLLMHandler(options),\n },\n });\n}\n\n/**\n * Shorthand for creating a proxy model reference with default model ID.\n *\n * Creates a proxy provider and immediately returns a model reference using\n * 'default' as the model identifier. Useful for simple single-endpoint setups.\n *\n * @param endpoint - The URL to proxy requests to\n * @returns A model reference for use with llm()\n *\n * @example\n * ```typescript\n * import { proxyModel } from './providers/proxy';\n * import { llm } from './core/llm';\n *\n * const model = llm({ model: proxyModel('/api/ai') });\n * const turn = await model.generate('Hello!');\n * ```\n */\nexport function proxyModel(endpoint: string): ModelReference<ProxyRequestOptions> {\n return proxy({ endpoint })('default');\n}\n\n// Re-export types\nexport type {\n ProxyLLMParams,\n ProxyProviderOptions,\n ProxyRequestOptions,\n} from './types.ts';\n\n// Re-export serialization utilities\nexport {\n serializeMessage,\n deserializeMessage,\n serializeTurn,\n serializeStreamEvent,\n deserializeStreamEvent,\n} from './serialization.ts';\n\n// Re-export server adapters\nexport { server, express, fastify, h3 } from './server/index.ts';\nexport type { ParsedBody, ProxyHandler, RequestMeta, AdapterOptions } from './server/index.ts';\n"],"mappings":"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","names":["sendJSON","streamSSE","sendError","sendJSON","streamSSE","sendError"]}
|