Developers

Chatbot SDK

The real-time chatbot framework YuAskMe runs on, published as an open-source npm package. Bring your own AI model, database, and business rules.

Overview

@yuaskme/chatbot-sdk is a backend framework for real-time chatbots on Socket.IO. Every message passes through an ordered pipeline of small async steps (classify, check rules, add context, call the AI, act on the result), so each concern stays separate and testable.

  • Guards run before the AI and can stop the pipeline, for example when the store is closed.
  • Enrichers add live data such as a cart or loyalty tier to the prompt.
  • Dispatchers run after the AI replies, to save an order or show a booking form.
  • A session store keeps per-customer state across turns, with no extra infrastructure.
You don't need the SDK to use YuAskMe. It's for developers building their own chatbot backend. To add YuAskMe to your website, use the chat widget from your dashboard.

Source: GitHub · npm · ISC license

Install

bash
npm install @yuaskme/chatbot-sdk socket.io

Quick start

A server with one guard, one enricher, and one dispatcher:

js
import http from "http";
import express from "express";
import {
  createChatbotSocket,
  createSessionStore,
  buildGuardRegistry,
  buildEnricherRegistry,
  buildDispatcherRegistry,
  runGuards,
  runEnrichers,
  runDispatcher,
} from "@yuaskme/chatbot-sdk";

const sessionStore = { ...createSessionStore(), cartItems: new Map() };

const outsideHoursGuard = async ({ socket }) => {
  const hour = new Date().getHours();
  if (hour < 8 || hour >= 20) {
    socket.emit("message", { message: "We are open 8 AM – 8 PM." });
    return true; // stop the pipeline
  }
  return false;
};

const loyaltyEnricher = async ({ socket, sessionStore }) => {
  const tier = sessionStore.loyaltyTier?.get(socket.id);
  return tier ? { systemNote: `Customer is a ${tier} loyalty member.` } : null;
};

const bookingDispatcher = async ({ socket, responseData }) => {
  socket.emit("show_booking_form", { prefill: responseData?.extracted });
};

const pipeline = [
  async (ctx) => {
    const category = ctx.message.category ?? "general";
    const PromptIntent = await myAI.classify(ctx.message.message, category);
    return { ...ctx, PromptIntent, category };
  },
  async (ctx) => {
    const handled = await runGuards(ctx.guardRegistry[ctx.category] ?? [], ctx);
    return handled ? { ...ctx, done: true } : ctx;
  },
  async (ctx) => {
    const enrichments = await runEnrichers(ctx.enricherRegistry[ctx.category] ?? [], ctx);
    return { ...ctx, enrichments };
  },
  async (ctx) => {
    const reply = await myAI.respond(ctx.message.message, ctx.PromptIntent.Intent);
    ctx.socket.emit("message", { message: reply });
    return { ...ctx, responseData: { text: reply } };
  },
  async (ctx) => {
    ctx.sessionStore.lastIntent.set(ctx.socket.id, ctx.PromptIntent.Intent);
    await runDispatcher(ctx.dispatcherRegistry, ctx.PromptIntent.Intent, ctx);
    return ctx;
  },
];

const server = http.createServer(express());

createChatbotSocket(server, {
  pipeline,
  sessionStore,
  guardRegistry: buildGuardRegistry([{ for: "general", guard: outsideHoursGuard }]),
  enricherRegistry: buildEnricherRegistry([{ for: "general", enricher: loyaltyEnricher }]),
  dispatcherRegistry: buildDispatcherRegistry([
    { intent: "booking", dispatcher: bookingDispatcher },
    { intent: "default", dispatcher: async () => {} },
  ]),
  corsOptions: { origin: "http://localhost:5173" },
  onConnection: (io, socket, sessionStore) => {
    socket.on("disconnect", () => sessionStore.cartItems.delete(socket.id));
  },
});

server.listen(3001);

myAI stands in for your own AI calls; the SDK doesn't pick a provider for you.

Concepts

Pipeline

An ordered array of async (ctx) => ctx steps. Each step can add fields to the context for later steps. Setting ctx.done = true stops the rest. A step that returns nothing throws an error naming the step.

Guards

Run per category, in the order you register them. The first guard that returns true stops the pipeline. Check sessionStore.lastIntent to protect multi-step flows such as order confirmation.

Enrichers

Every enricher for a category runs; their non-null results are collected into an array you add to the prompt.

Dispatchers

Map intents to side effects. Several intents can share a dispatcher, and "default" catches anything unmatched.

Session store

One store of Maps keyed by socket.id, shared by all connections. The SDK initializes its own Maps on connect and clears them on disconnect; clear any Maps you add inside onConnection's disconnect handler, which runs before the SDK cleanup.

Name gate

Pass extractName and set sessionStore.awaitingName for a socket to collect the customer's name before the conversation continues. The SDK emits name_updated once a name is confirmed.

API reference

createChatbotSocket(httpServer, config)

Creates a Socket.IO server wired to your pipeline and returns it.

OptionTypeDefaultDescription
pipelineStepFn[][]Ordered pipeline steps
sessionStoreSessionStoreautoShare it when you add your own Maps
guardRegistryobject{}From buildGuardRegistry()
enricherRegistryobject{}From buildEnricherRegistry()
dispatcherRegistryobject{}From buildDispatcherRegistry()
chatEventstring"chat"Socket event for incoming messages
extractNameasync fn—Enables the name gate
nameGateMessagesobject—Override the name gate prompts
onErrorasync fn—(error, ctx) => void
corsOptionsobject{}Merged into Socket.IO CORS settings
maxHttpBufferSizenumber5e6Max message size in bytes
onConnectionfn—(io, socket, sessionStore) => void

Other exports

ExportUse
buildGuardRegistry / buildEnricherRegistry / buildDispatcherRegistryBuild the registries
runGuards / runEnrichers / runDispatcherRun a registry from inside a step
createSessionStore()A fresh store of Maps keyed by socket.id
initSocketSession / cleanupSocketSessionSet up or clear one socket (automatic with createChatbotSocket)
createChatbotHandler(...)One message handler without creating a server
runPipeline(steps, ctx, onError)Run a steps array directly
withNameGate(socket, handler, options)Wrap any handler with the name gate

React chat widget

The repository's examples/react folder has a ChatWidget component and a useChatbot hook that manage the socket connection, messages, and typing state:

jsx
<ChatWidget
  serverUrl="http://localhost:3001"
  identifier="my-workspace"
  language="english"
  title="Chat with us"
  placeholder="Ask me anything…"
/>

Something unclear or out of date? Report a docs issue.