@moureau/pipeflow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +833 -0
  3. package/dist/cjs/agents/agent.js +2 -0
  4. package/dist/cjs/agents/agent.js.map +7 -0
  5. package/dist/cjs/agents/index.js +2 -0
  6. package/dist/cjs/agents/index.js.map +7 -0
  7. package/dist/cjs/agents/tools/index.js +2 -0
  8. package/dist/cjs/agents/tools/index.js.map +7 -0
  9. package/dist/cjs/agents/tools/tools.js +2 -0
  10. package/dist/cjs/agents/tools/tools.js.map +7 -0
  11. package/dist/cjs/conversations/conversation/conversation.js +2 -0
  12. package/dist/cjs/conversations/conversation/conversation.js.map +7 -0
  13. package/dist/cjs/conversations/conversation/index.js +2 -0
  14. package/dist/cjs/conversations/conversation/index.js.map +7 -0
  15. package/dist/cjs/conversations/conversations.js +2 -0
  16. package/dist/cjs/conversations/conversations.js.map +7 -0
  17. package/dist/cjs/conversations/index.js +2 -0
  18. package/dist/cjs/conversations/index.js.map +7 -0
  19. package/dist/cjs/conversations/orchestration/orchestrator/index.js +2 -0
  20. package/dist/cjs/conversations/orchestration/orchestrator/index.js.map +7 -0
  21. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js +3 -0
  22. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js.map +7 -0
  23. package/dist/cjs/conversations/transcription/index.js +2 -0
  24. package/dist/cjs/conversations/transcription/index.js.map +7 -0
  25. package/dist/cjs/conversations/transcription/transcription.js +2 -0
  26. package/dist/cjs/conversations/transcription/transcription.js.map +7 -0
  27. package/dist/cjs/conversations/types.js +2 -0
  28. package/dist/cjs/conversations/types.js.map +7 -0
  29. package/dist/cjs/index.js +2 -0
  30. package/dist/cjs/index.js.map +7 -0
  31. package/dist/cjs/package.json +3 -0
  32. package/dist/cjs/persistence/adapters/index.js +2 -0
  33. package/dist/cjs/persistence/adapters/index.js.map +7 -0
  34. package/dist/cjs/persistence/adapters/memory/index.js +2 -0
  35. package/dist/cjs/persistence/adapters/memory/index.js.map +7 -0
  36. package/dist/cjs/persistence/adapters/memory/memory.js +2 -0
  37. package/dist/cjs/persistence/adapters/memory/memory.js.map +7 -0
  38. package/dist/cjs/persistence/adapters/sqlite/index.js +2 -0
  39. package/dist/cjs/persistence/adapters/sqlite/index.js.map +7 -0
  40. package/dist/cjs/persistence/adapters/sqlite/sqlite.js +58 -0
  41. package/dist/cjs/persistence/adapters/sqlite/sqlite.js.map +7 -0
  42. package/dist/cjs/persistence/index.js +2 -0
  43. package/dist/cjs/persistence/index.js.map +7 -0
  44. package/dist/cjs/persistence/persistence.js +2 -0
  45. package/dist/cjs/persistence/persistence.js.map +7 -0
  46. package/dist/cjs/providers/index.js +2 -0
  47. package/dist/cjs/providers/index.js.map +7 -0
  48. package/dist/cjs/providers/llm/adapters/deepseek/deepseek.js +2 -0
  49. package/dist/cjs/providers/llm/adapters/deepseek/deepseek.js.map +7 -0
  50. package/dist/cjs/providers/llm/adapters/deepseek/index.js +2 -0
  51. package/dist/cjs/providers/llm/adapters/deepseek/index.js.map +7 -0
  52. package/dist/cjs/providers/llm/adapters/index.js +2 -0
  53. package/dist/cjs/providers/llm/adapters/index.js.map +7 -0
  54. package/dist/cjs/providers/llm/index.js +2 -0
  55. package/dist/cjs/providers/llm/index.js.map +7 -0
  56. package/dist/cjs/providers/llm/types.js +2 -0
  57. package/dist/cjs/providers/llm/types.js.map +7 -0
  58. package/dist/cjs/providers/shared.js +2 -0
  59. package/dist/cjs/providers/shared.js.map +7 -0
  60. package/dist/cjs/providers/stt/adapters/deepgram/deepgram.js +2 -0
  61. package/dist/cjs/providers/stt/adapters/deepgram/deepgram.js.map +7 -0
  62. package/dist/cjs/providers/stt/adapters/deepgram/index.js +2 -0
  63. package/dist/cjs/providers/stt/adapters/deepgram/index.js.map +7 -0
  64. package/dist/cjs/providers/stt/adapters/index.js +2 -0
  65. package/dist/cjs/providers/stt/adapters/index.js.map +7 -0
  66. package/dist/cjs/providers/stt/index.js +2 -0
  67. package/dist/cjs/providers/stt/index.js.map +7 -0
  68. package/dist/cjs/providers/stt/types.js +2 -0
  69. package/dist/cjs/providers/stt/types.js.map +7 -0
  70. package/dist/cjs/providers/tts/adapters/index.js +2 -0
  71. package/dist/cjs/providers/tts/adapters/index.js.map +7 -0
  72. package/dist/cjs/providers/tts/adapters/kokoro/index.js +2 -0
  73. package/dist/cjs/providers/tts/adapters/kokoro/index.js.map +7 -0
  74. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js +2 -0
  75. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js.map +7 -0
  76. package/dist/cjs/providers/tts/index.js +2 -0
  77. package/dist/cjs/providers/tts/index.js.map +7 -0
  78. package/dist/cjs/providers/tts/types.js +2 -0
  79. package/dist/cjs/providers/tts/types.js.map +7 -0
  80. package/dist/cjs/transport/adapters/index.js +2 -0
  81. package/dist/cjs/transport/adapters/index.js.map +7 -0
  82. package/dist/cjs/transport/adapters/memory/index.js +2 -0
  83. package/dist/cjs/transport/adapters/memory/index.js.map +7 -0
  84. package/dist/cjs/transport/adapters/memory/memory.js +2 -0
  85. package/dist/cjs/transport/adapters/memory/memory.js.map +7 -0
  86. package/dist/cjs/transport/index.js +2 -0
  87. package/dist/cjs/transport/index.js.map +7 -0
  88. package/dist/cjs/transport/types.js +2 -0
  89. package/dist/cjs/transport/types.js.map +7 -0
  90. package/dist/esm/agents/agent.js +2 -0
  91. package/dist/esm/agents/agent.js.map +7 -0
  92. package/dist/esm/agents/index.js +2 -0
  93. package/dist/esm/agents/index.js.map +7 -0
  94. package/dist/esm/agents/tools/index.js +2 -0
  95. package/dist/esm/agents/tools/index.js.map +7 -0
  96. package/dist/esm/agents/tools/tools.js +2 -0
  97. package/dist/esm/agents/tools/tools.js.map +7 -0
  98. package/dist/esm/conversations/conversation/conversation.js +2 -0
  99. package/dist/esm/conversations/conversation/conversation.js.map +7 -0
  100. package/dist/esm/conversations/conversation/index.js +2 -0
  101. package/dist/esm/conversations/conversation/index.js.map +7 -0
  102. package/dist/esm/conversations/conversations.js +2 -0
  103. package/dist/esm/conversations/conversations.js.map +7 -0
  104. package/dist/esm/conversations/index.js +2 -0
  105. package/dist/esm/conversations/index.js.map +7 -0
  106. package/dist/esm/conversations/orchestration/orchestrator/index.js +2 -0
  107. package/dist/esm/conversations/orchestration/orchestrator/index.js.map +7 -0
  108. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js +3 -0
  109. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js.map +7 -0
  110. package/dist/esm/conversations/transcription/index.js +2 -0
  111. package/dist/esm/conversations/transcription/index.js.map +7 -0
  112. package/dist/esm/conversations/transcription/transcription.js +2 -0
  113. package/dist/esm/conversations/transcription/transcription.js.map +7 -0
  114. package/dist/esm/conversations/types.js +2 -0
  115. package/dist/esm/conversations/types.js.map +7 -0
  116. package/dist/esm/index.js +2 -0
  117. package/dist/esm/index.js.map +7 -0
  118. package/dist/esm/persistence/adapters/index.js +2 -0
  119. package/dist/esm/persistence/adapters/index.js.map +7 -0
  120. package/dist/esm/persistence/adapters/memory/index.js +2 -0
  121. package/dist/esm/persistence/adapters/memory/index.js.map +7 -0
  122. package/dist/esm/persistence/adapters/memory/memory.js +2 -0
  123. package/dist/esm/persistence/adapters/memory/memory.js.map +7 -0
  124. package/dist/esm/persistence/adapters/sqlite/index.js +2 -0
  125. package/dist/esm/persistence/adapters/sqlite/index.js.map +7 -0
  126. package/dist/esm/persistence/adapters/sqlite/sqlite.js +58 -0
  127. package/dist/esm/persistence/adapters/sqlite/sqlite.js.map +7 -0
  128. package/dist/esm/persistence/index.js +2 -0
  129. package/dist/esm/persistence/index.js.map +7 -0
  130. package/dist/esm/persistence/persistence.js +1 -0
  131. package/dist/esm/persistence/persistence.js.map +7 -0
  132. package/dist/esm/providers/index.js +2 -0
  133. package/dist/esm/providers/index.js.map +7 -0
  134. package/dist/esm/providers/llm/adapters/deepseek/deepseek.js +2 -0
  135. package/dist/esm/providers/llm/adapters/deepseek/deepseek.js.map +7 -0
  136. package/dist/esm/providers/llm/adapters/deepseek/index.js +2 -0
  137. package/dist/esm/providers/llm/adapters/deepseek/index.js.map +7 -0
  138. package/dist/esm/providers/llm/adapters/index.js +2 -0
  139. package/dist/esm/providers/llm/adapters/index.js.map +7 -0
  140. package/dist/esm/providers/llm/index.js +2 -0
  141. package/dist/esm/providers/llm/index.js.map +7 -0
  142. package/dist/esm/providers/llm/types.js +1 -0
  143. package/dist/esm/providers/llm/types.js.map +7 -0
  144. package/dist/esm/providers/shared.js +1 -0
  145. package/dist/esm/providers/shared.js.map +7 -0
  146. package/dist/esm/providers/stt/adapters/deepgram/deepgram.js +2 -0
  147. package/dist/esm/providers/stt/adapters/deepgram/deepgram.js.map +7 -0
  148. package/dist/esm/providers/stt/adapters/deepgram/index.js +2 -0
  149. package/dist/esm/providers/stt/adapters/deepgram/index.js.map +7 -0
  150. package/dist/esm/providers/stt/adapters/index.js +2 -0
  151. package/dist/esm/providers/stt/adapters/index.js.map +7 -0
  152. package/dist/esm/providers/stt/index.js +2 -0
  153. package/dist/esm/providers/stt/index.js.map +7 -0
  154. package/dist/esm/providers/stt/types.js +1 -0
  155. package/dist/esm/providers/stt/types.js.map +7 -0
  156. package/dist/esm/providers/tts/adapters/index.js +2 -0
  157. package/dist/esm/providers/tts/adapters/index.js.map +7 -0
  158. package/dist/esm/providers/tts/adapters/kokoro/index.js +2 -0
  159. package/dist/esm/providers/tts/adapters/kokoro/index.js.map +7 -0
  160. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js +2 -0
  161. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js.map +7 -0
  162. package/dist/esm/providers/tts/index.js +2 -0
  163. package/dist/esm/providers/tts/index.js.map +7 -0
  164. package/dist/esm/providers/tts/types.js +1 -0
  165. package/dist/esm/providers/tts/types.js.map +7 -0
  166. package/dist/esm/transport/adapters/index.js +2 -0
  167. package/dist/esm/transport/adapters/index.js.map +7 -0
  168. package/dist/esm/transport/adapters/memory/index.js +2 -0
  169. package/dist/esm/transport/adapters/memory/index.js.map +7 -0
  170. package/dist/esm/transport/adapters/memory/memory.js +2 -0
  171. package/dist/esm/transport/adapters/memory/memory.js.map +7 -0
  172. package/dist/esm/transport/index.js +2 -0
  173. package/dist/esm/transport/index.js.map +7 -0
  174. package/dist/esm/transport/types.js +1 -0
  175. package/dist/esm/transport/types.js.map +7 -0
  176. package/dist/types/agents/agent.d.ts +60 -0
  177. package/dist/types/agents/index.d.ts +4 -0
  178. package/dist/types/agents/tools/index.d.ts +2 -0
  179. package/dist/types/agents/tools/tools.d.ts +22 -0
  180. package/dist/types/conversations/conversation/conversation.d.ts +138 -0
  181. package/dist/types/conversations/conversation/index.d.ts +2 -0
  182. package/dist/types/conversations/conversations.d.ts +23 -0
  183. package/dist/types/conversations/index.d.ts +10 -0
  184. package/dist/types/conversations/orchestration/orchestrator/index.d.ts +2 -0
  185. package/dist/types/conversations/orchestration/orchestrator/orchestrator.d.ts +98 -0
  186. package/dist/types/conversations/transcription/index.d.ts +2 -0
  187. package/dist/types/conversations/transcription/transcription.d.ts +59 -0
  188. package/dist/types/conversations/types.d.ts +63 -0
  189. package/dist/types/index.d.ts +45 -0
  190. package/dist/types/persistence/adapters/index.d.ts +2 -0
  191. package/dist/types/persistence/adapters/memory/index.d.ts +1 -0
  192. package/dist/types/persistence/adapters/memory/memory.d.ts +27 -0
  193. package/dist/types/persistence/adapters/sqlite/index.d.ts +2 -0
  194. package/dist/types/persistence/adapters/sqlite/sqlite.d.ts +31 -0
  195. package/dist/types/persistence/index.d.ts +2 -0
  196. package/dist/types/persistence/persistence.d.ts +38 -0
  197. package/dist/types/providers/index.d.ts +6 -0
  198. package/dist/types/providers/llm/adapters/deepseek/deepseek.d.ts +24 -0
  199. package/dist/types/providers/llm/adapters/deepseek/index.d.ts +2 -0
  200. package/dist/types/providers/llm/adapters/index.d.ts +1 -0
  201. package/dist/types/providers/llm/index.d.ts +10 -0
  202. package/dist/types/providers/llm/types.d.ts +55 -0
  203. package/dist/types/providers/shared.d.ts +8 -0
  204. package/dist/types/providers/stt/adapters/deepgram/deepgram.d.ts +57 -0
  205. package/dist/types/providers/stt/adapters/deepgram/index.d.ts +2 -0
  206. package/dist/types/providers/stt/adapters/index.d.ts +1 -0
  207. package/dist/types/providers/stt/index.d.ts +2 -0
  208. package/dist/types/providers/stt/types.d.ts +30 -0
  209. package/dist/types/providers/tts/adapters/index.d.ts +1 -0
  210. package/dist/types/providers/tts/adapters/kokoro/index.d.ts +2 -0
  211. package/dist/types/providers/tts/adapters/kokoro/kokoro.d.ts +34 -0
  212. package/dist/types/providers/tts/index.d.ts +2 -0
  213. package/dist/types/providers/tts/types.d.ts +16 -0
  214. package/dist/types/transport/adapters/index.d.ts +1 -0
  215. package/dist/types/transport/adapters/memory/index.d.ts +1 -0
  216. package/dist/types/transport/adapters/memory/memory.d.ts +20 -0
  217. package/dist/types/transport/index.d.ts +2 -0
  218. package/dist/types/transport/types.d.ts +49 -0
  219. package/package.json +82 -0
package/README.md ADDED
@@ -0,0 +1,833 @@
1
+ # Pipeflow
2
+
3
+ Realtime voice infrastructure for TypeScript.
4
+
5
+ Pipeflow is an open-source backend SDK for building voice agents, conversational applications, meeting transcribers, Discord bots, recruiters, assistants, and other realtime audio experiences.
6
+
7
+ It handles the plumbing between audio, speech-to-text, LLMs, text-to-speech, conversations, tools, and persistence—while keeping your application in control.
8
+
9
+ > **Pipeflow is the pipe. You build what flows through it.**
10
+
11
+ ## Highlights
12
+
13
+ * **Small** — under 100 kB packed, zero runtime dependencies.
14
+ * **Realtime by default** — audio, transcripts, and speech stream continuously, with built-in interruption and barge-in handling.
15
+ * **Provider-agnostic** — STT, LLM, and TTS are swappable adapters (Deepgram, DeepSeek, and Kokoro today).
16
+ * **Your backend stays yours** — tools and the audio transport are owned by your application; Pipeflow never executes your code.
17
+
18
+ ## Status
19
+
20
+ 🚧 **Early development**
21
+
22
+ The API is evolving and should be considered experimental.
23
+
24
+ ## Philosophy
25
+
26
+ Pipeflow has four core concepts:
27
+
28
+ * **Agent** — intelligence, context, and tools.
29
+ * **Conversation** — a persistent realtime conversation and its participants.
30
+ * **Tool** — application capabilities executed by your backend.
31
+ * **Provider** — an implementation of STT, LLM, or TTS.
32
+
33
+ The goal is to keep these concerns independent.
34
+
35
+ An agent does not inherently own a conversation. A conversation does not require an agent. A provider should not leak into your application logic.
36
+
37
+ ```text
38
+ Pipeflow
39
+
40
+ ┌──────────────┐
41
+ │ Agent │
42
+ │ context │
43
+ │ tools │
44
+ └──────┬───────┘
45
+
46
+
47
+ ┌──────────────┐
48
+ │ Conversation │
49
+ │ │
50
+ │ participants │
51
+ │ turns │
52
+ │ audio │
53
+ │ interruption │
54
+ └──────┬───────┘
55
+
56
+
57
+ ┌─────────────────────────┐
58
+ │ Orchestrator │
59
+ └──────┬──────┬──────┬────┘
60
+ │ │ │
61
+ STT LLM TTS
62
+ │ │ │
63
+ ▼ ▼ ▼
64
+ Deepgram DeepSeek Kokoro
65
+ ```
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ bun add pipeflow
71
+ ```
72
+
73
+ The package is not published to npm yet. Until then, install from the
74
+ repository (`bun add pipeflow`) or build from
75
+ source — see [Development](#development).
76
+
77
+ ## Basic voice agent
78
+
79
+ Create an agent:
80
+
81
+ ```ts
82
+ import { Pipeflow, Orchestrator } from "pipeflow";
83
+ import { DeepSeekLLM, DeepgramSTT, KokoroTTS } from "pipeflow/providers";
84
+
85
+ // Providers are configured explicitly with their own credentials —
86
+ // Pipeflow itself does not hold an API key.
87
+ const stt = new DeepgramSTT({ apiKey: process.env.DEEPGRAM_API_KEY });
88
+ const tts = new KokoroTTS();
89
+
90
+ const pipeflow = new Pipeflow({
91
+ llm: new DeepSeekLLM({ apiKey: process.env.DEEPSEEK_API_KEY }),
92
+ stt,
93
+ tts,
94
+ });
95
+
96
+ const jarvis = pipeflow.agent({
97
+ name: "Jarvis",
98
+
99
+ context: `
100
+ You are Jarvis, a helpful voice assistant.
101
+ Keep your responses concise and conversational.
102
+ `,
103
+ });
104
+ ```
105
+
106
+ Create a conversation:
107
+
108
+ ```ts
109
+ const conversation = await pipeflow.conversations.create({
110
+ agents: [jarvis],
111
+ });
112
+ ```
113
+
114
+ Attach the orchestrator — it runs the realtime pipeline (STT → turns → LLM → TTS) for the conversation:
115
+
116
+ ```ts
117
+ const orchestrator = new Orchestrator({
118
+ conversation,
119
+ agent: jarvis,
120
+ stt,
121
+ tts,
122
+ });
123
+
124
+ await orchestrator.start();
125
+ ```
126
+
127
+ Start the conversation and add a participant:
128
+
129
+ ```ts
130
+ conversation.start();
131
+
132
+ await conversation.participate({
133
+ userId: "alice",
134
+ });
135
+ ```
136
+
137
+ Feed it audio as it arrives:
138
+
139
+ ```ts
140
+ voice.onAudio((audio) => {
141
+ conversation.listen({
142
+ userId: "alice",
143
+ audio,
144
+ });
145
+ });
146
+ ```
147
+
148
+ Listen for generated audio:
149
+
150
+ ```ts
151
+ conversation.on("audio", ({ audio }) => {
152
+ voice.play(audio);
153
+ });
154
+ ```
155
+
156
+ When finished:
157
+
158
+ ```ts
159
+ await conversation.stop();
160
+ ```
161
+
162
+ The application owns the audio transport. Pipeflow handles the realtime voice pipeline.
163
+
164
+ ```text
165
+ Your application
166
+
167
+ │ audio chunks
168
+
169
+ Pipeflow
170
+
171
+ ├── STT
172
+ ├── conversation orchestration
173
+ ├── LLM
174
+ └── TTS
175
+
176
+ │ audio chunks
177
+
178
+ Your application
179
+ ```
180
+
181
+ ## Conversations
182
+
183
+ A conversation is the persistent entity representing a realtime interaction.
184
+
185
+ ```ts
186
+ const conversation =
187
+ await pipeflow.conversations.create({
188
+ agents: [jarvis],
189
+ });
190
+
191
+ console.log(conversation.id);
192
+
193
+ conversation.start();
194
+ ```
195
+
196
+ Creation and realtime execution are deliberately separate.
197
+
198
+ ```ts
199
+ create() // creates the persistent conversation
200
+ start() // moves the conversation into the started state
201
+ participate() // adds participants
202
+ listen() // sends audio
203
+ stop() // finalizes the realtime session
204
+ ```
205
+
206
+ Realtime processing itself is the orchestrator's job: once attached, it
207
+ subscribes to `audio-in` events, runs the STT/LLM/TTS pipeline, and pushes
208
+ generated audio, turns, transcripts, and tool calls back through conversation
209
+ events.
210
+
211
+ `listen()` is intentionally synchronous:
212
+
213
+ ```ts
214
+ conversation.listen({
215
+ userId,
216
+ audio,
217
+ });
218
+ ```
219
+
220
+ It means:
221
+
222
+ > Send this audio packet.
223
+
224
+ It does not mean:
225
+
226
+ > Wait for this utterance to finish.
227
+
228
+ This makes it suitable for high-frequency realtime audio streams.
229
+
230
+ ## Participants
231
+
232
+ Participants can be added individually:
233
+
234
+ ```ts
235
+ await conversation.participate({
236
+ userId: "alice",
237
+ });
238
+ ```
239
+
240
+ Or in batches:
241
+
242
+ ```ts
243
+ await conversation.participate([
244
+ { userId: "alice" },
245
+
246
+ {
247
+ userId: "bob",
248
+ aliases: ["robert", "rob"],
249
+ },
250
+
251
+ {
252
+ userId: "charlie",
253
+ aliases: ["charles"],
254
+ },
255
+ ]);
256
+ ```
257
+
258
+ Participant information can be used for speaker attribution, conversation history, addressing, and multi-participant floor management.
259
+
260
+ ## Interruption
261
+
262
+ Voice conversations need to feel immediate.
263
+
264
+ If an agent is speaking and a participant starts talking, Pipeflow can interrupt
265
+ the current output immediately rather than waiting for the entire utterance to
266
+ be transcribed.
267
+
268
+ Interruptions can be triggered by the application — or automatically: the
269
+ orchestrator detects when a participant starts speaking while the agent is
270
+ responding (barge-in).
271
+
272
+ ```ts
273
+ conversation.interrupt(); // stops TTS, cancels the current generation
274
+ ```
275
+
276
+ The interrupting speech keeps flowing into STT and becomes the next turn.
277
+
278
+ Conceptually:
279
+
280
+ ```text
281
+ Agent is speaking
282
+
283
+
284
+ Participant starts speaking
285
+
286
+ ├── stop TTS
287
+ ├── cancel current generation
288
+ └── continue receiving speech
289
+
290
+
291
+ STT
292
+
293
+
294
+ new conversation turn
295
+ ```
296
+
297
+ This keeps interactions responsive even when the user interrupts the agent halfway through a sentence.
298
+
299
+ ## Conversation events
300
+
301
+ The conversation emits a typed event stream the application can subscribe to:
302
+
303
+ * `audio-in` — raw audio fed in via `listen()`
304
+ * `partial-transcript` — live STT partials (captions)
305
+ * `turn` — a finalized participant turn
306
+ * `transcript` — a transcript entry
307
+ * `audio` — generated audio to play
308
+ * `generation` — an agent generation
309
+ * `tool-call` / `tool-call-result` — tool execution round trips
310
+ * `interrupt` — an interruption occurred
311
+ * `error` — a provider failure
312
+ * `start` / `stop` / `state` — lifecycle
313
+
314
+ ```ts
315
+ conversation.on("audio", ({ audio }) => voice.play(audio));
316
+ conversation.on("partial-transcript", ({ text }) => captions.update(text));
317
+ ```
318
+
319
+ ## Multi-participant conversations
320
+
321
+ Conversations can contain multiple participants and agents.
322
+
323
+ ```ts
324
+ const conversation =
325
+ await pipeflow.conversations.create({
326
+ agents: [jarvis],
327
+ });
328
+
329
+ await conversation.participate([
330
+ { userId: "alice" },
331
+ { userId: "bob" },
332
+ { userId: "charlie" },
333
+ ]);
334
+ ```
335
+
336
+ The conversation orchestrator maintains conversational state such as:
337
+
338
+ * active turns
339
+ * participant identity
340
+ * interruptions
341
+ * agent generations
342
+ * transcript state
343
+
344
+ In a one-to-one conversation, speech is treated as an interaction: every
345
+ finalized participant turn produces an agent generation.
346
+
347
+ Multi-participant floor management and addressing (determining when an agent is
348
+ being spoken to using participant context, agent names, aliases, and floor
349
+ rules) are on the roadmap. A wake word is not intended to be a fundamental
350
+ requirement.
351
+
352
+ ## Agents
353
+
354
+ An agent defines an AI persona and its capabilities.
355
+
356
+ ```ts
357
+ const jarvis = pipeflow.agent({
358
+ name: "Jarvis",
359
+
360
+ context: `
361
+ You are Jarvis.
362
+ You are concise, helpful, and conversational.
363
+ `,
364
+
365
+ tools: [
366
+ getWeather,
367
+ searchCalendar,
368
+ ],
369
+ });
370
+ ```
371
+
372
+ Agents can also be run independently of conversations:
373
+
374
+ ```ts
375
+ const result = await jarvis.run({
376
+ prompt: "Explain how a neural network works.",
377
+ });
378
+
379
+ console.log(result.text);
380
+ ```
381
+
382
+ This is useful for ordinary LLM workloads where realtime audio and conversation state are not required.
383
+
384
+ ### Conversational agents
385
+
386
+ When an agent is attached to a conversation, it can take conversational turns as part of the realtime orchestration.
387
+
388
+ The conversation owns the runtime.
389
+
390
+ The agent owns the intelligence.
391
+
392
+ ```text
393
+ Agent
394
+ ├── context
395
+ └── tools
396
+
397
+ Conversation
398
+ ├── participants
399
+ ├── turns
400
+ ├── audio
401
+ ├── interruption
402
+ └── orchestration
403
+ ```
404
+
405
+ ## Tools
406
+
407
+ Tools expose capabilities from your application to an agent.
408
+
409
+ ```ts
410
+ const getWeather = new PipeflowTool({
411
+ name: "get_weather",
412
+
413
+ description: "Get the current weather for a city.",
414
+
415
+ execute: async ({ city }) => {
416
+ return weatherService.getCurrent(city);
417
+ },
418
+ });
419
+ ```
420
+
421
+ Then:
422
+
423
+ ```ts
424
+ const jarvis = pipeflow.agent({
425
+ name: "Jarvis",
426
+
427
+ context: "You are a helpful assistant.",
428
+
429
+ tools: [getWeather],
430
+ });
431
+ ```
432
+
433
+ Tools execute in **your backend**.
434
+
435
+ Pipeflow does not execute arbitrary application code.
436
+
437
+ ```text
438
+ Pipeflow
439
+
440
+ LLM requests tool
441
+
442
+
443
+ ┌─────────────────┐
444
+ │ Your application │
445
+ │ │
446
+ │ execute() │
447
+ └────────┬────────┘
448
+
449
+
450
+ tool result
451
+
452
+
453
+ LLM
454
+ ```
455
+
456
+ This allows tools to access your database, APIs, Discord bot, business logic, filesystem, or anything else your application controls.
457
+
458
+ In a conversation, tool calls never execute inside Pipeflow. The orchestrator
459
+ emits a `tool-call` event with the requested tool and its arguments; your
460
+ backend executes it and reports back:
461
+
462
+ ```ts
463
+ conversation.on("tool-call", async ({ call }) => {
464
+ const result = await myBackend.execute(call.name, call.arguments);
465
+
466
+ conversation.resolveToolCall({
467
+ id: call.id,
468
+ result,
469
+ });
470
+ });
471
+ ```
472
+
473
+ The agent's narration continues while the tool runs, and the generation resumes
474
+ with the tool result once it is resolved.
475
+
476
+ ## Meeting transcription
477
+
478
+ A conversation does not require an agent.
479
+
480
+ This makes Pipeflow useful as a realtime transcription primitive.
481
+
482
+ ```ts
483
+ const conversation =
484
+ await pipeflow.conversations.create();
485
+
486
+ // Without an agent, the orchestrator runs in transcription-only mode:
487
+ // audio in, turns and transcripts out.
488
+ const orchestrator = new Orchestrator({
489
+ conversation,
490
+ stt,
491
+ });
492
+
493
+ await orchestrator.start();
494
+
495
+ conversation.start();
496
+
497
+ await conversation.participate([
498
+ { userId: "alice" },
499
+ { userId: "bob", aliases: ["robert"] },
500
+ ]);
501
+
502
+ discordVoice.onAudio((userId, audio) => {
503
+ conversation.listen({
504
+ userId,
505
+ audio,
506
+ });
507
+ });
508
+
509
+ await conversation.stop();
510
+ ```
511
+
512
+ Retrieve the transcript afterward:
513
+
514
+ ```ts
515
+ const transcript =
516
+ await pipeflow.conversations.transcript(
517
+ conversation.id,
518
+ );
519
+ ```
520
+
521
+ Transcript retrieval is separate from `stop()` so that ending a conversation does not require loading an arbitrarily large transcript into memory.
522
+
523
+ Pagination can be supported for long conversations.
524
+
525
+ ## Meeting summaries
526
+
527
+ A meeting summary can simply be another agent task.
528
+
529
+ ```ts
530
+ const notetaker = pipeflow.agent({
531
+ name: "Meeting Notetaker",
532
+
533
+ context: `
534
+ You are a meeting notetaker.
535
+
536
+ Produce concise notes containing:
537
+ - summary
538
+ - decisions
539
+ - action items
540
+ - unresolved questions
541
+ `,
542
+ });
543
+ ```
544
+
545
+ Retrieve the transcript:
546
+
547
+ ```ts
548
+ const transcript =
549
+ await pipeflow.conversations.transcript(
550
+ conversation.id,
551
+ );
552
+ ```
553
+
554
+ Then run the agent:
555
+
556
+ ```ts
557
+ const result = await notetaker.run({
558
+ prompt: `
559
+ The meeting transcription is:
560
+
561
+ ${transcript.join("\n")}
562
+
563
+ Produce the meeting notes.
564
+ `,
565
+ });
566
+ ```
567
+
568
+ Or the notetaker can retrieve the transcript itself through a tool:
569
+
570
+ ```ts
571
+ const getTranscript = new PipeflowTool({
572
+ name: "get_transcript",
573
+
574
+ description: "Retrieve the meeting transcript.",
575
+
576
+ execute: async () => {
577
+ return pipeflow.conversations.transcript(
578
+ conversation.id,
579
+ );
580
+ },
581
+ });
582
+ ```
583
+
584
+ The agent does not receive special access to conversations.
585
+
586
+ If it needs conversation data, **a tool provides that capability**.
587
+
588
+ ## Providers
589
+
590
+ Pipeflow separates provider interfaces from provider implementations.
591
+
592
+ ```text
593
+ providers/
594
+ ├── llm/
595
+ │ ├── types.ts
596
+ │ └── adapters/
597
+ │ └── deepseek/
598
+
599
+ ├── stt/
600
+ │ ├── types.ts
601
+ │ └── adapters/
602
+ │ └── deepgram/
603
+
604
+ └── tts/
605
+ ├── types.ts
606
+ └── adapters/
607
+ └── kokoro/
608
+ ```
609
+
610
+ The orchestrator works against provider interfaces rather than directly against vendor APIs.
611
+
612
+ This makes it possible to replace providers without changing the conversation layer.
613
+
614
+ ### Current providers
615
+
616
+ The project currently contains adapters for:
617
+
618
+ * **STT:** Deepgram
619
+ * **LLM:** DeepSeek
620
+ * **TTS:** Kokoro
621
+
622
+ Providers are configured with their own credentials — Pipeflow itself does
623
+ not hold an API key.
624
+
625
+ Provider availability and configuration are evolving during early development.
626
+
627
+ ## Architecture
628
+
629
+ Pipeflow is designed around a small number of independent layers.
630
+
631
+ ```text
632
+ src/
633
+ ├── agents/
634
+ ├── conversations/
635
+ │ ├── conversation/
636
+ │ ├── orchestration/
637
+ │ └── transcription/
638
+ ├── persistence/
639
+ │ └── adapters/
640
+ ├── providers/
641
+ │ ├── llm/
642
+ │ ├── stt/
643
+ │ └── tts/
644
+ └── transport/
645
+ ```
646
+
647
+ ### Conversation
648
+
649
+ Public realtime conversation API and lifecycle.
650
+
651
+ ### Orchestration
652
+
653
+ The state machine coordinating:
654
+
655
+ * speech
656
+ * transcription
657
+ * turns
658
+ * floor state
659
+ * agent generation
660
+ * interruptions
661
+ * tools
662
+ * TTS
663
+
664
+ ### Transcription
665
+
666
+ Conversation transcription and transcript state.
667
+
668
+ ### Providers
669
+
670
+ Vendor-independent interfaces and provider adapters.
671
+
672
+ ### Persistence
673
+
674
+ Persistence abstractions with adapters such as SQLite and in-memory storage.
675
+
676
+ ### Transport
677
+
678
+ Realtime communication between Pipeflow and the application using Pipeflow.
679
+
680
+ ## Persistence
681
+
682
+ Pipeflow separates persistence from the conversation domain.
683
+
684
+ The project currently includes:
685
+
686
+ ```text
687
+ persistence/
688
+ └── adapters/
689
+ ├── memory/
690
+ └── sqlite/
691
+ ```
692
+
693
+ The in-memory adapter is useful for tests and development.
694
+
695
+ SQLite provides a lightweight persistent backend suitable for local applications and early deployments.
696
+
697
+ The persistence interface is intentionally provider-independent so other storage implementations can be added later.
698
+
699
+ ## Realtime architecture
700
+
701
+ A typical voice interaction looks like:
702
+
703
+ ```text
704
+ Audio input
705
+
706
+
707
+ Speech detection
708
+
709
+
710
+ STT stream
711
+
712
+ partial transcript
713
+
714
+
715
+ Conversation state
716
+
717
+ turn completed
718
+
719
+
720
+ LLM stream
721
+
722
+ token stream
723
+
724
+
725
+ TTS stream
726
+
727
+ audio chunks
728
+
729
+
730
+ Application
731
+ ```
732
+
733
+ Everything happens as a stream.
734
+
735
+ Pipeflow does not wait for a complete recording before beginning transcription, nor does it wait for a complete LLM response before beginning TTS.
736
+
737
+ The intended flow is:
738
+
739
+ ```text
740
+ audio
741
+
742
+ partial STT
743
+
744
+ turn detection
745
+
746
+ LLM streaming
747
+
748
+ TTS streaming
749
+
750
+ audio
751
+ ```
752
+
753
+ This allows the system to begin producing speech as early as possible.
754
+
755
+ ## Open source
756
+
757
+ Pipeflow is open source.
758
+
759
+ The project is designed to make realtime voice infrastructure accessible without requiring applications to implement their own orchestration layer.
760
+
761
+ The provider layer is intentionally modular so applications can choose between hosted and self-hosted services.
762
+
763
+ ## Development
764
+
765
+ Clone the repository and install dependencies:
766
+
767
+ ```bash
768
+ git clone git@github.com:moureau-dev/pipeflow.git
769
+ cd pipeflow
770
+ bun install
771
+ ```
772
+
773
+ Run tests:
774
+
775
+ ```bash
776
+ bun test
777
+ ```
778
+
779
+ Build and type-check:
780
+
781
+ ```bash
782
+ bun run build # transpile to dist/esm + dist/cjs and emit dist/types
783
+ bun run typecheck
784
+ ```
785
+
786
+ The project uses Bun and TypeScript.
787
+
788
+ ## Design principles
789
+
790
+ ### Realtime first
791
+
792
+ Audio is streamed continuously rather than processed as completed recordings.
793
+
794
+ ### Provider agnostic
795
+
796
+ STT, LLM, and TTS providers are adapters, not application-level concepts.
797
+
798
+ ### Application-owned tools
799
+
800
+ Your application executes your tools.
801
+
802
+ ### Conversations are persistent entities
803
+
804
+ A realtime `Conversation` instance is a runtime handle to a persistent conversation.
805
+
806
+ ### Agents are independent
807
+
808
+ An agent can participate in a conversation or simply be invoked with `run()`.
809
+
810
+ ### Explicit boundaries
811
+
812
+ Pipeflow owns orchestration.
813
+
814
+ Your application owns application logic.
815
+
816
+ Providers own their respective AI services.
817
+
818
+ ### Small public API
819
+
820
+ The core API should remain centered around:
821
+
822
+ ```text
823
+ Pipeflow
824
+ Agent
825
+ Conversation
826
+ Tool
827
+ ```
828
+
829
+ Everything else should remain replaceable implementation detail for as long as possible.
830
+
831
+ ## License
832
+
833
+ See [LICENSE](LICENSE).