| name | ax-signature |
| description | This skill helps an LLM generate correct DSPy signature code using @ax-llm/ax. Use when the user asks about signatures, s(), f(), field types, string syntax, fluent builder API, validation constraints, or type-safe inputs/outputs. |
| version | 19.0.17 |
Ax Signature Reference
Signature Syntax
[description] input1:type, input2:type -> output1:type, output2:type
Field Types
| Type | Syntax | TypeScript | Example |
|---|
| String | :string | string | userName:string |
| Number | :number | number | score:number |
| Boolean | :boolean | boolean | isValid:boolean |
| JSON | :json | any | metadata:json |
| Date | :date | Date | birthDate:date |
| DateTime | :datetime | Date | timestamp:datetime |
| Image | :image | {mimeType, data} | photo:image (input only) |
| Audio | :audio | {format?, data} | recording:audio (input only) |
| File | :file | {mimeType, data} | document:file (input only) |
| URL | :url | string | website:url |
| Code | :code | string | pythonScript:code |
| Class | :class "a, b, c" | "a" | "b" | "c" | mood:class "happy, sad" |
Arrays, Optional, and Internal Fields
'tags:string[] -> processedTags:string[]'
'query:string, context?:string -> response:string'
'problem:string -> reasoning!:string, solution:string'
Three Ways to Create Signatures
1. String-Based (Recommended for simple cases)
import { ax, s } from '@ax-llm/ax';
const gen = ax('input:string -> output:string');
const sig = s('query:string -> response:string');
2. Pure Fluent Builder API
import { f } from '@ax-llm/ax';
const sig = f()
.input('userMessage', f.string('User input'))
.input('contextData', f.string('Additional context').optional())
.input('tags', f.string('Keywords').array())
.output('responseText', f.string('AI response'))
.output('confidenceScore', f.number('Confidence 0-1'))
.output('debugInfo', f.string('Debug info').internal())
.build();
3. Hybrid
import { s, f } from '@ax-llm/ax';
const sig = s('base:string -> result:string')
.appendInputField('extra', f.json('Metadata').optional())
.appendOutputField('score', f.number('Quality score'));
Fluent API Reference
Type creators:
f.string(desc), f.number(desc), f.boolean(desc), f.json(desc)
f.image(desc), f.audio(desc), f.file(desc), f.url(desc)
f.email(desc), f.date(desc), f.datetime(desc)
f.class(['a','b','c'], desc), f.code(desc)
f.object({ field: f.string() }, desc)
Chainable modifiers (method chaining only, no nesting):
.optional() - make field optional
.array() / .array('list description') - make field an array
.internal() - output only, hidden from final output
.cache() - input only, mark for prompt caching
f.string('description').optional().array()
f.string('context').cache().optional()
f.object({ field: f.string() }, 'item desc').array('list desc')
f.array(f.string('description'))
f.optional(f.string('description'))
f.internal(f.string('description'))
Validation Constraints
String Constraints
f.string('username').min(3).max(20)
f.string('email').email()
f.string('website').url()
f.string('birthDate').date()
f.string('timestamp').datetime()
f.string('pattern').regex('^[A-Z0-9]')
Number Constraints
f.number('age').min(18).max(120)
f.number('score').min(0).max(100)
Complete Validation Example
const sig = f()
.input('formData', f.string('Raw form data'))
.output('user', f.object({
username: f.string('Username').min(3).max(20),
email: f.string('Email').email(),
age: f.number('Age').min(18).max(120),
bio: f.string('Bio').max(500).optional(),
website: f.string('Website').url().optional(),
tags: f.string('Tag').min(2).max(30).array()
}, 'User profile'))
.build();
Cached Input Fields
const sig = f()
.input('staticContext', f.string('Context').cache())
.input('userQuery', f.string('Dynamic query'))
.output('answer', f.string('Response'))
.build();
Field Naming Rules
Good: userQuestion, customerEmail, analysisResult, confidenceScore
Bad: text, data, input, output, a, x, val (too generic), 1field (starts with number)
Media Type Restrictions
- Media types (image, audio, file) are top-level input fields only
- Cannot be nested in objects
- Cannot be output fields
Common Patterns
'problem:string -> reasoning!:string, solution:string'
'email:string -> priority:class "urgent, normal, low"'
'imageData:image, question?:string -> description:string, objects:string[]'
'invoiceText:string -> invoiceNumber:string, totalAmount:number, lineItems:json[]'
'"Answer TypeScript questions" question:string -> answer:string, confidence:number'
Critical Rules
- Use
f() fluent builder, NOT nested f.array(f.string()) -- those are removed.
- Field names must be descriptive (not generic like
text, data, input).
- Media types are input-only, top-level only.
.internal() is output-only (for chain-of-thought reasoning).
.cache() is input-only (for prompt caching).
- Validation errors trigger auto-retry with correction feedback.
f.email(), f.url(), f.date(), f.datetime() are shorthand for f.string().email() etc.
Examples
Fetch these for full working code: