LLMChat Documentation

repository·main·Indexed 21 days ago

https://github.com/trendy-design/llmchat

An AI-powered chatbot platform for privacy-focused research and agentic workflows. It features a custom workflow orchestration engine with specialized nodes (Executor, Router, Memory, and Observer) and modes like Deep Research and Pro Search. The system includes an event-driven architecture, a Prisma-based database layer, and a credit service for managing daily API request limits.

Tokens
36.5K
Snippets
123
Records
147
Agent score
76%

What's inside LLMChat

  1. How workflow orchestration works

    main

    LLMChat uses a modular, step-by-step workflow orchestration system to create agentic capabilities (like research agents). The process follows a specific lifecycle:

    1. Define Event and Context Types: Establish the data structure for events emitted by tasks and the shared context passed between them.
    2. Initialize Core Components: Set up a TypedEventEmitter for events, a Context object for shared state, and a WorkflowBuilder to manage the orchestration.
    3. Define Research Tasks: Create individual tasks using createTask. Each task defines an execute function (containing the logic, e.g., LLM calls) and a route function (determining which task comes next).
    4. Build and Execute: Register tasks with the WorkflowBuilder, call .build(), and start the workflow using .start(initialTaskName, initialData).

    This architecture allows for real-time UI updates as each step emits events during the research process.

  2. Understand the Node Types in the Agentic Graph System

    main

    Workflows are built using specialized nodes that perform specific functions within the graph:

    • Executor Node: Handles specific tasks by processing input and generating responses. These can be specialized for different roles.
    • Router Node: Analyzes input to route it to the appropriate nodes. It uses confidence scoring and supports multiple routing strategies.
    • Memory Node: Manages state by storing interaction history, including both short-term and long-term memory to provide context for decision-making.
    • Observer Node: Monitors system behavior, analyzes patterns and performance, and generates insights or recommendations.
  3. Install and run LLMChat locally

    main

    To set up the LLMChat development environment, ensure you have bun (recommended) or yarn installed. Follow these steps:

    1. Clone the repository:
      git clone https://github.com/your-repo/llmchat.git
      cd llmchat
    2. Install dependencies:
      bun install
      # or
      yarn install
    3. Start the development server:
      bun dev
      # or
      yarn dev
    4. Access the application at http://localhost:3000.
    git clone https://github.com/your-repo/llmchat.git
    cd llmchat
    bun install
    bun dev
  4. Set up the Prisma package

    main

    To set up the Prisma client for database access, follow these steps:

    1. Configure your PostgreSQL connection string in your .env file using the DATABASE_URL key: DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public"
    2. Navigate to the prisma package directory and generate the client: cd packages/prisma && bun prisma generate
    3. Push your current schema to the database: cd packages/prisma && bun prisma db push
    cd packages/prisma
    bun prisma generate
    bun prisma db push
  5. Use the Prisma client in your code

    main

    Import the prisma instance from @repo/prisma to perform database operations. The client provides type-safe methods for interacting with your models (e.g., create, findMany, update, etc.).

    import { prisma } from '@repo/prisma';
    
    // Example: Create a new feedback entry
    async function createFeedback(userId: string, userEmail: string, feedback: string) {
        return await prisma.feedback.create({
            data: {
                userId,
                userEmail,
                feedback,
            },
        });
    }
    
    // Example: Get all feedback entries
    async function getAllFeedback() {
        return await prisma.feedback.findMany();
    }
  6. Apply schema changes to the database

    main

    Whenever you modify the schema.prisma file, you must synchronize the client and the database.

    For Local Development

    Use db push to quickly sync your schema without creating migration files:

    1. bun prisma generate (to update the client types)
    2. bun prisma db push (to update the database structure)

    For Production/Migration-based workflows

    If you require formal migrations, use the migrate dev command: bun prisma migrate dev --name <description_of_changes>

    # Local development sync
    bun prisma generate
    bun prisma db push
    
    # Migration-based approach
    bun prisma migrate dev --name description_of_changes
  7. Create a research agent workflow

    main

    To build a research agent, you must define the event/context types, initialize the builder, define the tasks, and then assemble them.

    1. Define Types

    type AgentEvents = {
        taskPlanner: { tasks: string[]; query: string; };
        informationGatherer: { searchResults: string[]; };
        informationAnalyzer: { analysis: string; insights: string[]; };
        reportGenerator: { report: string; };
    };
    
    type AgentContext = {
        query: string;
        tasks: string[];
        searchResults: string[];
        analysis: string;
        insights: string[];
        report: string;
    };

    2. Initialize Components

    import { OpenAI } from 'openai';
    import { createTask } from 'task';
    import { WorkflowBuilder } from './builder';
    import { Context } from './context';
    import { TypedEventEmitter } from './events';
    
    const events = new TypedEventEmitter<AgentEvents>();
    const builder = new WorkflowBuilder<AgentEvents, AgentContext>('research-agent', {
        events,
        context: new Context<AgentContext>({
            query: '',
            tasks: [],
            searchResults: [],
            analysis: '',
            insights: [],
            report: '',
        }),
    });
    
    const llm = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

    3. Define and Build Tasks

    Each task uses createTask with an execute function and a route function. Tasks can also declare dependencies.

    // Example Task Planner
    const taskPlanner = createTask({
        name: 'taskPlanner',
        execute: async ({ context, data }) => {
            // ... LLM logic to generate tasks ...
            context?.set('query', userQuery);
            context?.set('tasks', tasks);
            return { tasks, query: userQuery };
        },
        route: () => 'informationGatherer',
    });
    
    // Assemble
    builder.addTask(taskPlanner);
    // ... add other tasks ...
    const workflow = builder.build();
    workflow.start('taskPlanner', { query: 'Research the impact of AI on healthcare' });
    // Full assembly pattern
    builder.addTask(taskPlanner);
    builder.addTask(informationGatherer);
    builder.addTask(informationAnalyzer);
    builder.addTask(reportGenerator);
    
    const workflow = builder.build();
    workflow.start('taskPlanner', { query: 'Research the impact of AI on healthcare' });
  8. Install and set up the Agentic Graph System

    main

    To get started with the @repo/ai package, follow these steps to install dependencies and configure your environment:

    1. Install dependencies using pnpm:

      pnpm install
    2. Configure environment variables:

      • Copy the example environment file to a local file:
        cp .env.example .env.local
      • Open .env.local and provide your required API keys (e.g., OPENAI_API_KEY).
    3. Run an example (optional, to verify setup):

      ts-node examples/customer-support-workflow.ts
    pnpm install
    cp .env.example .env.local
    ts-node examples/customer-support-workflow.ts
  9. Understand the WorkflowEventSchema flow state

    main

    The flowState returned by useWorkflowWorker follows the WorkflowEventSchema['flow'] structure. This object tracks the real-time progress of a research or agentic task.

    Key properties within the flow object include:

    • status: The overall status of the flow ('PENDING' | 'COMPLETED' | 'FAILED').
    • goals: A record of sub-goals being pursued, including their id, text, and status.
    • steps: A record of discrete execution steps, containing type, queries used, and results (title/link pairs).
    • reasoning: The agent's internal thought process (text, status).
    • answer: The final output produced by the workflow (text, object, status).
    • toolCalls / toolResults: Arrays tracking the execution of external tools.
    • final: A boolean indicating if the workflow has reached a terminal state.
  10. Understand the Data Privacy and Storage Model

    main

    The llmchat service is designed with a privacy-first architecture where data is primarily stored on the client side.

    Data Storage

    • Local Storage & IndexedDB: All user data, including API keys, chat history, and messages, is stored locally in your browser.
    • No Backend Storage: The service does not have a backend server to collect or store your personal data.
    • Data Deletion: You can delete all data (API keys, configurations, and chat histories) at any time directly from your browser.

    Data Transmission

    • Direct Communication: When sending messages, your browser communicates directly with the API server via HTTPS. There is no middle server intercepting your data.
    • Proxy Requests: For specific models, requests are routed through a proxy server that acts as a pass-through; the proxy does not log or store any request data.
    • No External JavaScript: The service does not execute external JavaScript to maintain high security.

    Third-Party Services

    While the core service is local, the following third-party tools are used for service optimization:

    • PostHog: Used for analytics and understanding user interactions.
    • Sentry: Used for error logging and performance monitoring.
    • Authentication: Google or GitHub login is required for limited access to the GPT-4o mini model (limited to 10 messages per day).
  11. Configure LLM providers and parameters

    main

    The system uses environment variables for configuring LLM providers and generation parameters. Ensure these are set in your .env.local file.

    Required Variables

    • OPENAI_API_KEY: Your OpenAI API key.

    Optional Provider Variables

    • Anthropic:
      • ANTHROPIC_API_KEY
      • ANTHROPIC_MODEL
    • Together AI:
      • TOGETHER_API_KEY
      • TOGETHER_MODEL

    Global Generation Settings

    • OPENAI_MODEL: Defaults to gpt-4.
    • TEMPERATURE: Defaults to 0.7.
    • MAX_TOKENS: Defaults to 4000.
  12. Handle task routing and redirection

    main

    Workflows progress based on the output of tasks. There are three ways to determine the next task:

    1. Explicit Redirection: Inside the execute function, call the redirectTo callback provided in the TaskParams.
    2. Return Value: Return an object from the execute function containing { result, next }.
    3. Router Function: The route function defined in the TaskConfig is called with the task result and execution context.

    To end the workflow, return the special string 'end' from the router or redirection logic.

    // Example 1: Using redirectTo
    engine.task({
        name: 'decide',
        execute: async ({ redirectTo }) => {
            if (condition) redirectTo('task-a');
            else redirectTo(['task-b', 'task-c']); // Parallel execution
        }
    });
    
    // Example 2: Returning next in result
    engine.task({
        name: 'process',
        execute: async () => {
            return { result: 'data', next: 'final-step' };
        }
    });
    
    // Example 3: Using the route function
    engine.task({
        name: 'router-task',
        execute: async () => 'some-result',
        route: (result) => (result === 'success' ? 'end' : 'retry-task')
    });