0.1.6 • Published 9 months ago

@cultores/agentic v0.1.6

Weekly downloads
-
License
MIT
Repository
github
Last release
9 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

9 months ago

0.1.5

9 months ago

0.1.4

9 months ago

0.1.3

9 months ago

0.1.2

9 months ago

0.1.1

10 months ago

0.1.0

10 months ago