Juspay Agent Framework (JAF) Documentation¶
Welcome to the comprehensive documentation for the Juspay Agent Framework (JAF) - a purely functional agent framework built on immutable state, type safety, and composable policies.
๐ Quick Start¶
New to JAF? Start here:
- Getting Started - Installation, basic concepts, and your first agent
- Core Concepts - Understanding JAF's functional architecture
- Examples - Working examples and tutorials
๐ Documentation Structure¶
Core Framework¶
- Core Concepts - RunState, agents, tools, and functional programming principles
- API Reference - Complete TypeScript API documentation
- Tools - Building robust, production-ready tools with validation and error handling
System Components¶
- Memory System - Conversation persistence with in-memory, Redis, and PostgreSQL providers
- Model Providers - LLM integration, configuration, and custom providers
- Server & API - HTTP server setup and REST API documentation
Development & Deployment¶
- Examples - Server demo, RAG demo, and integration patterns
- Testing & CI/CD - Comprehensive testing infrastructure, patterns, and continuous integration
- Deployment - Production deployment with Docker, Kubernetes, and infrastructure
- Troubleshooting - Common issues, debugging, and performance optimization
๐ฏ Use Case Navigation¶
I want to...¶
Build my first agent
โ Getting Started โ Examples
Create robust tools
โ Tools โ API Reference
Add conversation memory
โ Memory System โ Examples: Memory Persistence
Deploy to production
โ Deployment โ Server & API
Build an HTTP API
โ Server & API โ Examples: Server Demo
Integrate with external LLMs
โ Model Providers โ Deployment: Environment Setup
Debug issues
โ Troubleshooting โ Core Concepts: Error Handling
Understand the architecture
โ Core Concepts โ API Reference
๐ง Framework Philosophy¶
JAF is built on functional programming principles:
- Immutability: All core data structures are deeply
readonly
- Pure Functions: Core logic expressed as pure, predictable functions
- Effects at the Edge: Side effects isolated in Provider modules
- Type Safety: Comprehensive TypeScript types with runtime validation
- Composability: Small, focused components that compose into complex systems
๐ Documentation Quality¶
All documentation has been:
โ
Validated against source code - Every example and API reference is verified against the actual framework implementation
โ
Tested with real examples - Code snippets are based on working examples in the repository
โ
Production-ready - Includes best practices, error handling, and deployment considerations
โ
Comprehensive - Covers all framework features from basic concepts to advanced patterns
๐ค Contributing¶
Found an issue or want to improve the documentation?
- Check the source code to verify current implementation
- Review the examples for usage patterns
- Ensure all code examples are tested and working
- Submit improvements via pull request
๐ Quick Reference¶
Key Functions¶
Essential Types¶
type Agent<Ctx, Out> = { name: string; instructions: string; tools?: Tool<any, Ctx>[] }
type Tool<Args, Ctx> = { schema: ToolSchema<Args>; execute: ToolFunction<Args, Ctx> }
type RunState<Ctx> = { runId: RunId; traceId: TraceId; messages: readonly Message[]; ... }
Memory Providers¶
// Development
const memory = await createInMemoryProvider();
// Production
const memory = await createRedisProvider(config, redisClient);
const memory = await createPostgresProvider(config, pgClient);
Server Setup¶
Testing Guidelines and CI/CD¶
JAF follows functional programming principles throughout its testing infrastructure, ensuring robust, reliable code with comprehensive coverage.
๐งช Testing Infrastructure¶
Jest Configuration¶
JAF uses Jest with TypeScript for comprehensive testing:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
transform: {
'^.+\\.ts$': 'ts-jest',
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/demo/**/*',
'!src/providers/mcp.ts',
'!src/a2a/examples/**/*',
],
moduleNameMapping: {
'^(\\.{1,2}/.*)\\.js$': '$1',
}
};
Coverage Requirements¶
- Minimum Coverage: 80% overall coverage
- Line Coverage: 85% for core modules
- Branch Coverage: 75% for complex logic
- Function Coverage: 90% for public APIs
Test Structure¶
src/
โโโ __tests__/ # Core framework tests
โโโ a2a/__tests__/ # A2A protocol tests
โโโ adk/__tests__/ # ADK layer tests
โโโ module/__tests__/ # Module-specific tests
๐ CI/CD Pipeline¶
GitHub Actions Workflow¶
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run type checking
run: npm run typecheck
- name: Run tests
run: npm test
- name: Run build
run: npm run build
Multi-Version Testing¶
- Node.js 18.x: LTS baseline support
- Node.js 20.x: Current LTS (primary development)
- Node.js 22.x: Latest stable for future compatibility
Quality Checks¶
- ESLint: TypeScript/functional programming rules
- TypeScript: Strict type checking with
--noEmit
- Prettier: Code formatting consistency
- Jest: Comprehensive testing with coverage reporting
๐ Testing Patterns¶
Functional Testing Patterns¶
JAF tests follow functional programming principles:
// โ
Pure function testing
describe('createAgent', () => {
it('should create agent with immutable configuration', () => {
const config = { name: 'test', model: 'gpt-4', instruction: 'help' };
const agent = createAgent(config);
expect(agent.config).toEqual(config);
expect(agent.config).not.toBe(config); // Different reference
expect(Object.isFrozen(agent.config)).toBe(true);
});
});
// โ
Factory function testing
describe('createInMemoryProvider', () => {
it('should create provider with default configuration', async () => {
const provider = await createInMemoryProvider();
expect(provider.config.type).toBe('memory');
expect(typeof provider.store).toBe('function');
expect(typeof provider.retrieve).toBe('function');
});
});
Integration Testing¶
describe('A2A Integration', () => {
let taskProvider: A2ATaskProvider;
beforeAll(async () => {
taskProvider = await createSimpleA2ATaskProvider('memory');
});
afterAll(async () => {
await taskProvider.close();
});
it('should handle complete task lifecycle', async () => {
// Test complete workflow from submission to completion
const task = createTestTask('task_123', 'ctx_456', 'submitted');
const storeResult = await taskProvider.storeTask(task);
expect(storeResult.success).toBe(true);
// Update to working state
await taskProvider.updateTaskStatus('task_123', 'working');
// Complete the task
await taskProvider.updateTaskStatus('task_123', 'completed');
const finalTask = await taskProvider.getTask('task_123');
expect(finalTask.data?.status.state).toBe('completed');
});
});
Async Testing Patterns¶
describe('Streaming Operations', () => {
it('should handle async generators', async () => {
const streamingEvents = runAgentStream(config, context, message);
const events = [];
for await (const event of streamingEvents) {
events.push(event);
if (events.length > 10) break; // Prevent infinite loops
}
expect(events).toHaveLength(10);
expect(events[0].type).toBe('message_start');
});
it('should handle concurrent operations', async () => {
const promises = Array.from({ length: 5 }, (_, i) =>
runAgent(config, { userId: `user_${i}` }, createUserMessage(`Test ${i}`))
);
const results = await Promise.all(promises);
expect(results).toHaveLength(5);
results.forEach(result => expect(result.success).toBe(true));
});
});
๐ฏ Best Practices¶
Test Organization¶
__tests__/
โโโ unit/ # Pure function tests
โโโ integration/ # Multi-component tests
โโโ e2e/ # End-to-end scenarios
โโโ fixtures/ # Test data and helpers
Helper Functions¶
// Test utilities following functional patterns
export const createTestTask = (
id: string = 'task_123',
contextId: string = 'ctx_456',
state: TaskState = 'submitted'
): A2ATask => ({
id,
contextId,
kind: 'task',
status: {
state,
message: createTestMessage(`Task ${id} is ${state}`),
timestamp: new Date().toISOString()
},
history: [],
artifacts: [],
metadata: {
createdAt: new Date().toISOString(),
priority: 'normal'
}
});
export const createMockModelProvider = () => ({
async getCompletion(params: any) {
const lastMessage = params.messages?.[params.messages.length - 1];
return {
message: {
content: `Echo: ${lastMessage?.content || 'Default response'}`
}
};
}
});
Mocking Strategies¶
// โ
Functional mocking - create pure mock functions
const createMockProvider = (responses: string[]) => {
let callCount = 0;
return {
async getCompletion() {
return { content: responses[callCount++] || 'Default' };
}
};
};
// โ
Dependency injection for testing
describe('Agent Execution', () => {
it('should use injected model provider', async () => {
const mockProvider = createMockProvider(['Test response']);
const result = await runAgent(config, context, message, { modelProvider: mockProvider });
expect(result.content).toContain('Test response');
});
});
Error Testing¶
describe('Error Handling', () => {
it('should handle tool execution failures', async () => {
const errorTool = createFunctionTool(
'error_tool',
'Tool that fails',
() => { throw new Error('Tool failed'); }
);
const agent = createAgent({ name: 'test', tools: [errorTool] });
const result = await runAgent(config, context, message);
expect(result.toolResponses).toContainEqual(
expect.objectContaining({ success: false })
);
});
it('should maintain data integrity on partial failures', async () => {
const provider = await createInMemoryProvider();
// Test invalid operations don't affect valid data
await provider.store('valid', { data: 'test' });
try {
await provider.store('invalid', null); // Should fail
} catch (error) {
// Error expected
}
const validData = await provider.retrieve('valid');
expect(validData).toEqual({ data: 'test' });
});
});
๐ค Contributing Guidelines¶
Pre-Commit Checks¶
# Required before committing
npm run lint # ESLint validation
npm run typecheck # TypeScript validation
npm test # Test suite execution
npm run build # Build verification
Coverage Requirements¶
- New Features: Must include comprehensive tests
- Bug Fixes: Must include regression tests
- Refactoring: Must maintain existing test coverage
- Breaking Changes: Must update all affected tests
CI Requirements¶
- All GitHub Actions checks must pass
- Code coverage must not decrease
- No TypeScript errors or warnings
- All tests must pass across supported Node.js versions
๐ Testing Examples¶
A2A Protocol Testing¶
describe('A2A Memory Serialization', () => {
it('should serialize and deserialize tasks correctly', async () => {
const originalTask = createTestTask('serialize_test', 'ctx_123');
const serialized = serializeA2ATask(originalTask);
const deserialized = deserializeA2ATask(serialized);
expect(deserialized).toEqual(originalTask);
expect(deserialized).not.toBe(originalTask); // Different reference
});
it('should handle complex task artifacts', async () => {
const task = createTestTask('artifact_test', 'ctx_456');
task.artifacts = [
{
artifactId: 'test_artifact',
name: 'Test Result',
description: 'Complex test data',
parts: [
{ kind: 'text', text: 'Result data' },
{ kind: 'json', json: { complex: { nested: 'data' } } }
]
}
];
const provider = await createA2AInMemoryTaskProvider({
type: 'memory',
enableArtifacts: true
});
await provider.storeTask(task);
const retrieved = await provider.getTask('artifact_test');
expect(retrieved.data?.artifacts).toEqual(task.artifacts);
});
});
ADK Layer Testing¶
describe('Multi-Agent Coordination', () => {
it('should delegate tasks to appropriate agents', async () => {
const weatherAgent = createAgent({
name: 'weather',
model: 'gpt-4',
instruction: 'Provide weather info',
tools: [createWeatherTool()]
});
const mathAgent = createAgent({
name: 'math',
model: 'gpt-4',
instruction: 'Perform calculations',
tools: [createCalculatorTool()]
});
const coordinator = createMultiAgent(
'coordinator',
'gpt-4',
'Route requests to specialists',
[weatherAgent.config, mathAgent.config],
'conditional'
);
const sessionProvider = createInMemorySessionProvider();
const runnerConfig = createRunnerConfig(coordinator, sessionProvider);
// Test weather delegation
const weatherMessage = createUserMessage('What\'s the weather in Tokyo?');
const response = await runAgent(runnerConfig, { userId: 'test' }, weatherMessage);
expect(response.metadata.executedAgent).toBe('weather');
expect(response.toolCalls.some(call => call.name === 'get_weather')).toBe(true);
});
});
Performance Testing¶
describe('Performance and Scalability', () => {
it('should handle high-volume task operations', async () => {
const provider = await createA2AInMemoryTaskProvider({
type: 'memory',
maxTasks: 10000
});
const startTime = Date.now();
// Create 1000 tasks
const createPromises = Array.from({ length: 1000 }, (_, i) =>
provider.storeTask(createTestTask(`perf_task_${i}`, 'perf_ctx'))
);
await Promise.all(createPromises);
const createTime = Date.now() - startTime;
// Query performance
const queryStart = Date.now();
const tasks = await provider.getTasksByContext('perf_ctx');
const queryTime = Date.now() - queryStart;
expect(tasks.data).toHaveLength(1000);
expect(createTime).toBeLessThan(5000); // 5 seconds max for 1000 creates
expect(queryTime).toBeLessThan(1000); // 1 second max for query
});
});
๐ฏ Quick Testing Commands¶
# Run all tests
npm test
# Run with coverage
npm test -- --coverage
# Run specific test suite
npm test -- --testPathPattern=a2a
# Watch mode for development
npm test -- --watch
# Run tests for specific file
npm test -- src/a2a/__tests__/integration.test.ts
# Verbose output with detailed test results
npm test -- --verbose
# Update snapshots (if using snapshot testing)
npm test -- --updateSnapshot
Ready to build with JAF? Start with the Getting Started Guide and explore the examples!