0.1.6 • Published 12 months ago

@cultores/agentic v0.1.6

Weekly downloads
-
License
MIT
Repository
github
Last release
12 months ago

@cultores/agentic

NPM Version License: MIT

A powerful NestJS framework for building LLM-powered agents with ease and flexibility.

šŸš€ Features

  • šŸ¤– Simple and intuitive API for creating LLM-powered agents
  • šŸ”Œ Seamless integration with NestJS
  • šŸŽÆ Type-safe agent development
  • šŸ”„ Support for sequential and parallel agent execution
  • šŸ› ļø Extensible architecture
  • šŸ“¦ Built on top of LangChain.js

šŸ“¦ Installation

npm install @cultores/agentic

šŸ”§ Quick Start

  1. Create an Agency:
import { Agency, BaseAgency, AgencyConfig } from '@cultores/agentic';

@Agency({
  name: 'my-agency',
  description: 'My custom agency implementation',
})
export class MyAgency extends BaseAgency {
  protected getConfig(): AgencyConfig {
    return {
      name: 'my-agency',
      description: 'My custom agency implementation',
      metadata: { /* your metadata */ }
    };
  }
}
  1. Create an Agent:
import { BaseAgent, AgentState, AgentGraph, AgentNode, AgentEdge } from '@cultores/agentic';

@AgentGraph({
  name: 'my-agent',
  description: 'My custom agent implementation'
})
export class MyAgent extends BaseAgent {
  @AgentNode({
    name: 'start',
    description: 'Initial processing',
    type: 'llm'
  })
  async start(input: NodeInput): Promise<NodeOutput> {
    return {
      state: {
        ...input.state,
        context: { start: true }
      }
    };
  }

  @AgentNode({
    name: 'process',
    description: 'Main processing',
    type: 'tool'
  })
  async process(input: NodeInput): Promise<NodeOutput> {
    return {
      state: {
        ...input.state,
        context: { processed: true }
      }
    };
  }

  @AgentEdge({
    from: 'start',
    to: 'process',
    condition: (state) => state.context.start ? 'process' : undefined
  })
  startToProcess(state: AgentState): AgentState {
    return state;
  }
}
  1. Register and Use:
import { Module } from '@nestjs/common';

@Module({
  providers: [MyAgency, MyAgent],
})
export class AppModule {}

🌟 Key Concepts

Agency

An Agency is the top-level orchestrator that manages multiple agents. It handles:

  • Agent registration and lifecycle management
  • Sequential or parallel execution of agents
  • Error handling and retries
  • State management across agent executions
// Register multiple agents
agency.registerAgent(agent1);
agency.registerAgent(agent2);

// Execute sequentially with error handling
const result = await agency.execute(
  { 
    messages: [initialMessage], 
    context: {}, 
    metadata: {} 
  },
  { 
    sequential: true, 
    stopOnError: true,
    maxRetries: 3
  }
);

// Execute in parallel
const parallelResult = await agency.execute(
  initialState,
  { 
    sequential: false,
    stopOnError: false
  }
);

Agents

Agents are the core processing units that can:

  • Process messages using LLMs
  • Execute tools and functions
  • Chain multiple operations
  • Maintain state and memory
  • Handle loops and conditions

Node Types

  1. LLM Nodes: Process text using language models
@AgentNode({
  name: 'generate',
  type: 'llm',
  model: 'gpt-4',  // Can be string identifier or model instance
  temperature: 0.7,
  maxTokens: 1000
})
async generate(input: NodeInput): Promise<NodeOutput> {
  // The model will be automatically loaded and used
  return {
    state: {
      ...input.state,
      context: { generated: true }
    }
  };
}
  1. Tool Nodes: Execute specific functions or tools
@AgentNode({
  name: 'calculate',
  type: 'tool',
  toolName: 'calculator',
  callbacks: new CallbackManager()  // Optional callbacks
})
async calculate(input: NodeInput): Promise<NodeOutput> {
  // The tool will be automatically executed with input.params
  return {
    state: {
      ...input.state,
      context: { calculated: true }
    }
  };
}
  1. Chain Nodes: Combine multiple operations
@AgentNode({
  name: 'process',
  type: 'chain',
  chainType: 'sequential',
  steps: ['parse', 'validate', 'transform'],
  memory: true  // Enable memory for the chain
})
async process(input: NodeInput): Promise<NodeOutput> {
  return {
    state: {
      ...input.state,
      context: { chained: true },
      memory: {
        chatHistory: [...(input.state.memory?.chatHistory || [])],
        variables: { processed: true }
      }
    }
  };
}
  1. Loop Nodes: Handle repetitive operations
@AgentNode({
  name: 'retry',
  type: 'loop',
  maxIterations: 3,
  loopCondition: (state) => {
    const counter = state.context?.counter || 0;
    const target = state.metadata?.targetCount || 0;
    return counter < target;
  }
})
async retry(input: NodeInput): Promise<NodeOutput> {
  const counter = (input.state.context?.counter || 0) + 1;
  return {
    state: {
      ...input.state,
      context: { 
        ...input.state.context,
        counter,
        retried: true
      }
    }
  };
}

Complex Flow Control

Example of an agent with conditional branching and loops:

@AgentGraph({
  name: 'workflow-agent',
  description: 'Agent with complex workflow'
})
class WorkflowAgent extends BaseAgent {
  @AgentNode({
    name: 'start',
    type: 'llm'
  })
  async start(input: NodeInput): Promise<NodeOutput> {
    return {
      state: {
        ...input.state,
        context: { start: true }
      }
    };
  }

  @AgentNode({
    name: 'conditional',
    type: 'llm'
  })
  async conditional(input: NodeInput): Promise<NodeOutput> {
    const route = input.state.metadata?.route || 'A';
    return {
      state: {
        ...input.state,
        context: { 
          ...input.state.context,
          conditional: true,
          route
        }
      }
    };
  }

  @AgentNode({
    name: 'processA',
    type: 'tool'
  })
  async processA(input: NodeInput): Promise<NodeOutput> {
    return {
      state: {
        ...input.state,
        context: { ...input.state.context, processA: true }
      }
    };
  }

  @AgentNode({
    name: 'processB',
    type: 'tool'
  })
  async processB(input: NodeInput): Promise<NodeOutput> {
    return {
      state: {
        ...input.state,
        context: { ...input.state.context, processB: true }
      }
    };
  }

  @AgentEdge({
    from: 'start',
    to: 'conditional'
  })
  startToConditional(state: AgentState): AgentState {
    return state;
  }

  @AgentEdge({
    from: 'conditional',
    to: 'processA',
    condition: (state) => state.context?.route === 'A' ? 'processA' : undefined
  })
  conditionalToA(state: AgentState): AgentState {
    return state;
  }

  @AgentEdge({
    from: 'conditional',
    to: 'processB',
    condition: (state) => state.context?.route === 'B' ? 'processB' : undefined
  })
  conditionalToB(state: AgentState): AgentState {
    return state;
  }
}

šŸ“Š Visualization

Agentic provides a built-in visualization tool to help you understand and debug your agent's flow. You can use it in three ways:

  1. CLI Command: After installing the package globally or in your project:
# Using npx
npx @cultores/agentic visualize

# Or if installed globally
agentic visualize
  1. Package Script: Add to your package.json:
{
  "scripts": {
    "visualize": "@cultores/agentic visualize"
  }
}
  1. Programmatically: Import and use in your TypeScript code:
import { AgenticVisualizer } from '@cultores/agentic';

// Create an instance of your agent
const myAgent = new MyAgent();

// Generate the visualization
const visualization = AgenticVisualizer.visualize(myAgent);
console.log(visualization);

The visualizer will scan your project for agent instances and generate an ASCII representation of each agent's nodes and their connections:

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  __start__   │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
       │
       ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│    start     │
│    (llm)     │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
       │
       ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│   process    │
│   (tool)     │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
       │
       ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│    decide    │
│    (llm)     │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

This visualization helps you:

  • Verify the structure of your agent's flow
  • Debug connection issues
  • Understand the execution path
  • Document your agent's architecture

Error Handling

The framework provides robust error handling:

// Handle node execution errors
try {
  const result = await agent.run({
    messages: [],
    context: {},
    metadata: { route: 'invalid' }
  });
} catch (error) {
  console.error('Node execution failed:', error.message);
}

// Handle parallel execution errors
const result = await agency.execute(
  initialState,
  { 
    sequential: false,
    stopOnError: false  // Continue even if some agents fail
  }
);

// Retry logic
const result = await agency.execute(
  initialState,
  { 
    maxRetries: 3,  // Retry failed operations
    sequential: true
  }
);

Message Handling

The framework supports different message types:

// Human message
const humanMessage = {
  role: 'human',
  content: 'Hello agent',
  additionalKwargs: { key: 'value' }
};

// AI message
const aiMessage = {
  role: 'ai',
  content: 'Hello human',
  additionalKwargs: { confidence: 0.9 }
};

// System message
const systemMessage = {
  role: 'system',
  content: 'You are a helpful assistant',
  additionalKwargs: { priority: 'high' }
};

šŸ› ļø Development

# Install dependencies
npm install

# Run tests
npm test

# Build the project
npm run build

# Format code
npm run format

# Lint code
npm run lint

šŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

šŸ‘¤ Author

Frederick Bejarano Sanchez

šŸ¤ Contributing

Contributions, issues, and feature requests are welcome! Feel free to check the issues page.

šŸ“ Changelog

See CHANGELOG.md for more information on what has changed recently.

0.1.6

12 months ago

0.1.5

12 months ago

0.1.4

12 months ago

0.1.3

12 months ago

0.1.2

12 months ago

0.1.1

12 months ago

0.1.0

12 months ago