← Back to Ditto Documentation

Installation

Get ditto-ai running in seconds.

via npm/bun

bun add ditto-ai
npm install ditto-ai
pnpm add ditto-ai

Requirements

  • Node/Bun: LTS or newer
  • TypeScript: 5.0+
  • Effect: ^3.0.0 (included)
  • Zod: ^3.0.0 (optional)

API Reference

Complete type definitions and exports.

Main Exports

dittoClient(options)

Creates a Ditto client instance.

createDittoWorkerHandler(opts?)

Creates a Cloudflare Worker handler.

DittoError

Error type thrown by ditto operations.

Types

type Strategy = "consensus" | "cooperative";

interface DittoClientOptions {
  endpoint: string;
  headers?: Record<string, string>;
  fetchImpl?: typeof fetch;
}

interface DittoRequest<T> {
  prompt: string;
  models: string[];
  strategy?: Strategy;
  temperature?: number;
  maxRetries?: number;
  metadata?: Record<string, unknown>;
}

interface DittoClientResponse<T> {
  result: T;
  responses?: Record<string, string>;
  structured?: MergedStructuredResult;
}

interface MergedStructuredResult {
  summary: string;
  intent: DittoIntent;
  confidence: number;
  needsClarification: boolean;
  supportingModels: string[];
  responses: StructuredModelResult[];
}

type DittoIntent = "answer" | "clarification" | "refusal" | "unknown";

Client API

How to use ditto-ai from your application.

Creating a Client

import { dittoClient } from "ditto-ai";

const ditto = dittoClient({
  endpoint: "https://your-worker.workers.dev/llm",
  headers: {
    "Authorization": "Bearer token" // optional
  }
});

Making Requests

// Basic string merge
const response = await ditto({
  prompt: "What is 2+2?",
  models: [
    "@cf/meta/llama-3.1-8b-instruct",
    "@cf/mistral/mistral-7b-instruct"
  ],
  strategy: "consensus"
});

console.log(response.result);        // merged string
console.log(response.responses);     // individual model outputs
console.log(response.structured);    // intent, confidence, etc.

DittoClientResponse Structure

result: string

The merged output from all models

responses?: Record<string, string>

Each model's raw output: @cf/meta/llama-3.1-8b-instruct → response

structured?: MergedStructuredResult

Analyzed merge: intent, confidence, supporting models

Server Setup

Configure Ditto in your Cloudflare Worker.

Worker Configuration

// worker/index.ts
import { DittoJob, createDittoWorkerHandler } from "ditto-ai/server";

// Export DittoJob for Durable Object binding
export { DittoJob };

type Env = {
  DITTO_JOB: DurableObjectNamespace<DittoJob>;
  AI: {
    run: (model: string, input: { prompt: string }) => Promise<{ response?: string }>;
  };
};

const dittoHandler = createDittoWorkerHandler({
  defaultStrategy: "consensus"
});

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const url = new URL(request.url);
    
    if (url.pathname === "/llm") {
      return dittoHandler.fetch(request, env, ctx);
    }
    
    return new Response("Not found", { status: 404 });
  }
};

Wrangler Configuration

# wrangler.toml

# AI binding (REQUIRED)
[[ai]]
binding = "AI"

# Durable Object binding (REQUIRED)
[[durable_objects.bindings]]
name = "DITTO_JOB"
class_name = "DittoJob"

Environment Bindings

env.AI

Required. Cloudflare AI binding. Must have .run(model, { prompt }) method.

env.DITTO_JOB

Required. Durable Object namespace for per-request job orchestration. Provides extreme durability and idempotency.

Error Handling

Ditto throws typed errors with detailed context.

DittoError

class DittoError extends Error {
  type: string;           // e.g. "BadRequest", "InternalError"
  message: string;        // Human-readable message
  status: number;         // HTTP status code
  details?: unknown;      // Additional context
}

Error Types

BadRequest (400)

Invalid prompt, models, or request format

InternalError (500)

Job execution failed, model unavailable, or system error

JobError

Specific error from Durable Object execution

Error Handling Example

import { dittoClient, DittoError } from "ditto-ai";

try {
  const response = await ditto({
    prompt: "What is 2+2?",
    models: ["@cf/meta/llama-3.1-8b-instruct"]
  });
} catch (error) {
  if (error instanceof DittoError) {
    console.error(`Error: ${error.type} (${error.status})`);
    console.error(`Message: ${error.message}`);
    console.error(`Details: ${JSON.stringify(error.details)}`);
  } else {
    console.error("Unknown error:", error);
  }
}

Merge Strategies

How Ditto combines multiple model outputs.

Consensus (v1)

All models answer the same prompt. Responses are analyzed for intent and merged into a single output.

Flow:
1. Run all models in parallel
2. Classify each response: answer | clarification | refusal | unknown
3. Estimate confidence for each response
4. Find winning intent (majority vote weighted by confidence)
5. Merge summaries of winning responses

Structured Analysis

Every response is analyzed and the merge result includes:

summary: string;           // Merged output, max 5 sentences
intent: DittoIntent;       // What the merged response is doing
confidence: 0-1;           // How confident in this response
supportingModels: string[]; // Which models voted for winning intent

Cooperative

Models execute sequentially, with each model seeing all previous outputs. Perfect for multi-step workflows where models build on each other's work.

// Model 1 gets: "What is 2+2?"
// Model 2 gets: "What is 2+2?

Previous responses:
1. 4

Build on these responses:"
// Model 3 gets: "What is 2+2?

Previous responses:
1. 4
2. The answer is four

Build on these responses:"

const response = await ditto({
  prompt: "What is 2+2?",
  models: ["@cf/meta/llama-3.1-8b-instruct", "@cf/mistral/mistral-7b-instruct"],
  strategy: "cooperative"
});

Examples

Real-world patterns for common use cases.

Email Summarization

import { dittoClient } from "ditto-ai";

const ditto = dittoClient({
  endpoint: "https://my-worker.workers.dev/llm"
});

async function summarizeEmail(emailBody: string) {
  const response = await ditto({
    prompt: `Summarize this email in 1-2 sentences:

${emailBody}`,
    models: [
      "@cf/meta/llama-3.1-8b-instruct",
      "@cf/mistral/mistral-7b-instruct"
    ],
    strategy: "consensus"
  });

  console.log("Summary:", response.result);
  console.log("Confidence:", response.structured?.confidence);
  console.log("Needs clarification:", response.structured?.needsClarification);
  
  return response.result;
}

Content Moderation

async function moderateContent(text: string) {
  const response = await ditto({
    prompt: `Is this content harmful? Answer only: yes, no, or unclear.

Content: ${text}`,
    models: [
      "@cf/meta/llama-3.1-70b-instruct",
      "@cf/mistral/mistral-7b-instruct",
      "@cf/qwen/qwen-2.5-14b-instruct"
    ],
    strategy: "consensus"
  });

  // Only block if consensus is confident
  if (response.result.toLowerCase().includes("yes") && 
      response.structured?.confidence > 0.75) {
    return { action: "block", confidence: response.structured.confidence };
  }

  return { action: "allow" };
}

Multi-Step Workflow (Cooperative)

async function analyzeDocument(document: string) {
  // Cooperative: Each model builds on previous outputs
  const response = await ditto({
    prompt: `Analyze this document step by step:

${document}`,
    models: [
      "@cf/meta/llama-3.1-8b-instruct",  // Step 1: Extract key facts
      "@cf/mistral/mistral-7b-instruct",   // Step 2: Build on facts, add context
      "@cf/qwen/qwen-2.5-14b-instruct"     // Step 3: Synthesize final analysis
    ],
    strategy: "cooperative"
  });

  // Each model's output builds on the previous ones
  console.log("Final analysis:", response.result);
  console.log("All steps:", response.responses);
  
  return response.result;
}

Data Extraction

async function extractData(document: string) {
  const response = await ditto({
    prompt: `Extract the company name from this document:

${document}

Answer with ONLY the company name.`,
    models: [
      "@cf/meta/llama-3.1-8b-instruct",
      "@cf/mistral/mistral-7b-instruct",
      "@cf/qwen/qwen-2.5-14b-instruct"
    ],
    strategy: "consensus"
  });

  // All supporting models agree = high confidence extraction
  return {
    company: response.result.trim(),
    agreementCount: response.structured?.supportingModels.length,
    confidence: response.structured?.confidence
  };
}

Questions?

Check the GitHub issues or open a new one.