Skip to content
Pilotoo
v0.1

Quickstart

This page builds the smallest useful setup: a NestJS server that serves one agent with one tool, and a React page that chats with it. The demo apps in the repository do the same with more around it.

You need the repository built as described in Installation, a PostgreSQL database and an API key for one model provider. To run on one machine with no PostgreSQL, use the Local backend instead.

For a throwaway database, one container is enough. The server creates its tables on first boot.

Terminal window
docker run -d --name pilotoo-db -e POSTGRES_PASSWORD=pass -e POSTGRES_DB=pilotoo -p 5432:5432 postgres:16
  1. Add the server packages

    Depend on @pilotoo/server, plus @langchain/core and zod for the tool in step 3. The NestJS packages are peers, so your app supplies them: @nestjs/common, @nestjs/core, @nestjs/platform-express, @nestjs/platform-socket.io, @nestjs/websockets, reflect-metadata and rxjs. This page also uses @nestjs/config to load a .env file.

  2. Create the configuration folder

    Agents, model profiles and providers are YAML files in a .pilotoo/ folder. Copy the repository’s, or write these three files. The folder is described in Directory structure.

    .pilotoo/providers/deepseek.yaml
    kind: deepseek
    apiKeyEnv: DEEPSEEK_API_KEY
    .pilotoo/profiles/default.yaml
    name: Default
    model: deepseek:deepseek-v4-flash
    params:
    temperature: 0
    .pilotoo/agents/assistant.yaml
    id: assistant
    name: Assistant
    description: A general-purpose assistant.
    status: active
    runtime:
    systemPrompt: You are a concise, helpful assistant.
    profileId: default
    tools: [get_weather]
    builtinTools: []
    skills: []
    knowledge: []
    presentation: {}
  3. Assemble the module

    PilotooModule.forRoot wires the REST routes, the streaming gateway, persistence, the identity guard and every port. You supply the tools.

    src/app.module.ts
    import { Module } from '@nestjs/common';
    import { ConfigModule } from '@nestjs/config';
    import { tool } from '@langchain/core/tools';
    import { PilotooModule } from '@pilotoo/server';
    import { z } from 'zod';
    @Module({
    imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    PilotooModule.forRoot({
    env: process.env,
    tools: (registry) => {
    registry.register(
    tool(async ({ city }) => `It is sunny in ${city}.`, {
    name: 'get_weather',
    description: 'Reads the weather of a city.',
    schema: z.object({ city: z.string() }),
    }),
    );
    },
    }),
    ],
    })
    export class AppModule {}

    A registered tool is only available to agents that list its name in runtime.tools, which is why assistant lists get_weather.

    src/main.ts
    import 'reflect-metadata';
    import { NestFactory } from '@nestjs/core';
    import type { NestExpressApplication } from '@nestjs/platform-express';
    import { configureBodyLimit } from '@pilotoo/server';
    import { AppModule } from './app.module';
    const app = await NestFactory.create<NestExpressApplication>(AppModule);
    configureBodyLimit(app);
    app.enableCors({ origin: true, credentials: false });
    await app.listen(4317);
  4. Set the environment and start

    .env
    DATABASE_URL=postgres://postgres:pass@localhost:5432/pilotoo
    PILOTOO_CONFIG_DIR=/absolute/path/to/.pilotoo
    AGENT_DEFAULT_ID=assistant
    DEEPSEEK_API_KEY=...

    Start the server, then check it: GET /health answers, and GET /protocol returns the protocol version the gateway serves.

  5. Connect a client

    @pilotoo/ui/chat gives you a ready chat. ChatProvider opens the connection and ChatPanel renders the thread and the composer.

    src/App.tsx
    import { ChatPanel, ChatProvider } from '@pilotoo/ui/chat';
    export function App() {
    return (
    <ChatProvider baseUrl="http://localhost:4317">
    <ChatPanel />
    </ChatProvider>
    );
    }

    If your server requires a token, pass getToken to ChatProvider: a function that returns the bearer token, called for every request and socket connection.