@moureau/pipeflow 0.0.2 → 0.0.3

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 (89) hide show
  1. package/README.md +39 -17
  2. package/dist/cjs/client/client.js +2 -0
  3. package/dist/cjs/client/client.js.map +7 -0
  4. package/dist/cjs/client/index.js +2 -0
  5. package/dist/cjs/client/index.js.map +7 -0
  6. package/dist/cjs/client/json.js +2 -0
  7. package/dist/cjs/client/json.js.map +7 -0
  8. package/dist/cjs/client/protocol/websocket.js +2 -0
  9. package/dist/cjs/client/protocol/websocket.js.map +7 -0
  10. package/dist/cjs/client/protocol.js +2 -0
  11. package/dist/cjs/client/protocol.js.map +7 -0
  12. package/dist/cjs/conversations/conversation/conversation.js +1 -1
  13. package/dist/cjs/conversations/conversation/conversation.js.map +3 -3
  14. package/dist/cjs/conversations/conversations.js +1 -1
  15. package/dist/cjs/conversations/conversations.js.map +2 -2
  16. package/dist/cjs/conversations/orchestration/orchestrator/generation/generation.js +1 -1
  17. package/dist/cjs/conversations/orchestration/orchestrator/generation/generation.js.map +3 -3
  18. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js +1 -1
  19. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js.map +3 -3
  20. package/dist/cjs/conversations/orchestration/orchestrator/speech/speech.js +1 -1
  21. package/dist/cjs/conversations/orchestration/orchestrator/speech/speech.js.map +3 -3
  22. package/dist/cjs/conversations/orchestration/orchestrator/tools/tools.js +1 -1
  23. package/dist/cjs/conversations/orchestration/orchestrator/tools/tools.js.map +3 -3
  24. package/dist/cjs/conversations/orchestration/test-harness.js +1 -1
  25. package/dist/cjs/conversations/orchestration/test-harness.js.map +2 -2
  26. package/dist/cjs/pipeflow/pipeflow.js +1 -1
  27. package/dist/cjs/pipeflow/pipeflow.js.map +2 -2
  28. package/dist/cjs/providers/llm/adapters/openai-compatible.js +6 -6
  29. package/dist/cjs/providers/llm/adapters/openai-compatible.js.map +3 -3
  30. package/dist/cjs/providers/llm/types.js.map +2 -2
  31. package/dist/cjs/providers/stt/adapters/openrouter/openrouter.js +1 -1
  32. package/dist/cjs/providers/stt/adapters/openrouter/openrouter.js.map +3 -3
  33. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js +3 -3
  34. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js.map +3 -3
  35. package/dist/cjs/providers/tts/adapters/openrouter/openrouter.js +1 -1
  36. package/dist/cjs/providers/tts/adapters/openrouter/openrouter.js.map +3 -3
  37. package/dist/esm/client/client.js +2 -0
  38. package/dist/esm/client/client.js.map +7 -0
  39. package/dist/esm/client/index.js +2 -0
  40. package/dist/esm/client/index.js.map +7 -0
  41. package/dist/esm/client/json.js +2 -0
  42. package/dist/esm/client/json.js.map +7 -0
  43. package/dist/esm/client/protocol/websocket.js +2 -0
  44. package/dist/esm/client/protocol/websocket.js.map +7 -0
  45. package/dist/esm/client/protocol.js +1 -0
  46. package/dist/esm/client/protocol.js.map +7 -0
  47. package/dist/esm/conversations/conversation/conversation.js +1 -1
  48. package/dist/esm/conversations/conversation/conversation.js.map +3 -3
  49. package/dist/esm/conversations/conversations.js +1 -1
  50. package/dist/esm/conversations/conversations.js.map +2 -2
  51. package/dist/esm/conversations/orchestration/orchestrator/generation/generation.js +1 -1
  52. package/dist/esm/conversations/orchestration/orchestrator/generation/generation.js.map +3 -3
  53. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js +1 -1
  54. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js.map +3 -3
  55. package/dist/esm/conversations/orchestration/orchestrator/speech/speech.js +1 -1
  56. package/dist/esm/conversations/orchestration/orchestrator/speech/speech.js.map +3 -3
  57. package/dist/esm/conversations/orchestration/orchestrator/tools/tools.js +1 -1
  58. package/dist/esm/conversations/orchestration/orchestrator/tools/tools.js.map +3 -3
  59. package/dist/esm/conversations/orchestration/test-harness.js +1 -1
  60. package/dist/esm/conversations/orchestration/test-harness.js.map +2 -2
  61. package/dist/esm/pipeflow/pipeflow.js +1 -1
  62. package/dist/esm/pipeflow/pipeflow.js.map +2 -2
  63. package/dist/esm/providers/llm/adapters/openai-compatible.js +6 -6
  64. package/dist/esm/providers/llm/adapters/openai-compatible.js.map +3 -3
  65. package/dist/esm/providers/llm/types.js.map +2 -2
  66. package/dist/esm/providers/stt/adapters/openrouter/openrouter.js +1 -1
  67. package/dist/esm/providers/stt/adapters/openrouter/openrouter.js.map +3 -3
  68. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js +3 -3
  69. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js.map +3 -3
  70. package/dist/esm/providers/tts/adapters/openrouter/openrouter.js +1 -1
  71. package/dist/esm/providers/tts/adapters/openrouter/openrouter.js.map +3 -3
  72. package/dist/types/client/client.d.ts +186 -0
  73. package/dist/types/client/index.d.ts +31 -0
  74. package/dist/types/client/json.d.ts +13 -0
  75. package/dist/types/client/protocol/websocket.d.ts +56 -0
  76. package/dist/types/client/protocol.d.ts +46 -0
  77. package/dist/types/conversations/conversation/conversation.d.ts +45 -0
  78. package/dist/types/conversations/conversations.d.ts +24 -0
  79. package/dist/types/conversations/orchestration/orchestrator/generation/generation.d.ts +17 -0
  80. package/dist/types/conversations/orchestration/orchestrator/orchestrator.d.ts +25 -6
  81. package/dist/types/conversations/orchestration/orchestrator/speech/speech.d.ts +31 -2
  82. package/dist/types/conversations/orchestration/orchestrator/tools/tools.d.ts +41 -7
  83. package/dist/types/conversations/orchestration/test-harness.d.ts +4 -0
  84. package/dist/types/pipeflow/pipeflow.d.ts +8 -0
  85. package/dist/types/providers/llm/types.d.ts +4 -1
  86. package/dist/types/providers/stt/adapters/openrouter/openrouter.d.ts +103 -1
  87. package/dist/types/providers/tts/adapters/kokoro/kokoro.d.ts +1 -1
  88. package/dist/types/providers/tts/adapters/openrouter/openrouter.d.ts +26 -7
  89. package/package.json +7 -1
@@ -1,31 +1,65 @@
1
1
  import type { Conversation } from "../../../conversation/conversation.js";
2
2
  import type { LLMToolCall } from "../../../../providers/llm/types.js";
3
3
  import type { ToolCallResult } from "../../../types.js";
4
+ import type { Tool } from "../../../../agents/tools/tools.js";
4
5
  export interface ResolvedToolCall {
5
6
  id: string;
6
7
  name: string;
7
8
  result?: unknown;
8
9
  error?: string;
9
10
  }
11
+ export interface ToolCallManagerOptions {
12
+ /**
13
+ * The tools the framework can execute on the agent's behalf, keyed by
14
+ * name. Built from the orchestrated agents' tool registries.
15
+ */
16
+ tools?: ReadonlyMap<string, Tool<never, unknown>>;
17
+ /**
18
+ * Execute tool calls automatically (default `true`), feeding each tool's
19
+ * result — or a caught error — back into the model loop. Set `false` to
20
+ * keep the application-managed contract: listen for `tool-call`, execute
21
+ * the tool in your own backend, and report back with
22
+ * `resolveToolCall()`. `tool-call` events are still emitted for
23
+ * visibility in either mode.
24
+ */
25
+ autoExecute?: boolean;
26
+ }
10
27
  /**
11
- * Tracks tool calls handed to the application and resolves them once the
12
- * application reports back (or a timeout fires). Stale results — timed out,
13
- * already resolved, or superseded by an interrupt are dropped.
28
+ * Resolves the tool calls a generation issues. By default it runs the
29
+ * matching tool itself (the conversation agents' tools auto-execute, exactly
30
+ * like `Agent.run()`); with `autoExecute: false` it hands the calls to the
31
+ * application and resolves them once the application reports back (or a
32
+ * timeout fires). Stale results — timed out, already resolved, or superseded
33
+ * by an interrupt — are dropped.
14
34
  */
15
35
  export declare class ToolCallManager {
16
36
  private readonly conversation;
17
37
  private readonly timeoutMs;
38
+ private readonly tools;
39
+ private readonly autoExecute;
18
40
  private readonly waiters;
19
- constructor(conversation: Conversation, timeoutMs: number);
41
+ constructor(conversation: Conversation, timeoutMs: number, options?: ToolCallManagerOptions);
20
42
  /**
21
- * Hand the calls to the application and resolve each one once it is
22
- * answered or times out. Returns them in call order, with either a result
23
- * or an error string.
43
+ * Resolve a batch of tool calls. In auto-execute mode each call's tool
44
+ * runs on the agent's behalf (errors caught and returned to the model);
45
+ * otherwise the application resolves them. Either way the calls are
46
+ * emitted as `tool-call` events and resolved in call order, with either a
47
+ * result or an error string.
24
48
  */
25
49
  resolveCalls(calls: LLMToolCall[]): Promise<ResolvedToolCall[]>;
26
50
  /** Resolve a waiter from a `tool-call-result` event; stale results drop. */
27
51
  handleResult(result: ToolCallResult): void;
28
52
  /** Force-resolve every pending call (interrupt, stop). */
29
53
  cancelAll(reason: string): void;
54
+ /**
55
+ * Execute one tool call and resolve it with the tool's result (or a
56
+ * caught error). If the application already resolved the call in its
57
+ * `tool-call` handler — the app-managed path — the tool is not run twice.
58
+ */
59
+ private runToolAndResolve;
60
+ /** Resolve through the conversation only while the call is still pending. */
61
+ private resolveIfPending;
62
+ private isPending;
63
+ private parseArguments;
30
64
  private wait;
31
65
  }
@@ -77,6 +77,8 @@ export declare function setup(options: {
77
77
  llm?: LLM;
78
78
  /** Override the conversation-history window (default 5 turns / 4k chars). */
79
79
  historyWindow?: HistoryWindow | false;
80
+ /** Auto-execute the agent's tools (default true); see OrchestratorOptions. */
81
+ autoExecuteTools?: boolean;
80
82
  }): Promise<Harness>;
81
83
  export declare function waitFor(condition: () => boolean, timeoutMs?: number): Promise<void>;
82
84
  export declare function speak(harness: Harness, userId: string, text: string): Promise<void>;
@@ -93,4 +95,6 @@ export declare function setupRoster(options: {
93
95
  llm: FakeLLM;
94
96
  }>;
95
97
  maxCoordinationSteps?: number;
98
+ /** Auto-execute the agents' tools (default true); see OrchestratorOptions. */
99
+ autoExecuteTools?: boolean;
96
100
  }): Promise<RosterHarness>;
@@ -9,6 +9,14 @@ export interface PipeflowOptions {
9
9
  stt?: STT;
10
10
  tts?: TTS;
11
11
  persistence?: Persistence;
12
+ /**
13
+ * Default for created conversations: auto-execute the agents' tools
14
+ * (default `true`), feeding each tool's result back into the model loop.
15
+ * Set `false` to resolve tool calls from your own backend via
16
+ * `resolveToolCall()`. Override per conversation in
17
+ * `pipeflow.conversations.create()`.
18
+ */
19
+ autoExecuteTools?: boolean;
12
20
  }
13
21
  /**
14
22
  * The Pipeflow entry point.
@@ -42,7 +42,10 @@ export interface LLMToolDefinition {
42
42
  * - `prompted` — no `tools`; the same envelope is requested by appending an
43
43
  * instruction to the last user message. The universal fallback: works on
44
44
  * any chat model, at the cost of extraction/repair/retry, higher token
45
- * use, and tail-latency risk.
45
+ * use, and tail-latency risk. Because nothing constrains the output, a
46
+ * model that ignores the envelope and replies in plain prose is answered
47
+ * directly — its text is treated as the reply rather than failing the
48
+ * generation.
46
49
  */
47
50
  export type ToolMode = "native" | "envelope" | "prompted";
48
51
  /**
@@ -23,6 +23,68 @@ export interface OpenRouterSTTOptions extends STTOptions {
23
23
  * sent as-is.
24
24
  */
25
25
  audioFormat?: OpenRouterAudioFormat;
26
+ /**
27
+ * Strip common whisper hallucinations from transcripts: asterisk stage
28
+ * directions ("*Dramatic music*"), repeated filler phrases ("Thank you.
29
+ * Thank you."), and pure-filler clips whisper emits on near-silence
30
+ * ("Thank you.", "E aí.", "Bye.", …). Set `false` to pass transcripts
31
+ * through untouched. Default `true`.
32
+ */
33
+ filterHallucinations?: boolean;
34
+ /**
35
+ * Extra phrases (lowercased) treated as hallucinations on top of the
36
+ * built-in multilingual filler list — for fillers whisper "hears" in the
37
+ * languages you deploy. A transcript that is *entirely* built from known
38
+ * fillers is dropped. Default none.
39
+ */
40
+ fillerPhrases?: string[];
41
+ /**
42
+ * Energy floor (0–1 RMS, on the raw sample range) for transcribing a
43
+ * buffered `pcm` clip. Near-silence clips — the ones whisper hallucinates
44
+ * "E aí." / "Thank you." / *stage directions* on — are skipped entirely
45
+ * (no transcription request, no `final`), instead of being filtered after
46
+ * the fact. Real speech sits far above any reasonable floor; start around
47
+ * `0.01`–`0.02` and raise it if artifacts persist. Only applies to
48
+ * `audioFormat: "pcm"` (the only format whose samples are measurable).
49
+ * Default: no floor (transcribe everything the client sends).
50
+ */
51
+ minClipRms?: number;
52
+ /**
53
+ * Called for every buffered `pcm` clip with its measured mean RMS and
54
+ * whether it was transcribed — for logging the real distribution and
55
+ * tuning `minClipRms` against it (speech clips sit far above artifact
56
+ * clips). Fires when this callback or `minClipRms` is set.
57
+ */
58
+ onClipEnergy?: (rms: number, transcribed: boolean) => void;
59
+ /**
60
+ * Abort a transcription request that produces no response within this
61
+ * long (default 30000ms). Transcriptions are serialized on one session
62
+ * chain, so a hung provider request would otherwise block every later
63
+ * clip (each turn silently waits) until the server restarts. Raise it for
64
+ * very long clips or slow providers.
65
+ */
66
+ transcriptionTimeoutMs?: number;
67
+ /**
68
+ * Sampling temperature (0–1) for transcription. Lower is more
69
+ * deterministic; whisper's API default is already 0, so this is rarely the
70
+ * lever for hallucinations.
71
+ */
72
+ temperature?: number;
73
+ /**
74
+ * Provider-specific passthrough, serialized as the `provider` multipart
75
+ * field. OpenRouter ignores the top-level `prompt` field ("accepted but
76
+ * ignored"), so the only way to reach whisper's prompt — vocabulary /
77
+ * context steering — is per-provider:
78
+ *
79
+ * ```ts
80
+ * providerOptions: { options: { groq: { prompt: "Transcribe exactly what is said." } } }
81
+ * ```
82
+ *
83
+ * Only the options for the provider that actually serves the request are
84
+ * forwarded, so the key must match the serving provider (openai, groq,
85
+ * together, …).
86
+ */
87
+ providerOptions?: Record<string, unknown>;
26
88
  /** Injectable fetch, mainly for tests. */
27
89
  fetch?: FetchLike;
28
90
  }
@@ -54,6 +116,13 @@ export declare class OpenRouterSTT implements STT {
54
116
  private readonly sessions;
55
117
  constructor(options: OpenRouterSTTOptions);
56
118
  start(options?: STTOptions): STTSession;
119
+ /**
120
+ * Live-tunable energy floor (0–1 RMS) for `pcm` clips: raise it to skip
121
+ * more near-silence clips (the whisper hallucinations), lower it to keep
122
+ * quieter speech. Applies to all sessions immediately.
123
+ */
124
+ set minClipRms(value: number | undefined);
125
+ get minClipRms(): number | undefined;
57
126
  cancel(): void;
58
127
  }
59
128
  export declare class OpenRouterSession implements STTSession {
@@ -65,14 +134,24 @@ export declare class OpenRouterSession implements STTSession {
65
134
  private readonly sampleRate;
66
135
  private readonly silenceMs;
67
136
  private readonly audioFormat;
137
+ private readonly filterHallucinations;
138
+ private readonly fillerPhrases;
139
+ private readonly onClipEnergy;
140
+ private readonly transcriptionTimeoutMs;
141
+ private readonly temperature;
142
+ private readonly providerOptions;
68
143
  private readonly fetchImpl;
144
+ /** The adapter's live options object; live-tunable fields are read here. */
145
+ private readonly liveOptions;
146
+ /** Live-tunable energy floor, read through the adapter (not a snapshot). */
147
+ private get minClipRms();
69
148
  private chunks;
70
149
  private timer;
71
150
  private chain;
72
151
  private readonly controllers;
73
152
  private ended;
74
153
  private aborted;
75
- constructor(options: OpenRouterSTTOptions);
154
+ constructor(options: OpenRouterSTTOptions, liveOptions: OpenRouterSTTOptions);
76
155
  write(audio: Uint8Array): void;
77
156
  end(): Promise<void>;
78
157
  /** Drop buffered audio and abort in-flight requests (cancel). */
@@ -82,6 +161,12 @@ export declare class OpenRouterSession implements STTSession {
82
161
  on(event: "error", listener: (error: Error) => void): void;
83
162
  on(event: "close", listener: () => void): void;
84
163
  /** Queue a transcription of whatever audio is buffered (serialized). */
164
+ /**
165
+ * Queue a transcription of whatever audio is buffered (serialized). The
166
+ * clip boundary is frozen *now* — the moment the trailing silence was
167
+ * detected — so audio that arrives while this transcription is queued or
168
+ * in flight starts the next clip instead of being swept into this one.
169
+ */
85
170
  private flush;
86
171
  private takeBuffer;
87
172
  private transcribe;
@@ -92,3 +177,20 @@ export declare class OpenRouterSession implements STTSession {
92
177
  * The header is the standard 44-byte RIFF/WAVE layout.
93
178
  */
94
179
  export declare function toWav(pcm: Uint8Array, sampleRate: number): Uint8Array;
180
+ /**
181
+ * Mean RMS of linear16 PCM (mono), in the 0–1 sample range. Used as the
182
+ * clip-level energy floor: near-silence clips sit far below real speech.
183
+ */
184
+ export declare function pcmRms(pcm: Uint8Array): number;
185
+ /**
186
+ * Clean a whisper transcript of its most common hallucinations, returning
187
+ * "" when nothing worth speaking remains:
188
+ *
189
+ * 1. Asterisk stage directions ("*Dramatic music*", "*laughs*") are
190
+ * dropped — whisper transcribes background noise as stage notes.
191
+ * 2. Consecutive repeated sentences ("Thank you. Thank you.") collapse to
192
+ * one — a doubled filler is a classic near-silence artifact.
193
+ * 3. A transcript built entirely from filler phrases (the built-in
194
+ * multilingual list plus `extraFillers`) is dropped.
195
+ */
196
+ export declare function cleanTranscript(text: string, extraFillers?: string[]): string;
@@ -42,7 +42,7 @@ export declare class KokoroTTS implements TTS {
42
42
  private readonly chunkSize;
43
43
  private readonly streaming;
44
44
  private readonly fetchImpl;
45
- private abort;
45
+ private readonly streams;
46
46
  constructor(options?: KokoroOptions);
47
47
  stop(): void;
48
48
  stream(request: TTSRequest): AsyncGenerator<Uint8Array>;
@@ -7,19 +7,36 @@ export interface OpenRouterTTSOptions {
7
7
  /**
8
8
  * TTS model. Defaults to the free `fish-audio/s2.1-pro-free:free` variant —
9
9
  * fast, per-character priced. Pass `fish-audio/s2.1-pro` for the paid tier
10
- * (no free-variant rate limits).
10
+ * (no free-variant rate limits). Note the free variant is intermittently
11
+ * unavailable on OpenRouter (404 "No endpoints found").
11
12
  */
12
13
  model?: string;
13
14
  /**
14
- * Default voice, sent when the request carries none. Omitted entirely when
15
- * unset: providers with a built-in default voice (e.g. fish-audio) accept
16
- * omission, and some reject an explicit `voice` outright while providers
17
- * that require one (e.g. OpenAI TTS) need it set. Voice support varies by
18
- * model and provider.
15
+ * Default voice, sent when the request carries none. Set it for a
16
+ * consistent voice: the default fish model accepts exactly `"alloy"` and
17
+ * rejects other names, and omitting `voice` lets the free variant vary the
18
+ * voice per request. Models like OpenAI TTS require a voice. Voice support
19
+ * varies by model and provider.
19
20
  */
20
21
  voice?: string;
22
+ /**
23
+ * Output format used when the request doesn't specify one. `"pcm"` (the
24
+ * default) is the lowest-latency realtime format, but its sample rate is
25
+ * provider-defined and opaque — clients must know it. `"mp3"` is
26
+ * self-describing: decoders read the real rate from the stream.
27
+ */
28
+ format?: "pcm" | "mp3";
21
29
  /** Size of the audio chunks yielded from the response stream. Default 8192. */
22
30
  chunkSize?: number;
31
+ /**
32
+ * Abort a synthesis that delivers no audio bytes for this long (default
33
+ * 15000ms). A provider connection that goes silent — or never responds —
34
+ * would otherwise wedge the speech pipeline forever, since the pipeline
35
+ * holds one synthesis slot and its delivery chain until the stream ends.
36
+ * Any byte resets the clock; a genuinely slow synthesis just needs a
37
+ * response within this window.
38
+ */
39
+ idleTimeoutMs?: number;
23
40
  /** Injectable fetch, mainly for tests. */
24
41
  fetch?: FetchLike;
25
42
  }
@@ -36,9 +53,11 @@ export declare class OpenRouterTTS implements TTS {
36
53
  private readonly baseUrl;
37
54
  private readonly model;
38
55
  private readonly voice;
56
+ private readonly format;
39
57
  private readonly chunkSize;
58
+ private readonly idleTimeoutMs;
40
59
  private readonly fetchImpl;
41
- private abort;
60
+ private readonly streams;
42
61
  constructor(options: OpenRouterTTSOptions);
43
62
  stop(): void;
44
63
  stream(request: TTSRequest): AsyncGenerator<Uint8Array>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moureau/pipeflow",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "sideEffects": false,
@@ -28,6 +28,12 @@
28
28
  "require": "./dist/cjs/index.js",
29
29
  "default": "./dist/esm/index.js"
30
30
  },
31
+ "./client": {
32
+ "types": "./dist/types/client/index.d.ts",
33
+ "import": "./dist/esm/client/index.js",
34
+ "require": "./dist/cjs/client/index.js",
35
+ "default": "./dist/esm/client/index.js"
36
+ },
31
37
  "./providers": {
32
38
  "types": "./dist/types/providers/index.d.ts",
33
39
  "import": "./dist/esm/providers/index.js",