SDK Reference - TypeScript

API reference for Flutch SDK in TypeScript. Core classes and services for building graphs with NestJS.

Installation

bash
flutch graph create my-graph
cd my-graph
npm install

Main imports:

typescript
import { AbstractGraphBuilder, WithCallbacks, Callback } from '@flutch/sdk';
import { ModelInitializer, McpRuntimeClient } from '@flutch/sdk';
import { Injectable } from '@nestjs/common';

Graph Builder

Base class for all graph versions. Every graph version must extend AbstractGraphBuilder.

Basic Structure

typescript
import { Injectable, Inject } from '@nestjs/common';
import { AbstractGraphBuilder, IGraphRequestPayload } from '@flutch/sdk';
import { MongoDBSaver } from '@langchain/langgraph-checkpoint-mongodb';

@Injectable()
export class MyGraphV1Builder extends AbstractGraphBuilder<'1.0.0'> {
  readonly version = '1.0.0' as const;

  constructor(
    @Inject('CHECKPOINTER')
    private readonly checkpointer: MongoDBSaver,
    private readonly generateNode: GenerateNode
  ) {
    super();
  }

  async buildGraph(payload?: any): Promise<CompiledGraph> {
    // Build your workflow here
    // See framework-specific guides for details
    return compiledGraph;
  }
}

Key methods:

MethodDescription
buildGraph(payload?)Build and return compiled workflow
prepareConfig(payload)Prepare config (auto-handled, override if needed)

Key properties:

PropertyTypeDescription
versionstringSemantic version (required)
graphTypestringAuto-generated from manifest (e.g., "acme.support::1.0.0")
manifestIGraphManifestLoaded graph manifest
loggerLoggerNestJS logger instance

Models

Initialize AI models (chat, embedding, rerank) in your graph nodes.

ModelInitializer

Inject via NestJS:

typescript
import { Injectable } from '@nestjs/common';
import { ModelInitializer } from '@flutch/sdk';

@Injectable()
export class GenerateNode {
  constructor(
    private readonly modelInitializer: ModelInitializer
  ) {}

  async execute(state: any, config: any) {
    // Get model ID from agent config
    const modelId = config.configurable.graphSettings?.modelSettings?.modelId;
    const temperature = config.configurable.graphSettings?.modelSettings?.temperature;

    // Initialize chat model
    const model = await this.modelInitializer.initializeChatModel({
      modelId,
      temperature
    });

    // Use the model
    const result = await model.invoke(state.messages, config);

    return { messages: [result] };
  }
}

Methods:

MethodParametersReturns
initializeChatModel(config){ modelId, temperature?, maxTokens? }BaseChatModel
initializeEmbeddingModel(config){ modelId }Embeddings
initializeRerankModel(config){ modelId, maxTokens? }BaseDocumentCompressor

Config object:

typescript
interface ModelByIdConfig {
  modelId: string;          // From model catalog
  temperature?: number;     // Override default temperature
  maxTokens?: number;       // Override default max tokens
}

Example with tools:

typescript
// Initialize model
const model = await this.modelInitializer.initializeChatModel({
  modelId: 'gpt-4o',
  temperature: 0.7
});

// Bind tools to model
const modelWithTools = model.bindTools(tools);

// Invoke with tools
const result = await modelWithTools.invoke(messages, config);

Tools

Execute MCP tools from your graph nodes.

McpRuntimeClient

Client for MCP tool runtime.

Usage:

typescript
import { Injectable } from '@nestjs/common';
import { ModelInitializer, McpRuntimeClient } from '@flutch/sdk';

@Injectable()
export class GenerateNode {
  constructor(
    private readonly modelInitializer: ModelInitializer,
    private readonly mcpClient: McpRuntimeClient
  ) {}

  async execute(state: any, config: any) {
    const enabledTools = config.configurable.graphSettings?.availableTools || [];
    const modelId = config.configurable.graphSettings?.modelSettings?.modelId;

    // Initialize model
    const model = await this.modelInitializer.initializeChatModel({ modelId });

    // Get tools (filtered and converted automatically)
    let modelWithTools = model;
    if (enabledTools.length > 0) {
      const tools = await this.mcpClient.getTools(enabledTools);
      modelWithTools = model.bindTools(tools);

      this.logger.debug(`Configured ${tools.length} tools`);
    }

    // Invoke model with tools
    const result = await modelWithTools.invoke(state.messages, config);

    return { messages: [result] };
  }
}

Methods:

MethodParametersReturns
getTools(enabledTools?)string[]?Promise<StructuredTool[]>
executeTool(name, args)name: string, args: objectPromise<McpToolResult>

Execute tool calls:

typescript
async executeTools(state: any, config: any) {
  const lastMessage = state.messages[state.messages.length - 1];
  const toolCalls = lastMessage?.tool_calls || [];

  const toolMessages = [];
  for (const toolCall of toolCalls) {
    const result = await this.mcpClient.executeTool(
      toolCall.name,
      toolCall.args
    );

    toolMessages.push({
      type: 'tool',
      tool_call_id: toolCall.id,
      content: result.success ? JSON.stringify(result.data) : result.error,
      name: toolCall.name
    });

    this.logger.debug(`Tool ${toolCall.name} executed successfully`);
  }

  return { messages: toolMessages };
}

Callbacks

Add interactive buttons that execute your code when clicked.

Decorator Pattern

Use @Callback() and @WithCallbacks() decorators to register callback handlers.

1. Create callback handler class:

typescript
import { Injectable } from '@nestjs/common';
import { Callback, ExtendedCallbackContext, CallbackResult } from '@flutch/sdk';

@Injectable()
export class MyCallbacks {
  private readonly logger = new Logger(MyCallbacks.name);

  @Callback('confirm-payment')
  async handleConfirmPayment(
    context: ExtendedCallbackContext
  ): Promise<CallbackResult> {
    const { amount, orderId } = context.params;

    this.logger.debug(`Processing payment: ${orderId}`);

    try {
      await this.processPayment(orderId, amount);

      return {
        success: true,
        message: 'Payment confirmed!',
        patch: {
          text: '✅ Payment processed successfully',
          disableButtons: true
        }
      };
    } catch (error) {
      this.logger.error(`Payment failed: ${error.message}`);

      return {
        success: false,
        error: 'Payment processing failed. Please try again.'
      };
    }
  }

  private async processPayment(orderId: string, amount: number) {
    // Payment processing logic
  }
}

2. Register callbacks in builder:

typescript
import { Injectable } from '@nestjs/common';
import { WithCallbacks, AbstractGraphBuilder } from '@flutch/sdk';
import { MyCallbacks } from '../callbacks/my.callbacks';

@Injectable()
@WithCallbacks(MyCallbacks)
export class MyGraphV1Builder extends AbstractGraphBuilder<'1.0.0'> {
  readonly version = '1.0.0' as const;

  // Builder implementation...
}

Callback context:

typescript
interface ExtendedCallbackContext {
  userId: string;           // User who clicked
  threadId: string;         // Conversation thread
  agentId: string;          // Agent ID
  params: any;              // Parameters from callback token
  platform?: string;        // Platform (web, telegram, etc.)
  builder?: AbstractGraphBuilder;  // Access to builder instance
}

Callback result:

typescript
interface CallbackResult {
  success: boolean;
  message?: string;
  error?: string;
  patch?: {
    text?: string;
    disableButtons?: boolean;
  };
}

Configuration

Access agent configuration defined in your config schema.

Runtime Configuration

Agent configuration is injected at runtime through config.configurable.graphSettings.

Access in node:

typescript
async execute(state: any, config: any) {
  // Get all settings
  const graphSettings = config?.configurable?.graphSettings;

  // Access specific settings
  const systemPrompt = graphSettings?.systemPrompt || '';
  const modelSettings = graphSettings?.modelSettings;
  const enabledTools = graphSettings?.availableTools || [];
  const customSettings = graphSettings?.customSettings;

  this.logger.debug('Model:', modelSettings?.modelId);
  this.logger.debug('Temperature:', modelSettings?.temperature);
  this.logger.debug('System prompt:', systemPrompt);
  this.logger.debug('Enabled tools:', enabledTools);

  // Use configuration
  const model = await this.modelInitializer.initializeChatModel({
    modelId: modelSettings?.modelId,
    temperature: modelSettings?.temperature
  });
}

Settings structure (from config-schema.json):

typescript
interface GraphSettings {
  systemPrompt: string;
  modelSettings: {
    modelId: string;
    temperature: number;
    maxTokens?: number;
  };
  availableTools: string[];
  customSettings?: {
    [key: string]: any;
  };
}

Module Setup

Configure your graph module with NestJS and bootstrap the application.

Graph Module

typescript
// src/my-graph.module.ts
import { Module } from '@nestjs/common';
import { UniversalGraphModule } from '@flutch/sdk';
import { MyGraphV1Builder } from './versions/v1.0.0/builder';
import { MyGraphV2Builder } from './versions/v2.0.0/builder';
import { GenerateNode } from './nodes/generate.node';

@Module({
  imports: [
    UniversalGraphModule.forRoot({
      builders: [
        MyGraphV1Builder,
        MyGraphV2Builder
      ]
    })
  ],
  providers: [
    GenerateNode,
    // ... other services
  ]
})
export class MyGraphModule {}

Bootstrap

typescript
// src/main.ts
import { bootstrap } from '@flutch/sdk';
import { MyGraphModule } from './my-graph.module';

async function main() {
  await bootstrap(MyGraphModule);
}

main();

The bootstrap() function:

  • Initializes NestJS application
  • Registers all graph versions
  • Starts HTTP server for graph execution
  • Sets up health checks
  • Configures callback handlers

Dependency Injection

Use NestJS dependency injection to inject services into your builders and nodes.

Inject Services

typescript
import { Injectable, Inject } from '@nestjs/common';
import { AbstractGraphBuilder } from '@flutch/sdk';
import { ModelInitializer } from '@flutch/sdk';
import { MongoDBSaver } from '@langchain/langgraph-checkpoint-mongodb';

@Injectable()
export class MyGraphV1Builder extends AbstractGraphBuilder<'1.0.0'> {
  readonly version = '1.0.0' as const;

  constructor(
    @Inject('CHECKPOINTER')
    private readonly checkpointer: MongoDBSaver,

    private readonly modelInitializer: ModelInitializer,
    private readonly generateNode: GenerateNode,
    private readonly toolsNode: ToolsNode
  ) {
    super();
  }

  async buildGraph() {
    // Use injected services
    const workflow = buildWorkflow(
      this.generateNode,
      this.toolsNode,
      this.checkpointer
    );

    return workflow;
  }
}

Custom Services

typescript
// src/services/my.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class MyService {
  async doSomething() {
    // Service logic
  }
}

// Register in module
@Module({
  providers: [MyService],
  exports: [MyService]
})
export class MyGraphModule {}

// Use in node
@Injectable()
export class MyNode {
  constructor(
    private readonly myService: MyService
  ) {}

  async execute(state: any, config: any) {
    await this.myService.doSomething();
    return { result: 'done' };
  }
}