@browserlessapi/inferlo 0.4.0 → 0.5.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.
Files changed (96) hide show
  1. package/README.md +15 -3
  2. package/dist/adonisjs.d.ts +33 -0
  3. package/dist/adonisjs.js +30 -0
  4. package/dist/adonisjs.js.map +1 -0
  5. package/dist/ai/index.d.ts +15 -8
  6. package/dist/ai/index.js +39 -13
  7. package/dist/ai/index.js.map +1 -1
  8. package/dist/ai/providers.js +5 -4
  9. package/dist/ai/providers.js.map +1 -1
  10. package/dist/ai/public.d.ts +20 -0
  11. package/dist/ai/public.js +17 -0
  12. package/dist/ai/public.js.map +1 -0
  13. package/dist/ai/tool.d.ts +47 -0
  14. package/dist/ai/tool.js +85 -0
  15. package/dist/ai/tool.js.map +1 -0
  16. package/dist/ai-client.d.ts +3 -0
  17. package/dist/ai-client.js +8 -0
  18. package/dist/ai-client.js.map +1 -0
  19. package/dist/app.d.ts +87 -2
  20. package/dist/app.js +471 -127
  21. package/dist/app.js.map +1 -1
  22. package/dist/cli/main.js +21 -2
  23. package/dist/cli/main.js.map +1 -1
  24. package/dist/config.d.ts +16 -0
  25. package/dist/config.js +101 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/db/table.d.ts +31 -0
  28. package/dist/db/table.js +140 -1
  29. package/dist/db/table.js.map +1 -1
  30. package/dist/deprecation.d.ts +23 -0
  31. package/dist/deprecation.js +70 -0
  32. package/dist/deprecation.js.map +1 -0
  33. package/dist/describe.d.ts +27 -5
  34. package/dist/describe.js +14 -2
  35. package/dist/describe.js.map +1 -1
  36. package/dist/elysia.d.ts +49 -0
  37. package/dist/elysia.js +10 -0
  38. package/dist/elysia.js.map +1 -0
  39. package/dist/env.d.ts +2 -0
  40. package/dist/env.js +5 -4
  41. package/dist/env.js.map +1 -1
  42. package/dist/express.d.ts +38 -0
  43. package/dist/express.js +18 -0
  44. package/dist/express.js.map +1 -0
  45. package/dist/fastify.d.ts +44 -0
  46. package/dist/fastify.js +47 -0
  47. package/dist/fastify.js.map +1 -0
  48. package/dist/hono.d.ts +26 -0
  49. package/dist/hono.js +10 -0
  50. package/dist/hono.js.map +1 -0
  51. package/dist/index.d.ts +13 -4
  52. package/dist/index.js +4 -2
  53. package/dist/index.js.map +1 -1
  54. package/dist/jobs.d.ts +4 -0
  55. package/dist/jobs.js +18 -1
  56. package/dist/jobs.js.map +1 -1
  57. package/dist/logging.d.ts +23 -0
  58. package/dist/logging.js +87 -0
  59. package/dist/logging.js.map +1 -0
  60. package/dist/mcp.d.ts +0 -6
  61. package/dist/mcp.js +11 -1
  62. package/dist/mcp.js.map +1 -1
  63. package/dist/metrics.d.ts +48 -0
  64. package/dist/metrics.js +184 -0
  65. package/dist/metrics.js.map +1 -0
  66. package/dist/nestjs.d.ts +43 -0
  67. package/dist/nestjs.js +47 -0
  68. package/dist/nestjs.js.map +1 -0
  69. package/dist/openapi.d.ts +0 -1
  70. package/dist/openapi.js +4 -0
  71. package/dist/openapi.js.map +1 -1
  72. package/dist/redact.d.ts +19 -0
  73. package/dist/redact.js +150 -0
  74. package/dist/redact.js.map +1 -0
  75. package/dist/route.d.ts +18 -0
  76. package/dist/route.js.map +1 -1
  77. package/dist/secure-headers.d.ts +8 -0
  78. package/dist/secure-headers.js +61 -0
  79. package/dist/secure-headers.js.map +1 -0
  80. package/dist/serve.d.ts +53 -1
  81. package/dist/serve.js +157 -61
  82. package/dist/serve.js.map +1 -1
  83. package/dist/trpc.d.ts +57 -0
  84. package/dist/trpc.js +142 -0
  85. package/dist/trpc.js.map +1 -0
  86. package/dist/tsed.d.ts +32 -0
  87. package/dist/tsed.js +13 -0
  88. package/dist/tsed.js.map +1 -0
  89. package/llms.txt +10 -4
  90. package/package.json +57 -3
  91. package/templates/app/AGENTS.md +8 -4
  92. package/templates/app/_env.example +2 -0
  93. package/templates/app/src/app.ts +15 -8
  94. package/templates/app/src/config.ts +17 -0
  95. package/templates/app/src/server.ts +9 -9
  96. package/templates/app/src/routes/health.ts +0 -3
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Secure response headers, sent on every response unless the response already has the header:
3
+ *
4
+ * createApp({ secureHeaders: { "X-Frame-Options": "SAMEORIGIN", "Content-Security-Policy": false } })
5
+ * createApp({ secureHeaders: false }) // none of them
6
+ */
7
+ import { InferloError } from "./errors.js";
8
+ /** Sent on every response by default. Strict-Transport-Security is sent only over HTTPS. */
9
+ export const DEFAULT_SECURE_HEADERS = Object.freeze({
10
+ "content-security-policy": "default-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'",
11
+ "cross-origin-opener-policy": "same-origin",
12
+ "cross-origin-resource-policy": "same-origin",
13
+ "referrer-policy": "no-referrer",
14
+ "strict-transport-security": "max-age=31536000; includeSubDomains",
15
+ "x-content-type-options": "nosniff",
16
+ "x-frame-options": "DENY",
17
+ });
18
+ const HSTS = "strict-transport-security";
19
+ const TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
20
+ /** The headers to send (lowercase names), from createApp({ secureHeaders }). */
21
+ export function resolveSecureHeaders(option) {
22
+ if (option === false)
23
+ return [];
24
+ const fix = 'Use createApp({ secureHeaders: { "X-Frame-Options": "SAMEORIGIN", "Content-Security-Policy": false } }), or secureHeaders: false to send none.';
25
+ if (option !== undefined && option !== true && (typeof option !== "object" || option === null || Array.isArray(option))) {
26
+ throw new InferloError("IL6016", `secureHeaders must be true, false or an object of header names, got ${JSON.stringify(option)}.`, { fix });
27
+ }
28
+ const headers = new Map(Object.entries(DEFAULT_SECURE_HEADERS));
29
+ for (const [name, value] of Object.entries(typeof option === "object" ? option : {})) {
30
+ if (!TOKEN.test(name))
31
+ throw new InferloError("IL6016", `secureHeaders: "${name}" is not a valid header name.`, { fix });
32
+ if (value === false) {
33
+ headers.delete(name.toLowerCase());
34
+ continue;
35
+ }
36
+ if (typeof value !== "string" || /[\r\n\0]/.test(value)) {
37
+ throw new InferloError("IL6016", `secureHeaders["${name}"] must be a string (without line breaks) or false, got ${JSON.stringify(value)}.`, { fix });
38
+ }
39
+ headers.set(name.toLowerCase(), value);
40
+ }
41
+ return [...headers];
42
+ }
43
+ /** Add the headers the response does not set itself. Strict-Transport-Security only when the request came over HTTPS. */
44
+ export function applySecureHeaders(response, headers, https) {
45
+ const missing = headers.filter(([name]) => (name !== HSTS || https) && !response.headers.has(name));
46
+ if (!missing.length)
47
+ return response;
48
+ try {
49
+ for (const [name, value] of missing)
50
+ response.headers.set(name, value);
51
+ return response;
52
+ }
53
+ catch {
54
+ // Immutable headers (Response.redirect, a fetch() response passed through): copy the response.
55
+ const copy = new Response(response.body, response);
56
+ for (const [name, value] of missing)
57
+ copy.headers.set(name, value);
58
+ return copy;
59
+ }
60
+ }
61
+ //# sourceMappingURL=secure-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secure-headers.js","sourceRoot":"","sources":["../src/secure-headers.ts"],"names":[],"mappings":"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"}
package/dist/serve.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /** Start an HTTP server on Bun, Deno or Node using the app's fetch handler. */
2
1
  import type { App } from "./app.ts";
3
2
  export interface ServeOptions {
4
3
  port?: number;
@@ -8,4 +7,57 @@ export interface Server {
8
7
  port: number;
9
8
  close(): Promise<void>;
10
9
  }
10
+ /**
11
+ * The parts of a Node.js http.IncomingMessage that Inferlo reads. Express, Fastify (request.raw),
12
+ * NestJS, Koa (ctx.req) and plain node:http requests all have this shape.
13
+ */
14
+ export interface NodeRequest {
15
+ method?: string;
16
+ url?: string;
17
+ headers: Record<string, string | string[] | undefined>;
18
+ socket?: {
19
+ remoteAddress?: string;
20
+ encrypted?: boolean;
21
+ } | null;
22
+ /** A body the host framework already read (express.json(), body-parser). Used instead of the stream. */
23
+ body?: unknown;
24
+ [Symbol.asyncIterator](): AsyncIterator<unknown>;
25
+ }
26
+ /** The parts of a Node.js http.ServerResponse that Inferlo writes to. */
27
+ export interface NodeResponse {
28
+ readonly headersSent: boolean;
29
+ readonly writableFinished?: boolean;
30
+ writeHead(status: number, headers: Record<string, string | string[]>): unknown;
31
+ write(chunk: Uint8Array): boolean;
32
+ end(chunk?: string): unknown;
33
+ once(event: "drain", listener: () => void): unknown;
34
+ on(event: "close", listener: () => void): unknown;
35
+ }
36
+ export interface NodeHandlerOptions {
37
+ /**
38
+ * Path the app is mounted under, for example "/ai". It is removed from the URL before routing, so
39
+ * GET /ai/tickets matches route.get("/tickets"). Requests outside it go to next() (or get a 404).
40
+ * Leave it out when the host framework already strips the mount path (Express app.use("/ai", ...)).
41
+ */
42
+ prefix?: string;
43
+ }
44
+ export interface FromNodeRequestOptions extends NodeHandlerOptions {
45
+ /** Aborts the request (for example when the client disconnects). */
46
+ signal?: AbortSignal;
47
+ }
48
+ export type NodeHandler = (req: NodeRequest, res: NodeResponse, next?: (error?: unknown) => void) => Promise<void>;
49
+ /**
50
+ * Convert a Node.js request (node:http, Express, Fastify's request.raw, NestJS) to a Web Request.
51
+ * With prefix, the prefix is removed from the path; a request outside it keeps its path.
52
+ */
53
+ export declare function fromNodeRequest(req: NodeRequest, options?: FromNodeRequestOptions): Request;
54
+ /** Write a Web Response (status, headers with every Set-Cookie, streamed body) to a Node response. */
55
+ export declare function writeResponse(res: NodeResponse, response: Response, method: string): Promise<void>;
56
+ /**
57
+ * A (req, res, next) handler that runs a whole Inferlo app (routes, OpenAPI, MCP) inside node:http,
58
+ * Express, Fastify, NestJS or any framework that hands out Node requests and responses.
59
+ */
60
+ export declare function toNodeHandler(app: Pick<App<any>, "fetch">, options?: NodeHandlerOptions): NodeHandler;
11
61
  export declare function serve(app: Pick<App<any>, "fetch">, options?: ServeOptions): Promise<Server>;
62
+ /** Closes every open connection of a Node server from serve(), for app.close() after its timeout. */
63
+ export declare const forceClosers: WeakMap<Server, () => void>;
package/dist/serve.js CHANGED
@@ -1,86 +1,182 @@
1
+ /**
2
+ * Run an app's fetch handler as an HTTP server (Bun, Deno or Node), or inside another Node framework:
3
+ *
4
+ * await serve(app, { port: 3000 })
5
+ * expressApp.use("/ai", toNodeHandler(app)) // Express strips the mount path
6
+ * http.createServer(toNodeHandler(app, { prefix: "/ai" })) // or strip it with prefix
7
+ */
8
+ import { createRedactor } from "./redact.js";
1
9
  import { clientIps } from "./client-ip.js";
2
- export async function serve(app, options = {}) {
3
- const port = options.port ?? 3000;
4
- const hostname = options.hostname ?? "0.0.0.0";
5
- const g = globalThis;
6
- if (g.Bun) {
7
- const server = g.Bun.serve({ port, hostname, fetch: (req, bunServer) => app.fetch(req, bunServer) });
8
- return { port: server.port, close: async () => server.stop(true) };
10
+ function normalizePrefix(prefix) {
11
+ if (!prefix || prefix === "/")
12
+ return "";
13
+ return ("/" + prefix.replace(/^\/+|\/+$/g, "")).replace(/\/+/g, "/");
14
+ }
15
+ /** The path and query of a Node request with the prefix removed, or null when it is outside the prefix. */
16
+ function stripPrefix(rawUrl, prefix) {
17
+ const url = rawUrl.startsWith("/") ? rawUrl : "/" + rawUrl;
18
+ if (!prefix)
19
+ return url;
20
+ if (!url.startsWith(prefix))
21
+ return null;
22
+ const rest = url.slice(prefix.length);
23
+ if (rest === "" || rest[0] === "?")
24
+ return "/" + rest;
25
+ if (rest[0] !== "/")
26
+ return null;
27
+ return rest;
28
+ }
29
+ function bodyStream(req) {
30
+ const iterator = req[Symbol.asyncIterator]();
31
+ return new ReadableStream({
32
+ async pull(controller) {
33
+ try {
34
+ const { value, done } = await iterator.next();
35
+ if (done)
36
+ return controller.close();
37
+ controller.enqueue(typeof value === "string" ? new TextEncoder().encode(value) : value);
38
+ }
39
+ catch (error) {
40
+ controller.error(error);
41
+ }
42
+ },
43
+ async cancel() {
44
+ await iterator.return?.();
45
+ },
46
+ });
47
+ }
48
+ function buildRequest(req, path, signal) {
49
+ const host = [req.headers.host].flat()[0] ?? "localhost";
50
+ const url = new URL(path, `${req.socket?.encrypted ? "https" : "http"}://${host}`);
51
+ const headers = new Headers();
52
+ for (const [k, v] of Object.entries(req.headers)) {
53
+ if (v === undefined || k.startsWith(":"))
54
+ continue;
55
+ if (Array.isArray(v))
56
+ for (const item of v)
57
+ headers.append(k, item);
58
+ else
59
+ headers.set(k, v);
9
60
  }
10
- if (g.Deno) {
11
- const server = g.Deno.serve({ port, hostname }, (req, info) => app.fetch(req, info));
12
- return { port: server.addr.port, close: () => server.shutdown() };
61
+ const method = (req.method ?? "GET").toUpperCase();
62
+ let body;
63
+ if (method !== "GET" && method !== "HEAD") {
64
+ if (req.body !== undefined) {
65
+ // The host framework already read the stream: use what it parsed.
66
+ headers.delete("content-length");
67
+ if (typeof req.body === "string" || req.body instanceof Uint8Array)
68
+ body = req.body;
69
+ else {
70
+ body = JSON.stringify(req.body);
71
+ headers.set("content-type", "application/json");
72
+ }
73
+ }
74
+ else
75
+ body = bodyStream(req);
13
76
  }
14
- const http = await import("node:http");
15
- const { Readable } = await import("node:stream");
16
- const server = http.createServer(async (req, res) => {
77
+ const request = new Request(url, {
78
+ method,
79
+ headers,
80
+ body,
81
+ signal,
82
+ // @ts-expect-error duplex is required by Node for streaming bodies
83
+ duplex: "half",
84
+ });
85
+ if (req.socket?.remoteAddress)
86
+ clientIps.set(request, req.socket.remoteAddress);
87
+ return request;
88
+ }
89
+ /**
90
+ * Convert a Node.js request (node:http, Express, Fastify's request.raw, NestJS) to a Web Request.
91
+ * With prefix, the prefix is removed from the path; a request outside it keeps its path.
92
+ */
93
+ export function fromNodeRequest(req, options = {}) {
94
+ const raw = req.url ?? "/";
95
+ const path = stripPrefix(raw, normalizePrefix(options.prefix)) ?? (raw.startsWith("/") ? raw : "/" + raw);
96
+ return buildRequest(req, path, options.signal);
97
+ }
98
+ /** Write a Web Response (status, headers with every Set-Cookie, streamed body) to a Node response. */
99
+ export async function writeResponse(res, response, method) {
100
+ const outHeaders = {};
101
+ response.headers.forEach((value, key) => {
102
+ if (key === "set-cookie")
103
+ return;
104
+ outHeaders[key] = value;
105
+ });
106
+ const cookies = response.headers.getSetCookie?.() ?? [];
107
+ if (cookies.length)
108
+ outHeaders["set-cookie"] = cookies;
109
+ res.writeHead(response.status, outHeaders);
110
+ if (!response.body || method === "HEAD")
111
+ return void res.end();
112
+ const reader = response.body.getReader();
113
+ while (true) {
114
+ const { value, done } = await reader.read();
115
+ if (done)
116
+ break;
117
+ if (!res.write(value))
118
+ await new Promise((r) => res.once("drain", () => r()));
119
+ }
120
+ res.end();
121
+ }
122
+ /**
123
+ * A (req, res, next) handler that runs a whole Inferlo app (routes, OpenAPI, MCP) inside node:http,
124
+ * Express, Fastify, NestJS or any framework that hands out Node requests and responses.
125
+ */
126
+ export function toNodeHandler(app, options = {}) {
127
+ const prefix = normalizePrefix(options.prefix);
128
+ return async (req, res, next) => {
129
+ const path = stripPrefix(req.url ?? "/", prefix);
130
+ if (path === null && next)
131
+ return next();
17
132
  try {
18
- const host = req.headers.host ?? `localhost:${port}`;
19
- const url = new URL(req.url ?? "/", `http://${host}`);
20
- const headers = new Headers();
21
- for (const [k, v] of Object.entries(req.headers)) {
22
- if (v === undefined)
23
- continue;
24
- if (Array.isArray(v))
25
- for (const item of v)
26
- headers.append(k, item);
27
- else
28
- headers.set(k, v);
29
- }
30
- const hasBody = req.method !== "GET" && req.method !== "HEAD";
31
133
  const controller = new AbortController();
32
134
  res.on("close", () => {
33
135
  if (!res.writableFinished)
34
136
  controller.abort();
35
137
  });
36
- const request = new Request(url, {
37
- method: req.method,
38
- headers,
39
- body: hasBody ? Readable.toWeb(req) : undefined,
40
- signal: controller.signal,
41
- // @ts-expect-error duplex is required by Node for streaming bodies
42
- duplex: "half",
43
- });
44
- if (req.socket.remoteAddress)
45
- clientIps.set(request, req.socket.remoteAddress);
138
+ const request = buildRequest(req, path ?? (req.url ?? "/"), controller.signal);
46
139
  const response = await app.fetch(request);
47
- const outHeaders = {};
48
- response.headers.forEach((value, key) => {
49
- if (key === "set-cookie")
50
- return;
51
- outHeaders[key] = value;
52
- });
53
- const cookies = response.headers.getSetCookie?.() ?? [];
54
- if (cookies.length)
55
- outHeaders["set-cookie"] = cookies;
56
- res.writeHead(response.status, outHeaders);
57
- if (!response.body || req.method === "HEAD")
58
- return void res.end();
59
- const reader = response.body.getReader();
60
- while (true) {
61
- const { value, done } = await reader.read();
62
- if (done)
63
- break;
64
- if (!res.write(value))
65
- await new Promise((r) => res.once("drain", r));
66
- }
67
- res.end();
140
+ await writeResponse(res, response, request.method);
68
141
  }
69
142
  catch (error) {
70
- console.error("[inferlo] server error", error);
143
+ console.error("[inferlo] server error", createRedactor([]).value(error));
71
144
  if (!res.headersSent)
72
145
  res.writeHead(500, { "content-type": "application/json" });
73
146
  res.end(JSON.stringify({ error: { code: "IL9000", message: "Internal server error" } }));
74
147
  }
75
- });
148
+ };
149
+ }
150
+ export async function serve(app, options = {}) {
151
+ const port = options.port ?? 3000;
152
+ const hostname = options.hostname ?? "0.0.0.0";
153
+ const g = globalThis;
154
+ if (g.Bun) {
155
+ const server = g.Bun.serve({ port, hostname, fetch: (req, bunServer) => app.fetch(req, bunServer) });
156
+ return { port: server.port, close: async () => server.stop(true) };
157
+ }
158
+ if (g.Deno) {
159
+ const server = g.Deno.serve({ port, hostname }, (req, info) => app.fetch(req, info));
160
+ return { port: server.addr.port, close: () => server.shutdown() };
161
+ }
162
+ const http = await import("node:http");
163
+ const handler = toNodeHandler(app);
164
+ const server = http.createServer((req, res) => void handler(req, res));
76
165
  await new Promise((resolve, reject) => {
77
166
  server.once("error", reject);
78
167
  server.listen(port, hostname, () => resolve());
79
168
  });
80
169
  const address = server.address();
81
- return {
170
+ const result = {
82
171
  port: typeof address === "object" && address ? address.port : port,
83
- close: () => new Promise((resolve) => server.close(() => resolve())),
172
+ close: () => new Promise((resolve) => {
173
+ server.close(() => resolve());
174
+ server.closeIdleConnections();
175
+ }),
84
176
  };
177
+ forceClosers.set(result, () => server.closeAllConnections());
178
+ return result;
85
179
  }
180
+ /** Closes every open connection of a Node server from serve(), for app.close() after its timeout. */
181
+ export const forceClosers = new WeakMap();
86
182
  //# sourceMappingURL=serve.js.map
package/dist/serve.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
package/dist/trpc.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Inferlo inside tRPC (11.0 and later), without a runtime dependency on tRPC:
3
+ *
4
+ * import { initTRPC } from "@trpc/server";
5
+ * import { createAI, anthropic, t } from "@browserlessapi/inferlo/ai";
6
+ * import { inferloAI, trpcTools } from "@browserlessapi/inferlo/trpc";
7
+ *
8
+ * const trpc = initTRPC.context<Context>().create();
9
+ * const aiProcedure = trpc.procedure.use(inferloAI(ai)); // ctx.ai in every procedure, typed
10
+ *
11
+ * export const appRouter = trpc.router({
12
+ * summarize: aiProcedure.input(t.object({ text: t.string() })) // Inferlo schemas work as tRPC inputs
13
+ * .mutation(async ({ ctx, input }) => (await ctx.ai.generate({ model: "fast", prompt: input.text })).text),
14
+ * chat: aiProcedure.input(t.object({ prompt: t.string() }))
15
+ * .subscription(async function* ({ ctx, input, signal }) {
16
+ * yield* ctx.ai.stream({ model: "fast", prompt: input.prompt, signal }); // text chunks
17
+ * }),
18
+ * });
19
+ *
20
+ * // Procedures as AI tools, called as the current user through a caller:
21
+ * const tools = trpcTools(appRouter, {
22
+ * caller: appRouter.createCaller(ctx),
23
+ * procedures: { "tickets.create": "Create a support ticket for the current user." },
24
+ * });
25
+ * await ai.generate({ model: "fast", prompt, tools, maxSteps: 5 });
26
+ *
27
+ * This entry point never loads the router, database or jobs code.
28
+ */
29
+ import type { MiddlewareFunction } from "@trpc/server/unstable-core-do-not-import";
30
+ import type { AI } from "./ai/index.ts";
31
+ import { type ToolDef } from "./ai/tool.ts";
32
+ import { ObjectSchema, type Shape } from "./schema.ts";
33
+ /**
34
+ * A tRPC middleware that puts an AI client (createAI(...) or an Inferlo app's app.ai) on the context:
35
+ * const aiProcedure = trpc.procedure.use(inferloAI(ai)), then ctx.ai in its procedures.
36
+ */
37
+ export declare function inferloAI(ai: AI): MiddlewareFunction<any, any, any, {
38
+ ai: AI;
39
+ }, any>;
40
+ /** A procedure offered as a tool: its description, or { description, input } for procedures whose input is not a t.object. */
41
+ export type TrpcToolProcedure = string | {
42
+ description: string;
43
+ input: ObjectSchema<Shape>;
44
+ };
45
+ export interface TrpcToolsOptions {
46
+ /** Runs the procedures: appRouter.createCaller(ctx) or createCallerFactory(appRouter)(ctx), with the current user's context. */
47
+ caller: object;
48
+ /** Procedure paths ("tickets.create") and what each does. The model reads the description to decide when to call it. */
49
+ procedures: Record<string, TrpcToolProcedure>;
50
+ }
51
+ /**
52
+ * Makes AI tools from tRPC queries and mutations, for ai.generate({ tools }). Each tool is named after its
53
+ * path with "." replaced by "_" ("tickets.create" becomes tickets_create), takes the procedure's t.object input,
54
+ * and runs through the caller, so the procedure's middleware, auth and validation apply. TRPCErrors such as
55
+ * NOT_FOUND or FORBIDDEN go back to the model as failed tool results; server errors reject ai.generate.
56
+ */
57
+ export declare function trpcTools(router: object, options: TrpcToolsOptions): ToolDef[];
package/dist/trpc.js ADDED
@@ -0,0 +1,142 @@
1
+ import { tool } from "./ai/tool.js";
2
+ import { assertAIClient } from "./ai-client.js";
3
+ import { httpError, InferloError } from "./errors.js";
4
+ import { ObjectSchema, t } from "./schema.js";
5
+ /**
6
+ * A tRPC middleware that puts an AI client (createAI(...) or an Inferlo app's app.ai) on the context:
7
+ * const aiProcedure = trpc.procedure.use(inferloAI(ai)), then ctx.ai in its procedures.
8
+ */
9
+ export function inferloAI(ai) {
10
+ assertAIClient(ai, 'Pass createAI({ providers, models }) from "@browserlessapi/inferlo/ai", or an Inferlo app\'s app.ai: trpc.procedure.use(inferloAI(ai)).');
11
+ return ({ next }) => next({ ctx: { ai } });
12
+ }
13
+ /** HTTP status of each tRPC error code a model can correct (the others reject ai.generate). */
14
+ const CLIENT_ERRORS = {
15
+ BAD_REQUEST: 400,
16
+ PARSE_ERROR: 400,
17
+ UNAUTHORIZED: 401,
18
+ PAYMENT_REQUIRED: 402,
19
+ FORBIDDEN: 403,
20
+ NOT_FOUND: 404,
21
+ METHOD_NOT_SUPPORTED: 405,
22
+ TIMEOUT: 408,
23
+ CONFLICT: 409,
24
+ PRECONDITION_FAILED: 412,
25
+ PAYLOAD_TOO_LARGE: 413,
26
+ UNSUPPORTED_MEDIA_TYPE: 415,
27
+ UNPROCESSABLE_CONTENT: 422,
28
+ TOO_MANY_REQUESTS: 429,
29
+ CLIENT_CLOSED_REQUEST: 499,
30
+ };
31
+ /**
32
+ * Makes AI tools from tRPC queries and mutations, for ai.generate({ tools }). Each tool is named after its
33
+ * path with "." replaced by "_" ("tickets.create" becomes tickets_create), takes the procedure's t.object input,
34
+ * and runs through the caller, so the procedure's middleware, auth and validation apply. TRPCErrors such as
35
+ * NOT_FOUND or FORBIDDEN go back to the model as failed tool results; server errors reject ai.generate.
36
+ */
37
+ export function trpcTools(router, options) {
38
+ const problems = [];
39
+ const procedures = router?._def?.procedures;
40
+ if (!procedures || typeof procedures !== "object")
41
+ problems.push("the first argument must be a tRPC router (trpc.router({ ... }))");
42
+ if (!options || typeof options !== "object") {
43
+ problems.push("options must be { caller, procedures }");
44
+ throw invalid(problems);
45
+ }
46
+ if (!options.caller || (typeof options.caller !== "object" && typeof options.caller !== "function"))
47
+ problems.push("caller must be appRouter.createCaller(ctx)");
48
+ const entries = options.procedures && typeof options.procedures === "object" ? Object.entries(options.procedures) : [];
49
+ if (!entries.length)
50
+ problems.push('procedures must name at least one procedure: { "tickets.create": "Create a support ticket." }');
51
+ if (problems.length)
52
+ throw invalid(problems);
53
+ const tools = [];
54
+ for (const [path, entry] of entries) {
55
+ const procedure = procedures[path];
56
+ if (!procedure) {
57
+ const known = Object.keys(procedures);
58
+ problems.push(`"${path}" is not a procedure of this router${known.length ? ` (procedures: ${known.join(", ")})` : ""}`);
59
+ continue;
60
+ }
61
+ const type = procedure._def?.type;
62
+ if (type !== "query" && type !== "mutation") {
63
+ problems.push(`"${path}" is a ${type ?? "unknown"} procedure; only queries and mutations can be tools`);
64
+ continue;
65
+ }
66
+ const description = typeof entry === "string" ? entry : entry?.description;
67
+ if (typeof description !== "string" || !description.trim()) {
68
+ problems.push(`"${path}" needs a description of what it does`);
69
+ continue;
70
+ }
71
+ const inputs = procedure._def?.inputs ?? [];
72
+ let input;
73
+ if (typeof entry === "object" && entry !== null && entry.input !== undefined) {
74
+ if (!(entry.input instanceof ObjectSchema)) {
75
+ problems.push(`"${path}" input must be a t.object({ ... }) schema`);
76
+ continue;
77
+ }
78
+ input = entry.input;
79
+ }
80
+ else if (inputs.every((parser) => parser instanceof ObjectSchema)) {
81
+ const shape = {};
82
+ for (const parser of inputs)
83
+ Object.assign(shape, parser.shape);
84
+ input = t.object(shape);
85
+ }
86
+ else {
87
+ problems.push(`"${path}" has an input that is not a t.object schema from Inferlo; pass { description, input: t.object({ ... }) } for it`);
88
+ continue;
89
+ }
90
+ const name = path.replace(/\./g, "_");
91
+ const hasInput = inputs.length > 0;
92
+ const call = resolve(options.caller, path);
93
+ try {
94
+ tools.push(tool(name, { description, input }).handle(async ({ input: args }) => {
95
+ try {
96
+ return await call(hasInput ? args : undefined);
97
+ }
98
+ catch (error) {
99
+ throw toToolError(error);
100
+ }
101
+ }));
102
+ }
103
+ catch (error) {
104
+ if (error instanceof InferloError)
105
+ problems.push(`"${path}" does not make a valid tool name (${name})`);
106
+ else
107
+ throw error;
108
+ }
109
+ }
110
+ if (problems.length)
111
+ throw invalid(problems);
112
+ return tools;
113
+ }
114
+ /** caller.tickets.create, looked up when the tool runs (callers are proxies). */
115
+ function resolve(caller, path) {
116
+ return async (input) => {
117
+ let target = caller;
118
+ for (const key of path.split("."))
119
+ target = target[key];
120
+ return target(input);
121
+ };
122
+ }
123
+ /** TRPCErrors a model can correct become httpErrors (failed tool results); anything else is rethrown. */
124
+ function toToolError(error) {
125
+ if (error instanceof InferloError)
126
+ return error;
127
+ const candidate = error;
128
+ if (!candidate || typeof candidate !== "object" || candidate.name !== "TRPCError" || typeof candidate.code !== "string")
129
+ return error;
130
+ if (candidate.cause instanceof InferloError && candidate.cause.status < 500)
131
+ return candidate.cause;
132
+ const status = CLIENT_ERRORS[candidate.code];
133
+ if (status === undefined)
134
+ return error;
135
+ return httpError(status, typeof candidate.message === "string" ? candidate.message : candidate.code, { code: candidate.code });
136
+ }
137
+ function invalid(problems) {
138
+ return new InferloError("IL5017", `Invalid trpcTools() options: ${problems.join("; ")}.`, {
139
+ fix: 'Use trpcTools(appRouter, { caller: appRouter.createCaller(ctx), procedures: { "tickets.create": "Create a support ticket." } }) with queries and mutations whose input is a t.object({ ... }).',
140
+ });
141
+ }
142
+ //# sourceMappingURL=trpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trpc.js","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"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"}