@zmdb/ai 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +56 -0
  3. package/dist/chat/index.d.ts +81 -0
  4. package/dist/chat/index.d.ts.map +1 -0
  5. package/dist/chat/index.js +86 -0
  6. package/dist/chat/index.js.map +1 -0
  7. package/dist/compiler.d.ts +3 -0
  8. package/dist/compiler.d.ts.map +1 -0
  9. package/dist/compiler.js +2 -0
  10. package/dist/compiler.js.map +1 -0
  11. package/dist/http/caller.d.ts +3 -0
  12. package/dist/http/caller.d.ts.map +1 -0
  13. package/dist/http/caller.js +128 -0
  14. package/dist/http/caller.js.map +1 -0
  15. package/dist/http/generate.d.ts +7 -0
  16. package/dist/http/generate.d.ts.map +1 -0
  17. package/dist/http/generate.js +205 -0
  18. package/dist/http/generate.js.map +1 -0
  19. package/dist/http/index.d.ts +5 -0
  20. package/dist/http/index.d.ts.map +1 -0
  21. package/dist/http/index.js +6 -0
  22. package/dist/http/index.js.map +1 -0
  23. package/dist/http/parse.d.ts +11 -0
  24. package/dist/http/parse.d.ts.map +1 -0
  25. package/dist/http/parse.js +398 -0
  26. package/dist/http/parse.js.map +1 -0
  27. package/dist/http/types.d.ts +59 -0
  28. package/dist/http/types.d.ts.map +1 -0
  29. package/dist/http/types.js +20 -0
  30. package/dist/http/types.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +33 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/providers.d.ts +85 -0
  36. package/dist/providers.d.ts.map +1 -0
  37. package/dist/providers.js +252 -0
  38. package/dist/providers.js.map +1 -0
  39. package/dist/tool-runtime.d.ts +36 -0
  40. package/dist/tool-runtime.d.ts.map +1 -0
  41. package/dist/tool-runtime.js +55 -0
  42. package/dist/tool-runtime.js.map +1 -0
  43. package/package.json +66 -0
  44. package/src/chat/index.ts +202 -0
  45. package/src/compiler.ts +2 -0
  46. package/src/http/caller.ts +132 -0
  47. package/src/http/generate.ts +218 -0
  48. package/src/http/index.ts +16 -0
  49. package/src/http/parse.ts +666 -0
  50. package/src/http/types.ts +83 -0
  51. package/src/index.ts +40 -0
  52. package/src/providers.ts +443 -0
  53. package/src/tool-runtime.ts +91 -0
package/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ @zmdb/ai is free software licensed under the GNU General Public License,
5
+ version 3 or (at your option) any later version.
6
+
7
+ The complete license text is published by the Free Software Foundation at:
8
+ https://www.gnu.org/licenses/gpl-3.0.txt
9
+
10
+ SPDX-License-Identifier: GPL-3.0-or-later
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @zmdb/ai
2
+
3
+ `@zmdb/ai` provides provider-neutral tool documents, parsing, bounded chat orchestration, shared tool invocation, and OpenAPI-derived tools. Choosing a provider dialect changes a document shape; it
4
+ does not install an SDK or make a network request.
5
+
6
+ It is part of [zmdb](https://github.com/ambasta/zmdb). The root, chat, HTTP, compiler, and tool-runtime entry points are physically owned by this package. The dependency direction is `@zmdb/ai` →
7
+ `@zmdb/schema`; schema-core never imports or re-exports this package.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ yarn add @zmdb/ai@1.0.0-beta.1
13
+ ```
14
+
15
+ > **Prerelease** (`1.0.0-beta.1`). Requires **Node.js 26+** and is **ESM-only**. Ships built ESM `.js` + `.d.ts` under `./dist`.
16
+
17
+ ## Entry points
18
+
19
+ - Tool documents and parsing: `@zmdb/ai`
20
+ - Bounded provider-neutral chat loop: `@zmdb/ai/chat`
21
+ - Build-time provider document compiler: `@zmdb/ai/compiler`
22
+ - OpenAPI-derived tools and allowlisted callers: `@zmdb/ai/http`
23
+ - Shared invocation machinery for integrations: `@zmdb/ai/tool-runtime`
24
+
25
+ The only runtime dependency is `@zmdb/schema`. This package has no external dependency or peer dependency, and does not export Anthropic, LangChain, Vercel AI SDK, or MCP integrations.
26
+
27
+ ## Optional integrations
28
+
29
+ - Anthropic chat driver: `yarn add @zmdb/ai-anthropic@1.0.0-beta.1 @anthropic-ai/sdk@0.124.0`
30
+ - LangChain tool adapter: `yarn add @zmdb/ai-langchain@1.0.0-beta.1 @langchain/core@^1.2.9`
31
+ - Vercel AI SDK tool adapter: `yarn add @zmdb/ai-vercel@1.0.0-beta.1 ai@^7.0.93`
32
+ - MCP client and server cores: `yarn add @zmdb/mcp@1.0.0-beta.1`
33
+
34
+ The three SDK/framework packages declare their external dependency as an optional peer. Applications that install only `@zmdb/ai` or `@zmdb/mcp` do not install an Anthropic, LangChain, Vercel AI, or
35
+ MCP SDK.
36
+
37
+ ## Migrating from schema-core
38
+
39
+ There are no compatibility aliases in `@zmdb/schema`. Replace every removed subpath from that package directly:
40
+
41
+ | Removed schema-core subpath | Final import |
42
+ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
43
+ | `/llm` | `@zmdb/ai` for tool APIs; `@zmdb/schema/openapi` for `toJsonSchema`; use the explicit chat, HTTP, and MCP entries below for former star exports |
44
+ | `/llm/chat` | `@zmdb/ai/chat`; `anthropicDriver` and its types move to `@zmdb/ai-anthropic` |
45
+ | `/llm/http` | `@zmdb/ai/http` |
46
+ | `/llm/langchain` | `@zmdb/ai-langchain` |
47
+ | `/llm/ai-sdk` | `@zmdb/ai-vercel` |
48
+ | `/llm/mcp` | `@zmdb/mcp` |
49
+
50
+ ## Documentation
51
+
52
+ Full docs: **https://ambasta.github.io/zmdb/docs/llm-strategy.html**
53
+
54
+ ## License
55
+
56
+ GNU General Public License v3.0 or later (GPL-3.0-or-later) — see [LICENSE](./LICENSE).
@@ -0,0 +1,81 @@
1
+ import type { ToolSpec } from '../index.js';
2
+ export type ChatMessage = {
3
+ readonly role: 'system';
4
+ readonly content: string;
5
+ } | {
6
+ readonly role: 'user';
7
+ readonly content: string;
8
+ } | {
9
+ readonly role: 'assistant';
10
+ readonly content: string;
11
+ readonly toolCalls?: readonly ToolCall[];
12
+ readonly provider?: readonly ProviderPassthrough[];
13
+ } | {
14
+ readonly role: 'tool';
15
+ readonly callId: string;
16
+ readonly content: string;
17
+ readonly isError?: boolean;
18
+ };
19
+ export interface ToolCall {
20
+ readonly id: string;
21
+ readonly name: string;
22
+ readonly args: unknown;
23
+ }
24
+ export interface ProviderPassthrough {
25
+ readonly kind: string;
26
+ readonly raw: unknown;
27
+ }
28
+ export interface ChatDriver {
29
+ next(messages: readonly ChatMessage[], tools: readonly ToolSpec[]): Promise<ChatMessage>;
30
+ }
31
+ type ToolHandler<T> = {
32
+ bivarianceHack(input: T, identity?: unknown): unknown | PromiseLike<unknown>;
33
+ }['bivarianceHack'];
34
+ export interface ToolEntry<T> {
35
+ readonly spec: ToolSpec;
36
+ readonly validate: (args: unknown) => T;
37
+ readonly handler: ToolHandler<T>;
38
+ readonly effectful?: boolean;
39
+ }
40
+ export type ToolRegistry = Readonly<Record<string, ToolEntry<unknown>>>;
41
+ type ToolInputs = Readonly<Record<string, unknown>>;
42
+ interface LinkedToolEntry<T> {
43
+ readonly spec: ToolSpec;
44
+ readonly validate: (args: unknown) => T;
45
+ readonly handler: (input: T, identity?: unknown) => unknown | PromiseLike<unknown>;
46
+ readonly effectful?: boolean;
47
+ }
48
+ type LinkedRegistry<I extends ToolInputs> = {
49
+ readonly [K in keyof I]: LinkedToolEntry<I[K]>;
50
+ };
51
+ export declare function defineTools<const I extends ToolInputs, const R extends LinkedRegistry<I>>(tools: R & LinkedRegistry<I>): R;
52
+ export type HasEffectful<R> = {
53
+ [K in keyof R]: R[K] extends {
54
+ readonly effectful: false;
55
+ } ? never : K;
56
+ }[keyof R] extends never ? false : true;
57
+ export interface RunOptions {
58
+ readonly maxTurns: number;
59
+ readonly maxToolCallsPerTurn?: number;
60
+ readonly approve?: (call: ToolCall) => Promise<boolean>;
61
+ }
62
+ export type RunOptionsFor<R extends Readonly<Record<string, unknown>>> = HasEffectful<R> extends true ? RunOptions & {
63
+ readonly approve: (call: ToolCall) => Promise<boolean>;
64
+ } : RunOptions;
65
+ export interface RunResult {
66
+ readonly messages: readonly ChatMessage[];
67
+ readonly stop: 'complete' | 'max-turns' | 'max-tool-calls';
68
+ readonly turns: number;
69
+ readonly toolCalls: number;
70
+ readonly budget: number;
71
+ readonly declined: readonly ToolCall[];
72
+ readonly errors: readonly {
73
+ readonly callId: string;
74
+ readonly name: string;
75
+ readonly errorId: string;
76
+ readonly error: unknown;
77
+ }[];
78
+ }
79
+ export declare function run<const I extends ToolInputs, R extends LinkedRegistry<I>>(driver: ChatDriver, messages: readonly ChatMessage[], tools: R & LinkedRegistry<I>, opts: RunOptionsFor<R>): Promise<RunResult>;
80
+ export {};
81
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { invokeTool } from '../tool-runtime.js';
2
+ export function defineTools(tools) {
3
+ return tools;
4
+ }
5
+ const DEFAULT_MAX_TOOL_CALLS_PER_TURN = 8;
6
+ const toolErrorId = () => [...globalThis.crypto.getRandomValues(new Uint8Array(4))].map(byte => byte.toString(16).padStart(2, '0')).join('');
7
+ const toolResult = (callId, content, isError) => isError === undefined ? { role: 'tool', callId, content } : { role: 'tool', callId, content, isError };
8
+ const finish = (state, stop, budget) => ({
9
+ messages: state.messages,
10
+ stop,
11
+ turns: state.turns,
12
+ toolCalls: state.toolCalls,
13
+ budget,
14
+ declined: state.declined,
15
+ errors: state.errors,
16
+ });
17
+ export async function run(driver, messages, tools, opts) {
18
+ if (!Number.isSafeInteger(opts.maxTurns) || opts.maxTurns <= 0) {
19
+ throw new RangeError('maxTurns must be a positive safe integer');
20
+ }
21
+ const maxToolCallsPerTurn = opts.maxToolCallsPerTurn ?? DEFAULT_MAX_TOOL_CALLS_PER_TURN;
22
+ if (!Number.isSafeInteger(maxToolCallsPerTurn) || maxToolCallsPerTurn <= 0) {
23
+ throw new RangeError('maxToolCallsPerTurn must be a positive safe integer');
24
+ }
25
+ const entries = Object.entries(tools);
26
+ const firstEffectful = entries.find(([, entry]) => entry.effectful !== false);
27
+ if (firstEffectful !== undefined && opts.approve === undefined) {
28
+ throw new Error(`approve is required for effectful tool ${firstEffectful[0]}`);
29
+ }
30
+ const budget = opts.maxTurns * maxToolCallsPerTurn;
31
+ const state = {
32
+ messages: [...messages],
33
+ declined: [],
34
+ errors: [],
35
+ turns: 0,
36
+ toolCalls: 0,
37
+ };
38
+ const specs = entries.map(([, entry]) => entry.spec);
39
+ while (state.turns < opts.maxTurns) {
40
+ const answer = await driver.next(state.messages, specs);
41
+ state.messages.push(answer);
42
+ state.turns += 1;
43
+ const calls = answer.role === 'assistant' ? (answer.toolCalls ?? []) : [];
44
+ if (calls.length === 0)
45
+ return finish(state, 'complete', budget);
46
+ if (calls.length > maxToolCallsPerTurn)
47
+ return finish(state, 'max-tool-calls', budget);
48
+ for (const call of calls) {
49
+ state.toolCalls += 1;
50
+ const entry = Object.hasOwn(tools, call.name) ? tools[call.name] : undefined;
51
+ if (entry === undefined) {
52
+ state.messages.push(toolResult(call.id, `unknown tool ${call.name}`, true));
53
+ continue;
54
+ }
55
+ if (entry.effectful !== false) {
56
+ const approved = await opts.approve?.(call);
57
+ if (approved !== true) {
58
+ state.declined.push(call);
59
+ state.messages.push(toolResult(call.id, 'declined by the operator', true));
60
+ continue;
61
+ }
62
+ }
63
+ const invocation = await invokeTool(entry, call.args);
64
+ if (invocation.kind === 'validation-error') {
65
+ const { content } = invocation;
66
+ if (content !== undefined) {
67
+ state.messages.push(toolResult(call.id, content, true));
68
+ continue;
69
+ }
70
+ const id = toolErrorId();
71
+ state.errors.push({ callId: call.id, name: call.name, errorId: id, error: invocation.error });
72
+ state.messages.push(toolResult(call.id, `tool ${call.name} failed (${id})`, true));
73
+ continue;
74
+ }
75
+ if (invocation.kind === 'handler-error') {
76
+ const id = toolErrorId();
77
+ state.errors.push({ callId: call.id, name: call.name, errorId: id, error: invocation.error });
78
+ state.messages.push(toolResult(call.id, `tool ${call.name} failed (${id})`, true));
79
+ continue;
80
+ }
81
+ state.messages.push(toolResult(call.id, invocation.content));
82
+ }
83
+ }
84
+ return finish(state, 'max-turns', budget);
85
+ }
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { ToolSpecRefusalError, toolSchemaForProvider } from './providers.js';
2
+ export type { ToolSpecRefusal } from './providers.js';
3
+ //# sourceMappingURL=compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ToolSpecRefusalError, toolSchemaForProvider } from './providers.js';
2
+ //# sourceMappingURL=compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type BoundOpenApiTool, type OpenApiCallerOptions, type OpenApiGeneratedTool } from './types.js';
2
+ export declare function bindOpenApiTool<T>(tool: OpenApiGeneratedTool<T>, options: OpenApiCallerOptions): BoundOpenApiTool<T>;
3
+ //# sourceMappingURL=caller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../../src/http/caller.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAE1B,MAAM,YAAY,CAAC;AAgGpB,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,oBAAoB,GAAG,gBAAgB,CAAC,CAAC,CAAC,CA4BpH"}
@@ -0,0 +1,128 @@
1
+ // Runtime binding for provider-neutral OpenAPI tools owned by @zmdb/ai.
2
+ import { OpenApiHttpError, } from './types.js';
3
+ const DEFAULT_TIMEOUT_MS = 60_000;
4
+ const DEFAULT_MAX_RESPONSE_BYTES = 1_048_576;
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ }
8
+ function positive(name, value) {
9
+ if (!Number.isSafeInteger(value) || value <= 0)
10
+ throw new RangeError(`${name} must be a positive safe integer`);
11
+ }
12
+ function canonicalBase(value) {
13
+ const url = new URL(value);
14
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
15
+ throw new TypeError('OpenAPI tool baseUrl must use http or https');
16
+ }
17
+ if (url.username !== '' || url.password !== '') {
18
+ throw new TypeError('OpenAPI tool baseUrl must not contain credentials');
19
+ }
20
+ if (url.search !== '' || url.hash !== '') {
21
+ throw new TypeError('OpenAPI tool baseUrl must not contain a query or fragment');
22
+ }
23
+ if (!url.pathname.endsWith('/'))
24
+ url.pathname += '/';
25
+ return url;
26
+ }
27
+ function allowlistedBase(baseUrl, allowlist) {
28
+ const base = canonicalBase(baseUrl);
29
+ const allowed = allowlist.some(candidate => canonicalBase(candidate).href === base.href);
30
+ if (!allowed)
31
+ throw new Error(`OpenAPI tool base URL is not allowlisted: ${base.href}`);
32
+ return base;
33
+ }
34
+ function scalar(value, name) {
35
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
36
+ return String(value);
37
+ if (value === null)
38
+ return 'null';
39
+ throw new TypeError(`validated OpenAPI argument ${name} is not a URL scalar`);
40
+ }
41
+ function pathSegment(value, name) {
42
+ const segment = scalar(value, name);
43
+ if (segment === '.' || segment === '..') {
44
+ throw new RangeError(`validated OpenAPI argument ${name} is a URL dot segment`);
45
+ }
46
+ return encodeURIComponent(segment);
47
+ }
48
+ function requestUrl(base, request, input) {
49
+ let pathname = request.path;
50
+ for (const name of request.pathParameters) {
51
+ pathname = pathname.replaceAll(`{${name}}`, pathSegment(input[name], name));
52
+ }
53
+ if (/\{[^{}]+\}/.test(pathname))
54
+ throw new Error(`OpenAPI tool path still contains a placeholder: ${pathname}`);
55
+ const url = new URL(pathname.replace(/^\/+/, ''), base);
56
+ for (const name of request.queryParameters) {
57
+ const value = input[name];
58
+ if (value === undefined)
59
+ continue;
60
+ if (Array.isArray(value)) {
61
+ for (const item of value)
62
+ url.searchParams.append(name, scalar(item, name));
63
+ }
64
+ else {
65
+ url.searchParams.append(name, scalar(value, name));
66
+ }
67
+ }
68
+ return url;
69
+ }
70
+ function requestBody(request, input) {
71
+ if (!request.hasBody)
72
+ return undefined;
73
+ const body = {};
74
+ for (const name of request.bodyParameters) {
75
+ if (input[name] !== undefined)
76
+ body[name] = input[name];
77
+ }
78
+ return JSON.stringify(body);
79
+ }
80
+ async function responseBody(response, maximum) {
81
+ const announced = response.headers.get('content-length');
82
+ if (announced !== null && Number(announced) > maximum) {
83
+ throw new RangeError(`OpenAPI tool response exceeds ${maximum} bytes`);
84
+ }
85
+ const body = await response.text();
86
+ if (new TextEncoder().encode(body).byteLength > maximum) {
87
+ throw new RangeError(`OpenAPI tool response exceeds ${maximum} bytes`);
88
+ }
89
+ return body;
90
+ }
91
+ function parseResponse(response, body) {
92
+ if (body === '')
93
+ return undefined;
94
+ const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
95
+ return contentType.includes('json') ? JSON.parse(body) : body;
96
+ }
97
+ export function bindOpenApiTool(tool, options) {
98
+ const base = allowlistedBase(options.baseUrl, options.allowedBaseUrls);
99
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
100
+ const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
101
+ positive('timeoutMs', timeoutMs);
102
+ positive('maxResponseBytes', maxResponseBytes);
103
+ const fetch = options.fetch ?? globalThis.fetch;
104
+ return {
105
+ spec: tool.spec,
106
+ validate: tool.validate,
107
+ async handler(input) {
108
+ if (!isRecord(input))
109
+ throw new TypeError('generated OpenAPI validator returned a non-object');
110
+ const url = requestUrl(base, tool.request, input);
111
+ const body = requestBody(tool.request, input);
112
+ const headers = new Headers(options.headers);
113
+ if (body !== undefined && !headers.has('content-type'))
114
+ headers.set('content-type', 'application/json');
115
+ const response = await fetch(url, {
116
+ method: tool.request.method,
117
+ headers,
118
+ ...(body === undefined ? {} : { body }),
119
+ signal: AbortSignal.timeout(timeoutMs),
120
+ });
121
+ const text = await responseBody(response, maxResponseBytes);
122
+ if (!response.ok)
123
+ throw new OpenApiHttpError(response.status, text);
124
+ return parseResponse(response, text);
125
+ },
126
+ };
127
+ }
128
+ //# sourceMappingURL=caller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caller.js","sourceRoot":"","sources":["../../src/http/caller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { OpenApiToolsOptions } from './types.js';
2
+ /**
3
+ * Render a checked-in TypeScript module. The generated `assert<T>` calls are
4
+ * deliberately left for `@zmdb/validator`'s existing build transform.
5
+ */
6
+ export declare function generateOpenApiToolsModule(document: unknown, options?: OpenApiToolsOptions): string;
7
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/http/generate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAmLtD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAgCvG"}
@@ -0,0 +1,205 @@
1
+ // Checked-in source generation for the public @zmdb/ai/http boundary.
2
+ import { compileOpenApiTools } from './parse.js';
3
+ function isRecord(value) {
4
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
5
+ }
6
+ function stringLiteral(value) {
7
+ return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n').replaceAll('\r', '\\r')}'`;
8
+ }
9
+ function literal(value) {
10
+ if (typeof value === 'string')
11
+ return stringLiteral(value);
12
+ if (typeof value === 'number' || typeof value === 'boolean' || value === null)
13
+ return String(value);
14
+ throw new TypeError(`OpenAPI enum contains an unsupported literal at generation: ${String(value)}`);
15
+ }
16
+ function tag(context, name, value) {
17
+ if (typeof value !== (name === 'Pattern' ? 'string' : 'number'))
18
+ return undefined;
19
+ context.tags.add(name);
20
+ return `${name}<${literal(value)}>`;
21
+ }
22
+ function constrained(base, schema, context) {
23
+ const tags = [
24
+ tag(context, 'Min', schema['minimum']),
25
+ tag(context, 'Max', schema['maximum']),
26
+ tag(context, 'MinLength', schema['minLength']),
27
+ tag(context, 'MaxLength', schema['maxLength']),
28
+ tag(context, 'Pattern', schema['pattern']),
29
+ ].filter(value => value !== undefined);
30
+ return tags.length === 0 ? base : `${base} & ${tags.join(' & ')}`;
31
+ }
32
+ function propertyName(name) {
33
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : stringLiteral(name);
34
+ }
35
+ function requiredNames(schema) {
36
+ const value = schema['required'];
37
+ return new Set(Array.isArray(value) ? value.filter(name => typeof name === 'string') : []);
38
+ }
39
+ function objectType(schema, context) {
40
+ const properties = schema['properties'];
41
+ if (!isRecord(properties))
42
+ return 'Readonly<Record<never, never>>';
43
+ const required = requiredNames(schema);
44
+ const lines = Object.keys(properties)
45
+ .toSorted()
46
+ .map(name => {
47
+ const child = properties[name];
48
+ if (!isRecord(child))
49
+ throw new TypeError(`OpenAPI property ${context.path}.${name} is not a schema`);
50
+ const optional = required.has(name) ? '' : '?';
51
+ return ` readonly ${propertyName(name)}${optional}: ${schemaType(child, { ...context, path: `${context.path}.${name}` })};`;
52
+ });
53
+ return lines.length === 0 ? 'Readonly<Record<never, never>>' : `{\n${lines.join('\n')}\n}`;
54
+ }
55
+ function union(schema, key, context) {
56
+ const value = schema[key];
57
+ if (!Array.isArray(value))
58
+ return undefined;
59
+ return value
60
+ .map((member, index) => {
61
+ if (!isRecord(member))
62
+ throw new TypeError(`OpenAPI ${key} member ${index} at ${context.path} is not a schema`);
63
+ return schemaType(member, { ...context, path: `${context.path}.${key}[${index}]` });
64
+ })
65
+ .join(' | ');
66
+ }
67
+ function schemaType(schema, context) {
68
+ const oneOf = union(schema, 'oneOf', context);
69
+ if (oneOf !== undefined)
70
+ return oneOf;
71
+ const anyOf = union(schema, 'anyOf', context);
72
+ if (anyOf !== undefined)
73
+ return anyOf;
74
+ const values = schema['enum'];
75
+ if (Array.isArray(values) && values.length > 0)
76
+ return values.map(literal).join(' | ');
77
+ const type = schema['type'];
78
+ if (Array.isArray(type)) {
79
+ return type
80
+ .map(member => schemaType({ ...schema, type: member }, context))
81
+ .filter((member, index, all) => all.indexOf(member) === index)
82
+ .join(' | ');
83
+ }
84
+ let result;
85
+ switch (type) {
86
+ case 'string':
87
+ result = constrained('string', schema, context);
88
+ break;
89
+ case 'integer':
90
+ case 'number':
91
+ result = constrained('number', schema, context);
92
+ break;
93
+ case 'boolean':
94
+ result = 'boolean';
95
+ break;
96
+ case 'null':
97
+ result = 'null';
98
+ break;
99
+ case 'array': {
100
+ const items = schema['items'];
101
+ if (!isRecord(items))
102
+ throw new TypeError(`OpenAPI array ${context.path} has no item schema`);
103
+ result = `readonly (${schemaType(items, { ...context, path: `${context.path}[]` })})[]`;
104
+ break;
105
+ }
106
+ case 'object':
107
+ result = objectType(schema, context);
108
+ break;
109
+ default:
110
+ result = isRecord(schema['properties']) ? objectType(schema, context) : 'unknown';
111
+ }
112
+ return schema['nullable'] === true && result !== 'null' ? `${result} | null` : result;
113
+ }
114
+ function typeName(operationId, used) {
115
+ const words = operationId.split(/[^A-Za-z0-9]+/).filter(Boolean);
116
+ const base = `${words.map(word => `${word[0]?.toUpperCase() ?? ''}${word.slice(1)}`).join('') || 'Tool'}Arguments`;
117
+ let candidate = /^[A-Za-z_$]/.test(base) ? base : `Tool${base}`;
118
+ let suffix = 2;
119
+ while (used.has(candidate)) {
120
+ candidate = `${base}${suffix}`;
121
+ suffix += 1;
122
+ }
123
+ used.add(candidate);
124
+ return candidate;
125
+ }
126
+ function variableName(type) {
127
+ return `${type[0]?.toLowerCase() ?? 't'}${type.slice(1).replace(/Arguments$/, 'Tool')}`;
128
+ }
129
+ function renderValue(value, depth = 0) {
130
+ if (typeof value === 'string')
131
+ return stringLiteral(value);
132
+ if (typeof value === 'number' || typeof value === 'boolean' || value === null)
133
+ return String(value);
134
+ if (Array.isArray(value)) {
135
+ if (value.length === 0)
136
+ return '[]';
137
+ if (value.every(item => item === null || ['string', 'number', 'boolean'].includes(typeof item))) {
138
+ return `[${value.map(item => renderValue(item, depth + 1)).join(', ')}]`;
139
+ }
140
+ const indent = ' '.repeat(depth + 1);
141
+ return `[\n${value.map(item => `${indent}${renderValue(item, depth + 1)},`).join('\n')}\n${' '.repeat(depth)}]`;
142
+ }
143
+ if (isRecord(value)) {
144
+ const entries = Object.entries(value);
145
+ if (entries.length === 0)
146
+ return '{}';
147
+ const indent = ' '.repeat(depth + 1);
148
+ return `{\n${entries
149
+ .map(([key, item]) => `${indent}${propertyName(key)}: ${renderValue(item, depth + 1)},`)
150
+ .join('\n')}\n${' '.repeat(depth)}}`;
151
+ }
152
+ throw new TypeError(`cannot render generated OpenAPI value: ${String(value)}`);
153
+ }
154
+ function renderTool(tool, type, variable, tags) {
155
+ const context = { tags, path: tool.spec.name };
156
+ const schema = {
157
+ type: 'object',
158
+ properties: tool.argumentSchemas,
159
+ required: tool.required,
160
+ };
161
+ const args = schemaType(schema, context);
162
+ const validationType = Object.keys(tool.argumentSchemas).length === 0 ? 'OpenApiNoArguments' : type;
163
+ return `export type ${type} = ${args};
164
+
165
+ export const ${variable}: OpenApiGeneratedTool<${type}> = {
166
+ spec: ${renderValue(tool.spec, 1)},
167
+ request: ${renderValue(tool.request, 1)},
168
+ validate: (input: unknown): ${type} => assert<${validationType}>(input),
169
+ };`;
170
+ }
171
+ /**
172
+ * Render a checked-in TypeScript module. The generated `assert<T>` calls are
173
+ * deliberately left for `@zmdb/validator`'s existing build transform.
174
+ */
175
+ export function generateOpenApiToolsModule(document, options = {}) {
176
+ const tools = compileOpenApiTools(document, options);
177
+ const tags = new Set();
178
+ const names = new Set();
179
+ const rendered = tools.map(tool => {
180
+ const type = typeName(tool.spec.name, names);
181
+ return { tool, type, variable: variableName(type) };
182
+ });
183
+ const bodies = rendered.map(entry => renderTool(entry.tool, entry.type, entry.variable, tags));
184
+ const emptyArgumentsType = rendered.some(entry => Object.keys(entry.tool.argumentSchemas).length === 0)
185
+ ? `type OpenApiNoArguments = {
186
+ readonly __openApiNoArguments?: string;
187
+ };
188
+
189
+ `
190
+ : '';
191
+ const tagImport = tags.size === 0 ? '' : `import type { ${[...tags].toSorted().join(', ')} } from '@zmdb/schema/tags';\n`;
192
+ const registry = rendered.length === 0
193
+ ? 'export const openApiTools = {};\n'
194
+ : `export const openApiTools = {\n${rendered
195
+ .map(entry => ` ${propertyName(entry.tool.spec.name)}: ${entry.variable},`)
196
+ .join('\n')}\n};\n`;
197
+ return `// generated by @zmdb/ai/http — do not edit
198
+ import type { OpenApiGeneratedTool } from '@zmdb/ai/http';
199
+ ${tagImport}import { assert } from '@zmdb/validator';
200
+
201
+ ${emptyArgumentsType}${bodies.join('\n\n')}
202
+
203
+ ${registry}`;
204
+ }
205
+ //# sourceMappingURL=generate.js.map