pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog

Quickstart

Build and run your first pipedog pipeline with a custom frame processor.

This quickstart builds the smallest useful pipedog program: a pipeline of two custom frame processors, run by a PipelineWorker inside a WorkerRunner. No API keys, no network.

Want a voice bot?

The voice bot guide runs an assistant you talk to in the browser, with OpenAI speech-to-text, chat and text-to-speech over SmallWebRTC. Examples lists every runnable example, including the same bot over a WebSocket.

Set up a project

Follow Installation to get @pipedog/core and @pipedog/asyncio into a project. Any of Bun, Node, Deno or Cloudflare Workers works; this page uses Bun.

Write the bot

Create quickstart.ts. It defines two processors: one upper-cases every TextFrame, the other prints the text frames that reach it. Both pass every other frame through untouched.

quickstart.ts
import { , type ,  } from "@pipedog/core/frames/frames";
import {  } from "@pipedog/core/pipeline/pipeline";
import {  } from "@pipedog/core/pipeline/worker";
import { type ,  } from "@pipedog/core/processors/frame_processor";
import {  } from "@pipedog/core/workers/runner";

/** Upper-cases every TextFrame and passes every other frame through. */
class  extends  {
  override async (: , : ): <void> {
    await super.(, );

    if ( instanceof ) {
      await this.(new ({ : ..() }), );
    } else {
      await this.(, );
    }
  }
}

/** Prints the text frames that reach it. */
class  extends  {
  override async (: , : ): <void> {
    await super.(, );

    if ( instanceof ) {
      .(`${this.}: ${.}`);
    }
    await this.(, );
  }
}

const  = new ([new (), new ()]);
const  = new (, { : false });

.("on_pipeline_started", async () => {
  await .([
    new ({ : "hello" }),
    new ({ : "from pipedog" }),
    new (),
  ]);
});

.("on_pipeline_finished", async (: , : ) => {
  .(`pipeline finished with ${.}`);
});

const  = new ({ : false });
await .();
await .();

Hover over the code to see the types: this sample is type-checked against the real @pipedog/core sources when the docs are built.

Run it

bun quickstart.ts

Among the debug logs you will see:

PrintProcessor#0: HELLO
PrintProcessor#0: FROM PIPEDOG
pipeline finished with EndFrame#0

What just happened

  1. new Pipeline([...]) linked the processors in order, between a source and a sink.
  2. new PipelineWorker(pipeline, ...) wrapped the pipeline with lifecycle management. When the runner started it, it pushed a StartFrame through the pipeline; once that reached the end, on_pipeline_started fired.
  3. The handler queued two TextFrames and an EndFrame. The EndFrame is a control frame, so it is processed in order, after the text frames, and ends the pipeline gracefully once everything ahead of it is flushed.
  4. runner.run() returned once its only worker finished.

enableRtvi: false turns off the RTVI processor the worker adds by default, since this bot has no client. handleSigint: false keeps the runner from installing a Ctrl-C handler; leave it at its default (true) in a real bot.

Next steps

pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.

Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).

On this page