Package Exports
- @aeye/core
- @aeye/core/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@aeye/core) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@aeye/core
Core primitives for building AI agents, tools, and prompts with TypeScript. Provides a type-safe, composable framework for creating sophisticated AI applications with structured inputs/outputs, tool calling, and context management.
Features
- 🎯 Type-Safe Components - Prompts, Tools, and Agents with full TypeScript support
- 🔧 Tool Calling - Native support for function/tool calling with schema validation
- 📝 Template-Based Prompts - Handlebars templates for dynamic prompt generation
- ✅ Schema Validation - Zod integration for structured inputs and outputs
- 🌊 Streaming Support - First-class streaming for real-time AI responses
- 🔄 Composable Architecture - Tools can use other tools, prompts can use tools, agents orchestrate everything
- 📊 Context Management - Type-safe context threading and automatic token window management
- 🎛️ Flexible Execution - Sequential, parallel, or immediate tool execution modes
Installation
npm install @aeye/core zod handlebarsCore Concepts
Components
All AI primitives implement the Component interface:
- Prompt - Generates AI responses with optional tool usage and structured outputs
- Tool - Extends AI capabilities with custom functions and external integrations
- Agent - Orchestrates complex workflows combining prompts and tools
Context & Metadata
- Context (
TContext) - Application-specific data threaded through operations (user, db, etc.) - Metadata (
TMetadata) - Execution settings for AI requests (model, temperature, etc.)
Quick Start
Basic Prompt
import { Prompt } from '@aeye/core';
import z from 'zod';
const summarizer = new Prompt({
name: 'summarize',
description: 'Summarizes text concisely',
content: 'Summarize the following text:\n\n{{text}}',
// Transform input
input: (input: { text: string }) => ({ text: input.text }),
// Define output schema
schema: z.object({
summary: z.string().describe('A concise summary'),
keyPoints: z.array(z.string()).describe('Main points')
})
});
// Execute with a context that has an executor
const result = await summarizer.get(
'result',
{ text: 'Long article text...' },
{
execute: yourAIExecutor, // Executor function from a provider
messages: []
}
);
console.log(result.summary);
console.log(result.keyPoints);Creating Tools
import { Tool } from '@aeye/core';
import z from 'zod';
const weatherTool = new Tool({
name: 'getWeather',
description: 'Get current weather for a location',
instructions: 'Use this tool to get weather data',
schema: z.object({
location: z.string().describe('City name or coordinates'),
units: z.enum(['celsius', 'fahrenheit']).default('celsius')
}),
call: async (input, refs, ctx) => {
const response = await fetch(
`https://api.weather.com/v1/${input.location}`
);
const data = await response.json();
return {
temperature: data.temp,
condition: data.condition,
humidity: data.humidity
};
}
});Prompts with Tools
const travelAdvisor = new Prompt({
name: 'travelAdvisor',
description: 'Provides travel advice based on weather',
content: `You are a travel advisor. Help plan a trip to {{destination}}.
Use the weather tool to check current conditions, then provide:
- What to pack
- Activities to do
- Best times to visit`,
input: (input: { destination: string }) => ({
destination: input.destination
}),
tools: [weatherTool],
schema: z.object({
recommendations: z.array(z.string()),
packingList: z.array(z.string()),
weatherNotes: z.string()
})
});
// The AI will automatically call weatherTool if needed
const advice = await travelAdvisor.get(
'result',
{ destination: 'Paris' },
{ execute: yourAIExecutor, messages: [] }
);Streaming Responses
// Stream content only
for await (const chunk of summarizer.get(
'streamContent',
{ text: 'Long text...' },
{ stream: yourAIStreamer, messages: [] }
)) {
process.stdout.write(chunk);
}
// Stream all events (including tool calls)
for await (const event of summarizer.get(
'stream',
{ text: 'Long text...' },
{ stream: yourAIStreamer, messages: [] }
)) {
if (event.type === 'textPartial') {
console.log('Text:', event.content);
} else if (event.type === 'toolStart') {
console.log('Tool started:', event.tool.name);
} else if (event.type === 'toolOutput') {
console.log('Tool result:', event.result);
}
}Building Agents
import { Agent } from '@aeye/core';
const researchAgent = new Agent({
name: 'researcher',
description: 'Conducts research on topics',
refs: [searchTool, summarizeTool, analyzeTool],
call: async (input: { topic: string }, [search, summarize, analyze], ctx) => {
// Step 1: Search for information
const searchResults = await search.run(
{ query: input.topic, limit: 5 },
ctx
);
// Step 2: Summarize findings
const summaries = [];
for (const result of searchResults) {
const summary = await summarize.get(
'result',
{ text: result.content },
ctx
);
summaries.push(summary);
}
// Step 3: Analyze and synthesize
const analysis = await analyze.get(
'result',
{ topic: input.topic, sources: summaries },
ctx
);
return analysis;
}
});
const research = await researchAgent.run(
{ topic: 'Quantum Computing' },
{ execute: yourAIExecutor, messages: [] }
);Prompt Modes
The get method supports different execution modes:
| Mode | Description | Returns |
|---|---|---|
'result' |
Get final structured output | TOutput |
'tools' |
Get tool call results | PromptToolOutput[] |
'stream' |
Stream all events | AsyncGenerator<PromptEvent> |
'streamTools' |
Stream tool outputs | AsyncGenerator<PromptToolOutput> |
'streamContent' |
Stream text content only | AsyncGenerator<string> |
// Get structured result
const result = await prompt.get('result', input, ctx);
// Get tool outputs only
const tools = await prompt.get('tools', input, ctx);
// Stream everything
for await (const event of prompt.get('stream', input, ctx)) {
// Handle different event types
}Tool Execution Modes
Control how tools are executed:
const prompt = new Prompt({
name: 'multi-tool',
description: 'Uses multiple tools',
content: 'Analyze the data',
tools: [tool1, tool2, tool3],
// Tool execution mode
toolExecution: 'parallel', // 'sequential' | 'parallel' | 'immediate'
// Retry configuration
toolRetries: 2, // Retry failed tools
toolIterations: 3, // Max iterations for tool calls
toolsMax: 5, // Max total tool calls
});sequential- Wait for each tool to finish before continuingparallel- Start all tools at once, wait for all to completeimmediate- Start tools immediately as they're available
Advanced Features
Context-Aware Tools
const contextTool = new Tool({
name: 'contextAware',
description: 'A context-aware tool',
instructions: 'Use this tool...',
// Schema can depend on context
schema: (ctx) => {
if (ctx.userRole === 'admin') {
return z.object({
action: z.enum(['read', 'write', 'delete'])
});
}
return z.object({
action: z.enum(['read'])
});
},
// Check if tool is applicable
applicable: (ctx) => {
return ctx.isAuthenticated === true;
},
call: async (input, refs, ctx) => {
// Implementation with context access
}
});Custom Validation
const validatedTool = new Tool({
name: 'placeOrder',
description: 'Place an order',
schema: z.object({
itemId: z.string(),
quantity: z.number().min(1)
}),
// Additional validation beyond schema
validate: async (input, ctx) => {
const inventory = await checkInventory(input.itemId);
if (inventory < input.quantity) {
throw new Error(`Only ${inventory} items available`);
}
},
call: async (input, refs, ctx) => {
// Place order
}
});Event Tracking
import { withEvents } from '@aeye/core';
const runner = withEvents({
onStatus: (instance) => {
console.log(`${instance.component.name}: ${instance.status}`);
if (instance.status === 'completed') {
const duration = instance.completed - instance.started;
console.log(`Took ${duration}ms`);
}
},
onPromptEvent: (instance, event) => {
if (event.type === 'usage') {
console.log('Tokens used:', event.usage);
}
}
});
const result = await prompt.get(
'result',
{ text: 'Hello' },
{
execute: yourAIExecutor,
messages: [],
runner
}
);Token Management
Automatic conversation trimming when token limits are reached:
const context = {
execute: yourAIExecutor,
messages: conversationHistory,
// Token configuration
defaultCompletionTokens: 2048,
// Custom token estimation
estimateTokens: (message) => {
return message.content.length / 4; // Rough estimate
}
};
// Prompt will automatically trim messages if needed
const result = await prompt.get('result', { text: 'Query' }, context);Dynamic Reconfiguration
const adaptivePrompt = new Prompt({
name: 'adaptive',
description: 'Adapts based on execution stats',
content: 'Solve: {{problem}}',
schema: z.object({ solution: z.string() }),
// Adjust configuration during execution
reconfig: (stats, ctx) => {
// If tools are failing, change strategy
if (stats.toolCallErrors > 3) {
return {
config: { toolsOneAtATime: true },
maxIterations: 2
};
}
return {};
}
});API Reference
Prompt
class Prompt<TContext, TMetadata, TName, TInput, TOutput, TTools>Constructor Options:
name: string- Unique identifierdescription: string- Purpose descriptioncontent: string- Handlebars templateinput?: Function- Transform input for templateschema?: ZodType | Function- Output schemaconfig?: Partial<Request> | Function- AI request configtools?: Tool[]- Available toolstoolExecution?: 'sequential' | 'parallel' | 'immediate'toolRetries?: number- Retry failed toolstoolIterations?: number- Max tool call iterationsoutputRetries?: number- Retry invalid outputsmetadata?: TMetadata- Execution metadatavalidate?: Function- Post-validation hookapplicable?: Function- Applicability check
Methods:
get(input, mode, ctx)- Execute and retrieve outputrun(input, ctx)- Execute with full streamingapplicable(ctx)- Check if prompt can run
Tool
class Tool<TContext, TMetadata, TName, TParams, TOutput, TRefs>Constructor Options:
name: string- Unique identifierdescription: string- Purpose descriptioninstructions?: string- Handlebars usage instructionsschema: ZodType | Function- Input schemarefs?: Component[]- Referenced componentscall: Function- Implementationvalidate?: Function- Post-validation hookapplicable?: Function- Applicability check
Methods:
run(input, ctx)- Execute the toolcompile(ctx)- Generate tool definition for AIparse(ctx, args)- Parse and validate argumentsapplicable(ctx)- Check if tool can run
Agent
class Agent<TContext, TMetadata, TName, TInput, TOutput, TRefs>Constructor Options:
name: string- Unique identifierdescription: string- Purpose descriptionrefs?: Component[]- Referenced componentscall: Function- Implementation with refsapplicable?: Function- Applicability check
Methods:
run(input, ctx)- Execute the agentapplicable(ctx)- Check if agent can run
Context Structure
interface Context<TContext, TMetadata> {
// Required: AI execution
execute?: Executor<TContext, TMetadata>;
stream?: Streamer<TContext, TMetadata>;
// Messages
messages: Message[];
// Token management
defaultCompletionTokens?: number;
estimateTokens?: (message: Message) => number;
// Execution control
signal?: AbortSignal;
runner?: Events;
// User context (TContext) spreads here
[key: string]: any;
}Request Configuration
interface Request {
messages: Message[];
temperature?: number; // 0-2
topP?: number; // 0-1
maxTokens?: number;
stop?: string | string[];
tools?: ToolDefinition[];
toolChoice?: 'auto' | 'required' | 'none' | { tool: string };
responseFormat?: 'text' | 'json' | ResponseFormat;
// ... more options
}Best Practices
- Type Safety - Use TypeScript generics for context and metadata
- Schema Validation - Use Zod for robust input/output validation
- Error Handling - Wrap component execution in try-catch blocks
- Token Management - Provide
estimateTokensfor accurate trimming - Tool Organization - Group related tools in agents
- Testing - Unit test tools and prompts independently
- Documentation - Document template variables and schemas
- Context Minimization - Pass only necessary data in context
- Streaming - Use streaming for better UX with long responses
- Validation - Use
validatehooks for business logic validation
Examples
See the src/__tests__ directory for comprehensive examples:
- prompt-core-features.test.ts - Basic prompt usage
- prompt-streaming-tool-events.test.ts - Streaming and events
- tool.test.ts - Tool creation and usage
- agent.test.ts - Agent orchestration
- context-propagation.test.ts - Context handling
TypeScript Support
Full type inference across component hierarchies:
// Types are automatically inferred
const prompt = new Prompt({
name: 'example',
schema: z.object({ result: z.string() })
// ...
});
// TypeScript knows the output type
const output = await prompt.get({}); // { result: string }
// Tool parameters are type-safe
const tool = new Tool({
name: 'math',
schema: z.object({ a: z.number(), b: z.number() }),
call: (input, refs, ctx) => {
// input is typed as { a: number, b: number }
return input.a + input.b;
}
});Contributing
Contributions are welcome! See the main @aeye repository for contribution guidelines.
License
GPL-3.0 © ClickerMonkey