Skip to content

Commit b1dc107

Browse files
committed
feat: Introduce AxSignature.hasComplexFields() for consistent complex type detection and update example documentation.
1 parent 61d43ff commit b1dc107

File tree

10 files changed

+282
-25
lines changed

10 files changed

+282
-25
lines changed

docs/EXAMPLES.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,159 @@ export GOOGLE_APIKEY=your-key
876876
npm run tsx ./src/examples/summarize.ts
877877
```
878878

879+
## Complete Examples Reference
880+
881+
Below is a comprehensive list of all available examples in [`src/examples/`](https://github.com/ax-llm/ax/tree/main/src/examples), organized by category.
882+
883+
### Basic Concepts
884+
885+
- **[chat.ts](https://github.com/ax-llm/ax/tree/main/src/examples/chat.ts)** - Simple chat interface demonstrating basic conversation flow
886+
- **[simple-classify.ts](https://github.com/ax-llm/ax/tree/main/src/examples/simple-classify.ts)** - Basic classification example
887+
- **[extract.ts](https://github.com/ax-llm/ax/tree/main/src/examples/extract.ts)** - Extract structured data from unstructured text
888+
- **[extract-test.ts](https://github.com/ax-llm/ax/tree/main/src/examples/extract-test.ts)** - Testing extraction capabilities
889+
- **[summarize.ts](https://github.com/ax-llm/ax/tree/main/src/examples/summarize.ts)** - Document summarization with key insights
890+
- **[marketing.ts](https://github.com/ax-llm/ax/tree/main/src/examples/marketing.ts)** - Marketing content generation
891+
- **[embed.ts](https://github.com/ax-llm/ax/tree/main/src/examples/embed.ts)** - Text embeddings and vector operations
892+
893+
### Signatures & Type Safety
894+
895+
- **[fluent-signature-example.ts](https://github.com/ax-llm/ax/tree/main/src/examples/fluent-signature-example.ts)** - Using the fluent API for signature definition
896+
- **[signature-tool-calling.ts](https://github.com/ax-llm/ax/tree/main/src/examples/signature-tool-calling.ts)** - Combining signatures with tool calling
897+
- **[structured_output.ts](https://github.com/ax-llm/ax/tree/main/src/examples/structured_output.ts)** - Complex structured output with validation
898+
- **[debug_schema.ts](https://github.com/ax-llm/ax/tree/main/src/examples/debug_schema.ts)** - Debugging JSON schema generation
899+
900+
### Function Calling & Tools
901+
902+
- **[function.ts](https://github.com/ax-llm/ax/tree/main/src/examples/function.ts)** - Basic function calling (ReAct pattern)
903+
- **[food-search.ts](https://github.com/ax-llm/ax/tree/main/src/examples/food-search.ts)** - Restaurant search with multi-step reasoning
904+
- **[smart-home.ts](https://github.com/ax-llm/ax/tree/main/src/examples/smart-home.ts)** - Smart home control with multiple devices
905+
- **[stop-function.ts](https://github.com/ax-llm/ax/tree/main/src/examples/stop-function.ts)** - Controlling function execution flow
906+
- **[function-result-formatter.ts](https://github.com/ax-llm/ax/tree/main/src/examples/function-result-formatter.ts)** - Formatting function call results
907+
- **[function-result-picker.ts](https://github.com/ax-llm/ax/tree/main/src/examples/function-result-picker.ts)** - Selecting best function results
908+
- **[result-picker.ts](https://github.com/ax-llm/ax/tree/main/src/examples/result-picker.ts)** - Advanced result selection strategies
909+
910+
### Streaming
911+
912+
- **[streaming.ts](https://github.com/ax-llm/ax/tree/main/src/examples/streaming.ts)** - Basic streaming responses
913+
- **[streaming-asserts.ts](https://github.com/ax-llm/ax/tree/main/src/examples/streaming-asserts.ts)** - Streaming with real-time validation
914+
915+
### Assertions & Validation
916+
917+
- **[asserts.ts](https://github.com/ax-llm/ax/tree/main/src/examples/asserts.ts)** - Using assertions for output validation
918+
- **[sample-count.ts](https://github.com/ax-llm/ax/tree/main/src/examples/sample-count.ts)** - Controlling sampling and retries
919+
920+
### Agent Systems
921+
922+
- **[agent.ts](https://github.com/ax-llm/ax/tree/main/src/examples/agent.ts)** - Basic agent implementation
923+
- **[agent-migration-example.ts](https://github.com/ax-llm/ax/tree/main/src/examples/agent-migration-example.ts)** - Migrating to the agent pattern
924+
- **[customer-support.ts](https://github.com/ax-llm/ax/tree/main/src/examples/customer-support.ts)** - Complete customer support agent
925+
- **[meetings.ts](https://github.com/ax-llm/ax/tree/main/src/examples/meetings.ts)** - Meeting assistant with scheduling
926+
927+
### Workflow Orchestration (AxFlow)
928+
929+
- **[ax-flow.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow.ts)** - Comprehensive AxFlow demonstration
930+
- **[ax-flow-enhanced-demo.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow-enhanced-demo.ts)** - Advanced flow patterns
931+
- **[ax-flow-async-map.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow-async-map.ts)** - Parallel processing with map
932+
- **[ax-flow-auto-parallel.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow-auto-parallel.ts)** - Automatic parallelization
933+
- **[ax-flow-map-merge-test.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow-map-merge-test.ts)** - Map-reduce patterns
934+
- **[ax-flow-signature-inference.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow-signature-inference.ts)** - Type inference in flows
935+
- **[ax-flow-to-function.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-flow-to-function.ts)** - Converting flows to functions
936+
- **[fluent-flow-example.ts](https://github.com/ax-llm/ax/tree/main/src/examples/fluent-flow-example.ts)** - Fluent API for flows
937+
- **[flow-type-inference-demo.ts](https://github.com/ax-llm/ax/tree/main/src/examples/flow-type-inference-demo.ts)** - Type safety in flows
938+
- **[flow-type-safe-output.ts](https://github.com/ax-llm/ax/tree/main/src/examples/flow-type-safe-output.ts)** - Type-safe flow outputs
939+
- **[flow-logging-simple.ts](https://github.com/ax-llm/ax/tree/main/src/examples/flow-logging-simple.ts)** - Simple flow logging
940+
- **[flow-verbose-logging.ts](https://github.com/ax-llm/ax/tree/main/src/examples/flow-verbose-logging.ts)** - Detailed flow debugging
941+
942+
### Optimization & Training
943+
944+
- **[teacher-student-optimization.ts](https://github.com/ax-llm/ax/tree/main/src/examples/teacher-student-optimization.ts)** - MiPRO teacher-student optimization
945+
- **[mipro-python-optimizer.ts](https://github.com/ax-llm/ax/tree/main/src/examples/mipro-python-optimizer.ts)** - MiPRO with Python backend
946+
- **[gepa.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gepa.ts)** - GEPA optimizer basics
947+
- **[gepa-flow.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gepa-flow.ts)** - GEPA with workflows
948+
- **[gepa-train-inference.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gepa-train-inference.ts)** - GEPA training and inference
949+
- **[gepa-quality-vs-speed-optimization.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gepa-quality-vs-speed-optimization.ts)** - Multi-objective optimization
950+
- **[ace-train-inference.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ace-train-inference.ts)** - ACE optimizer demonstration
951+
- **[simple-optimizer-test.ts](https://github.com/ax-llm/ax/tree/main/src/examples/simple-optimizer-test.ts)** - Basic optimizer testing
952+
- **[optimizer-metrics.ts](https://github.com/ax-llm/ax/tree/main/src/examples/optimizer-metrics.ts)** - Optimization metrics tracking
953+
- **[use-examples.ts](https://github.com/ax-llm/ax/tree/main/src/examples/use-examples.ts)** - Using examples for few-shot learning
954+
955+
### Multi-Modal & Vision
956+
957+
- **[multi-modal.ts](https://github.com/ax-llm/ax/tree/main/src/examples/multi-modal.ts)** - Basic multi-modal processing
958+
- **[multi-modal-abstraction.ts](https://github.com/ax-llm/ax/tree/main/src/examples/multi-modal-abstraction.ts)** - Advanced multi-modal patterns
959+
- **[image-arrays-test.ts](https://github.com/ax-llm/ax/tree/main/src/examples/image-arrays-test.ts)** - Processing multiple images
960+
- **[image-arrays-multi-provider-test.ts](https://github.com/ax-llm/ax/tree/main/src/examples/image-arrays-multi-provider-test.ts)** - Multi-provider image handling
961+
- **[audio-arrays-test.ts](https://github.com/ax-llm/ax/tree/main/src/examples/audio-arrays-test.ts)** - Audio processing
962+
963+
### RAG & Document Processing
964+
965+
- **[rag-docs.ts](https://github.com/ax-llm/ax/tree/main/src/examples/rag-docs.ts)** - Basic RAG implementation
966+
- **[advanced-rag.ts](https://github.com/ax-llm/ax/tree/main/src/examples/advanced-rag.ts)** - Advanced RAG patterns
967+
- **[vectordb.ts](https://github.com/ax-llm/ax/tree/main/src/examples/vectordb.ts)** - Vector database integration
968+
- **[codingWithMemory.ts](https://github.com/ax-llm/ax/tree/main/src/examples/codingWithMemory.ts)** - Code generation with memory
969+
970+
### Provider-Specific Examples
971+
972+
#### Anthropic (Claude)
973+
- **[anthropic-thinking-function.ts](https://github.com/ax-llm/ax/tree/main/src/examples/anthropic-thinking-function.ts)** - Extended thinking with function calls
974+
- **[anthropic-thinking-separation.ts](https://github.com/ax-llm/ax/tree/main/src/examples/anthropic-thinking-separation.ts)** - Separating thinking from output
975+
- **[anthropic-web-search.ts](https://github.com/ax-llm/ax/tree/main/src/examples/anthropic-web-search.ts)** - Web search with Claude
976+
- **[test-anthropic-cache.ts](https://github.com/ax-llm/ax/tree/main/src/examples/test-anthropic-cache.ts)** - Prompt caching with Anthropic
977+
978+
#### Google Gemini
979+
- **[gemini-file-support.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gemini-file-support.ts)** - File uploads with Gemini
980+
- **[gemini-google-maps.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gemini-google-maps.ts)** - Google Maps integration
981+
- **[gemini-empty-params-function.ts](https://github.com/ax-llm/ax/tree/main/src/examples/gemini-empty-params-function.ts)** - Functions without parameters
982+
- **[vertex-auth-example.ts](https://github.com/ax-llm/ax/tree/main/src/examples/vertex-auth-example.ts)** - Vertex AI authentication
983+
984+
#### OpenAI
985+
- **[openai-responses.ts](https://github.com/ax-llm/ax/tree/main/src/examples/openai-responses.ts)** - OpenAI response handling
986+
- **[openai-web-search.ts](https://github.com/ax-llm/ax/tree/main/src/examples/openai-web-search.ts)** - Web search with OpenAI
987+
- **[reasoning-o3-example.ts](https://github.com/ax-llm/ax/tree/main/src/examples/reasoning-o3-example.ts)** - O3 reasoning model
988+
989+
#### Other Providers
990+
- **[grok-live-search.ts](https://github.com/ax-llm/ax/tree/main/src/examples/grok-live-search.ts)** - Grok with live search
991+
- **[openrouter.ts](https://github.com/ax-llm/ax/tree/main/src/examples/openrouter.ts)** - OpenRouter integration
992+
993+
### MCP (Model Context Protocol)
994+
995+
- **[mcp-client-memory.ts](https://github.com/ax-llm/ax/tree/main/src/examples/mcp-client-memory.ts)** - MCP memory server integration
996+
- **[mcp-client-blender.ts](https://github.com/ax-llm/ax/tree/main/src/examples/mcp-client-blender.ts)** - Blender MCP integration
997+
- **[mcp-client-pipedream.ts](https://github.com/ax-llm/ax/tree/main/src/examples/mcp-client-pipedream.ts)** - Pipedream MCP integration
998+
- **[mcp-client-notion-http-oauth.ts](https://github.com/ax-llm/ax/tree/main/src/examples/mcp-client-notion-http-oauth.ts)** - Notion MCP with HTTP OAuth
999+
- **[mcp-client-notion-sse-oauth.ts](https://github.com/ax-llm/ax/tree/main/src/examples/mcp-client-notion-sse-oauth.ts)** - Notion MCP with SSE OAuth
1000+
1001+
### Advanced Patterns
1002+
1003+
- **[react.ts](https://github.com/ax-llm/ax/tree/main/src/examples/react.ts)** - ReAct (Reasoning + Acting) pattern
1004+
- **[prime.ts](https://github.com/ax-llm/ax/tree/main/src/examples/prime.ts)** - Prime number generation with reasoning
1005+
- **[fibonacci.ts](https://github.com/ax-llm/ax/tree/main/src/examples/fibonacci.ts)** - Fibonacci sequence generation
1006+
- **[show-thoughts.ts](https://github.com/ax-llm/ax/tree/main/src/examples/show-thoughts.ts)** - Displaying model reasoning
1007+
- **[checkpoint-recovery.ts](https://github.com/ax-llm/ax/tree/main/src/examples/checkpoint-recovery.ts)** - Checkpointing and recovery
1008+
- **[balancer.ts](https://github.com/ax-llm/ax/tree/main/src/examples/balancer.ts)** - Load balancing across models
1009+
- **[ax-multiservice-router.ts](https://github.com/ax-llm/ax/tree/main/src/examples/ax-multiservice-router.ts)** - Routing between multiple AI services
1010+
1011+
### Monitoring & Debugging
1012+
1013+
- **[debug-logging.ts](https://github.com/ax-llm/ax/tree/main/src/examples/debug-logging.ts)** - Debug logging configuration
1014+
- **[telemetry.ts](https://github.com/ax-llm/ax/tree/main/src/examples/telemetry.ts)** - Telemetry and observability
1015+
- **[metrics-export.ts](https://github.com/ax-llm/ax/tree/main/src/examples/metrics-export.ts)** - Exporting metrics
1016+
1017+
### Abort & Control Flow
1018+
1019+
- **[abort-simple.ts](https://github.com/ax-llm/ax/tree/main/src/examples/abort-simple.ts)** - Simple abort handling
1020+
- **[abort-patterns.ts](https://github.com/ax-llm/ax/tree/main/src/examples/abort-patterns.ts)** - Advanced abort patterns
1021+
1022+
### Web & Browser
1023+
1024+
- **[web-chat.html](https://github.com/ax-llm/ax/tree/main/src/examples/web-chat.html)** - Browser-based chat interface
1025+
- **[webllm-chat.html](https://github.com/ax-llm/ax/tree/main/src/examples/webllm-chat.html)** - WebLLM browser integration
1026+
- **[cors-proxy.js](https://github.com/ax-llm/ax/tree/main/src/examples/cors-proxy.js)** - CORS proxy for browser usage
1027+
1028+
### Deployment
1029+
1030+
- **[docker.ts](https://github.com/ax-llm/ax/tree/main/src/examples/docker.ts)** - Docker deployment example
1031+
8791032
## Best Practices
8801033

8811034
1. **Start Simple**: Begin with basic signatures, add complexity as needed

src/ax/dsp/extract.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,43 @@ Model Answer:`;
391391
modelAnswer1: 'Only field one is present',
392392
});
393393
});
394+
395+
test('Issue #432: correctly extracts top level array of objects', () => {
396+
const sig = new AxSignature({
397+
inputs: [{ name: 'document', type: { name: 'string' } }],
398+
outputs: [
399+
{
400+
name: 'people',
401+
type: {
402+
name: 'object',
403+
isArray: true,
404+
fields: {
405+
name: { type: 'string', description: 'Person name' },
406+
age: { type: 'number', description: 'Person age', maximum: 80 },
407+
gender: {
408+
type: 'class',
409+
options: ['male', 'female'],
410+
description: 'Person gender',
411+
},
412+
},
413+
},
414+
},
415+
],
416+
});
417+
418+
const values: Record<string, unknown> = {};
419+
const content = JSON.stringify([
420+
{ name: 'John', age: 29, gender: 'male' },
421+
{ name: 'Sarah', age: 34, gender: 'female' },
422+
]);
423+
424+
extractValues(sig, values, content);
425+
426+
expect(values).toEqual({
427+
people: [
428+
{ name: 'John', age: 29, gender: 'male' },
429+
{ name: 'Sarah', age: 34, gender: 'female' },
430+
],
431+
});
432+
});
394433
});

src/ax/dsp/extract.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import type { AxField, AxSignature } from './sig.js';
1313
import type { AxGenOut, GenDeltaOut } from './types.js';
1414
import { matchesContent, parseMarkdownList } from './util.js';
1515
import {
16-
validateURL,
17-
validateStringConstraints,
1816
validateNumberConstraints,
17+
validateStringConstraints,
18+
validateURL,
1919
} from './validators.js';
2020

2121
export const extractValues = (
@@ -750,7 +750,12 @@ export function validateStructuredOutputValues(
750750
}
751751

752752
// Recursively validate nested object fields
753-
if (type.name === 'object' && type.fields && typeof value === 'object') {
753+
if (
754+
type.name === 'object' &&
755+
type.fields &&
756+
typeof value === 'object' &&
757+
!Array.isArray(value)
758+
) {
754759
validateNestedObjectFields(field, value as Record<string, unknown>);
755760
}
756761

src/ax/dsp/generate.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,7 @@ export class AxGen<IN = any, OUT extends AxGenOut = any>
348348
let responseFormat: AxChatRequest['responseFormat'];
349349

350350
const outputFields = this.signature.getOutputFields();
351-
const hasComplexFields = outputFields.some(
352-
(f) => f.type?.name === 'object' || (f.type?.isArray && f.type.fields)
353-
);
351+
const hasComplexFields = this.signature.hasComplexFields();
354352

355353
// Auto-detect structured output requirement
356354
// If we have object types in output or array of objects, we use structured outputs

src/ax/dsp/processResponse.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,7 @@ async function* ProcessStreamingResponse<OUT extends AxGenOut>({
237237

238238
// Check if we should use the partial JSON parser for structured outputs
239239
const outputFields = signature.getOutputFields();
240-
const hasComplexFields = outputFields.some(
241-
(f) => f.type?.name === 'object' || (f.type?.isArray && f.type.fields)
242-
);
240+
const hasComplexFields = signature.hasComplexFields();
243241

244242
if (hasComplexFields) {
245243
// Try to parse partial JSON
@@ -425,9 +423,7 @@ export async function* finalizeStreamingResponse<OUT extends AxGenOut>({
425423
state.functionCalls = [];
426424
} else {
427425
const outputFields = signature.getOutputFields();
428-
const hasComplexFields = outputFields.some(
429-
(f) => f.type?.name === 'object' || (f.type?.isArray && f.type.fields)
430-
);
426+
const hasComplexFields = signature.hasComplexFields();
431427

432428
let jsonParsed = false;
433429
if (hasComplexFields) {
@@ -756,9 +752,7 @@ export async function* processResponse<OUT>({
756752
}
757753

758754
const outputFields = signature.getOutputFields();
759-
const hasComplexFields = outputFields.some(
760-
(f) => f.type?.name === 'object' || (f.type?.isArray && f.type.fields)
761-
);
755+
const hasComplexFields = signature.hasComplexFields();
762756

763757
if (hasComplexFields) {
764758
try {

src/ax/dsp/prompt.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,7 @@ export class AxPromptTemplate {
105105
task.push(functionCallInstructions.trim());
106106
}
107107

108-
const hasComplexFields = this.sig
109-
.getOutputFields()
110-
.some(
111-
(f) => f.type?.name === 'object' || (f.type?.isArray && f.type.fields)
112-
);
108+
const hasComplexFields = this.sig.hasComplexFields();
113109

114110
if (!hasComplexFields) {
115111
task.push(formattingRules.trim());
@@ -235,11 +231,7 @@ export class AxPromptTemplate {
235231
* Build formatting rules section with protection
236232
*/
237233
private buildFormattingRulesSection(): string {
238-
const hasComplexFields = this.sig
239-
.getOutputFields()
240-
.some(
241-
(f) => f.type?.name === 'object' || (f.type?.isArray && f.type.fields)
242-
);
234+
const hasComplexFields = this.sig.hasComplexFields();
243235

244236
if (hasComplexFields) {
245237
return `**CRITICAL - Structured Output Format**:

src/ax/dsp/sig.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,45 @@ Thought Process: I am thinking.`;
692692
});
693693
});
694694

695+
describe('AxSignature hasComplexFields', () => {
696+
it('should return false for simple signature', () => {
697+
const sig = f().input('in', f.string()).output('out', f.string()).build();
698+
expect(sig.hasComplexFields()).toBe(false);
699+
});
700+
701+
it('should return true for complex output', () => {
702+
const sig = f()
703+
.input('in', f.string())
704+
.output('out', f.object({ field: f.string() }))
705+
.build();
706+
expect(sig.hasComplexFields()).toBe(true);
707+
});
708+
709+
it('should return true for complex input', () => {
710+
const sig = f()
711+
.input('in', f.object({ field: f.string() }))
712+
.output('out', f.string())
713+
.build();
714+
expect(sig.hasComplexFields()).toBe(true);
715+
});
716+
717+
it('should return true for array of objects in input', () => {
718+
const sig = f()
719+
.input('in', f.object({ field: f.string() }).array())
720+
.output('out', f.string())
721+
.build();
722+
expect(sig.hasComplexFields()).toBe(true);
723+
});
724+
725+
it('should return true for array of objects in output', () => {
726+
const sig = f()
727+
.input('in', f.string())
728+
.output('out', f.object({ field: f.string() }).array())
729+
.build();
730+
expect(sig.hasComplexFields()).toBe(true);
731+
});
732+
});
733+
695734
describe('Type-safe field addition methods', () => {
696735
it('should append input field with type safety', () => {
697736
const baseSig = AxSignature.create(

src/ax/dsp/sig.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,16 @@ export class AxSignature<
15221522
}
15231523
}
15241524

1525+
public hasComplexFields = (): boolean => {
1526+
const check = (fields: readonly AxField[]) =>
1527+
fields.some(
1528+
(f) =>
1529+
f.type?.name === 'object' ||
1530+
(f.type?.isArray && f.type.fields !== undefined)
1531+
);
1532+
return check(this.inputFields) || check(this.outputFields);
1533+
};
1534+
15251535
public validate = (): boolean => {
15261536
// Check if already validated at current hash
15271537
if (this.validatedAtHash === this.sigHash) {

src/examples/extract.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const res = await gen.forward(
6666
{ chatMessage, currentDate },
6767
{
6868
model: 'model-a',
69+
debug: true,
6970
}
7071
);
7172
console.log('>', res);

0 commit comments

Comments
 (0)