Skip to content

FlowEngine API

The FlowEngine class orchestrates conversation execution.

Basic Usage

typescript
import { FlowEngine, MemoryStorage, OllamaAdapter } from "@andresaya/flowkit";

const engine = new FlowEngine(flow, {
  llm: new OllamaAdapter({ model: "qwen3:4b" }),
  storage: new MemoryStorage(),
});

// Start a conversation
const result = await engine.start("session-123");
console.log(result.message);

// Handle user input
const response = await engine.handle("session-123", "Hello!");
console.log(response.message);

Constructor

typescript
new FlowEngine(flow: FlowConfig, config: EngineConfig)

EngineConfig

typescript
interface EngineConfig {
  /** LLM adapter (required) */
  llm: LLMAdapter;
  
  /** Storage adapter (required) */
  storage: StorageAdapter;
  
  /** Tools registry (optional) */
  tools?: ToolsRegistry | ToolDefinition[];
  
  /** Event handler (optional) */
  onEvent?: (event: EngineEvent) => void;
  
  /** Max extraction retries (default: 3) */
  maxRetries?: number;
  
  /** Handoff configuration (optional) */
  handoff?: boolean | Partial<HandoffConfig>;
  
  /** Timeout configuration (optional) */
  timeout?: TimeoutConfig;

  /** Knowledge base for RAG (optional) */
  knowledgeBase?: KnowledgeBase;

  /** Plugins (optional) */
  plugins?: Plugin[];

  /** Logger for debugging (optional) */
  logger?: Logger;
}

Methods

start(conversationId, initialSlots?)

Starts a new conversation.

typescript
const result = await engine.start("session-123");

// With pre-filled slots
const result = await engine.start("session-123", {
  user_name: "John",
  is_premium: true
});

Returns: EngineOutput

handle(conversationId, message)

Processes a user message.

typescript
const result = await engine.handle("session-123", "My name is John");

Returns: EngineOutput

getState(conversationId)

Gets the current conversation state.

typescript
const state = await engine.getState("session-123");
console.log(state.slots);
console.log(state.currentStep);

getSlots(conversationId)

Gets all slot values.

typescript
const slots = await engine.getSlots("session-123");
// { user_name: "John", user_email: "john@example.com" }

getSlot(conversationId, slotName)

Gets a specific slot value.

typescript
const name = await engine.getSlot("session-123", "user_name");

setSlot(conversationId, slotName, value)

Sets a slot value.

typescript
await engine.setSlot("session-123", "verified", true);

isEnded(conversationId)

Checks if conversation has ended.

typescript
const ended = await engine.isEnded("session-123");

delete(conversationId)

Deletes a conversation's state.

typescript
await engine.delete("session-123");

EngineOutput

The result returned by start() and handle():

typescript
interface EngineOutput {
  /** The bot's response message */
  message: string;
  
  /** Whether the conversation has ended */
  done: boolean;
  
  /** Current step and slots */
  state: {
    step: string;
    slots: Slots;
  };
}

Events

Use onEvent to track conversation events:

typescript
const engine = new FlowEngine(flow, {
  llm,
  storage,
  onEvent: (event) => {
    switch (event.type) {
      case "flow:start":
        console.log("Started:", event.conversationId);
        break;
      case "flow:end":
        console.log("Ended:", event.slots);
        break;
      case "step:enter":
        console.log("Step:", event.step);
        break;
      case "extract:success":
        console.log("Extracted:", event.extractType, "=", event.value);
        break;
      case "error":
        console.error("Error:", event.error);
        break;
    }
  }
});

Event Types

EventDescription
flow:startConversation started
flow:endConversation ended
step:enterEntered a step
step:exitLeft a step
extract:successData extracted
extract:failExtraction failed
user:messageUser sent message
agent:messageAgent sent message
tool:callTool being called
tool:resultTool returned
handoff:detectedHandoff detected
errorError occurred

Handoff Configuration

Enable handoff detection:

typescript
const engine = new FlowEngine(flow, {
  llm,
  storage,
  handoff: {
    enabled: true,
    keywords: ["human", "agent", "person"],
    phrases: ["speak to someone", "talk to a human"],
  },
  onEvent: (event) => {
    if (event.type === "handoff:detected") {
      // Transfer to human agent
      transferToHuman(event.conversationId, event.reason);
    }
  }
});

Timeout Configuration

typescript
const engine = new FlowEngine(flow, {
  llm,
  storage,
  timeout: {
    messageTimeout: 30000,   // 30 seconds
    sessionTimeout: 600000,  // 10 minutes
  },
  onEvent: (event) => {
    if (event.type === "timeout") {
      console.log("Timeout:", event.timeoutType);
    }
  }
});

Example: Complete Integration

typescript
import {
  agent, flow, FlowEngine,
  MemoryStorage, OllamaAdapter, Tools,
  name, email
} from "@andresaya/flowkit";

// Agent
const bot = agent("Assistant").build();

// Flow
const myFlow = flow("onboarding", bot)
  .ask("name", "What's your name?", name(), "name")
  .then("email")
  .ask("email", "What's your email?", email(), "email")
  .then("save")
  .say("save", "Saving...")
  .do("save_user")
  .then("done")
  .say("done", "All set!")
  .done()
  .build();

// Tools
const tools = new Tools();
tools.register("save_user", async (payload) => {
  await db.users.create(payload);
  return { success: true };
});

// Engine
const engine = new FlowEngine(myFlow, {
  llm: new OllamaAdapter({ model: "qwen3:4b" }),
  storage: new MemoryStorage(),
  tools,
  maxRetries: 3,
  handoff: { enabled: true },
  timeout: { sessionTimeout: 300000 },
  onEvent: (event) => {
    analytics.track(event);
  }
});

// Use
const session = "user-123";
let result = await engine.start(session);

while (!result.done) {
  const userInput = await getUserInput();
  result = await engine.handle(session, userInput);
}

Released under the MIT License.