@broberg/ai-sdk 0.42.1 → 0.44.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/index.d.ts CHANGED
@@ -61,6 +61,24 @@ interface Pronunciation {
61
61
  word: string;
62
62
  /** Say this instead. Azure `<sub alias>`; ElevenLabs plain substitution. */
63
63
  alias?: string;
64
+ /** Also match INSIDE a hyphenated compound — `AI` in `AI-agenter` (F051.3).
65
+ *
66
+ * Danish puts abbreviations there constantly. cms measured their own articles:
67
+ * "AI-agenter" 13 times, "AI-native" 12, 60+ occurrences in all, every one read aloud
68
+ * as one mangled word. Christian heard it as "HTLM" and asked why AI was not spelled
69
+ * out too — it WAS, just never in a compound.
70
+ *
71
+ * **Per entry, and defaulting to `false`, because the right answer depends on the
72
+ * word.** `AI` yes; `mail` no — a rule for `mail` must not fire inside `e-mail`.
73
+ * Measured rather than reasoned: `@broberg/speech-dictionary` ships the general
74
+ * loosening and pays exactly that price today. A single global answer is wrong for
75
+ * one of the two classes whichever way it is set, so the person who wrote the entry
76
+ * decides.
77
+ *
78
+ * Default `false` is also the safe direction to be wrong in: flipping it would change
79
+ * the audio for every existing consumer IN SOUND, where there is no compile error, no
80
+ * red test and no log line to notice it by. */
81
+ matchInCompounds?: boolean;
64
82
  /** IPA phonemes. Azure `<phoneme alphabet="ipa" ph>`. ElevenLabs cannot do this
65
83
  * (it THROWS rather than silently skipping). See the type doc above before reaching
66
84
  * for this on a foreign word. */
@@ -461,6 +479,11 @@ interface DialogueTurn {
461
479
  }
462
480
  interface DialogueRequest {
463
481
  inputs: DialogueTurn[];
482
+ /** F051.4 — the pronunciation dictionary, applied to EACH line. cms measured the
483
+ * gap: a two-host podcast could not get a dictionary at all, so "broberg.ai" was
484
+ * said wrong in every episode — they could fix the sponsor read and the host's
485
+ * hand-off (both `ai.tts`) and not the conversation, which is 95% of the audio. */
486
+ pronunciations?: Pronunciation[];
464
487
  /** Output container, e.g. "mp3" (default). */
465
488
  format?: string;
466
489
  spec: TierSpec;
@@ -1819,6 +1842,29 @@ declare const podcastInputSchema: z.ZodObject<{
1819
1842
  }>, "many">;
1820
1843
  voices: z.ZodRecord<z.ZodString, z.ZodString>;
1821
1844
  format: z.ZodOptional<z.ZodString>;
1845
+ /** F051.4 — same field, same semantics as tts. Applied PER LINE: ElevenLabs'
1846
+ * /text-to-dialogue takes inputs[].text separately, so there is no composed string
1847
+ * a replacement could run across a speaker boundary in. */
1848
+ pronunciations: z.ZodOptional<z.ZodArray<z.ZodObject<{
1849
+ word: z.ZodString;
1850
+ alias: z.ZodOptional<z.ZodString>;
1851
+ ipa: z.ZodOptional<z.ZodString>;
1852
+ lang: z.ZodOptional<z.ZodString>;
1853
+ /** F051.3 — also match inside a hyphenated compound ("AI" in "AI-agenter"). */
1854
+ matchInCompounds: z.ZodOptional<z.ZodBoolean>;
1855
+ }, "strip", z.ZodTypeAny, {
1856
+ word: string;
1857
+ matchInCompounds?: boolean | undefined;
1858
+ alias?: string | undefined;
1859
+ ipa?: string | undefined;
1860
+ lang?: string | undefined;
1861
+ }, {
1862
+ word: string;
1863
+ matchInCompounds?: boolean | undefined;
1864
+ alias?: string | undefined;
1865
+ ipa?: string | undefined;
1866
+ lang?: string | undefined;
1867
+ }>, "many">>;
1822
1868
  }, "strip", z.ZodTypeAny, {
1823
1869
  script: {
1824
1870
  text: string;
@@ -1839,6 +1885,13 @@ declare const podcastInputSchema: z.ZodObject<{
1839
1885
  })[] | undefined;
1840
1886
  labels?: Record<string, string> | undefined;
1841
1887
  format?: string | undefined;
1888
+ pronunciations?: {
1889
+ word: string;
1890
+ matchInCompounds?: boolean | undefined;
1891
+ alias?: string | undefined;
1892
+ ipa?: string | undefined;
1893
+ lang?: string | undefined;
1894
+ }[] | undefined;
1842
1895
  }, {
1843
1896
  script: {
1844
1897
  text: string;
@@ -1859,6 +1912,13 @@ declare const podcastInputSchema: z.ZodObject<{
1859
1912
  })[] | undefined;
1860
1913
  labels?: Record<string, string> | undefined;
1861
1914
  format?: string | undefined;
1915
+ pronunciations?: {
1916
+ word: string;
1917
+ matchInCompounds?: boolean | undefined;
1918
+ alias?: string | undefined;
1919
+ ipa?: string | undefined;
1920
+ lang?: string | undefined;
1921
+ }[] | undefined;
1862
1922
  }>;
1863
1923
  declare const ttsInputSchema: z.ZodObject<{
1864
1924
  tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
@@ -1899,13 +1959,17 @@ declare const ttsInputSchema: z.ZodObject<{
1899
1959
  alias: z.ZodOptional<z.ZodString>;
1900
1960
  ipa: z.ZodOptional<z.ZodString>;
1901
1961
  lang: z.ZodOptional<z.ZodString>;
1962
+ /** F051.3 — also match inside a hyphenated compound ("AI" in "AI-agenter"). */
1963
+ matchInCompounds: z.ZodOptional<z.ZodBoolean>;
1902
1964
  }, "strip", z.ZodTypeAny, {
1903
1965
  word: string;
1966
+ matchInCompounds?: boolean | undefined;
1904
1967
  alias?: string | undefined;
1905
1968
  ipa?: string | undefined;
1906
1969
  lang?: string | undefined;
1907
1970
  }, {
1908
1971
  word: string;
1972
+ matchInCompounds?: boolean | undefined;
1909
1973
  alias?: string | undefined;
1910
1974
  ipa?: string | undefined;
1911
1975
  lang?: string | undefined;
@@ -1940,10 +2004,11 @@ declare const ttsInputSchema: z.ZodObject<{
1940
2004
  transport: "http" | "subprocess";
1941
2005
  })[] | undefined;
1942
2006
  labels?: Record<string, string> | undefined;
1943
- format?: string | undefined;
1944
2007
  lang?: string | undefined;
2008
+ format?: string | undefined;
1945
2009
  pronunciations?: {
1946
2010
  word: string;
2011
+ matchInCompounds?: boolean | undefined;
1947
2012
  alias?: string | undefined;
1948
2013
  ipa?: string | undefined;
1949
2014
  lang?: string | undefined;
@@ -1966,10 +2031,11 @@ declare const ttsInputSchema: z.ZodObject<{
1966
2031
  transport: "http" | "subprocess";
1967
2032
  })[] | undefined;
1968
2033
  labels?: Record<string, string> | undefined;
1969
- format?: string | undefined;
1970
2034
  lang?: string | undefined;
2035
+ format?: string | undefined;
1971
2036
  pronunciations?: {
1972
2037
  word: string;
2038
+ matchInCompounds?: boolean | undefined;
1973
2039
  alias?: string | undefined;
1974
2040
  ipa?: string | undefined;
1975
2041
  lang?: string | undefined;
@@ -2366,8 +2432,8 @@ declare const falStubAdapter: ProviderAdapter;
2366
2432
  * wires the live adapters. */
2367
2433
  declare const stubProviders: Record<string, ProviderAdapter>;
2368
2434
 
2369
- declare const VERSION: "0.42.1";
2370
- declare const SDK_TAG: "@broberg/ai-sdk@0.42.1";
2435
+ declare const VERSION: "0.44.0";
2436
+ declare const SDK_TAG: "@broberg/ai-sdk@0.44.0";
2371
2437
 
2372
2438
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
2373
2439
  * per-call override.
package/dist/index.js CHANGED
@@ -1557,10 +1557,14 @@ function applyPronunciations(haystack, list, render, escape = (s) => s) {
1557
1557
  if (!byLower.has(k)) byLower.set(k, p);
1558
1558
  }
1559
1559
  const alternation = sorted.map((p) => escapeRegex(escape(p.word))).join("|");
1560
- const re = new RegExp(`(?<![\\w-])(${alternation})(?![\\w-])`, "gi");
1561
- return haystack.replace(re, (matched) => {
1560
+ const re = new RegExp(`(?<!\\w)(${alternation})(?!\\w)`, "gi");
1561
+ return haystack.replace(re, (matched, _g1, offset) => {
1562
1562
  const entry = byLower.get(matched.toLowerCase());
1563
- return entry ? render(entry, matched) : matched;
1563
+ if (!entry) return matched;
1564
+ if (!entry.matchInCompounds) {
1565
+ if (haystack[offset - 1] === "-" || haystack[offset + matched.length] === "-") return matched;
1566
+ }
1567
+ return render(entry, matched);
1564
1568
  });
1565
1569
  }
1566
1570
 
@@ -1613,7 +1617,13 @@ function elevenlabsAdapter(config = {}) {
1613
1617
  headers: { "xi-api-key": key(), "content-type": "application/json", accept: "audio/mpeg" },
1614
1618
  body: JSON.stringify({
1615
1619
  model_id: req.spec.model,
1616
- inputs: req.inputs.map((t) => ({ text: t.text, voice_id: t.voiceId })),
1620
+ // F051.4 — PER LINE. The endpoint takes each turn's text separately, so the
1621
+ // dictionary applies exactly as it does in tts; composing the script into one
1622
+ // string first would let a replacement run across a speaker boundary.
1623
+ inputs: req.inputs.map((t) => ({
1624
+ text: ttsText({ text: t.text, pronunciations: req.pronunciations }),
1625
+ voice_id: t.voiceId
1626
+ })),
1617
1627
  ...req.format ? { output_format: req.format } : {}
1618
1628
  })
1619
1629
  });
@@ -3051,18 +3061,24 @@ var moderationInputSchema = z.object({
3051
3061
  input: z.union([z.string(), z.array(z.string())]),
3052
3062
  ...callOptions
3053
3063
  });
3064
+ var pronunciationSchema = z.object({
3065
+ word: z.string(),
3066
+ alias: z.string().optional(),
3067
+ ipa: z.string().optional(),
3068
+ lang: z.string().optional(),
3069
+ /** F051.3 — also match inside a hyphenated compound ("AI" in "AI-agenter"). */
3070
+ matchInCompounds: z.boolean().optional()
3071
+ });
3054
3072
  var podcastInputSchema = z.object({
3055
3073
  script: z.array(z.object({ speaker: z.string(), text: z.string() })).min(1),
3056
3074
  voices: z.record(z.string(), z.string()),
3057
3075
  format: z.string().optional(),
3076
+ /** F051.4 — same field, same semantics as tts. Applied PER LINE: ElevenLabs'
3077
+ * /text-to-dialogue takes inputs[].text separately, so there is no composed string
3078
+ * a replacement could run across a speaker boundary in. */
3079
+ pronunciations: z.array(pronunciationSchema).optional(),
3058
3080
  ...callOptions
3059
3081
  });
3060
- var pronunciationSchema = z.object({
3061
- word: z.string(),
3062
- alias: z.string().optional(),
3063
- ipa: z.string().optional(),
3064
- lang: z.string().optional()
3065
- });
3066
3082
  var ttsInputSchema = z.object({
3067
3083
  text: z.string(),
3068
3084
  voice: z.string(),
@@ -3110,8 +3126,8 @@ var aiConfigSchema = z.object({
3110
3126
  });
3111
3127
 
3112
3128
  // src/version.ts
3113
- var VERSION = "0.42.1";
3114
- var SDK_TAG = "@broberg/ai-sdk@0.42.1";
3129
+ var VERSION = "0.44.0";
3130
+ var SDK_TAG = "@broberg/ai-sdk@0.44.0";
3115
3131
 
3116
3132
  // src/cost/sinks/upmetrics.ts
3117
3133
  function upmetricsSink(config) {
@@ -3640,7 +3656,7 @@ function createAI(config = {}) {
3640
3656
  invoke: async (spec) => {
3641
3657
  const adapter = pickProvider(spec.provider);
3642
3658
  if (!adapter.dialogue) throw new Error(`createAI: provider "${spec.provider}" does not support podcast/dialogue`);
3643
- return adapter.dialogue({ inputs, format: input.format, spec });
3659
+ return adapter.dialogue({ inputs, format: input.format, pronunciations: input.pronunciations, spec });
3644
3660
  }
3645
3661
  });
3646
3662
  },