@pipedog/core 0.1.0 → 0.2.0
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.
- package/dist/audio/resamplers/engine.d.ts +60 -0
- package/dist/audio/resamplers/engine.js +89 -0
- package/dist/audio/resamplers/sinc_engine.d.ts +10 -0
- package/dist/audio/resamplers/sinc_engine.js +235 -0
- package/dist/audio/resamplers/soxr_resampler.d.ts +10 -6
- package/dist/audio/resamplers/soxr_resampler.js +17 -7
- package/dist/audio/resamplers/soxr_stream_resampler.d.ts +9 -6
- package/dist/audio/resamplers/soxr_stream_resampler.js +24 -11
- package/dist/audio/turn/smart_turn/local_smart_turn_v3.js +16 -8
- package/dist/audio/vad/vad_analyzer.d.ts +11 -3
- package/dist/audio/vad/vad_analyzer.js +72 -34
- package/dist/pipeline/worker.d.ts +7 -0
- package/dist/pipeline/worker.js +40 -23
- package/dist/utils/file_storage.d.ts +37 -21
- package/dist/utils/file_storage.js +87 -9
- package/dist/utils/frame_queue.js +2 -2
- package/dist/utils/image.d.ts +0 -1
- package/dist/utils/image.js +37 -4
- package/dist/utils/jpeg_encoder.d.ts +14 -0
- package/dist/utils/jpeg_encoder.js +315 -0
- package/dist/utils/text/transforms/acronyms.d.ts +21 -0
- package/dist/utils/text/transforms/acronyms.js +31 -0
- package/dist/utils/text/transforms/currency.d.ts +16 -0
- package/dist/utils/text/transforms/currency.js +56 -0
- package/dist/utils/text/transforms/dates.d.ts +18 -0
- package/dist/utils/text/transforms/dates.js +102 -0
- package/dist/utils/text/transforms/email.d.ts +17 -0
- package/dist/utils/text/transforms/email.js +35 -0
- package/dist/utils/text/transforms/index.d.ts +37 -0
- package/dist/utils/text/transforms/index.js +42 -0
- package/dist/utils/text/transforms/numbers.d.ts +25 -0
- package/dist/utils/text/transforms/numbers.js +59 -0
- package/dist/utils/text/transforms/percentages.d.ts +16 -0
- package/dist/utils/text/transforms/percentages.js +32 -0
- package/dist/utils/text/transforms/phone.d.ts +21 -0
- package/dist/utils/text/transforms/phone.js +39 -0
- package/dist/utils/text/transforms/pronunciations.js +2 -1
- package/dist/utils/text/transforms/replacements.d.ts +39 -0
- package/dist/utils/text/transforms/replacements.js +46 -0
- package/dist/utils/text/transforms/strip_markdown.d.ts +25 -0
- package/dist/utils/text/transforms/strip_markdown.js +60 -0
- package/dist/utils/text/transforms/units.d.ts +22 -0
- package/dist/utils/text/transforms/units.js +81 -0
- package/dist/utils/text/transforms/voice_formatter.d.ts +86 -0
- package/dist/utils/text/transforms/voice_formatter.js +98 -0
- package/dist/utils/text/unicode_digits.d.ts +3 -0
- package/dist/utils/text/unicode_digits.js +34 -0
- package/dist/utils/tracing/service_decorators.js +2 -2
- package/dist/utils/tracing/setup.d.ts +18 -6
- package/dist/utils/tracing/setup.js +63 -13
- package/dist/utils/tracing/turn_trace_observer.d.ts +5 -0
- package/dist/utils/tracing/turn_trace_observer.js +18 -4
- package/dist/utils/yaml.d.ts +151 -36
- package/dist/utils/yaml.js +527 -12
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workers/proxy/index.d.ts +3 -0
- package/dist/workers/proxy/index.js +9 -0
- package/dist/workers/proxy/websocket/client.d.ts +110 -0
- package/dist/workers/proxy/websocket/client.js +196 -0
- package/dist/workers/proxy/websocket/index.d.ts +3 -0
- package/dist/workers/proxy/websocket/index.js +10 -0
- package/package.json +18 -3
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Copyright (c) 2024-2026, Daily
|
|
2
|
+
// Copyright (c) 2026, pipedog contributors
|
|
3
|
+
// SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
//
|
|
5
|
+
// Ported from pipecat: src/pipecat/utils/text/transforms/voice_formatter.py
|
|
6
|
+
import { normalizeAcronyms } from "./acronyms.js";
|
|
7
|
+
import { expandCurrency } from "./currency.js";
|
|
8
|
+
import { normalizeDates } from "./dates.js";
|
|
9
|
+
import { emailToSpeech } from "./email.js";
|
|
10
|
+
import { expandNumbers } from "./numbers.js";
|
|
11
|
+
import { expandPercentages } from "./percentages.js";
|
|
12
|
+
import { expandPhoneNumbers } from "./phone.js";
|
|
13
|
+
import { replaceText } from "./replacements.js";
|
|
14
|
+
import { stripMarkdown } from "./strip_markdown.js";
|
|
15
|
+
import { expandUnits } from "./units.js";
|
|
16
|
+
/**
|
|
17
|
+
* Configurable bundle that applies a pipeline of voice-formatting transforms.
|
|
18
|
+
*
|
|
19
|
+
* Each option enables or disables one transform. Transforms are applied in a
|
|
20
|
+
* deliberate order: structural cleanup first, language expansions second, user
|
|
21
|
+
* replacements last.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const formatter = new VoiceFormatter({
|
|
26
|
+
* stripMarkdown: true,
|
|
27
|
+
* expandCurrency: true,
|
|
28
|
+
* numberDigitCutoff: 2025,
|
|
29
|
+
* customReplacements: [[/\bDr\./, "Doctor"]],
|
|
30
|
+
* });
|
|
31
|
+
* const tts = new CartesiaTTSService({
|
|
32
|
+
* textTransforms: [["*", formatter]],
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: adds Python's `__call__` signature to the class.
|
|
37
|
+
export class VoiceFormatter {
|
|
38
|
+
_transforms;
|
|
39
|
+
/** Initialize the voice formatter. */
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
const call = async (text, textType) => self.transform(text, textType);
|
|
42
|
+
const self = Object.setPrototypeOf(call, new.target.prototype);
|
|
43
|
+
self._transforms = [];
|
|
44
|
+
if (options.stripMarkdown ?? true) {
|
|
45
|
+
self._transforms.push(stripMarkdown);
|
|
46
|
+
}
|
|
47
|
+
// emailToSpeech must run before expandPhoneNumbers (phone regex matches
|
|
48
|
+
// digit-only domains) and before normalizeAcronyms (all-caps local parts get
|
|
49
|
+
// letter-spaced, breaking the email pattern).
|
|
50
|
+
if (options.emailToSpeech ?? true) {
|
|
51
|
+
self._transforms.push(emailToSpeech);
|
|
52
|
+
}
|
|
53
|
+
if (options.expandPhoneNumbers ?? true) {
|
|
54
|
+
self._transforms.push(expandPhoneNumbers);
|
|
55
|
+
}
|
|
56
|
+
if (options.normalizeDates ?? true) {
|
|
57
|
+
self._transforms.push(normalizeDates);
|
|
58
|
+
}
|
|
59
|
+
if (options.expandCurrency ?? true) {
|
|
60
|
+
self._transforms.push(expandCurrency);
|
|
61
|
+
}
|
|
62
|
+
if (options.expandPercentages ?? true) {
|
|
63
|
+
self._transforms.push(expandPercentages);
|
|
64
|
+
}
|
|
65
|
+
// expandUnits must run before normalizeAcronyms: uppercase unit abbreviations
|
|
66
|
+
// like "MB" or "MPH" would be letter-spaced first and then not recognized.
|
|
67
|
+
if (options.expandUnits ?? true) {
|
|
68
|
+
self._transforms.push(expandUnits);
|
|
69
|
+
}
|
|
70
|
+
if (options.normalizeAcronyms ?? true) {
|
|
71
|
+
self._transforms.push(normalizeAcronyms);
|
|
72
|
+
}
|
|
73
|
+
if (options.expandNumbers ?? false) {
|
|
74
|
+
self._transforms.push(expandNumbers(options.numberDigitCutoff ?? null));
|
|
75
|
+
}
|
|
76
|
+
if (options.customReplacements && options.customReplacements.length > 0) {
|
|
77
|
+
self._transforms.push(replaceText(options.customReplacements));
|
|
78
|
+
}
|
|
79
|
+
// biome-ignore lint/correctness/noConstructorReturn: returning the callable is the point (see above).
|
|
80
|
+
return self;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Apply all configured transforms in order.
|
|
84
|
+
*
|
|
85
|
+
* Python's `__call__`; calling the instance runs this.
|
|
86
|
+
*
|
|
87
|
+
* @param text Input text to transform.
|
|
88
|
+
* @param textType Text type passed through to each transform.
|
|
89
|
+
* @returns Transformed text ready for TTS synthesis.
|
|
90
|
+
*/
|
|
91
|
+
async transform(text, textType) {
|
|
92
|
+
for (const transform of this._transforms) {
|
|
93
|
+
text = await transform(text, textType);
|
|
94
|
+
}
|
|
95
|
+
return text;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=voice_formatter.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright (c) 2026, pipedog contributors
|
|
2
|
+
// SPDX-License-Identifier: BSD-2-Clause
|
|
3
|
+
//
|
|
4
|
+
// Python's `\\d` (in str patterns) matches any Unicode decimal digit, and
|
|
5
|
+
// `int()` / `float()` accept them: `int("٥٠") == 50`. JavaScript's `\\p{Nd}`
|
|
6
|
+
// matches the same characters, but `Number` / `BigInt` only take ASCII digits.
|
|
7
|
+
// `toAsciiDigits` converts first, so ported code parses what Python parses.
|
|
8
|
+
//
|
|
9
|
+
// Generated from Python 3.14.5 unicodedata (Unicode 16.0.0):
|
|
10
|
+
// the code points whose decimal value is 0; each starts a run of ten digits 0-9.
|
|
11
|
+
const ZERO_CODE_POINTS = [
|
|
12
|
+
0x30, 0x660, 0x6f0, 0x7c0, 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66, 0xce6, 0xd66, 0xde6,
|
|
13
|
+
0xe50, 0xed0, 0xf20, 0x1040, 0x1090, 0x17e0, 0x1810, 0x1946, 0x19d0, 0x1a80, 0x1a90, 0x1b50,
|
|
14
|
+
0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900, 0xa9d0, 0xa9f0, 0xaa50, 0xabf0, 0xff10, 0x104a0,
|
|
15
|
+
0x10d30, 0x10d40, 0x11066, 0x110f0, 0x11136, 0x111d0, 0x112f0, 0x11450, 0x114d0, 0x11650, 0x116c0,
|
|
16
|
+
0x116d0, 0x116da, 0x11730, 0x118e0, 0x11950, 0x11bf0, 0x11c50, 0x11d50, 0x11da0, 0x11f50, 0x16130,
|
|
17
|
+
0x16a60, 0x16ac0, 0x16b50, 0x16d70, 0x1ccf0, 0x1d7ce, 0x1d7d8, 0x1d7e2, 0x1d7ec, 0x1d7f6, 0x1e140,
|
|
18
|
+
0x1e2f0, 0x1e4f0, 0x1e5f1, 0x1e950, 0x1fbf0,
|
|
19
|
+
];
|
|
20
|
+
const DIGIT_VALUE = new Map();
|
|
21
|
+
for (const zero of ZERO_CODE_POINTS) {
|
|
22
|
+
for (let i = 0; i < 10; i++)
|
|
23
|
+
DIGIT_VALUE.set(zero + i, i);
|
|
24
|
+
}
|
|
25
|
+
/** Replace every Unicode decimal digit with its ASCII digit (`"٥٠"` → `"50"`). */
|
|
26
|
+
export function toAsciiDigits(text) {
|
|
27
|
+
let out = "";
|
|
28
|
+
for (const ch of text) {
|
|
29
|
+
const value = DIGIT_VALUE.get(ch.codePointAt(0));
|
|
30
|
+
out += value === undefined ? ch : String(value);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=unicode_digits.js.map
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Porting notes:
|
|
14
14
|
* - Upstream decides at import time whether tracing is available and returns
|
|
15
|
-
* a no-op decorator otherwise. Here OpenTelemetry is
|
|
16
|
-
* (`
|
|
15
|
+
* a no-op decorator otherwise. Here OpenTelemetry is imported asynchronously
|
|
16
|
+
* or registered at runtime (see `setup.ts`), possibly after service classes are defined,
|
|
17
17
|
* so the wrappers check `isTracingAvailable()` when they run instead. With
|
|
18
18
|
* tracing unavailable they call straight through, as upstream's no-op does.
|
|
19
19
|
* - Every decorator works three ways:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type OtelAttributeValue = string | number | boolean |
|
|
1
|
+
export type OtelAttributeValue = string | number | boolean | ReadonlyArray<string | null | undefined> | ReadonlyArray<number | null | undefined> | ReadonlyArray<boolean | null | undefined>;
|
|
2
2
|
/** `SpanContext` from `@opentelemetry/api`. */
|
|
3
3
|
export interface OtelSpanContext {
|
|
4
4
|
traceId: string;
|
|
@@ -35,10 +35,10 @@ export interface OtelApi {
|
|
|
35
35
|
/** The `@opentelemetry/sdk-trace-base` module (v2). */
|
|
36
36
|
export interface OtelSdkTraceBase {
|
|
37
37
|
BasicTracerProvider: new (config?: {
|
|
38
|
-
resource?:
|
|
39
|
-
spanProcessors?:
|
|
38
|
+
resource?: any;
|
|
39
|
+
spanProcessors?: any[];
|
|
40
40
|
}) => unknown;
|
|
41
|
-
BatchSpanProcessor: new (exporter:
|
|
41
|
+
BatchSpanProcessor: new (exporter: any) => unknown;
|
|
42
42
|
ConsoleSpanExporter: new () => unknown;
|
|
43
43
|
}
|
|
44
44
|
/** The `@opentelemetry/resources` module (v2). */
|
|
@@ -55,8 +55,20 @@ export interface OpenTelemetryModules {
|
|
|
55
55
|
resources?: OtelResources | null;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* Wait for the automatic `import("@opentelemetry/api")` started when core
|
|
59
|
+
* loaded, and report whether tracing is available.
|
|
60
|
+
*
|
|
61
|
+
* `PipelineWorker` awaits this in its async setup, so a worker built before
|
|
62
|
+
* the import finished (e.g. at module top level) still traces; the turn trace
|
|
63
|
+
* observer and the service decorators check availability when they run.
|
|
64
|
+
*
|
|
65
|
+
* @returns True if tracing is available, False otherwise.
|
|
66
|
+
*/
|
|
67
|
+
export declare function loadOpenTelemetry(): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Make OpenTelemetry available to Pipecat explicitly, instead of (or before)
|
|
70
|
+
* the automatic import of `@opentelemetry/api`. Pass `null` to disable
|
|
71
|
+
* tracing again.
|
|
60
72
|
*/
|
|
61
73
|
export declare function registerOpenTelemetry(modules: OpenTelemetryModules | null): void;
|
|
62
74
|
/** The registered OpenTelemetry modules, or null if tracing is unavailable. */
|
|
@@ -10,33 +10,83 @@
|
|
|
10
10
|
* OpenTelemetry tracing for Pipecat applications. It handles the optional
|
|
11
11
|
* nature of OpenTelemetry dependencies and provides a safe setup process.
|
|
12
12
|
*
|
|
13
|
-
* Porting notes: upstream detects OpenTelemetry by trying to import it
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Porting notes: upstream detects OpenTelemetry by trying to import it, and
|
|
14
|
+
* treats it as an optional extra (`pipecat-ai[tracing]`). Here
|
|
15
|
+
* `@opentelemetry/api` is an optional peer dependency of `@pipedog/core`:
|
|
16
|
+
*
|
|
17
|
+
* - When this module loads it starts a dynamic `import("@opentelemetry/api")`
|
|
18
|
+
* (no top-level await, so importing core never waits or throws). If the
|
|
19
|
+
* package is not installed the import fails quietly and tracing stays
|
|
20
|
+
* unavailable, exactly like upstream's `OPENTELEMETRY_AVAILABLE = False`.
|
|
21
|
+
* Because the import is asynchronous, `isTracingAvailable()` turns true a
|
|
22
|
+
* moment after core loads; `await loadOpenTelemetry()` waits for it.
|
|
23
|
+
* Nothing latches the early "unavailable": `PipelineWorker` awaits it in
|
|
24
|
+
* its async setup before creating the tracing observers.
|
|
25
|
+
* - `registerOpenTelemetry()` hands the modules in explicitly and
|
|
26
|
+
* synchronously (it wins over the automatic import). `@pipedog/tracing`'s
|
|
27
|
+
* `setupTracing()` does this, also registering `@opentelemetry/sdk-trace-base`
|
|
28
|
+
* and `@opentelemetry/resources`, which this module's `setupTracing()` needs.
|
|
29
|
+
*
|
|
30
|
+
* Without OpenTelemetry every tracing helper is a no-op, as upstream behaves.
|
|
16
31
|
*
|
|
17
32
|
* @example
|
|
18
33
|
* import * as api from "@opentelemetry/api";
|
|
19
34
|
* import * as sdk from "@opentelemetry/sdk-trace-base";
|
|
20
35
|
* import * as resources from "@opentelemetry/resources";
|
|
21
|
-
* import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
22
36
|
*
|
|
23
37
|
* registerOpenTelemetry({ api, sdk, resources });
|
|
24
|
-
* setupTracing({
|
|
25
|
-
* serviceName: "my-service",
|
|
26
|
-
* exporter: new OTLPTraceExporter({ url: "http://localhost:4318/v1/traces" }),
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* Until `registerOpenTelemetry()` is called, `isTracingAvailable()` is false and
|
|
30
|
-
* every tracing helper is a no-op, as upstream behaves without OpenTelemetry.
|
|
38
|
+
* setupTracing({ serviceName: "my-service", exporter: new sdk.ConsoleSpanExporter() });
|
|
31
39
|
*/
|
|
32
40
|
import { rethrowIfCancelled } from "@pipedog/asyncio";
|
|
33
41
|
import { logger } from "../logger.js";
|
|
34
42
|
let openTelemetry = null;
|
|
43
|
+
/** Set once `registerOpenTelemetry()` is called; the automatic import then leaves things alone. */
|
|
44
|
+
let registeredExplicitly = false;
|
|
45
|
+
/**
|
|
46
|
+
* Import `@opentelemetry/api` if it is installed (upstream's optional import).
|
|
47
|
+
*
|
|
48
|
+
* The specifier is a string literal so bundlers (esbuild/wrangler, Bun.build)
|
|
49
|
+
* bundle the package when it is installed; when it is not, they leave the
|
|
50
|
+
* `import()` in place and it rejects at runtime, which is caught here.
|
|
51
|
+
*/
|
|
52
|
+
async function importOpenTelemetryApi() {
|
|
53
|
+
try {
|
|
54
|
+
const mod = (await import("@opentelemetry/api"));
|
|
55
|
+
// CommonJS interop (some bundlers) puts the exports on `default`.
|
|
56
|
+
const api = mod?.trace ? mod : mod?.default;
|
|
57
|
+
return api?.trace && api.context ? api : null;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
rethrowIfCancelled(e);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const autoLoad = importOpenTelemetryApi().then((api) => {
|
|
65
|
+
if (api && !registeredExplicitly) {
|
|
66
|
+
openTelemetry = { api };
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Wait for the automatic `import("@opentelemetry/api")` started when core
|
|
71
|
+
* loaded, and report whether tracing is available.
|
|
72
|
+
*
|
|
73
|
+
* `PipelineWorker` awaits this in its async setup, so a worker built before
|
|
74
|
+
* the import finished (e.g. at module top level) still traces; the turn trace
|
|
75
|
+
* observer and the service decorators check availability when they run.
|
|
76
|
+
*
|
|
77
|
+
* @returns True if tracing is available, False otherwise.
|
|
78
|
+
*/
|
|
79
|
+
export async function loadOpenTelemetry() {
|
|
80
|
+
await autoLoad;
|
|
81
|
+
return isTracingAvailable();
|
|
82
|
+
}
|
|
35
83
|
/**
|
|
36
|
-
* Make OpenTelemetry available to Pipecat (
|
|
37
|
-
* Pass `null` to disable
|
|
84
|
+
* Make OpenTelemetry available to Pipecat explicitly, instead of (or before)
|
|
85
|
+
* the automatic import of `@opentelemetry/api`. Pass `null` to disable
|
|
86
|
+
* tracing again.
|
|
38
87
|
*/
|
|
39
88
|
export function registerOpenTelemetry(modules) {
|
|
89
|
+
registeredExplicitly = true;
|
|
40
90
|
openTelemetry = modules;
|
|
41
91
|
}
|
|
42
92
|
/** The registered OpenTelemetry modules, or null if tracing is unavailable. */
|
|
@@ -38,6 +38,11 @@ export declare class TurnTraceObserver extends BaseObserver {
|
|
|
38
38
|
protected _additionalSpanAttributes: Record<string, OtelAttributeValue>;
|
|
39
39
|
/** Initialize the turn trace observer. */
|
|
40
40
|
constructor(options: TurnTraceObserverOptions);
|
|
41
|
+
/**
|
|
42
|
+
* The turn tracer, resolved on first use if OpenTelemetry was not available
|
|
43
|
+
* yet when the observer was built (core imports it asynchronously).
|
|
44
|
+
*/
|
|
45
|
+
protected _getTracer(): OtelTracer | null;
|
|
41
46
|
/**
|
|
42
47
|
* Handle latency measurement events.
|
|
43
48
|
*
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* This module provides an observer that creates trace spans for each
|
|
10
10
|
* conversation turn, integrating with the turn tracking system to provide
|
|
11
11
|
* hierarchical tracing of conversation flows.
|
|
12
|
+
*
|
|
13
|
+
* Porting notes: upstream gets the tracer in `__init__`. Core imports
|
|
14
|
+
* OpenTelemetry asynchronously, so an observer built before that import
|
|
15
|
+
* finished gets its tracer on first use instead (`_getTracer()`).
|
|
12
16
|
*/
|
|
13
17
|
import { StartFrame } from "../../frames/frames.js";
|
|
14
18
|
import { BaseObserver, } from "../../observers/base_observer.js";
|
|
@@ -56,6 +60,14 @@ export class TurnTraceObserver extends BaseObserver {
|
|
|
56
60
|
await this._handleLatencyMeasured(latencySeconds);
|
|
57
61
|
});
|
|
58
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The turn tracer, resolved on first use if OpenTelemetry was not available
|
|
65
|
+
* yet when the observer was built (core imports it asynchronously).
|
|
66
|
+
*/
|
|
67
|
+
_getTracer() {
|
|
68
|
+
this._tracer ??= getOpenTelemetry()?.api.trace.getTracer("pipecat.turn") ?? null;
|
|
69
|
+
return this._tracer;
|
|
70
|
+
}
|
|
59
71
|
/**
|
|
60
72
|
* Handle latency measurement events.
|
|
61
73
|
*
|
|
@@ -90,7 +102,8 @@ export class TurnTraceObserver extends BaseObserver {
|
|
|
90
102
|
* @param conversationId Optional custom ID for the conversation. If null, a UUID will be generated.
|
|
91
103
|
*/
|
|
92
104
|
startConversationTracing(conversationId = null) {
|
|
93
|
-
|
|
105
|
+
const tracer = this._getTracer();
|
|
106
|
+
if (!isTracingAvailable() || !tracer) {
|
|
94
107
|
return;
|
|
95
108
|
}
|
|
96
109
|
// Generate a conversation ID if not provided
|
|
@@ -100,7 +113,7 @@ export class TurnTraceObserver extends BaseObserver {
|
|
|
100
113
|
}
|
|
101
114
|
this._conversationId = conversationId;
|
|
102
115
|
// Create a new span for this conversation
|
|
103
|
-
const conversationSpan =
|
|
116
|
+
const conversationSpan = tracer.startSpan("conversation");
|
|
104
117
|
this._conversationSpan = conversationSpan;
|
|
105
118
|
// Set span attributes
|
|
106
119
|
conversationSpan.setAttribute("conversation.id", conversationId);
|
|
@@ -142,7 +155,8 @@ export class TurnTraceObserver extends BaseObserver {
|
|
|
142
155
|
}
|
|
143
156
|
/** Handle a turn start event by creating a new span. */
|
|
144
157
|
async _handleTurnStarted(turnNumber) {
|
|
145
|
-
|
|
158
|
+
const tracer = this._getTracer();
|
|
159
|
+
if (!isTracingAvailable() || !tracer) {
|
|
146
160
|
return;
|
|
147
161
|
}
|
|
148
162
|
if (turnNumber === 1 && !this._conversationSpan) {
|
|
@@ -154,7 +168,7 @@ export class TurnTraceObserver extends BaseObserver {
|
|
|
154
168
|
parentContext = this._tracingContext.getConversationContext();
|
|
155
169
|
}
|
|
156
170
|
// Create a new span for this turn
|
|
157
|
-
const currentSpan =
|
|
171
|
+
const currentSpan = tracer.startSpan("turn", undefined, parentContext ?? undefined);
|
|
158
172
|
this._currentSpan = currentSpan;
|
|
159
173
|
this._currentTurnNumber = turnNumber;
|
|
160
174
|
// Set span attributes
|
package/dist/utils/yaml.d.ts
CHANGED
|
@@ -1,37 +1,138 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* judge: !include judge_audio.yaml
|
|
10
|
-
* task_messages: !include prompts/greeting.yaml
|
|
11
|
-
* ```
|
|
12
|
-
*
|
|
13
|
-
* Include paths are not confined to the base directory; `..` and absolute
|
|
14
|
-
* paths reach anywhere the process can read. Use these loaders only on
|
|
15
|
-
* trusted documents.
|
|
16
|
-
*
|
|
17
|
-
* Not ported yet: Upstream builds on PyYAML loader classes and reads
|
|
18
|
-
* included files from disk. Core takes no YAML dependency and has no
|
|
19
|
-
* filesystem access (Workers), so only the interface is here; both functions
|
|
20
|
-
* throw until a YAML parser and file reader are wired in.
|
|
21
|
-
*/
|
|
22
|
-
/** A YAML node, as handed to a constructor. */
|
|
23
|
-
export interface YamlNode {
|
|
24
|
-
/** Whether the node is a scalar (as opposed to a sequence or mapping). */
|
|
25
|
-
readonly isScalar: boolean;
|
|
26
|
-
/** The scalar value, for scalar nodes. */
|
|
27
|
-
readonly value?: unknown;
|
|
1
|
+
/** Position of a node in its document (0-based line and column, like PyYAML's `Mark`). */
|
|
2
|
+
export interface YamlMark {
|
|
3
|
+
line: number;
|
|
4
|
+
column: number;
|
|
5
|
+
}
|
|
6
|
+
/** Base class of the errors raised while loading YAML (`yaml.YAMLError`). */
|
|
7
|
+
export declare class YAMLError extends Error {
|
|
8
|
+
name: string;
|
|
28
9
|
}
|
|
10
|
+
/** A YAML error tied to a place in the document (`yaml.MarkedYAMLError`). */
|
|
11
|
+
export declare class MarkedYAMLError extends YAMLError {
|
|
12
|
+
name: string;
|
|
13
|
+
readonly problem: string;
|
|
14
|
+
readonly problemMark: YamlMark | null;
|
|
15
|
+
constructor(problem: string, problemMark?: YamlMark | null);
|
|
16
|
+
}
|
|
17
|
+
/** The document is not valid YAML (`yaml.parser.ParserError` / `yaml.scanner.ScannerError`). */
|
|
18
|
+
export declare class ParserError extends MarkedYAMLError {
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
/** A node could not be turned into a value (`yaml.constructor.ConstructorError`). */
|
|
22
|
+
export declare class ConstructorError extends MarkedYAMLError {
|
|
23
|
+
name: string;
|
|
24
|
+
}
|
|
25
|
+
/** A scalar node (`yaml.ScalarNode`). `value` is its text. */
|
|
26
|
+
export declare class ScalarNode {
|
|
27
|
+
readonly isScalar = true;
|
|
28
|
+
readonly tag: string;
|
|
29
|
+
readonly value: string;
|
|
30
|
+
readonly startMark: YamlMark | null;
|
|
31
|
+
/** How the scalar was written: `null` when plain, else `'`, `"`, `|` or `>`. */
|
|
32
|
+
readonly style: string | null;
|
|
33
|
+
constructor(tag: string, value: string, startMark: YamlMark | null, style?: string | null);
|
|
34
|
+
}
|
|
35
|
+
/** A sequence node (`yaml.SequenceNode`). */
|
|
36
|
+
export declare class SequenceNode {
|
|
37
|
+
readonly isScalar = false;
|
|
38
|
+
readonly tag: string;
|
|
39
|
+
readonly value: YamlNode[];
|
|
40
|
+
readonly startMark: YamlMark | null;
|
|
41
|
+
constructor(tag: string, value: YamlNode[], startMark: YamlMark | null);
|
|
42
|
+
}
|
|
43
|
+
/** A mapping node (`yaml.MappingNode`). `value` holds the key and value nodes in order. */
|
|
44
|
+
export declare class MappingNode {
|
|
45
|
+
readonly isScalar = false;
|
|
46
|
+
readonly tag: string;
|
|
47
|
+
value: [YamlNode, YamlNode][];
|
|
48
|
+
readonly startMark: YamlMark | null;
|
|
49
|
+
constructor(tag: string, value: [YamlNode, YamlNode][], startMark: YamlMark | null);
|
|
50
|
+
}
|
|
51
|
+
/** A YAML node, as handed to a constructor. */
|
|
52
|
+
export type YamlNode = ScalarNode | SequenceNode | MappingNode;
|
|
29
53
|
/** A tag constructor registered on a loader class. */
|
|
30
|
-
export type YamlConstructor = (loader:
|
|
54
|
+
export type YamlConstructor = (loader: SafeLoader, node: YamlNode) => unknown;
|
|
55
|
+
/**
|
|
56
|
+
* Implicit resolvers by the first character of the plain scalars they apply
|
|
57
|
+
* to (`""` for the empty scalar, `null` for any): `[tag, pattern]` pairs,
|
|
58
|
+
* tried in order.
|
|
59
|
+
*/
|
|
60
|
+
export type YamlImplicitResolvers = Map<string | null, [string, RegExp][]>;
|
|
31
61
|
/** The loader-class shape the helpers work with (a `yaml.SafeLoader` subclass upstream). */
|
|
32
62
|
export interface YamlLoaderClass {
|
|
63
|
+
new (stream: string): SafeLoader;
|
|
64
|
+
yamlConstructors: Map<string, YamlConstructor>;
|
|
65
|
+
yamlImplicitResolvers: YamlImplicitResolvers;
|
|
33
66
|
/** Register a constructor for a tag on this loader class. */
|
|
34
67
|
addConstructor(tag: string, fn: YamlConstructor): void;
|
|
68
|
+
/** Register an implicit resolver on this loader class. */
|
|
69
|
+
addImplicitResolver(tag: string, regexp: RegExp, first: Iterable<string> | null): void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The safe loader (`yaml.SafeLoader`): loads standard YAML 1.1 tags into
|
|
73
|
+
* plain values. Subclass it and register constructors or resolvers on the
|
|
74
|
+
* subclass to extend it; registrations never reach the parent class.
|
|
75
|
+
*/
|
|
76
|
+
export declare class SafeLoader {
|
|
77
|
+
#private;
|
|
78
|
+
static yamlConstructors: Map<string, YamlConstructor>;
|
|
79
|
+
static yamlImplicitResolvers: YamlImplicitResolvers;
|
|
80
|
+
/** Register `fn` as the constructor for `tag` on this class (copying the parent's table first). */
|
|
81
|
+
static addConstructor(tag: string, fn: YamlConstructor): void;
|
|
82
|
+
/**
|
|
83
|
+
* Resolve plain scalars matching `regexp` (and starting with one of `first`,
|
|
84
|
+
* or any character when null) to `tag` on this class.
|
|
85
|
+
*/
|
|
86
|
+
static addImplicitResolver(tag: string, regexp: RegExp, first: Iterable<string> | null): void;
|
|
87
|
+
constructor(stream: string);
|
|
88
|
+
/** Load the stream's single document (`get_single_data`); `null` for an empty stream. */
|
|
89
|
+
getSingleData(): unknown;
|
|
90
|
+
/** Compose the stream's single document into nodes, resolving tags. */
|
|
91
|
+
getSingleNode(): YamlNode | null;
|
|
92
|
+
/** The tag a plain scalar resolves to under this class's implicit resolvers. */
|
|
93
|
+
resolveScalar(value: string): string;
|
|
94
|
+
/** Construct the value of a whole document. */
|
|
95
|
+
constructDocument(node: YamlNode): unknown;
|
|
96
|
+
/** Construct the value of `node` with the constructor registered for its tag. */
|
|
97
|
+
constructObject(node: YamlNode): unknown;
|
|
98
|
+
/** The text of a scalar node. */
|
|
99
|
+
constructScalar(node: YamlNode): string;
|
|
100
|
+
/** The values of a sequence node. */
|
|
101
|
+
constructSequence(node: YamlNode): unknown[];
|
|
102
|
+
/**
|
|
103
|
+
* The entries of a mapping node, as an object, after applying `<<` merge
|
|
104
|
+
* keys. Keys are scalars; a sequence or mapping key is an error, as an
|
|
105
|
+
* unhashable key is upstream.
|
|
106
|
+
*/
|
|
107
|
+
constructMapping(node: YamlNode): Record<string, unknown>;
|
|
108
|
+
/** The entries of a sequence of one-entry mappings (`!!omap`, `!!pairs`) as `[key, value]` pairs. */
|
|
109
|
+
constructPairs(node: YamlNode): [unknown, unknown][];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Parse the single YAML document in `stream` (`yaml.load(stream, Loader)`).
|
|
113
|
+
*
|
|
114
|
+
* @param stream The YAML text.
|
|
115
|
+
* @param Loader The loader class to load with. Defaults to `SafeLoader`.
|
|
116
|
+
*/
|
|
117
|
+
export declare function load(stream: string, Loader?: YamlLoaderClass): unknown;
|
|
118
|
+
/**
|
|
119
|
+
* Register an implicit resolver (`yaml.add_implicit_resolver`) on `Loader`.
|
|
120
|
+
*
|
|
121
|
+
* @param tag The tag plain scalars matching `regexp` resolve to.
|
|
122
|
+
* @param regexp The pattern a plain scalar must match.
|
|
123
|
+
* @param first The characters such scalars start with, or null for any.
|
|
124
|
+
*/
|
|
125
|
+
export declare function addImplicitResolver(tag: string, regexp: RegExp, first?: Iterable<string> | null, options?: {
|
|
126
|
+
Loader?: YamlLoaderClass;
|
|
127
|
+
}): void;
|
|
128
|
+
/**
|
|
129
|
+
* Reads an included file as UTF-8 text. Throws if the file can't be read
|
|
130
|
+
* (Node's `ENOENT` error is upstream's `FileNotFoundError`).
|
|
131
|
+
*/
|
|
132
|
+
export type YamlFileReader = (path: string) => string;
|
|
133
|
+
export interface IncludeOptions {
|
|
134
|
+
/** Reads `!include`d files, e.g. `(p) => readFileSync(p, "utf8")`. */
|
|
135
|
+
readFile: YamlFileReader;
|
|
35
136
|
}
|
|
36
137
|
/**
|
|
37
138
|
* Register an `!include <relative-path>` constructor on `loaderClass`.
|
|
@@ -41,24 +142,38 @@ export interface YamlLoaderClass {
|
|
|
41
142
|
* resolve against `baseDir` and may reach outside it.
|
|
42
143
|
*
|
|
43
144
|
* This mutates `loaderClass`. Register on a private subclass rather than on
|
|
44
|
-
*
|
|
145
|
+
* `SafeLoader` itself so the constructor has no global side effects;
|
|
45
146
|
* `includeLoader` does that for you.
|
|
46
147
|
*
|
|
47
148
|
* @param loaderClass The loader class to register the constructor on.
|
|
48
149
|
* @param baseDir Directory that `!include` paths resolve against.
|
|
150
|
+
* @param options How to read the included files.
|
|
49
151
|
*/
|
|
50
|
-
export declare function addIncludeConstructor(
|
|
152
|
+
export declare function addIncludeConstructor(loaderClass: YamlLoaderClass, baseDir: string, options: IncludeOptions): void;
|
|
153
|
+
export interface IncludeLoaderOptions extends IncludeOptions {
|
|
154
|
+
/**
|
|
155
|
+
* Loader class to subclass. Defaults to `SafeLoader`; pass a custom
|
|
156
|
+
* `SafeLoader` subclass to keep its resolvers and constructors.
|
|
157
|
+
*/
|
|
158
|
+
base?: YamlLoaderClass;
|
|
159
|
+
}
|
|
51
160
|
/**
|
|
52
161
|
* Build a loader class that resolves `!include` relative to `baseDir`.
|
|
53
162
|
*
|
|
54
|
-
* Returns a fresh subclass of `base` with the include constructor
|
|
55
|
-
* so `base` itself is left untouched.
|
|
163
|
+
* Returns a fresh subclass of `base` with the include constructor
|
|
164
|
+
* registered, so `base` itself is left untouched.
|
|
56
165
|
*
|
|
57
166
|
* @param baseDir Directory that `!include` paths resolve against, typically
|
|
58
167
|
* the directory of the document being loaded.
|
|
59
|
-
* @param
|
|
60
|
-
*
|
|
61
|
-
*
|
|
168
|
+
* @param options How to read included files, and the loader class to subclass.
|
|
169
|
+
* @returns A loader class to pass to `load`.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* const data = load(readFileSync(path, "utf8"), includeLoader(dirname(path), {
|
|
174
|
+
* readFile: (p) => readFileSync(p, "utf8"),
|
|
175
|
+
* }));
|
|
176
|
+
* ```
|
|
62
177
|
*/
|
|
63
|
-
export declare function includeLoader(
|
|
178
|
+
export declare function includeLoader(baseDir: string, options: IncludeLoaderOptions): YamlLoaderClass;
|
|
64
179
|
//# sourceMappingURL=yaml.d.ts.map
|