@browserlessapi/inferlo 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/ai/ai-sdk.d.ts +48 -0
- package/dist/ai/ai-sdk.js +235 -0
- package/dist/ai/ai-sdk.js.map +1 -0
- package/dist/ai/index.d.ts +37 -3
- package/dist/ai/index.js +96 -7
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/public.d.ts +5 -1
- package/dist/ai/public.js +2 -0
- package/dist/ai/public.js.map +1 -1
- package/dist/ai/ui.d.ts +47 -0
- package/dist/ai/ui.js +33 -0
- package/dist/ai/ui.js.map +1 -0
- package/dist/app.d.ts +31 -4
- package/dist/app.js +106 -29
- package/dist/app.js.map +1 -1
- package/dist/astro.d.ts +60 -0
- package/dist/astro.js +68 -0
- package/dist/astro.js.map +1 -0
- package/dist/audit.d.ts +8 -1
- package/dist/audit.js +9 -2
- package/dist/audit.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +1 -0
- package/dist/auth.js.map +1 -1
- package/dist/azure.d.ts +66 -0
- package/dist/azure.js +119 -0
- package/dist/azure.js.map +1 -0
- package/dist/cache.d.ts +13 -9
- package/dist/cache.js +11 -9
- package/dist/cache.js.map +1 -1
- package/dist/cli/main.js +4 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/client-ip.d.ts +36 -1
- package/dist/client-ip.js +80 -1
- package/dist/client-ip.js.map +1 -1
- package/dist/db/memory.js +2 -1
- package/dist/db/memory.js.map +1 -1
- package/dist/db/postgres.js +4 -1
- package/dist/db/postgres.js.map +1 -1
- package/dist/db/table.d.ts +37 -5
- package/dist/db/table.js +96 -15
- package/dist/db/table.js.map +1 -1
- package/dist/db/types.d.ts +2 -0
- package/dist/describe.d.ts +15 -4
- package/dist/describe.js +10 -2
- package/dist/describe.js.map +1 -1
- package/dist/events.d.ts +134 -0
- package/dist/events.js +196 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +12 -5
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +50 -10
- package/dist/jobs.js +66 -13
- package/dist/jobs.js.map +1 -1
- package/dist/langchain.d.ts +58 -0
- package/dist/langchain.js +220 -0
- package/dist/langchain.js.map +1 -0
- package/dist/model.d.ts +23 -6
- package/dist/model.js +35 -6
- package/dist/model.js.map +1 -1
- package/dist/rate-limit.d.ts +5 -3
- package/dist/rate-limit.js +3 -3
- package/dist/rate-limit.js.map +1 -1
- package/dist/redis-events.d.ts +27 -0
- package/dist/redis-events.js +163 -0
- package/dist/redis-events.js.map +1 -0
- package/dist/redis-jobs.d.ts +24 -0
- package/dist/redis-jobs.js +203 -0
- package/dist/redis-jobs.js.map +1 -0
- package/dist/redis.d.ts +30 -0
- package/dist/redis.js +106 -0
- package/dist/redis.js.map +1 -0
- package/dist/response-body.d.ts +6 -0
- package/dist/response-body.js +7 -0
- package/dist/response-body.js.map +1 -0
- package/dist/route.d.ts +14 -1
- package/dist/route.js.map +1 -1
- package/dist/schema.d.ts +2 -2
- package/dist/serve.d.ts +6 -1
- package/dist/serve.js +64 -7
- package/dist/serve.js.map +1 -1
- package/dist/service-client.d.ts +91 -0
- package/dist/service-client.js +177 -0
- package/dist/service-client.js.map +1 -0
- package/dist/sessions.d.ts +2 -0
- package/dist/sessions.js +1 -0
- package/dist/sessions.js.map +1 -1
- package/dist/tracing.d.ts +7 -3
- package/dist/tracing.js +8 -2
- package/dist/tracing.js.map +1 -1
- package/llms.txt +1 -0
- package/package.json +36 -4
- package/templates/app/AGENTS.md +5 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**The enterprise-ready TypeScript backend framework, with AI models built in.**
|
|
4
4
|
|
|
5
|
-
Inferlo is a batteries-included TypeScript backend framework for production APIs, SaaS products and enterprise services, in the same class as NestJS, Fastify and AdonisJS. It gives you routing, validation, a typed database layer with PostgreSQL and migrations, auth, rate limiting, background jobs, cron, OpenAPI and secure defaults in one package with zero runtime dependencies, running on Node.js, Bun and edge runtimes.
|
|
5
|
+
Inferlo is a batteries-included TypeScript backend framework for production APIs, SaaS products and enterprise services, in the same class as NestJS, Fastify and AdonisJS. It gives you routing, validation, a typed database layer with PostgreSQL and migrations, auth, rate limiting, background jobs, cron, OpenAPI and secure defaults in one package with zero runtime dependencies, running on Node.js, Bun, Deno and edge runtimes.
|
|
6
6
|
|
|
7
7
|
What sets it apart is that AI models are part of the core, not a plugin: Anthropic, OpenAI, Gemini, local models through Ollama and any OpenAI-compatible server behind one API, with fallback between models, typed structured output, streaming, tool calling, image input and per-user usage and cost tracking, plus an MCP server built from your routes.
|
|
8
8
|
|
|
@@ -47,7 +47,7 @@ That one model definition gives you the database table, insert and update valida
|
|
|
47
47
|
- **Embeddings** for search and recommendations.
|
|
48
48
|
- **Usage, cost and budgets.** Every call is tracked per user, so you can bill customers and stop calls when their credits run out.
|
|
49
49
|
- **MCP server.** Mark a route `tool: true` and AI agents can call your API, with the same permissions as any other client.
|
|
50
|
-
- **Use it in any framework.** `import { createAI } from "@browserlessapi/inferlo/ai"` gives you the same AI module without `createApp`, for Express, Hono, Fastify, NestJS or a plain script, and `toNodeHandler(app, { prefix: "/ai" })` mounts a whole Inferlo app (routes, OpenAPI, MCP) inside Express, Fastify, NestJS or `node:http`, `hono.use(inferloAI(ai))` from `@browserlessapi/inferlo/hono` gives Hono handlers `c.var.ai`, `server.use(inferloAI(ai))` from `@browserlessapi/inferlo/express` gives Express handlers `req.ai`, and `fastify.register(inferloPlugin, { ai })` from `@browserlessapi/inferlo/fastify` gives Fastify `request.ai`, `server.register({ plugin: inferloPlugin, options: { ai } })` from `@browserlessapi/inferlo/hapi` gives hapi `request.ai` (and `options: { app }` with `routes: { prefix }` serves a whole Inferlo app), `new Elysia().use(inferloAI(ai))` from `@browserlessapi/inferlo/elysia` gives Elysia handlers `ai`, `InferloModule.forRoot({ ai })` from `@browserlessapi/inferlo/nestjs` makes the AI client injectable in NestJS, the provider from `@browserlessapi/inferlo/adonisjs` gives AdonisJS routes `ctx.ai`, `@Configuration({ imports: [inferloAI(ai)] })` from `@browserlessapi/inferlo/tsed` makes the AI client injectable in Ts.ED as `InferloAI`, `trpc.procedure.use(inferloAI(ai))` from `@browserlessapi/inferlo/trpc` gives tRPC procedures `ctx.ai` while `trpcTools(router, { caller, procedures })` lets a model call tRPC procedures as tools, `koa.use(inferloAI(ai))` and `koa.use(toKoaMiddleware(app, { prefix: "/api" }))` from `@browserlessapi/inferlo/koa` give Koa `ctx.ai` and a whole Inferlo app, `export const { GET, POST } = toNextHandlers(app, { prefix: "/api" })` from `@browserlessapi/inferlo/nextjs` serves a whole Inferlo app from a Next.js App Router route, `export default toH3Handler(app, { prefix: "/api" })` from `@browserlessapi/inferlo/h3` serves it from a Nuxt or Nitro server route (with `event.context.ai` from a Nitro plugin), `export const { GET, POST, fallback } = toSvelteKitHandlers(app, { prefix: "/api" })` from `@browserlessapi/inferlo/sveltekit` serves it from a SvelteKit rest route (with `event.locals.ai` from a handle hook), `export const { loader, action } = toReactRouterHandlers(app, { prefix: "/api" })` from `@browserlessapi/inferlo/react-router` serves it from a React Router (Remix) resource route, `export const handler = toLambdaHandler(app)` from `@browserlessapi/inferlo/lambda` runs it on AWS Lambda (function URLs and API Gateway, with response streaming), and an Encore.ts raw endpoint serves a whole Inferlo app with `api.raw({ expose: true, method: "*", path: "/inferlo/*rest" }, toNodeHandler(app, { prefix: "/inferlo" }))` (see [Use with other frameworks](docs/integrations.md)).
|
|
50
|
+
- **Use it in any framework.** `import { createAI } from "@browserlessapi/inferlo/ai"` gives you the same AI module without `createApp`, for Express, Hono, Fastify, NestJS or a plain script, and `toNodeHandler(app, { prefix: "/ai" })` mounts a whole Inferlo app (routes, OpenAPI, MCP) inside Express, Fastify, NestJS or `node:http`, `hono.use(inferloAI(ai))` from `@browserlessapi/inferlo/hono` gives Hono handlers `c.var.ai`, `server.use(inferloAI(ai))` from `@browserlessapi/inferlo/express` gives Express handlers `req.ai`, and `fastify.register(inferloPlugin, { ai })` from `@browserlessapi/inferlo/fastify` gives Fastify `request.ai`, `server.register({ plugin: inferloPlugin, options: { ai } })` from `@browserlessapi/inferlo/hapi` gives hapi `request.ai` (and `options: { app }` with `routes: { prefix }` serves a whole Inferlo app), `new Elysia().use(inferloAI(ai))` from `@browserlessapi/inferlo/elysia` gives Elysia handlers `ai`, `InferloModule.forRoot({ ai })` from `@browserlessapi/inferlo/nestjs` makes the AI client injectable in NestJS, the provider from `@browserlessapi/inferlo/adonisjs` gives AdonisJS routes `ctx.ai`, `@Configuration({ imports: [inferloAI(ai)] })` from `@browserlessapi/inferlo/tsed` makes the AI client injectable in Ts.ED as `InferloAI`, `trpc.procedure.use(inferloAI(ai))` from `@browserlessapi/inferlo/trpc` gives tRPC procedures `ctx.ai` while `trpcTools(router, { caller, procedures })` lets a model call tRPC procedures as tools, `koa.use(inferloAI(ai))` and `koa.use(toKoaMiddleware(app, { prefix: "/api" }))` from `@browserlessapi/inferlo/koa` give Koa `ctx.ai` and a whole Inferlo app, `export const { GET, POST } = toNextHandlers(app, { prefix: "/api" })` from `@browserlessapi/inferlo/nextjs` serves a whole Inferlo app from a Next.js App Router route, `export default toH3Handler(app, { prefix: "/api" })` from `@browserlessapi/inferlo/h3` serves it from a Nuxt or Nitro server route (with `event.context.ai` from a Nitro plugin), `export const { GET, POST, fallback } = toSvelteKitHandlers(app, { prefix: "/api" })` from `@browserlessapi/inferlo/sveltekit` serves it from a SvelteKit rest route (with `event.locals.ai` from a handle hook), `export const ALL = toAstroHandler(app, { prefix: "/api" })` from `@browserlessapi/inferlo/astro` serves it from an Astro endpoint (with `locals.ai` from a middleware), `export const { loader, action } = toReactRouterHandlers(app, { prefix: "/api" })` from `@browserlessapi/inferlo/react-router` serves it from a React Router (Remix) resource route, `export const handler = toLambdaHandler(app)` from `@browserlessapi/inferlo/lambda` runs it on AWS Lambda (function URLs and API Gateway, with response streaming), `handler: toAzureHandler(app, { prefix: "/api" })` from `@browserlessapi/inferlo/azure` runs it in an Azure Functions HTTP trigger (with streaming), `functions.http("inferlo", toNodeHandler(app))` runs it as a Google Cloud Run function, `export default app` runs it as a Cloudflare Worker or a Vercel Function and `export default app.fetch` as a Netlify Function, and an Encore.ts raw endpoint serves a whole Inferlo app with `api.raw({ expose: true, method: "*", path: "/inferlo/*rest" }, toNodeHandler(app, { prefix: "/inferlo" }))` An app that already uses Drizzle ORM adds Inferlo on the same Postgres pool, with Drizzle and Inferlo writes in one transaction through `tx.$pgClient()` (see [Use with other frameworks](docs/integrations.md)).
|
|
51
51
|
- **Background jobs for slow AI work.** Queue a typed job from a route with `ctx.jobs.enqueue(...)` and it runs after the response, with retries, from a Postgres queue shared by every instance (`inferlo worker` runs them in their own process), and schedule jobs with `cron("0 3 * * *", job)`.
|
|
52
52
|
|
|
53
53
|
## Why Inferlo
|
|
@@ -81,9 +81,9 @@ Inferlo is also designed so AI coding tools (Claude Code, Cursor, Codex, NeonCod
|
|
|
81
81
|
|
|
82
82
|
## Quick start
|
|
83
83
|
|
|
84
|
-
Requires Node.js 22.18+ or
|
|
84
|
+
Requires Node.js 22.18+, Bun 1.3+ or Deno 2.9+. TypeScript runs directly with no build step.
|
|
85
85
|
|
|
86
|
-
Every release is tested on Node.js 22.18, 22, 24, 26 and the current release; Bun 1.3.0 and latest; PostgreSQL 14, 15, 16, 17, 18 and latest; and TypeScript 5.9 and latest.
|
|
86
|
+
Every release is tested on Node.js 22.18, 22, 24, 26 and the current release; Bun 1.3.0 and latest; Deno 2.9.0 and latest; PostgreSQL 14, 15, 16, 17, 18 and latest; and TypeScript 5.9 and latest.
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
npx @browserlessapi/inferlo new my-app
|
|
@@ -119,7 +119,9 @@ npm install @browserlessapi/inferlo
|
|
|
119
119
|
- [Auth](docs/auth.md)
|
|
120
120
|
- [AI](docs/ai.md)
|
|
121
121
|
- [Modules](docs/modules.md)
|
|
122
|
+
- [Service-to-service calls](docs/services.md)
|
|
122
123
|
- [Caching](docs/caching.md)
|
|
124
|
+
- [Redis](docs/redis.md)
|
|
123
125
|
- [MCP, OpenAPI and describe](docs/mcp.md)
|
|
124
126
|
- [CLI](docs/cli.md)
|
|
125
127
|
- [Testing](docs/testing.md)
|
|
@@ -132,6 +134,7 @@ npm install @browserlessapi/inferlo
|
|
|
132
134
|
- [Security](docs/security.md)
|
|
133
135
|
- [Observability](docs/observability.md)
|
|
134
136
|
- [Deployment](docs/deploy.md)
|
|
137
|
+
- [Performance](docs/performance.md)
|
|
135
138
|
- [Error codes](docs/errors.md)
|
|
136
139
|
- [Support policy](docs/support.md)
|
|
137
140
|
- [Example: helpdesk with AI triage](examples/helpdesk)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type Provider } from "./providers.ts";
|
|
2
|
+
/** The parts of an AI SDK language model (LanguageModelV2, V3 or V4) that Inferlo uses. */
|
|
3
|
+
export interface AISdkLanguageModel {
|
|
4
|
+
readonly specificationVersion: string;
|
|
5
|
+
readonly provider: string;
|
|
6
|
+
readonly modelId: string;
|
|
7
|
+
doGenerate(options: any): PromiseLike<{
|
|
8
|
+
content: readonly object[];
|
|
9
|
+
usage: unknown;
|
|
10
|
+
}>;
|
|
11
|
+
doStream(options: any): PromiseLike<{
|
|
12
|
+
stream: ReadableStream<any>;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/** The parts of an AI SDK embedding model (EmbeddingModelV2, V3 or V4) that Inferlo uses. */
|
|
16
|
+
export interface AISdkEmbeddingModel {
|
|
17
|
+
readonly specificationVersion: string;
|
|
18
|
+
readonly modelId: string;
|
|
19
|
+
readonly maxEmbeddingsPerCall?: PromiseLike<number | undefined> | number | undefined;
|
|
20
|
+
doEmbed(options: {
|
|
21
|
+
values: string[];
|
|
22
|
+
abortSignal?: AbortSignal;
|
|
23
|
+
}): PromiseLike<{
|
|
24
|
+
embeddings: number[][];
|
|
25
|
+
usage?: {
|
|
26
|
+
tokens: number;
|
|
27
|
+
};
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* An AI SDK provider: what createOpenAI(), createAnthropic(), bedrock, mistral, gateway and the others
|
|
32
|
+
* return, and what customProvider() from "ai" builds. Only languageModel is required.
|
|
33
|
+
*/
|
|
34
|
+
export interface AISdkProvider {
|
|
35
|
+
readonly specificationVersion?: string;
|
|
36
|
+
languageModel(modelId: string): AISdkLanguageModel;
|
|
37
|
+
embeddingModel?(modelId: string): AISdkEmbeddingModel;
|
|
38
|
+
textEmbeddingModel?(modelId: string): AISdkEmbeddingModel;
|
|
39
|
+
}
|
|
40
|
+
export interface AISdkOptions {
|
|
41
|
+
/** Provider name in errors, usage and traces. Default: the AI SDK provider's own name (the part before the first dot, e.g. "openai"). */
|
|
42
|
+
name?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Use any Vercel AI SDK provider in createAI({ providers }) or createApp({ ai: { providers } }):
|
|
46
|
+
* providers: { openai: aiSdk(createOpenAI({ apiKey })) }, then models: { fast: "openai:gpt-5-mini" }.
|
|
47
|
+
*/
|
|
48
|
+
export declare function aiSdk(provider: AISdkProvider, options?: AISdkOptions): Provider;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel AI SDK providers as Inferlo providers: every @ai-sdk/* provider (OpenAI, Anthropic, Google,
|
|
3
|
+
* Mistral, Amazon Bedrock, Azure, Vertex, Groq, xAI, the AI Gateway, ...) works with aliases, fallback,
|
|
4
|
+
* usage tracking, structured output, tools, streaming and embeddings.
|
|
5
|
+
*
|
|
6
|
+
* import { createMistral } from "@ai-sdk/mistral";
|
|
7
|
+
* createAI({ providers: { mistral: aiSdk(createMistral({ apiKey })) }, models: { fast: "mistral:mistral-small-latest" } })
|
|
8
|
+
*
|
|
9
|
+
* Inferlo has no dependency on the AI SDK: aiSdk() calls the provider's languageModel(id) and
|
|
10
|
+
* embeddingModel(id) (textEmbeddingModel(id) on older providers) and speaks the model specification
|
|
11
|
+
* versions v2 (ai 5), v3 (ai 6) and v4 (ai 7) itself.
|
|
12
|
+
*/
|
|
13
|
+
import { InferloError } from "../errors.js";
|
|
14
|
+
import { imageData, ProviderError } from "./providers.js";
|
|
15
|
+
/** The specification versions aiSdk() speaks. */
|
|
16
|
+
const VERSIONS = new Set(["v2", "v3", "v4"]);
|
|
17
|
+
/**
|
|
18
|
+
* Use any Vercel AI SDK provider in createAI({ providers }) or createApp({ ai: { providers } }):
|
|
19
|
+
* providers: { openai: aiSdk(createOpenAI({ apiKey })) }, then models: { fast: "openai:gpt-5-mini" }.
|
|
20
|
+
*/
|
|
21
|
+
export function aiSdk(provider, options = {}) {
|
|
22
|
+
if (!provider || typeof provider !== "object" && typeof provider !== "function" || typeof provider.languageModel !== "function") {
|
|
23
|
+
throw new InferloError("IL5029", "aiSdk() needs an AI SDK provider, such as createOpenAI({ apiKey }) from @ai-sdk/openai.", {
|
|
24
|
+
fix: 'Pass the provider, not a model: aiSdk(createOpenAI({ apiKey })) with models: { fast: "openai:gpt-5-mini" }. For single models use customProvider({ languageModels: { ... } }) from "ai".',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (options.name !== undefined && (typeof options.name !== "string" || !/^[a-zA-Z0-9._-]{1,64}$/.test(options.name))) {
|
|
28
|
+
throw new InferloError("IL5029", `aiSdk() name must be 1 to 64 letters, digits, dots, dashes or underscores, got ${JSON.stringify(options.name)}.`);
|
|
29
|
+
}
|
|
30
|
+
const languageModels = new Map();
|
|
31
|
+
const embeddingModels = new Map();
|
|
32
|
+
let label = options.name;
|
|
33
|
+
const nameOf = (model) => (label ??= String(model.provider ?? "ai-sdk").split(".")[0] || "ai-sdk");
|
|
34
|
+
const languageModel = (id) => {
|
|
35
|
+
let model = languageModels.get(id);
|
|
36
|
+
if (!model) {
|
|
37
|
+
model = provider.languageModel(id);
|
|
38
|
+
checkVersion(model, id, "language");
|
|
39
|
+
languageModels.set(id, model);
|
|
40
|
+
}
|
|
41
|
+
return model;
|
|
42
|
+
};
|
|
43
|
+
const embeddingModel = (id) => {
|
|
44
|
+
let model = embeddingModels.get(id);
|
|
45
|
+
if (!model) {
|
|
46
|
+
const make = provider.embeddingModel ?? provider.textEmbeddingModel;
|
|
47
|
+
if (typeof make !== "function") {
|
|
48
|
+
throw new InferloError("IL5029", `The AI SDK provider${label ? ` "${label}"` : ""} has no embedding models.`, { fix: "Use an embedding model of another provider in ai.embed({ model })." });
|
|
49
|
+
}
|
|
50
|
+
model = make.call(provider, id);
|
|
51
|
+
checkVersion(model, id, "embedding");
|
|
52
|
+
embeddingModels.set(id, model);
|
|
53
|
+
}
|
|
54
|
+
return model;
|
|
55
|
+
};
|
|
56
|
+
const out = {
|
|
57
|
+
get name() {
|
|
58
|
+
return label ?? "ai-sdk";
|
|
59
|
+
},
|
|
60
|
+
async generate(r) {
|
|
61
|
+
const model = languageModel(r.model);
|
|
62
|
+
const name = nameOf(model);
|
|
63
|
+
const v2 = model.specificationVersion === "v2";
|
|
64
|
+
let result;
|
|
65
|
+
try {
|
|
66
|
+
result = (await model.doGenerate(callOptions(r, v2)));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw providerError(name, error);
|
|
70
|
+
}
|
|
71
|
+
const text = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
|
|
72
|
+
const toolCalls = result.content
|
|
73
|
+
.filter((c) => c.type === "tool-call")
|
|
74
|
+
.map((c) => ({ id: c.toolCallId ?? "", name: c.toolName ?? "", input: parseInput(c.input) }));
|
|
75
|
+
return { text, usage: usage(result.usage), ...(toolCalls.length ? { toolCalls } : {}) };
|
|
76
|
+
},
|
|
77
|
+
async *stream(r) {
|
|
78
|
+
const model = languageModel(r.model);
|
|
79
|
+
const name = nameOf(model);
|
|
80
|
+
const v2 = model.specificationVersion === "v2";
|
|
81
|
+
let stream;
|
|
82
|
+
try {
|
|
83
|
+
({ stream } = await model.doStream(callOptions(r, v2)));
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw providerError(name, error);
|
|
87
|
+
}
|
|
88
|
+
const reader = stream.getReader();
|
|
89
|
+
let total;
|
|
90
|
+
try {
|
|
91
|
+
while (true) {
|
|
92
|
+
const { done, value: part } = await reader.read();
|
|
93
|
+
if (done)
|
|
94
|
+
break;
|
|
95
|
+
if (part.type === "text-delta") {
|
|
96
|
+
const text = part.delta ?? part.textDelta;
|
|
97
|
+
if (text)
|
|
98
|
+
yield text;
|
|
99
|
+
}
|
|
100
|
+
else if (part.type === "finish")
|
|
101
|
+
total = usage(part.usage);
|
|
102
|
+
else if (part.type === "error")
|
|
103
|
+
throw providerError(name, part.error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
reader.releaseLock();
|
|
108
|
+
}
|
|
109
|
+
yield { usage: total ?? { inputTokens: 0, outputTokens: 0 } };
|
|
110
|
+
},
|
|
111
|
+
async embed({ model: id, input, signal }) {
|
|
112
|
+
const model = embeddingModel(id);
|
|
113
|
+
const name = nameOf({ provider: model.provider });
|
|
114
|
+
const max = (await model.maxEmbeddingsPerCall) ?? input.length;
|
|
115
|
+
const embeddings = [];
|
|
116
|
+
let tokens = 0;
|
|
117
|
+
for (let i = 0; i < input.length; i += Math.max(1, max)) {
|
|
118
|
+
let result;
|
|
119
|
+
try {
|
|
120
|
+
result = await model.doEmbed({ values: input.slice(i, i + Math.max(1, max)), abortSignal: signal });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw providerError(name, error);
|
|
124
|
+
}
|
|
125
|
+
embeddings.push(...result.embeddings.map((e) => Array.from(e)));
|
|
126
|
+
tokens += result.usage?.tokens ?? 0;
|
|
127
|
+
}
|
|
128
|
+
return { embeddings, usage: { inputTokens: tokens, outputTokens: 0 } };
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
function checkVersion(model, id, kind) {
|
|
134
|
+
if (!model || typeof model !== "object" || !VERSIONS.has(String(model.specificationVersion))) {
|
|
135
|
+
throw new InferloError("IL5029", `The AI SDK ${kind} model "${id}" uses specification ${String(model?.specificationVersion ?? "(none)")}, which aiSdk() does not speak.`, {
|
|
136
|
+
fix: "Use a provider package made for ai 5 or later (@ai-sdk/* 2.0 or later, LanguageModelV2, V3 or V4).",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Inferlo's request as AI SDK call options. v2 file parts carry data or a URL directly; v3 and v4 wrap it. */
|
|
141
|
+
function callOptions(r, v2) {
|
|
142
|
+
const out = { prompt: prompt(r.messages, v2), abortSignal: r.signal };
|
|
143
|
+
if (r.maxTokens !== undefined)
|
|
144
|
+
out.maxOutputTokens = r.maxTokens;
|
|
145
|
+
if (r.temperature !== undefined)
|
|
146
|
+
out.temperature = r.temperature;
|
|
147
|
+
if (r.json)
|
|
148
|
+
out.responseFormat = r.jsonSchema ? { type: "json", schema: r.jsonSchema, name: "output" } : { type: "json" };
|
|
149
|
+
if (r.tools?.length) {
|
|
150
|
+
out.tools = r.tools.map((tool) => ({ type: "function", name: tool.name, description: tool.description, inputSchema: tool.inputSchema }));
|
|
151
|
+
out.toolChoice = { type: "auto" };
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
function prompt(messages, v2) {
|
|
156
|
+
const names = new Map();
|
|
157
|
+
const out = [];
|
|
158
|
+
for (const m of messages) {
|
|
159
|
+
if (m.role === "system")
|
|
160
|
+
out.push({ role: "system", content: m.content });
|
|
161
|
+
else if (m.role === "user") {
|
|
162
|
+
const content = [];
|
|
163
|
+
if (m.content)
|
|
164
|
+
content.push({ type: "text", text: m.content });
|
|
165
|
+
for (const image of m.images ?? [])
|
|
166
|
+
content.push(filePart(image, v2));
|
|
167
|
+
out.push({ role: "user", content: content.length ? content : [{ type: "text", text: "" }] });
|
|
168
|
+
}
|
|
169
|
+
else if (m.role === "assistant") {
|
|
170
|
+
const content = [];
|
|
171
|
+
if (m.content)
|
|
172
|
+
content.push({ type: "text", text: m.content });
|
|
173
|
+
for (const call of m.toolCalls ?? []) {
|
|
174
|
+
names.set(call.id, call.name);
|
|
175
|
+
content.push({ type: "tool-call", toolCallId: call.id, toolName: call.name, input: call.input ?? {} });
|
|
176
|
+
}
|
|
177
|
+
out.push({ role: "assistant", content });
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const id = m.toolCallId ?? "";
|
|
181
|
+
const part = { type: "tool-result", toolCallId: id, toolName: names.get(id) ?? "", output: { type: m.isError ? "error-text" : "text", value: m.content } };
|
|
182
|
+
// Results of one assistant turn's calls go in one tool message, as the AI SDK expects.
|
|
183
|
+
const last = out.at(-1);
|
|
184
|
+
if (last?.role === "tool")
|
|
185
|
+
last.content.push(part);
|
|
186
|
+
else
|
|
187
|
+
out.push({ role: "tool", content: [part] });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
function filePart(image, v2) {
|
|
193
|
+
const data = imageData(image);
|
|
194
|
+
if (v2)
|
|
195
|
+
return data ? { type: "file", data: data.base64, mediaType: data.mediaType } : { type: "file", data: new URL(image.url), mediaType: "image/*" };
|
|
196
|
+
return data
|
|
197
|
+
? { type: "file", data: { type: "data", data: data.base64 }, mediaType: data.mediaType }
|
|
198
|
+
: { type: "file", data: { type: "url", url: new URL(image.url) }, mediaType: "image/*" };
|
|
199
|
+
}
|
|
200
|
+
/** Tool call arguments: v2 to v4 send them as a JSON string. */
|
|
201
|
+
function parseInput(input) {
|
|
202
|
+
if (typeof input !== "string")
|
|
203
|
+
return input ?? {};
|
|
204
|
+
if (!input.trim())
|
|
205
|
+
return {};
|
|
206
|
+
try {
|
|
207
|
+
return JSON.parse(input);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return input;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** Usage of v2 ({ inputTokens: number }) and v3/v4 ({ inputTokens: { total } }). */
|
|
214
|
+
function usage(raw) {
|
|
215
|
+
const u = (raw ?? {});
|
|
216
|
+
const count = (value) => {
|
|
217
|
+
const n = typeof value === "object" && value !== null ? value.total : value;
|
|
218
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
219
|
+
};
|
|
220
|
+
return { inputTokens: count(u.inputTokens), outputTokens: count(u.outputTokens) };
|
|
221
|
+
}
|
|
222
|
+
/** AI SDK errors (APICallError has statusCode) as ProviderError, so fallback and IL5006 work as with other providers. */
|
|
223
|
+
function providerError(name, error) {
|
|
224
|
+
if (error instanceof InferloError)
|
|
225
|
+
return error;
|
|
226
|
+
if (error && typeof error === "object" && error.name === "AbortError")
|
|
227
|
+
return error;
|
|
228
|
+
const e = (error ?? {});
|
|
229
|
+
const status = typeof e.statusCode === "number" ? e.statusCode : 0;
|
|
230
|
+
const body = typeof e.responseBody === "string" && e.responseBody ? e.responseBody : String(e.message ?? error);
|
|
231
|
+
const wrapped = new ProviderError(name, status, body);
|
|
232
|
+
wrapped.cause = error;
|
|
233
|
+
return wrapped;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=ai-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-sdk.js","sourceRoot":"","sources":["../../src/ai/ai-sdk.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { type AnySchema, type Infer } from "../schema.ts";
|
|
|
2
2
|
import type { RouteDef } from "../route.ts";
|
|
3
3
|
import { type ToolDef } from "./tool.ts";
|
|
4
4
|
import { type TracingConfig } from "../tracing.ts";
|
|
5
|
-
import { type Image, type Message, type Provider, type ProviderUsage, type ToolSpec } from "./providers.ts";
|
|
5
|
+
import { type Image, type Message, type Provider, type ProviderUsage, type ToolCall, type ToolSpec } from "./providers.ts";
|
|
6
6
|
export * from "./providers.ts";
|
|
7
7
|
export { tool, isTool, ToolBuilder } from "./tool.ts";
|
|
8
8
|
export type { ToolDef, FunctionToolOptions, ToolContext } from "./tool.ts";
|
|
9
|
+
export { uiChatBody, fromUIMessages, type UIChatMessage } from "./ui.ts";
|
|
9
10
|
export interface Pricing {
|
|
10
11
|
/** USD per million input tokens. */
|
|
11
12
|
input: number;
|
|
@@ -114,18 +115,51 @@ export type ToolRunner = (route: RouteDef, input: unknown) => Promise<{
|
|
|
114
115
|
output: unknown;
|
|
115
116
|
text: string;
|
|
116
117
|
}>;
|
|
118
|
+
/** One model call that returns the tool calls instead of running them, for agent loops such as LangChain's. */
|
|
119
|
+
export interface ModelCallOptions {
|
|
120
|
+
model?: string;
|
|
121
|
+
messages: Message[];
|
|
122
|
+
tools?: ToolSpec[];
|
|
123
|
+
temperature?: number;
|
|
124
|
+
maxTokens?: number;
|
|
125
|
+
for?: string;
|
|
126
|
+
signal?: AbortSignal;
|
|
127
|
+
}
|
|
128
|
+
export interface ModelCallResult {
|
|
129
|
+
text: string;
|
|
130
|
+
toolCalls: ToolCall[];
|
|
131
|
+
usage: Usage;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* One model call with the AI's aliases, fallback, budgets, usage tracking and tracing, which returns the tool calls
|
|
135
|
+
* the model asked for without running them. Internal: used by the LangChain integration.
|
|
136
|
+
*/
|
|
137
|
+
export declare function callModel(ai: AI, options: ModelCallOptions): Promise<ModelCallResult>;
|
|
138
|
+
/** The route tools of ctx.ai (a request's AI client), or undefined for other clients. Internal. */
|
|
139
|
+
export declare function routeToolsOf(ai: AI): {
|
|
140
|
+
runner: ToolRunner;
|
|
141
|
+
describe: (route: RouteDef) => ToolSpec;
|
|
142
|
+
} | undefined;
|
|
117
143
|
/**
|
|
118
144
|
* ctx.ai for one request: the same AI, with tools that run as the request's user, and the request's signal
|
|
119
145
|
* on every call that does not pass its own. Internal: used by createApp.
|
|
120
146
|
*/
|
|
121
147
|
export declare function bindTools(ai: AI, runner: ToolRunner, describe: (route: RouteDef) => ToolSpec, signal?: AbortSignal): AI;
|
|
148
|
+
/** Options of StreamResult.toResponse: a ResponseInit (status, headers) and the stream format. */
|
|
149
|
+
export interface StreamResponseInit extends ResponseInit {
|
|
150
|
+
/** "sse" (default): Inferlo's text and done events. "ai-sdk": the AI SDK UI message stream protocol, for useChat. */
|
|
151
|
+
format?: "sse" | "ai-sdk";
|
|
152
|
+
}
|
|
122
153
|
export interface StreamResult {
|
|
123
154
|
/** Text chunks as they arrive. */
|
|
124
155
|
[Symbol.asyncIterator](): AsyncIterator<string>;
|
|
125
156
|
/** Full text once the stream finishes. */
|
|
126
157
|
text(): Promise<string>;
|
|
127
|
-
/**
|
|
128
|
-
|
|
158
|
+
/**
|
|
159
|
+
* A server-sent events Response for browsers. The default format sends "text" events, then "done" with usage.
|
|
160
|
+
* format: "ai-sdk" speaks the AI SDK UI message stream protocol (v1), for useChat from @ai-sdk/react.
|
|
161
|
+
*/
|
|
162
|
+
toResponse(init?: StreamResponseInit): Response;
|
|
129
163
|
/** Usage once the stream finishes. */
|
|
130
164
|
usage(): Promise<Usage>;
|
|
131
165
|
}
|
package/dist/ai/index.js
CHANGED
|
@@ -17,7 +17,23 @@ import { checkTracing, clientTracing } from "../tracing.js";
|
|
|
17
17
|
import { ProviderError, imageData } from "./providers.js";
|
|
18
18
|
export * from "./providers.js";
|
|
19
19
|
export { tool, isTool, ToolBuilder } from "./tool.js";
|
|
20
|
+
export { uiChatBody, fromUIMessages } from "./ui.js";
|
|
20
21
|
const toolRunners = new WeakMap();
|
|
22
|
+
const modelCalls = new WeakMap();
|
|
23
|
+
/**
|
|
24
|
+
* One model call with the AI's aliases, fallback, budgets, usage tracking and tracing, which returns the tool calls
|
|
25
|
+
* the model asked for without running them. Internal: used by the LangChain integration.
|
|
26
|
+
*/
|
|
27
|
+
export function callModel(ai, options) {
|
|
28
|
+
const call = modelCalls.get(ai);
|
|
29
|
+
if (!call)
|
|
30
|
+
throw new InferloError("IL5030", "Expected an AI client made by createAI() or createApp({ ai }).", { fix: "Pass app.ai, ctx.ai or the client createAI() returns." });
|
|
31
|
+
return call(options);
|
|
32
|
+
}
|
|
33
|
+
/** The route tools of ctx.ai (a request's AI client), or undefined for other clients. Internal. */
|
|
34
|
+
export function routeToolsOf(ai) {
|
|
35
|
+
return toolRunners.get(ai);
|
|
36
|
+
}
|
|
21
37
|
/**
|
|
22
38
|
* ctx.ai for one request: the same AI, with tools that run as the request's user, and the request's signal
|
|
23
39
|
* on every call that does not pass its own. Internal: used by createApp.
|
|
@@ -29,6 +45,9 @@ export function bindTools(ai, runner, describe, signal) {
|
|
|
29
45
|
bound.generate = (options) => ai.generate.call(bound, withSignal(options));
|
|
30
46
|
bound.stream = (options) => ai.stream.call(bound, withSignal(options));
|
|
31
47
|
bound.embed = (options) => ai.embed.call(bound, withSignal(options));
|
|
48
|
+
const call = modelCalls.get(ai);
|
|
49
|
+
if (call)
|
|
50
|
+
modelCalls.set(bound, (options) => call(withSignal(options)));
|
|
32
51
|
return bound;
|
|
33
52
|
}
|
|
34
53
|
function buildMessages(o) {
|
|
@@ -469,23 +488,59 @@ export function createAI(config) {
|
|
|
469
488
|
},
|
|
470
489
|
usage: () => usagePromise,
|
|
471
490
|
toResponse(init) {
|
|
491
|
+
const { format = "sse", ...responseInit } = init ?? {};
|
|
492
|
+
if (format !== "sse" && format !== "ai-sdk") {
|
|
493
|
+
throw new InferloError("IL5027", `Unknown stream format "${String(format)}" in toResponse({ format }).`, {
|
|
494
|
+
fix: 'Use toResponse() for Inferlo\'s server-sent events, or toResponse({ format: "ai-sdk" }) for useChat from the Vercel AI SDK.',
|
|
495
|
+
});
|
|
496
|
+
}
|
|
472
497
|
const encoder = new TextEncoder();
|
|
473
498
|
const source = start();
|
|
499
|
+
const aiSdk = format === "ai-sdk";
|
|
500
|
+
const textId = aiSdk ? `text-${crypto.randomUUID()}` : "";
|
|
501
|
+
let opened = false;
|
|
502
|
+
// AI SDK UI message stream chunks are server-sent events with only a data field.
|
|
503
|
+
const chunk = (value) => encoder.encode(`data: ${JSON.stringify(value)}\n\n`);
|
|
474
504
|
const body = new ReadableStream({
|
|
505
|
+
start(controller) {
|
|
506
|
+
if (aiSdk)
|
|
507
|
+
controller.enqueue(chunk({ type: "start" }));
|
|
508
|
+
},
|
|
475
509
|
async pull(controller) {
|
|
476
510
|
try {
|
|
477
511
|
const { value, done } = await source.next();
|
|
478
512
|
if (done) {
|
|
479
513
|
const usage = await usagePromise;
|
|
480
|
-
|
|
514
|
+
if (!aiSdk)
|
|
515
|
+
controller.enqueue(encoder.encode(`event: done\ndata: ${JSON.stringify({ usage })}\n\n`));
|
|
516
|
+
else {
|
|
517
|
+
if (opened)
|
|
518
|
+
controller.enqueue(chunk({ type: "text-end", id: textId }));
|
|
519
|
+
controller.enqueue(chunk({ type: "finish" }));
|
|
520
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
521
|
+
}
|
|
481
522
|
controller.close();
|
|
482
523
|
return;
|
|
483
524
|
}
|
|
484
|
-
|
|
525
|
+
if (!aiSdk) {
|
|
526
|
+
controller.enqueue(encoder.encode(`event: text\ndata: ${JSON.stringify(value)}\n\n`));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (!opened) {
|
|
530
|
+
opened = true;
|
|
531
|
+
controller.enqueue(chunk({ type: "text-start", id: textId }));
|
|
532
|
+
}
|
|
533
|
+
controller.enqueue(chunk({ type: "text-delta", id: textId, delta: value }));
|
|
485
534
|
}
|
|
486
535
|
catch (error) {
|
|
487
536
|
const code = error instanceof InferloError ? error.code : "IL9000";
|
|
488
|
-
|
|
537
|
+
if (!aiSdk)
|
|
538
|
+
controller.enqueue(encoder.encode(`event: error\ndata: ${JSON.stringify({ code })}\n\n`));
|
|
539
|
+
else {
|
|
540
|
+
// Only the code reaches the browser; the details are logged below.
|
|
541
|
+
controller.enqueue(chunk({ type: "error", errorText: `The model call failed (${code}).` }));
|
|
542
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
543
|
+
}
|
|
489
544
|
controller.close();
|
|
490
545
|
console.error("[inferlo] stream failed:", redactor.value(error));
|
|
491
546
|
}
|
|
@@ -494,10 +549,13 @@ export function createAI(config) {
|
|
|
494
549
|
void source.return(undefined);
|
|
495
550
|
},
|
|
496
551
|
});
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
headers
|
|
500
|
-
|
|
552
|
+
const headers = { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache", connection: "keep-alive" };
|
|
553
|
+
if (aiSdk) {
|
|
554
|
+
headers["content-type"] = "text/event-stream";
|
|
555
|
+
headers["x-vercel-ai-ui-message-stream"] = "v1";
|
|
556
|
+
headers["x-accel-buffering"] = "no";
|
|
557
|
+
}
|
|
558
|
+
return new Response(body, { ...responseInit, headers: { ...headers, ...responseInit.headers } });
|
|
501
559
|
},
|
|
502
560
|
};
|
|
503
561
|
},
|
|
@@ -525,6 +583,37 @@ export function createAI(config) {
|
|
|
525
583
|
});
|
|
526
584
|
},
|
|
527
585
|
};
|
|
586
|
+
modelCalls.set(ai, async (options) => {
|
|
587
|
+
const requested = options.model ?? config?.defaultModel ?? Object.keys(aliases)[0] ?? "";
|
|
588
|
+
const targets = resolve(options.model);
|
|
589
|
+
await guard(options.for, requested);
|
|
590
|
+
if (!options.messages.some((m) => m.role !== "system")) {
|
|
591
|
+
throw new InferloError("IL5007", "A model call needs at least one message besides the system message.", { fix: 'Pass messages: [{ role: "user", content: "..." }].' });
|
|
592
|
+
}
|
|
593
|
+
options.messages.forEach(checkImages);
|
|
594
|
+
const started = Date.now();
|
|
595
|
+
const call = () => withFallback(targets, async (target) => {
|
|
596
|
+
const response = await target.provider.generate({
|
|
597
|
+
model: target.model,
|
|
598
|
+
messages: options.messages,
|
|
599
|
+
temperature: options.temperature,
|
|
600
|
+
maxTokens: options.maxTokens,
|
|
601
|
+
tools: options.tools?.length ? options.tools : undefined,
|
|
602
|
+
signal: options.signal,
|
|
603
|
+
});
|
|
604
|
+
const usage = { requested, model: target.id, ...response.usage, cost: cost(target.id, response.usage), for: options.for, kind: "generate", durationMs: Date.now() - started };
|
|
605
|
+
await record(usage);
|
|
606
|
+
return { text: response.text, toolCalls: response.toolCalls ?? [], usage };
|
|
607
|
+
});
|
|
608
|
+
if (!spans)
|
|
609
|
+
return call();
|
|
610
|
+
const { name, attributes } = spanStart("chat", requested, targets, options);
|
|
611
|
+
return spans.run(name, attributes, async (span) => {
|
|
612
|
+
const result = await call();
|
|
613
|
+
spanUsage(span, result.usage);
|
|
614
|
+
return result;
|
|
615
|
+
});
|
|
616
|
+
});
|
|
528
617
|
return ai;
|
|
529
618
|
}
|
|
530
619
|
/** Cosine similarity of two vectors, for small in-memory searches. */
|