s() Signatures
Use s() when you want a parsed signature object. Use the string form directly in ax() or agent() when you do not need to inspect or compose the signature.
auto sig = axllm::s("email:string -> priority:class \"high, normal, low\"");Signatures are the contract shared by generation, tools, examples, validation, and optimizer traces.
What It Does
s() parses the Ax signature grammar into a signature object. That object can be reused, inspected, extended, passed into ax(), passed into agent(), or combined with fluent/schema fields where the language surface supports it.
flowchart LR A["Signature string"] --> B["Parsed signature object"] B --> C["Input fields + types"] B --> D["Output fields + class options"] C --> E["Prompt contract"] D --> E D --> F["JSON schema"] D --> G["Parser + validators"] B --> H["Reuse in ax() or agent()"]
Core Call Shape
signature = s("input:type -> output:type")
program = ax(signature)Common Patterns
- Keep string signatures for simple contracts.
- Use parsed signatures when several programs share the same input/output shape.
- Use fluent/schema builders when Standard Schema (zod/valibot) fields matter; everything else — constraints, nested objects, caching — is expressible in the string form.
- Use
classfields for bounded labels instead of vague prose instructions. - Use optional fields only when missing data is truly acceptable.
- Use internal outputs for model scratch structure that should not reach callers.
Constraints and nested objects fit in the string form directly:
s('userAge:number(min 0, max 120), contextText:string(cache) -> profileList:object{ fullName:string, userAge?:number }[] "matched profiles"')More contracts in the same grammar — moderation, extraction, retrieval:
postText:string -> moderationVerdict:class "allow, review, block", flaggedSpans:object{ spanText:string, reasonNote:string }[]
invoiceText:string -> invoiceNumber:string(pattern "^INV-\\d+$" "INV- then digits"), totalAmount:number(min 0), lineItems:object{ description:string, quantity:number(min 1), unitPrice:number }[]
corpusText:string(cache), userQuestion:string -> answerText:string, citedChunks:string(item "verbatim quote")[]
schemaText:string(cache), questionText:string -> sqlQuery:code(sql), queryNotes?:string(max 200)
emailText:string -> eventTitle:string, startsAt:datetime, endsAt?:datetime, attendeeNames:string[]
claimText:string, policyText:string(cache) -> isCovered:boolean, confidenceScore:number(min 0, max 1), citedClause?:stringThe string API is strict: a modifier that does not apply to its type (say min on a boolean) is a parse error, where the fluent API would silently ignore it. AxSignature.toString() renders every construct back to this grammar losslessly, which is what lets flows serialize their node contracts into mermaid %%ax directives.
Flows as mermaid diagrams
Because every signature renders back losslessly, a whole flow can be written as — or exported to — a mermaid flowchart, with each node’s contract in a %%ax directive:
flowchart TD
%%ax classify: ticketText:string -> ticketClass:class "bug, billing"
%%ax reply: ticketText:string, ticketClass:string -> replyText:string
classify --> replyIn TypeScript, flow(diagram) compiles that string into a runnable flow and String(flow) renders any flow back, so flow(String(flow)) round-trips (toString({ direction: 'LR' }) for render options).
Parsed string
auto program = axllm::ax("question:string -> answer:string");
auto output = program.forward(client, axllm::object({{"question", "What is Ax?"}}));Fluent or native schema surface
auto request_type = axllm::Core::record_new("FieldType", axllm::object({
{"name", "string"}, {"minLength", 10}, {"maxLength", 500},
}));
auto request_field = field("requestText", "Request Text", request_type);Validation constraints
auto code_type = axllm::Core::record_new("FieldType", axllm::object({
{"name", "string"},
{"pattern", "^[A-Z]{3}-\\d{4}$"},
{"patternDescription", "Must look like ABC-1234"},
}));Hybrid composition
Start with the concise string grammar, then attach native fields when the language surface makes that clearer.
axllm::Core::validate_signature(signature);
axllm::AxGen program = axllm::ax(signature);
axllm::Value output = program.forward(client, inputs);Production Notes
Treat signatures as API contracts. Renaming fields changes examples, traces, optimizer artifacts, and caller code. Prefer descriptive field names and validation over long prompt instructions.
See s() API.