@formwright/ai 0.2.0 → 0.2.2

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.
@@ -84,7 +84,7 @@ ${options.guidelines}` : "");
84
84
  );
85
85
  }
86
86
  async function defaultProvider(options) {
87
- const { claudeProvider: claudeProvider2 } = await import('./claude-BNP64G4N.js');
87
+ const { claudeProvider: claudeProvider2 } = await import('./claude-X22WWSMS.js');
88
88
  const opts = {};
89
89
  if (options.apiKey !== void 0) opts.apiKey = options.apiKey;
90
90
  if (options.model !== void 0) opts.model = options.model;
@@ -103,6 +103,15 @@ Field types:
103
103
  - "select" | "radio" \u2014 need "options": [{ "label": string, "value": string|number }]
104
104
  - "group" \u2014 a nested object; needs "fields": [ ...child fields ]. Produces an object in the payload.
105
105
  - "collection" \u2014 a repeatable list of objects; needs "fields", optional "itemLabel", "addLabel", "minItems", "maxItems". Produces an array of objects.
106
+ - "steps" \u2014 a multi-step wizard; needs "fields": [ step, step, \u2026 ]. Each child must have "type": "step".
107
+ - "step" \u2014 one wizard step (like a group); needs "label", optional "description", and "fields". Produces a nested object in the payload under the step id.
108
+
109
+ For multi-step / wizard forms, wrap steps in a "steps" container:
110
+ { "id": "wizard", "type": "steps", "layout": "bar"|"tabs"|"numbers", "fields": [
111
+ { "id": "personal", "type": "step", "label": "Personal", "fields": [ ... ] },
112
+ { "id": "account", "type": "step", "label": "Account", "fields": [ ... ] }
113
+ ]}
114
+ Optional on "steps": "showProgress" (default true), "validateOnNext" (default true), "nextLabel", "prevLabel", "submitLabel".
106
115
 
107
116
  Validation (optional): "validation": { "kind": "string"|"number", "required"?, "min"?, "max"?, "minLength"?, "maxLength"?, "pattern"?, "format"?: "email"|"url"|"uuid", "message"? }.
108
117
 
@@ -152,5 +161,5 @@ function claudeProvider(options = {}) {
152
161
  }
153
162
 
154
163
  export { DEFAULT_CLAUDE_MODEL, SYSTEM_PROMPT, SchemaGenerationError, buildPrompt, claudeProvider, defineProvider, generateSchema, openaiProvider };
155
- //# sourceMappingURL=chunk-I3HZFUC7.js.map
156
- //# sourceMappingURL=chunk-I3HZFUC7.js.map
164
+ //# sourceMappingURL=chunk-UHEGF537.js.map
165
+ //# sourceMappingURL=chunk-UHEGF537.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/openai.ts","../src/index.ts","../src/claude.ts"],"names":["claudeProvider"],"mappings":"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","file":"chunk-UHEGF537.js","sourcesContent":["/**\n * OpenAI (GPT) provider — uses JSON mode on the Chat Completions API.\n *\n * To avoid a hard dependency on the `openai` package, pass your own client:\n *\n * import OpenAI from \"openai\";\n * import { generateSchema, openaiProvider } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a contact form\", {\n * provider: openaiProvider({ client: new OpenAI() }),\n * });\n *\n * Any client matching {@link OpenAILike} works (Azure OpenAI, compatible gateways).\n */\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Minimal structural shape of an OpenAI client's `chat.completions.create`. */\nexport interface OpenAILike {\n chat: {\n completions: {\n create(body: {\n model: string;\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>;\n response_format?: { type: \"json_object\" };\n }): Promise<{ choices: Array<{ message: { content: string | null } }> }>;\n };\n };\n}\n\nexport interface OpenAIProviderOptions {\n /** A configured OpenAI client (e.g. `new OpenAI()`). Required. */\n readonly client: OpenAILike;\n readonly model?: string;\n}\n\nexport function openaiProvider(options: OpenAIProviderOptions): SchemaProvider {\n const model = options.model ?? \"gpt-4o\";\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await options.client.chat.completions.create({\n model,\n response_format: { type: \"json_object\" },\n messages: [\n { role: \"system\", content: input.system },\n {\n role: \"user\",\n content: `${buildPrompt(input)}\\n\\nRespond with ONLY the JSON schema object.`,\n },\n ],\n });\n const content = response.choices[0]?.message?.content;\n if (!content) throw new Error(\"OpenAI returned no content.\");\n return JSON.parse(content) as unknown;\n },\n };\n}\n","/**\n * @formwright/ai — turn a natural-language form description into a *validated*\n * Formwright schema using an LLM.\n *\n * import { generateSchema } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a signup form with a US-only state field\");\n * new Form(schema).mount(el);\n *\n * Provider-agnostic: the validate → repair loop lives here, and the actual model\n * call is a pluggable {@link SchemaProvider}. A Claude provider ships built-in\n * (the default); an OpenAI provider and a custom-function provider let you use\n * GPT, Gemini, Mistral, a local model, or anything else — each through its own\n * native SDK, never a compatibility shim.\n *\n * Whatever the provider returns is checked with `@formwright/schema`'s validator;\n * if it's invalid, the precise issues are fed back for repair — so what you get\n * out always satisfies the runtime (or a thrown {@link SchemaGenerationError}).\n */\nimport { validateSchema, type FormSchema, type ValidationIssue } from \"@formwright/schema\";\n\nexport { claudeProvider, type ClaudeProviderOptions } from \"./claude.js\";\nexport { openaiProvider, type OpenAIProviderOptions, type OpenAILike } from \"./openai.js\";\n\n/** A pluggable model backend: produce a candidate schema object for a request. */\nexport interface SchemaProvider {\n propose(input: ProposeInput): Promise<unknown>;\n}\n\nexport interface ProposeInput {\n readonly description: string;\n readonly system: string;\n /** Present on a repair attempt: the previous (invalid) output and why it failed. */\n readonly repair?: RepairContext;\n}\n\nexport interface RepairContext {\n readonly previous: unknown;\n readonly issues: readonly ValidationIssue[];\n}\n\nexport interface GenerateOptions {\n /** Model backend. Defaults to Claude (needs `ANTHROPIC_API_KEY` or `apiKey`). */\n readonly provider?: SchemaProvider;\n /** Convenience for the default Claude provider when `provider` is omitted. */\n readonly apiKey?: string;\n readonly model?: string;\n /** How many times to feed validation errors back for repair (default 2). */\n readonly maxRepairAttempts?: number;\n /** Override the Formwright DSL system prompt. */\n readonly system?: string;\n /** Extra guidance appended to the system prompt. */\n readonly guidelines?: string;\n}\n\nexport interface GenerateResult {\n readonly schema: FormSchema;\n /** Number of model round-trips it took (1 = valid on the first try). */\n readonly attempts: number;\n}\n\nexport class SchemaGenerationError extends Error {\n readonly issues: readonly ValidationIssue[];\n constructor(message: string, issues: readonly ValidationIssue[]) {\n super(message);\n this.name = \"SchemaGenerationError\";\n this.issues = issues;\n }\n}\n\n/** Wrap a plain async function as a provider (for Gemini, Mistral, local models, …). */\nexport function defineProvider(propose: (input: ProposeInput) => Promise<unknown>): SchemaProvider {\n return { propose };\n}\n\n/** Build the user-facing instruction, including repair feedback when retrying. */\nexport function buildPrompt(input: ProposeInput): string {\n if (!input.repair) {\n return `Design a Formwright form for: ${input.description}`;\n }\n return (\n `Design a Formwright form for: ${input.description}\\n\\n` +\n `Your previous attempt was INVALID:\\n${JSON.stringify(input.repair.previous, null, 2)}\\n\\n` +\n `Validation issues to fix:\\n` +\n input.repair.issues.map((i) => `- ${i.path}: ${i.message}`).join(\"\\n\") +\n `\\n\\nReturn a corrected schema that resolves every issue.`\n );\n}\n\n/** Some providers wrap the schema under a `schema` key; accept either shape. */\nfunction unwrap(candidate: unknown): unknown {\n if (candidate && typeof candidate === \"object\" && \"schema\" in candidate) {\n return (candidate as { schema: unknown }).schema;\n }\n return candidate;\n}\n\n/**\n * Generate a validated {@link FormSchema} from a natural-language description.\n * Throws {@link SchemaGenerationError} if the model can't produce a valid schema\n * within `maxRepairAttempts`.\n */\nexport async function generateSchema(\n description: string,\n options: GenerateOptions = {},\n): Promise<GenerateResult> {\n const provider = options.provider ?? (await defaultProvider(options));\n const maxRepair = options.maxRepairAttempts ?? 2;\n const system =\n (options.system ?? SYSTEM_PROMPT) +\n (options.guidelines ? `\\n\\nAdditional guidelines:\\n${options.guidelines}` : \"\");\n\n let repair: RepairContext | undefined;\n let lastIssues: readonly ValidationIssue[] = [];\n\n for (let attempt = 1; attempt <= maxRepair + 1; attempt++) {\n const input: ProposeInput = repair ? { description, system, repair } : { description, system };\n const candidate = await provider.propose(input);\n const result = validateSchema(unwrap(candidate));\n if (result.ok) return { schema: result.value, attempts: attempt };\n\n lastIssues = result.issues;\n if (attempt > maxRepair) break;\n repair = { previous: unwrap(candidate), issues: result.issues };\n }\n\n throw new SchemaGenerationError(\n `Could not produce a valid schema after ${maxRepair + 1} attempts.`,\n lastIssues,\n );\n}\n\n/** Lazily construct the default (Claude) provider so OpenAI-only users don't need a key. */\nasync function defaultProvider(options: GenerateOptions): Promise<SchemaProvider> {\n const { claudeProvider } = await import(\"./claude.js\");\n const opts: { apiKey?: string; model?: string } = {};\n if (options.apiKey !== undefined) opts.apiKey = options.apiKey;\n if (options.model !== undefined) opts.model = options.model;\n return claudeProvider(opts);\n}\n\nexport const SYSTEM_PROMPT = `You design forms as Formwright schemas — plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" — need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" — a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" — a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n- \"steps\" — a multi-step wizard; needs \"fields\": [ step, step, … ]. Each child must have \"type\": \"step\".\n- \"step\" — one wizard step (like a group); needs \"label\", optional \"description\", and \"fields\". Produces a nested object in the payload under the step id.\n\nFor multi-step / wizard forms, wrap steps in a \"steps\" container:\n{ \"id\": \"wizard\", \"type\": \"steps\", \"layout\": \"bar\"|\"tabs\"|\"numbers\", \"fields\": [\n { \"id\": \"personal\", \"type\": \"step\", \"label\": \"Personal\", \"fields\": [ ... ] },\n { \"id\": \"account\", \"type\": \"step\", \"label\": \"Account\", \"fields\": [ ... ] }\n]}\nOptional on \"steps\": \"showProgress\" (default true), \"validateOnNext\" (default true), \"nextLabel\", \"prevLabel\", \"submitLabel\".\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic — data, not code — via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root — so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.`;\n","/**\n * Claude provider — emits the schema through a forced tool call on the Anthropic\n * Messages API. Default model is Claude Opus 4.8 (`claude-opus-4-8`).\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Claude Opus 4.8 — the most capable model. */\nexport const DEFAULT_CLAUDE_MODEL = \"claude-opus-4-8\";\n\nexport interface ClaudeProviderOptions {\n readonly apiKey?: string;\n /** Bring your own configured client (takes precedence over `apiKey`). */\n readonly client?: Anthropic;\n readonly model?: string;\n readonly maxTokens?: number;\n}\n\nconst TOOL_NAME = \"emit_form_schema\";\n\nconst TOOL: Anthropic.Tool = {\n name: TOOL_NAME,\n description: \"Emit the complete Formwright form schema as a structured object.\",\n input_schema: {\n type: \"object\",\n properties: {\n schema: { type: \"object\", description: \"A complete, valid Formwright FormSchema.\" },\n },\n required: [\"schema\"],\n },\n};\n\nexport function claudeProvider(options: ClaudeProviderOptions = {}): SchemaProvider {\n const client =\n options.client ?? new Anthropic(options.apiKey ? { apiKey: options.apiKey } : undefined);\n const model = options.model ?? DEFAULT_CLAUDE_MODEL;\n const maxTokens = options.maxTokens ?? 16000;\n\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await client.messages.create({\n model,\n max_tokens: maxTokens,\n system: input.system,\n tools: [TOOL],\n tool_choice: { type: \"tool\", name: TOOL_NAME },\n messages: [{ role: \"user\", content: buildPrompt(input) }],\n });\n const toolUse = response.content.find(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n if (!toolUse) throw new Error(\"Claude did not emit a schema via the tool.\");\n return toolUse.input;\n },\n };\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_CLAUDE_MODEL, claudeProvider } from './chunk-UHEGF537.js';
2
+ //# sourceMappingURL=claude-X22WWSMS.js.map
3
+ //# sourceMappingURL=claude-X22WWSMS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"claude-BNP64G4N.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"claude-X22WWSMS.js"}
package/dist/index.cjs CHANGED
@@ -173,6 +173,15 @@ Field types:
173
173
  - "select" | "radio" \u2014 need "options": [{ "label": string, "value": string|number }]
174
174
  - "group" \u2014 a nested object; needs "fields": [ ...child fields ]. Produces an object in the payload.
175
175
  - "collection" \u2014 a repeatable list of objects; needs "fields", optional "itemLabel", "addLabel", "minItems", "maxItems". Produces an array of objects.
176
+ - "steps" \u2014 a multi-step wizard; needs "fields": [ step, step, \u2026 ]. Each child must have "type": "step".
177
+ - "step" \u2014 one wizard step (like a group); needs "label", optional "description", and "fields". Produces a nested object in the payload under the step id.
178
+
179
+ For multi-step / wizard forms, wrap steps in a "steps" container:
180
+ { "id": "wizard", "type": "steps", "layout": "bar"|"tabs"|"numbers", "fields": [
181
+ { "id": "personal", "type": "step", "label": "Personal", "fields": [ ... ] },
182
+ { "id": "account", "type": "step", "label": "Account", "fields": [ ... ] }
183
+ ]}
184
+ Optional on "steps": "showProgress" (default true), "validateOnNext" (default true), "nextLabel", "prevLabel", "submitLabel".
176
185
 
177
186
  Validation (optional): "validation": { "kind": "string"|"number", "required"?, "min"?, "max"?, "minLength"?, "maxLength"?, "pattern"?, "format"?: "email"|"url"|"uuid", "message"? }.
178
187
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/claude.ts","../src/openai.ts","../src/index.ts"],"names":["Anthropic","SYSTEM_PROMPT","validateSchema","SchemaGenerationError","claudeProvider"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Claude provider — emits the schema through a forced tool call on the Anthropic\n * Messages API. Default model is Claude Opus 4.8 (`claude-opus-4-8`).\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Claude Opus 4.8 — the most capable model. */\nexport const DEFAULT_CLAUDE_MODEL = \"claude-opus-4-8\";\n\nexport interface ClaudeProviderOptions {\n readonly apiKey?: string;\n /** Bring your own configured client (takes precedence over `apiKey`). */\n readonly client?: Anthropic;\n readonly model?: string;\n readonly maxTokens?: number;\n}\n\nconst TOOL_NAME = \"emit_form_schema\";\n\nconst TOOL: Anthropic.Tool = {\n name: TOOL_NAME,\n description: \"Emit the complete Formwright form schema as a structured object.\",\n input_schema: {\n type: \"object\",\n properties: {\n schema: { type: \"object\", description: \"A complete, valid Formwright FormSchema.\" },\n },\n required: [\"schema\"],\n },\n};\n\nexport function claudeProvider(options: ClaudeProviderOptions = {}): SchemaProvider {\n const client =\n options.client ?? new Anthropic(options.apiKey ? { apiKey: options.apiKey } : undefined);\n const model = options.model ?? DEFAULT_CLAUDE_MODEL;\n const maxTokens = options.maxTokens ?? 16000;\n\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await client.messages.create({\n model,\n max_tokens: maxTokens,\n system: input.system,\n tools: [TOOL],\n tool_choice: { type: \"tool\", name: TOOL_NAME },\n messages: [{ role: \"user\", content: buildPrompt(input) }],\n });\n const toolUse = response.content.find(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n if (!toolUse) throw new Error(\"Claude did not emit a schema via the tool.\");\n return toolUse.input;\n },\n };\n}\n","/**\n * OpenAI (GPT) provider — uses JSON mode on the Chat Completions API.\n *\n * To avoid a hard dependency on the `openai` package, pass your own client:\n *\n * import OpenAI from \"openai\";\n * import { generateSchema, openaiProvider } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a contact form\", {\n * provider: openaiProvider({ client: new OpenAI() }),\n * });\n *\n * Any client matching {@link OpenAILike} works (Azure OpenAI, compatible gateways).\n */\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Minimal structural shape of an OpenAI client's `chat.completions.create`. */\nexport interface OpenAILike {\n chat: {\n completions: {\n create(body: {\n model: string;\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>;\n response_format?: { type: \"json_object\" };\n }): Promise<{ choices: Array<{ message: { content: string | null } }> }>;\n };\n };\n}\n\nexport interface OpenAIProviderOptions {\n /** A configured OpenAI client (e.g. `new OpenAI()`). Required. */\n readonly client: OpenAILike;\n readonly model?: string;\n}\n\nexport function openaiProvider(options: OpenAIProviderOptions): SchemaProvider {\n const model = options.model ?? \"gpt-4o\";\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await options.client.chat.completions.create({\n model,\n response_format: { type: \"json_object\" },\n messages: [\n { role: \"system\", content: input.system },\n {\n role: \"user\",\n content: `${buildPrompt(input)}\\n\\nRespond with ONLY the JSON schema object.`,\n },\n ],\n });\n const content = response.choices[0]?.message?.content;\n if (!content) throw new Error(\"OpenAI returned no content.\");\n return JSON.parse(content) as unknown;\n },\n };\n}\n","/**\n * @formwright/ai — turn a natural-language form description into a *validated*\n * Formwright schema using an LLM.\n *\n * import { generateSchema } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a signup form with a US-only state field\");\n * new Form(schema).mount(el);\n *\n * Provider-agnostic: the validate → repair loop lives here, and the actual model\n * call is a pluggable {@link SchemaProvider}. A Claude provider ships built-in\n * (the default); an OpenAI provider and a custom-function provider let you use\n * GPT, Gemini, Mistral, a local model, or anything else — each through its own\n * native SDK, never a compatibility shim.\n *\n * Whatever the provider returns is checked with `@formwright/schema`'s validator;\n * if it's invalid, the precise issues are fed back for repair — so what you get\n * out always satisfies the runtime (or a thrown {@link SchemaGenerationError}).\n */\nimport { validateSchema, type FormSchema, type ValidationIssue } from \"@formwright/schema\";\n\nexport { claudeProvider, type ClaudeProviderOptions } from \"./claude.js\";\nexport { openaiProvider, type OpenAIProviderOptions, type OpenAILike } from \"./openai.js\";\n\n/** A pluggable model backend: produce a candidate schema object for a request. */\nexport interface SchemaProvider {\n propose(input: ProposeInput): Promise<unknown>;\n}\n\nexport interface ProposeInput {\n readonly description: string;\n readonly system: string;\n /** Present on a repair attempt: the previous (invalid) output and why it failed. */\n readonly repair?: RepairContext;\n}\n\nexport interface RepairContext {\n readonly previous: unknown;\n readonly issues: readonly ValidationIssue[];\n}\n\nexport interface GenerateOptions {\n /** Model backend. Defaults to Claude (needs `ANTHROPIC_API_KEY` or `apiKey`). */\n readonly provider?: SchemaProvider;\n /** Convenience for the default Claude provider when `provider` is omitted. */\n readonly apiKey?: string;\n readonly model?: string;\n /** How many times to feed validation errors back for repair (default 2). */\n readonly maxRepairAttempts?: number;\n /** Override the Formwright DSL system prompt. */\n readonly system?: string;\n /** Extra guidance appended to the system prompt. */\n readonly guidelines?: string;\n}\n\nexport interface GenerateResult {\n readonly schema: FormSchema;\n /** Number of model round-trips it took (1 = valid on the first try). */\n readonly attempts: number;\n}\n\nexport class SchemaGenerationError extends Error {\n readonly issues: readonly ValidationIssue[];\n constructor(message: string, issues: readonly ValidationIssue[]) {\n super(message);\n this.name = \"SchemaGenerationError\";\n this.issues = issues;\n }\n}\n\n/** Wrap a plain async function as a provider (for Gemini, Mistral, local models, …). */\nexport function defineProvider(propose: (input: ProposeInput) => Promise<unknown>): SchemaProvider {\n return { propose };\n}\n\n/** Build the user-facing instruction, including repair feedback when retrying. */\nexport function buildPrompt(input: ProposeInput): string {\n if (!input.repair) {\n return `Design a Formwright form for: ${input.description}`;\n }\n return (\n `Design a Formwright form for: ${input.description}\\n\\n` +\n `Your previous attempt was INVALID:\\n${JSON.stringify(input.repair.previous, null, 2)}\\n\\n` +\n `Validation issues to fix:\\n` +\n input.repair.issues.map((i) => `- ${i.path}: ${i.message}`).join(\"\\n\") +\n `\\n\\nReturn a corrected schema that resolves every issue.`\n );\n}\n\n/** Some providers wrap the schema under a `schema` key; accept either shape. */\nfunction unwrap(candidate: unknown): unknown {\n if (candidate && typeof candidate === \"object\" && \"schema\" in candidate) {\n return (candidate as { schema: unknown }).schema;\n }\n return candidate;\n}\n\n/**\n * Generate a validated {@link FormSchema} from a natural-language description.\n * Throws {@link SchemaGenerationError} if the model can't produce a valid schema\n * within `maxRepairAttempts`.\n */\nexport async function generateSchema(\n description: string,\n options: GenerateOptions = {},\n): Promise<GenerateResult> {\n const provider = options.provider ?? (await defaultProvider(options));\n const maxRepair = options.maxRepairAttempts ?? 2;\n const system =\n (options.system ?? SYSTEM_PROMPT) +\n (options.guidelines ? `\\n\\nAdditional guidelines:\\n${options.guidelines}` : \"\");\n\n let repair: RepairContext | undefined;\n let lastIssues: readonly ValidationIssue[] = [];\n\n for (let attempt = 1; attempt <= maxRepair + 1; attempt++) {\n const input: ProposeInput = repair ? { description, system, repair } : { description, system };\n const candidate = await provider.propose(input);\n const result = validateSchema(unwrap(candidate));\n if (result.ok) return { schema: result.value, attempts: attempt };\n\n lastIssues = result.issues;\n if (attempt > maxRepair) break;\n repair = { previous: unwrap(candidate), issues: result.issues };\n }\n\n throw new SchemaGenerationError(\n `Could not produce a valid schema after ${maxRepair + 1} attempts.`,\n lastIssues,\n );\n}\n\n/** Lazily construct the default (Claude) provider so OpenAI-only users don't need a key. */\nasync function defaultProvider(options: GenerateOptions): Promise<SchemaProvider> {\n const { claudeProvider } = await import(\"./claude.js\");\n const opts: { apiKey?: string; model?: string } = {};\n if (options.apiKey !== undefined) opts.apiKey = options.apiKey;\n if (options.model !== undefined) opts.model = options.model;\n return claudeProvider(opts);\n}\n\nexport const SYSTEM_PROMPT = `You design forms as Formwright schemas — plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" — need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" — a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" — a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic — data, not code — via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root — so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.`;\n"]}
1
+ {"version":3,"sources":["../src/claude.ts","../src/openai.ts","../src/index.ts"],"names":["Anthropic","SYSTEM_PROMPT","validateSchema","SchemaGenerationError","claudeProvider"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Claude provider — emits the schema through a forced tool call on the Anthropic\n * Messages API. Default model is Claude Opus 4.8 (`claude-opus-4-8`).\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Claude Opus 4.8 — the most capable model. */\nexport const DEFAULT_CLAUDE_MODEL = \"claude-opus-4-8\";\n\nexport interface ClaudeProviderOptions {\n readonly apiKey?: string;\n /** Bring your own configured client (takes precedence over `apiKey`). */\n readonly client?: Anthropic;\n readonly model?: string;\n readonly maxTokens?: number;\n}\n\nconst TOOL_NAME = \"emit_form_schema\";\n\nconst TOOL: Anthropic.Tool = {\n name: TOOL_NAME,\n description: \"Emit the complete Formwright form schema as a structured object.\",\n input_schema: {\n type: \"object\",\n properties: {\n schema: { type: \"object\", description: \"A complete, valid Formwright FormSchema.\" },\n },\n required: [\"schema\"],\n },\n};\n\nexport function claudeProvider(options: ClaudeProviderOptions = {}): SchemaProvider {\n const client =\n options.client ?? new Anthropic(options.apiKey ? { apiKey: options.apiKey } : undefined);\n const model = options.model ?? DEFAULT_CLAUDE_MODEL;\n const maxTokens = options.maxTokens ?? 16000;\n\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await client.messages.create({\n model,\n max_tokens: maxTokens,\n system: input.system,\n tools: [TOOL],\n tool_choice: { type: \"tool\", name: TOOL_NAME },\n messages: [{ role: \"user\", content: buildPrompt(input) }],\n });\n const toolUse = response.content.find(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n if (!toolUse) throw new Error(\"Claude did not emit a schema via the tool.\");\n return toolUse.input;\n },\n };\n}\n","/**\n * OpenAI (GPT) provider — uses JSON mode on the Chat Completions API.\n *\n * To avoid a hard dependency on the `openai` package, pass your own client:\n *\n * import OpenAI from \"openai\";\n * import { generateSchema, openaiProvider } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a contact form\", {\n * provider: openaiProvider({ client: new OpenAI() }),\n * });\n *\n * Any client matching {@link OpenAILike} works (Azure OpenAI, compatible gateways).\n */\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Minimal structural shape of an OpenAI client's `chat.completions.create`. */\nexport interface OpenAILike {\n chat: {\n completions: {\n create(body: {\n model: string;\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>;\n response_format?: { type: \"json_object\" };\n }): Promise<{ choices: Array<{ message: { content: string | null } }> }>;\n };\n };\n}\n\nexport interface OpenAIProviderOptions {\n /** A configured OpenAI client (e.g. `new OpenAI()`). Required. */\n readonly client: OpenAILike;\n readonly model?: string;\n}\n\nexport function openaiProvider(options: OpenAIProviderOptions): SchemaProvider {\n const model = options.model ?? \"gpt-4o\";\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await options.client.chat.completions.create({\n model,\n response_format: { type: \"json_object\" },\n messages: [\n { role: \"system\", content: input.system },\n {\n role: \"user\",\n content: `${buildPrompt(input)}\\n\\nRespond with ONLY the JSON schema object.`,\n },\n ],\n });\n const content = response.choices[0]?.message?.content;\n if (!content) throw new Error(\"OpenAI returned no content.\");\n return JSON.parse(content) as unknown;\n },\n };\n}\n","/**\n * @formwright/ai — turn a natural-language form description into a *validated*\n * Formwright schema using an LLM.\n *\n * import { generateSchema } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a signup form with a US-only state field\");\n * new Form(schema).mount(el);\n *\n * Provider-agnostic: the validate → repair loop lives here, and the actual model\n * call is a pluggable {@link SchemaProvider}. A Claude provider ships built-in\n * (the default); an OpenAI provider and a custom-function provider let you use\n * GPT, Gemini, Mistral, a local model, or anything else — each through its own\n * native SDK, never a compatibility shim.\n *\n * Whatever the provider returns is checked with `@formwright/schema`'s validator;\n * if it's invalid, the precise issues are fed back for repair — so what you get\n * out always satisfies the runtime (or a thrown {@link SchemaGenerationError}).\n */\nimport { validateSchema, type FormSchema, type ValidationIssue } from \"@formwright/schema\";\n\nexport { claudeProvider, type ClaudeProviderOptions } from \"./claude.js\";\nexport { openaiProvider, type OpenAIProviderOptions, type OpenAILike } from \"./openai.js\";\n\n/** A pluggable model backend: produce a candidate schema object for a request. */\nexport interface SchemaProvider {\n propose(input: ProposeInput): Promise<unknown>;\n}\n\nexport interface ProposeInput {\n readonly description: string;\n readonly system: string;\n /** Present on a repair attempt: the previous (invalid) output and why it failed. */\n readonly repair?: RepairContext;\n}\n\nexport interface RepairContext {\n readonly previous: unknown;\n readonly issues: readonly ValidationIssue[];\n}\n\nexport interface GenerateOptions {\n /** Model backend. Defaults to Claude (needs `ANTHROPIC_API_KEY` or `apiKey`). */\n readonly provider?: SchemaProvider;\n /** Convenience for the default Claude provider when `provider` is omitted. */\n readonly apiKey?: string;\n readonly model?: string;\n /** How many times to feed validation errors back for repair (default 2). */\n readonly maxRepairAttempts?: number;\n /** Override the Formwright DSL system prompt. */\n readonly system?: string;\n /** Extra guidance appended to the system prompt. */\n readonly guidelines?: string;\n}\n\nexport interface GenerateResult {\n readonly schema: FormSchema;\n /** Number of model round-trips it took (1 = valid on the first try). */\n readonly attempts: number;\n}\n\nexport class SchemaGenerationError extends Error {\n readonly issues: readonly ValidationIssue[];\n constructor(message: string, issues: readonly ValidationIssue[]) {\n super(message);\n this.name = \"SchemaGenerationError\";\n this.issues = issues;\n }\n}\n\n/** Wrap a plain async function as a provider (for Gemini, Mistral, local models, …). */\nexport function defineProvider(propose: (input: ProposeInput) => Promise<unknown>): SchemaProvider {\n return { propose };\n}\n\n/** Build the user-facing instruction, including repair feedback when retrying. */\nexport function buildPrompt(input: ProposeInput): string {\n if (!input.repair) {\n return `Design a Formwright form for: ${input.description}`;\n }\n return (\n `Design a Formwright form for: ${input.description}\\n\\n` +\n `Your previous attempt was INVALID:\\n${JSON.stringify(input.repair.previous, null, 2)}\\n\\n` +\n `Validation issues to fix:\\n` +\n input.repair.issues.map((i) => `- ${i.path}: ${i.message}`).join(\"\\n\") +\n `\\n\\nReturn a corrected schema that resolves every issue.`\n );\n}\n\n/** Some providers wrap the schema under a `schema` key; accept either shape. */\nfunction unwrap(candidate: unknown): unknown {\n if (candidate && typeof candidate === \"object\" && \"schema\" in candidate) {\n return (candidate as { schema: unknown }).schema;\n }\n return candidate;\n}\n\n/**\n * Generate a validated {@link FormSchema} from a natural-language description.\n * Throws {@link SchemaGenerationError} if the model can't produce a valid schema\n * within `maxRepairAttempts`.\n */\nexport async function generateSchema(\n description: string,\n options: GenerateOptions = {},\n): Promise<GenerateResult> {\n const provider = options.provider ?? (await defaultProvider(options));\n const maxRepair = options.maxRepairAttempts ?? 2;\n const system =\n (options.system ?? SYSTEM_PROMPT) +\n (options.guidelines ? `\\n\\nAdditional guidelines:\\n${options.guidelines}` : \"\");\n\n let repair: RepairContext | undefined;\n let lastIssues: readonly ValidationIssue[] = [];\n\n for (let attempt = 1; attempt <= maxRepair + 1; attempt++) {\n const input: ProposeInput = repair ? { description, system, repair } : { description, system };\n const candidate = await provider.propose(input);\n const result = validateSchema(unwrap(candidate));\n if (result.ok) return { schema: result.value, attempts: attempt };\n\n lastIssues = result.issues;\n if (attempt > maxRepair) break;\n repair = { previous: unwrap(candidate), issues: result.issues };\n }\n\n throw new SchemaGenerationError(\n `Could not produce a valid schema after ${maxRepair + 1} attempts.`,\n lastIssues,\n );\n}\n\n/** Lazily construct the default (Claude) provider so OpenAI-only users don't need a key. */\nasync function defaultProvider(options: GenerateOptions): Promise<SchemaProvider> {\n const { claudeProvider } = await import(\"./claude.js\");\n const opts: { apiKey?: string; model?: string } = {};\n if (options.apiKey !== undefined) opts.apiKey = options.apiKey;\n if (options.model !== undefined) opts.model = options.model;\n return claudeProvider(opts);\n}\n\nexport const SYSTEM_PROMPT = `You design forms as Formwright schemas — plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" — need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" — a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" — a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n- \"steps\" — a multi-step wizard; needs \"fields\": [ step, step, … ]. Each child must have \"type\": \"step\".\n- \"step\" — one wizard step (like a group); needs \"label\", optional \"description\", and \"fields\". Produces a nested object in the payload under the step id.\n\nFor multi-step / wizard forms, wrap steps in a \"steps\" container:\n{ \"id\": \"wizard\", \"type\": \"steps\", \"layout\": \"bar\"|\"tabs\"|\"numbers\", \"fields\": [\n { \"id\": \"personal\", \"type\": \"step\", \"label\": \"Personal\", \"fields\": [ ... ] },\n { \"id\": \"account\", \"type\": \"step\", \"label\": \"Account\", \"fields\": [ ... ] }\n]}\nOptional on \"steps\": \"showProgress\" (default true), \"validateOnNext\" (default true), \"nextLabel\", \"prevLabel\", \"submitLabel\".\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic — data, not code — via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root — so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.`;\n"]}
package/dist/index.d.cts CHANGED
@@ -124,6 +124,6 @@ declare function buildPrompt(input: ProposeInput): string;
124
124
  * within `maxRepairAttempts`.
125
125
  */
126
126
  declare function generateSchema(description: string, options?: GenerateOptions): Promise<GenerateResult>;
127
- declare const SYSTEM_PROMPT = "You design forms as Formwright schemas \u2014 plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" \u2014 need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" \u2014 a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" \u2014 a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic \u2014 data, not code \u2014 via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root \u2014 so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.";
127
+ declare const SYSTEM_PROMPT = "You design forms as Formwright schemas \u2014 plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" \u2014 need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" \u2014 a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" \u2014 a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n- \"steps\" \u2014 a multi-step wizard; needs \"fields\": [ step, step, \u2026 ]. Each child must have \"type\": \"step\".\n- \"step\" \u2014 one wizard step (like a group); needs \"label\", optional \"description\", and \"fields\". Produces a nested object in the payload under the step id.\n\nFor multi-step / wizard forms, wrap steps in a \"steps\" container:\n{ \"id\": \"wizard\", \"type\": \"steps\", \"layout\": \"bar\"|\"tabs\"|\"numbers\", \"fields\": [\n { \"id\": \"personal\", \"type\": \"step\", \"label\": \"Personal\", \"fields\": [ ... ] },\n { \"id\": \"account\", \"type\": \"step\", \"label\": \"Account\", \"fields\": [ ... ] }\n]}\nOptional on \"steps\": \"showProgress\" (default true), \"validateOnNext\" (default true), \"nextLabel\", \"prevLabel\", \"submitLabel\".\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic \u2014 data, not code \u2014 via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root \u2014 so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.";
128
128
 
129
129
  export { type ClaudeProviderOptions, type GenerateOptions, type GenerateResult, type OpenAILike, type OpenAIProviderOptions, type ProposeInput, type RepairContext, SYSTEM_PROMPT, SchemaGenerationError, type SchemaProvider, buildPrompt, claudeProvider, defineProvider, generateSchema, openaiProvider };
package/dist/index.d.ts CHANGED
@@ -124,6 +124,6 @@ declare function buildPrompt(input: ProposeInput): string;
124
124
  * within `maxRepairAttempts`.
125
125
  */
126
126
  declare function generateSchema(description: string, options?: GenerateOptions): Promise<GenerateResult>;
127
- declare const SYSTEM_PROMPT = "You design forms as Formwright schemas \u2014 plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" \u2014 need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" \u2014 a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" \u2014 a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic \u2014 data, not code \u2014 via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root \u2014 so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.";
127
+ declare const SYSTEM_PROMPT = "You design forms as Formwright schemas \u2014 plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" \u2014 need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" \u2014 a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" \u2014 a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n- \"steps\" \u2014 a multi-step wizard; needs \"fields\": [ step, step, \u2026 ]. Each child must have \"type\": \"step\".\n- \"step\" \u2014 one wizard step (like a group); needs \"label\", optional \"description\", and \"fields\". Produces a nested object in the payload under the step id.\n\nFor multi-step / wizard forms, wrap steps in a \"steps\" container:\n{ \"id\": \"wizard\", \"type\": \"steps\", \"layout\": \"bar\"|\"tabs\"|\"numbers\", \"fields\": [\n { \"id\": \"personal\", \"type\": \"step\", \"label\": \"Personal\", \"fields\": [ ... ] },\n { \"id\": \"account\", \"type\": \"step\", \"label\": \"Account\", \"fields\": [ ... ] }\n]}\nOptional on \"steps\": \"showProgress\" (default true), \"validateOnNext\" (default true), \"nextLabel\", \"prevLabel\", \"submitLabel\".\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic \u2014 data, not code \u2014 via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root \u2014 so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.";
128
128
 
129
129
  export { type ClaudeProviderOptions, type GenerateOptions, type GenerateResult, type OpenAILike, type OpenAIProviderOptions, type ProposeInput, type RepairContext, SYSTEM_PROMPT, SchemaGenerationError, type SchemaProvider, buildPrompt, claudeProvider, defineProvider, generateSchema, openaiProvider };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { SYSTEM_PROMPT, SchemaGenerationError, buildPrompt, claudeProvider, defineProvider, generateSchema, openaiProvider } from './chunk-I3HZFUC7.js';
1
+ export { SYSTEM_PROMPT, SchemaGenerationError, buildPrompt, claudeProvider, defineProvider, generateSchema, openaiProvider } from './chunk-UHEGF537.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formwright/ai",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Generate a validated Formwright schema from a natural-language description using Claude.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,7 +27,7 @@
27
27
  "types": "./dist/index.d.ts",
28
28
  "dependencies": {
29
29
  "@anthropic-ai/sdk": "^0.68.0",
30
- "@formwright/schema": "0.2.0"
30
+ "@formwright/schema": "0.2.2"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/openai.ts","../src/index.ts","../src/claude.ts"],"names":["claudeProvider"],"mappings":"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","file":"chunk-I3HZFUC7.js","sourcesContent":["/**\n * OpenAI (GPT) provider — uses JSON mode on the Chat Completions API.\n *\n * To avoid a hard dependency on the `openai` package, pass your own client:\n *\n * import OpenAI from \"openai\";\n * import { generateSchema, openaiProvider } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a contact form\", {\n * provider: openaiProvider({ client: new OpenAI() }),\n * });\n *\n * Any client matching {@link OpenAILike} works (Azure OpenAI, compatible gateways).\n */\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Minimal structural shape of an OpenAI client's `chat.completions.create`. */\nexport interface OpenAILike {\n chat: {\n completions: {\n create(body: {\n model: string;\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>;\n response_format?: { type: \"json_object\" };\n }): Promise<{ choices: Array<{ message: { content: string | null } }> }>;\n };\n };\n}\n\nexport interface OpenAIProviderOptions {\n /** A configured OpenAI client (e.g. `new OpenAI()`). Required. */\n readonly client: OpenAILike;\n readonly model?: string;\n}\n\nexport function openaiProvider(options: OpenAIProviderOptions): SchemaProvider {\n const model = options.model ?? \"gpt-4o\";\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await options.client.chat.completions.create({\n model,\n response_format: { type: \"json_object\" },\n messages: [\n { role: \"system\", content: input.system },\n {\n role: \"user\",\n content: `${buildPrompt(input)}\\n\\nRespond with ONLY the JSON schema object.`,\n },\n ],\n });\n const content = response.choices[0]?.message?.content;\n if (!content) throw new Error(\"OpenAI returned no content.\");\n return JSON.parse(content) as unknown;\n },\n };\n}\n","/**\n * @formwright/ai — turn a natural-language form description into a *validated*\n * Formwright schema using an LLM.\n *\n * import { generateSchema } from \"@formwright/ai\";\n * const { schema } = await generateSchema(\"a signup form with a US-only state field\");\n * new Form(schema).mount(el);\n *\n * Provider-agnostic: the validate → repair loop lives here, and the actual model\n * call is a pluggable {@link SchemaProvider}. A Claude provider ships built-in\n * (the default); an OpenAI provider and a custom-function provider let you use\n * GPT, Gemini, Mistral, a local model, or anything else — each through its own\n * native SDK, never a compatibility shim.\n *\n * Whatever the provider returns is checked with `@formwright/schema`'s validator;\n * if it's invalid, the precise issues are fed back for repair — so what you get\n * out always satisfies the runtime (or a thrown {@link SchemaGenerationError}).\n */\nimport { validateSchema, type FormSchema, type ValidationIssue } from \"@formwright/schema\";\n\nexport { claudeProvider, type ClaudeProviderOptions } from \"./claude.js\";\nexport { openaiProvider, type OpenAIProviderOptions, type OpenAILike } from \"./openai.js\";\n\n/** A pluggable model backend: produce a candidate schema object for a request. */\nexport interface SchemaProvider {\n propose(input: ProposeInput): Promise<unknown>;\n}\n\nexport interface ProposeInput {\n readonly description: string;\n readonly system: string;\n /** Present on a repair attempt: the previous (invalid) output and why it failed. */\n readonly repair?: RepairContext;\n}\n\nexport interface RepairContext {\n readonly previous: unknown;\n readonly issues: readonly ValidationIssue[];\n}\n\nexport interface GenerateOptions {\n /** Model backend. Defaults to Claude (needs `ANTHROPIC_API_KEY` or `apiKey`). */\n readonly provider?: SchemaProvider;\n /** Convenience for the default Claude provider when `provider` is omitted. */\n readonly apiKey?: string;\n readonly model?: string;\n /** How many times to feed validation errors back for repair (default 2). */\n readonly maxRepairAttempts?: number;\n /** Override the Formwright DSL system prompt. */\n readonly system?: string;\n /** Extra guidance appended to the system prompt. */\n readonly guidelines?: string;\n}\n\nexport interface GenerateResult {\n readonly schema: FormSchema;\n /** Number of model round-trips it took (1 = valid on the first try). */\n readonly attempts: number;\n}\n\nexport class SchemaGenerationError extends Error {\n readonly issues: readonly ValidationIssue[];\n constructor(message: string, issues: readonly ValidationIssue[]) {\n super(message);\n this.name = \"SchemaGenerationError\";\n this.issues = issues;\n }\n}\n\n/** Wrap a plain async function as a provider (for Gemini, Mistral, local models, …). */\nexport function defineProvider(propose: (input: ProposeInput) => Promise<unknown>): SchemaProvider {\n return { propose };\n}\n\n/** Build the user-facing instruction, including repair feedback when retrying. */\nexport function buildPrompt(input: ProposeInput): string {\n if (!input.repair) {\n return `Design a Formwright form for: ${input.description}`;\n }\n return (\n `Design a Formwright form for: ${input.description}\\n\\n` +\n `Your previous attempt was INVALID:\\n${JSON.stringify(input.repair.previous, null, 2)}\\n\\n` +\n `Validation issues to fix:\\n` +\n input.repair.issues.map((i) => `- ${i.path}: ${i.message}`).join(\"\\n\") +\n `\\n\\nReturn a corrected schema that resolves every issue.`\n );\n}\n\n/** Some providers wrap the schema under a `schema` key; accept either shape. */\nfunction unwrap(candidate: unknown): unknown {\n if (candidate && typeof candidate === \"object\" && \"schema\" in candidate) {\n return (candidate as { schema: unknown }).schema;\n }\n return candidate;\n}\n\n/**\n * Generate a validated {@link FormSchema} from a natural-language description.\n * Throws {@link SchemaGenerationError} if the model can't produce a valid schema\n * within `maxRepairAttempts`.\n */\nexport async function generateSchema(\n description: string,\n options: GenerateOptions = {},\n): Promise<GenerateResult> {\n const provider = options.provider ?? (await defaultProvider(options));\n const maxRepair = options.maxRepairAttempts ?? 2;\n const system =\n (options.system ?? SYSTEM_PROMPT) +\n (options.guidelines ? `\\n\\nAdditional guidelines:\\n${options.guidelines}` : \"\");\n\n let repair: RepairContext | undefined;\n let lastIssues: readonly ValidationIssue[] = [];\n\n for (let attempt = 1; attempt <= maxRepair + 1; attempt++) {\n const input: ProposeInput = repair ? { description, system, repair } : { description, system };\n const candidate = await provider.propose(input);\n const result = validateSchema(unwrap(candidate));\n if (result.ok) return { schema: result.value, attempts: attempt };\n\n lastIssues = result.issues;\n if (attempt > maxRepair) break;\n repair = { previous: unwrap(candidate), issues: result.issues };\n }\n\n throw new SchemaGenerationError(\n `Could not produce a valid schema after ${maxRepair + 1} attempts.`,\n lastIssues,\n );\n}\n\n/** Lazily construct the default (Claude) provider so OpenAI-only users don't need a key. */\nasync function defaultProvider(options: GenerateOptions): Promise<SchemaProvider> {\n const { claudeProvider } = await import(\"./claude.js\");\n const opts: { apiKey?: string; model?: string } = {};\n if (options.apiKey !== undefined) opts.apiKey = options.apiKey;\n if (options.model !== undefined) opts.model = options.model;\n return claudeProvider(opts);\n}\n\nexport const SYSTEM_PROMPT = `You design forms as Formwright schemas — plain JSON, no code.\n\nA FormSchema has:\n- \"id\" (string), \"version\" (string, e.g. \"1.0\"), optional \"title\", and \"fields\" (non-empty array).\n- optional \"submit\": { \"endpoint\": { \"method\": \"POST\"|\"GET\"|\"PUT\"|\"PATCH\"|\"DELETE\", \"url\": string }, \"transform\"?, \"onSuccess\"?, \"onError\"? }.\n\nEach field has \"id\" (unique within its scope) and \"type\", plus optional \"label\", \"placeholder\", \"help\", \"description\", \"defaultValue\".\nField types:\n- \"text\" | \"email\" | \"password\" | \"number\" | \"textarea\"\n- \"checkbox\" | \"toggle\" (boolean; toggle renders as a switch)\n- \"select\" | \"radio\" — need \"options\": [{ \"label\": string, \"value\": string|number }]\n- \"group\" — a nested object; needs \"fields\": [ ...child fields ]. Produces an object in the payload.\n- \"collection\" — a repeatable list of objects; needs \"fields\", optional \"itemLabel\", \"addLabel\", \"minItems\", \"maxItems\". Produces an array of objects.\n\nValidation (optional): \"validation\": { \"kind\": \"string\"|\"number\", \"required\"?, \"min\"?, \"max\"?, \"minLength\"?, \"maxLength\"?, \"pattern\"?, \"format\"?: \"email\"|\"url\"|\"uuid\", \"message\"? }.\n\nConditional logic — data, not code — via \"visibleWhen\" / \"enabledWhen\" / \"requiredWhen\", a JSONLogic-style expression:\n{ \"==\": [a, b] }, \"!=\", \">\", \">=\", \"<\", \"<=\", { \"in\": [needle, haystack] }, { \"and\": [...] }, { \"or\": [...] }, { \"not\": x }, and { \"var\": \"fieldId\" } to read another field's value.\nNames resolve to a sibling first, then outward to the form root — so a field inside a group or collection row can react to an outer toggle.\n\nOther per-field options: \"omit\": true (keep in the UI but exclude from the payload), \"labelPosition\": \"start\"|\"end\" (checkbox/toggle), \"layout\" (\"accordion\" for group; \"cards\"|\"accordion\" for collection).\n\nProduce sensible labels, validation, and conditions matching the request. Prefer \"toggle\" for yes/no switches and add helpful placeholders. Output ONLY the schema object.`;\n","/**\n * Claude provider — emits the schema through a forced tool call on the Anthropic\n * Messages API. Default model is Claude Opus 4.8 (`claude-opus-4-8`).\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { buildPrompt, type ProposeInput, type SchemaProvider } from \"./index.js\";\n\n/** Claude Opus 4.8 — the most capable model. */\nexport const DEFAULT_CLAUDE_MODEL = \"claude-opus-4-8\";\n\nexport interface ClaudeProviderOptions {\n readonly apiKey?: string;\n /** Bring your own configured client (takes precedence over `apiKey`). */\n readonly client?: Anthropic;\n readonly model?: string;\n readonly maxTokens?: number;\n}\n\nconst TOOL_NAME = \"emit_form_schema\";\n\nconst TOOL: Anthropic.Tool = {\n name: TOOL_NAME,\n description: \"Emit the complete Formwright form schema as a structured object.\",\n input_schema: {\n type: \"object\",\n properties: {\n schema: { type: \"object\", description: \"A complete, valid Formwright FormSchema.\" },\n },\n required: [\"schema\"],\n },\n};\n\nexport function claudeProvider(options: ClaudeProviderOptions = {}): SchemaProvider {\n const client =\n options.client ?? new Anthropic(options.apiKey ? { apiKey: options.apiKey } : undefined);\n const model = options.model ?? DEFAULT_CLAUDE_MODEL;\n const maxTokens = options.maxTokens ?? 16000;\n\n return {\n async propose(input: ProposeInput): Promise<unknown> {\n const response = await client.messages.create({\n model,\n max_tokens: maxTokens,\n system: input.system,\n tools: [TOOL],\n tool_choice: { type: \"tool\", name: TOOL_NAME },\n messages: [{ role: \"user\", content: buildPrompt(input) }],\n });\n const toolUse = response.content.find(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n if (!toolUse) throw new Error(\"Claude did not emit a schema via the tool.\");\n return toolUse.input;\n },\n };\n}\n"]}
@@ -1,3 +0,0 @@
1
- export { DEFAULT_CLAUDE_MODEL, claudeProvider } from './chunk-I3HZFUC7.js';
2
- //# sourceMappingURL=claude-BNP64G4N.js.map
3
- //# sourceMappingURL=claude-BNP64G4N.js.map