@shogo-ai/voice 2.4.3 → 2.4.5

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.
@@ -856,10 +856,45 @@ var VOICE_OPS = [
856
856
  },
857
857
  accept: "audio/mpeg",
858
858
  errorLabel: "Music generation failed",
859
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
859
860
  validate: (fields) => {
860
861
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
861
862
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
862
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
863
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
864
+ const provider = fields.provider;
865
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
866
+ return "`provider` must be 'elevenlabs' or 'google'";
867
+ }
868
+ if (provider !== "google") {
869
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
870
+ if (googleOnly) {
871
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
872
+ }
873
+ return null;
874
+ }
875
+ const elevenLabsOnly = [
876
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
877
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
878
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
879
+ ["forceInstrumental", "use `instrumental`"],
880
+ ["outputFormat", "Lyria always returns mp3"]
881
+ ];
882
+ for (const [name, instead] of elevenLabsOnly) {
883
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
884
+ }
885
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
886
+ return "`lyrics` and `instrumental` cannot both be set";
887
+ }
888
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
889
+ return "`model` must be a non-empty string";
890
+ }
891
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
892
+ return "`lyrics` must be a string";
893
+ }
894
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
895
+ return "`instrumental` must be a boolean";
896
+ }
897
+ return null;
863
898
  }
864
899
  },
865
900
  {
@@ -931,6 +966,9 @@ function parseVoiceOpJsonBody(op, raw) {
931
966
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
932
967
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
933
968
  }
969
+ for (const name of op.callerFields ?? []) {
970
+ if (body[name] !== void 0) fields[name] = body[name];
971
+ }
934
972
  for (const name of op.required ?? []) {
935
973
  const value = fields[name];
936
974
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1075,4 +1113,4 @@ export {
1075
1113
  WORKSPACE_RUNTIME_TOKEN_MESSAGE,
1076
1114
  readRuntimeAuthSecret
1077
1115
  };
1078
- //# sourceMappingURL=chunk-IOFWML5L.js.map
1116
+ //# sourceMappingURL=chunk-ANUVG2N7.js.map
@@ -16,7 +16,7 @@ import {
16
16
  voiceOpReturnsAudio,
17
17
  voiceOpUpstreamBody,
18
18
  voiceOpUpstreamPath
19
- } from "./chunk-IOFWML5L.js";
19
+ } from "./chunk-ANUVG2N7.js";
20
20
  import {
21
21
  AUDIO_TAGS,
22
22
  AUDIO_TAG_GROUPS,
@@ -699,6 +699,15 @@ function createVoiceHandlers(config) {
699
699
  if (u instanceof Response) return u;
700
700
  }
701
701
  const raw = await readJson(req);
702
+ if (raw && typeof raw === "object" && raw.provider === "google") {
703
+ return json(
704
+ {
705
+ error: "provider_unavailable",
706
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
707
+ },
708
+ 400
709
+ );
710
+ }
702
711
  const parsed = parseMusicBody(raw);
703
712
  if ("error" in parsed) return json({ error: parsed.error }, 400);
704
713
  try {
@@ -772,6 +781,15 @@ function createVoiceHandlers(config) {
772
781
  return new Response(result.audio, { headers: headers2 });
773
782
  }
774
783
  if (op.call === "composeMusic") {
784
+ if (fields.provider === "google") {
785
+ return json(
786
+ {
787
+ error: "provider_unavailable",
788
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
789
+ },
790
+ 400
791
+ );
792
+ }
775
793
  const { audio, contentType } = await el.composeMusic({
776
794
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
777
795
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -985,4 +1003,4 @@ export {
985
1003
  createVoiceHandlers,
986
1004
  toNodeListener
987
1005
  };
988
- //# sourceMappingURL=chunk-U5UC7ZVS.js.map
1006
+ //# sourceMappingURL=chunk-VC4WSDEL.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createVoiceHandlers
3
- } from "./chunk-U5UC7ZVS.js";
3
+ } from "./chunk-VC4WSDEL.js";
4
4
 
5
5
  // src/route/index.ts
6
6
  function defaultHandlers() {
@@ -29,4 +29,4 @@ export {
29
29
  defaultHandlers,
30
30
  createVoiceRoute
31
31
  };
32
- //# sourceMappingURL=chunk-RALZRMFL.js.map
32
+ //# sourceMappingURL=chunk-VZ5MMFML.js.map
@@ -540,8 +540,7 @@ function useLiveVoiceConversation(options) {
540
540
  });
541
541
  }, [sendEvent]);
542
542
  const sendUserActivity = useCallback2(() => {
543
- sendEvent({ type: "session.input_audio.commit" });
544
- }, [sendEvent]);
543
+ }, []);
545
544
  const getOutputByteFrequencyData = useCallback2(() => {
546
545
  return frequencyRef.current;
547
546
  }, []);
@@ -578,4 +577,4 @@ export {
578
577
  useLiveVoiceConversation,
579
578
  ShogoVoiceProvider
580
579
  };
581
- //# sourceMappingURL=chunk-ME3SIZZU.js.map
580
+ //# sourceMappingURL=chunk-ZAX5HPVI.js.map
package/dist/index.cjs CHANGED
@@ -1090,10 +1090,45 @@ var VOICE_OPS = [
1090
1090
  },
1091
1091
  accept: "audio/mpeg",
1092
1092
  errorLabel: "Music generation failed",
1093
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1093
1094
  validate: (fields) => {
1094
1095
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1095
1096
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1096
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1097
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1098
+ const provider = fields.provider;
1099
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1100
+ return "`provider` must be 'elevenlabs' or 'google'";
1101
+ }
1102
+ if (provider !== "google") {
1103
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1104
+ if (googleOnly) {
1105
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1106
+ }
1107
+ return null;
1108
+ }
1109
+ const elevenLabsOnly = [
1110
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1111
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1112
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1113
+ ["forceInstrumental", "use `instrumental`"],
1114
+ ["outputFormat", "Lyria always returns mp3"]
1115
+ ];
1116
+ for (const [name, instead] of elevenLabsOnly) {
1117
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1118
+ }
1119
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1120
+ return "`lyrics` and `instrumental` cannot both be set";
1121
+ }
1122
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1123
+ return "`model` must be a non-empty string";
1124
+ }
1125
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1126
+ return "`lyrics` must be a string";
1127
+ }
1128
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1129
+ return "`instrumental` must be a boolean";
1130
+ }
1131
+ return null;
1097
1132
  }
1098
1133
  },
1099
1134
  {
@@ -1165,6 +1200,9 @@ function parseVoiceOpJsonBody(op, raw) {
1165
1200
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1166
1201
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1167
1202
  }
1203
+ for (const name of op.callerFields ?? []) {
1204
+ if (body[name] !== void 0) fields[name] = body[name];
1205
+ }
1168
1206
  for (const name of op.required ?? []) {
1169
1207
  const value = fields[name];
1170
1208
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
package/dist/index.d.cts CHANGED
@@ -130,6 +130,14 @@ interface VoiceOp {
130
130
  defaultVoiceId?: boolean;
131
131
  /** Caller field → upstream field. Anything absent here is not forwarded. */
132
132
  forward?: Readonly<Record<string, string>>;
133
+ /**
134
+ * Caller fields the row accepts but never forwards upstream — routing
135
+ * decisions and per-provider options (music's `provider` / `model` /
136
+ * `lyrics`). The parser keeps them so the executor can read them; the
137
+ * upstream body is built from {@link forward} only, so they cannot leak to
138
+ * an API that would reject them.
139
+ */
140
+ callerFields?: readonly string[];
133
141
  /** Caller fields that must be present (before mapping). */
134
142
  required?: readonly string[];
135
143
  /** Query params forwarded upstream. */
@@ -308,7 +316,8 @@ declare const VOICE_OPS: readonly [{
308
316
  };
309
317
  readonly accept: "audio/mpeg";
310
318
  readonly errorLabel: "Music generation failed";
311
- readonly validate: (fields: Record<string, unknown>) => "Provide exactly one of `prompt` or `compositionPlan`" | null;
319
+ readonly callerFields: readonly ["provider", "model", "lyrics", "instrumental"];
320
+ readonly validate: (fields: Record<string, unknown>) => string | null;
312
321
  }, {
313
322
  readonly id: "voices";
314
323
  readonly method: "GET";
package/dist/index.d.ts CHANGED
@@ -130,6 +130,14 @@ interface VoiceOp {
130
130
  defaultVoiceId?: boolean;
131
131
  /** Caller field → upstream field. Anything absent here is not forwarded. */
132
132
  forward?: Readonly<Record<string, string>>;
133
+ /**
134
+ * Caller fields the row accepts but never forwards upstream — routing
135
+ * decisions and per-provider options (music's `provider` / `model` /
136
+ * `lyrics`). The parser keeps them so the executor can read them; the
137
+ * upstream body is built from {@link forward} only, so they cannot leak to
138
+ * an API that would reject them.
139
+ */
140
+ callerFields?: readonly string[];
133
141
  /** Caller fields that must be present (before mapping). */
134
142
  required?: readonly string[];
135
143
  /** Query params forwarded upstream. */
@@ -308,7 +316,8 @@ declare const VOICE_OPS: readonly [{
308
316
  };
309
317
  readonly accept: "audio/mpeg";
310
318
  readonly errorLabel: "Music generation failed";
311
- readonly validate: (fields: Record<string, unknown>) => "Provide exactly one of `prompt` or `compositionPlan`" | null;
319
+ readonly callerFields: readonly ["provider", "model", "lyrics", "instrumental"];
320
+ readonly validate: (fields: Record<string, unknown>) => string | null;
312
321
  }, {
313
322
  readonly id: "voices";
314
323
  readonly method: "GET";
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  voiceOpReturnsAudio,
43
43
  voiceOpUpstreamBody,
44
44
  voiceOpUpstreamPath
45
- } from "./chunk-IOFWML5L.js";
45
+ } from "./chunk-ANUVG2N7.js";
46
46
  import {
47
47
  AUDIO_TAGS,
48
48
  AUDIO_TAG_GROUPS,
@@ -748,8 +748,7 @@ function useLiveVoiceConversation(options) {
748
748
  });
749
749
  }, [sendEvent]);
750
750
  const sendUserActivity = (0, import_react3.useCallback)(() => {
751
- sendEvent({ type: "session.input_audio.commit" });
752
- }, [sendEvent]);
751
+ }, []);
753
752
  const getOutputByteFrequencyData = (0, import_react3.useCallback)(() => {
754
753
  return frequencyRef.current;
755
754
  }, []);
@@ -2,7 +2,7 @@ import {
2
2
  ShogoVoiceProvider,
3
3
  useLiveVoiceConversation,
4
4
  useVoiceConversation
5
- } from "../chunk-ME3SIZZU.js";
5
+ } from "../chunk-ZAX5HPVI.js";
6
6
  import "../chunk-ZAW6F3YT.js";
7
7
  import "../chunk-Q3TJAJ54.js";
8
8
  export {
@@ -772,8 +772,7 @@ function useLiveVoiceConversation(options) {
772
772
  });
773
773
  }, [sendEvent]);
774
774
  const sendUserActivity = (0, import_react3.useCallback)(() => {
775
- sendEvent({ type: "session.input_audio.commit" });
776
- }, [sendEvent]);
775
+ }, []);
777
776
  const getOutputByteFrequencyData = (0, import_react3.useCallback)(() => {
778
777
  return frequencyRef.current;
779
778
  }, []);
@@ -5,7 +5,7 @@ import {
5
5
  ShogoVoiceProvider,
6
6
  useLiveVoiceConversation,
7
7
  useVoiceConversation
8
- } from "../chunk-ME3SIZZU.js";
8
+ } from "../chunk-ZAX5HPVI.js";
9
9
  import "../chunk-ZAW6F3YT.js";
10
10
  import {
11
11
  OrganicParticles,
@@ -1017,10 +1017,45 @@ var VOICE_OPS = [
1017
1017
  },
1018
1018
  accept: "audio/mpeg",
1019
1019
  errorLabel: "Music generation failed",
1020
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1020
1021
  validate: (fields) => {
1021
1022
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1022
1023
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1023
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1024
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1025
+ const provider = fields.provider;
1026
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1027
+ return "`provider` must be 'elevenlabs' or 'google'";
1028
+ }
1029
+ if (provider !== "google") {
1030
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1031
+ if (googleOnly) {
1032
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1033
+ }
1034
+ return null;
1035
+ }
1036
+ const elevenLabsOnly = [
1037
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1038
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1039
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1040
+ ["forceInstrumental", "use `instrumental`"],
1041
+ ["outputFormat", "Lyria always returns mp3"]
1042
+ ];
1043
+ for (const [name, instead] of elevenLabsOnly) {
1044
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1045
+ }
1046
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1047
+ return "`lyrics` and `instrumental` cannot both be set";
1048
+ }
1049
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1050
+ return "`model` must be a non-empty string";
1051
+ }
1052
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1053
+ return "`lyrics` must be a string";
1054
+ }
1055
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1056
+ return "`instrumental` must be a boolean";
1057
+ }
1058
+ return null;
1024
1059
  }
1025
1060
  },
1026
1061
  {
@@ -1079,6 +1114,9 @@ function parseVoiceOpJsonBody(op, raw) {
1079
1114
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1080
1115
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1081
1116
  }
1117
+ for (const name of op.callerFields ?? []) {
1118
+ if (body[name] !== void 0) fields[name] = body[name];
1119
+ }
1082
1120
  for (const name of op.required ?? []) {
1083
1121
  const value = fields[name];
1084
1122
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1849,6 +1887,15 @@ function createVoiceHandlers(config) {
1849
1887
  if (u instanceof Response) return u;
1850
1888
  }
1851
1889
  const raw = await readJson(req);
1890
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1891
+ return json(
1892
+ {
1893
+ error: "provider_unavailable",
1894
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1895
+ },
1896
+ 400
1897
+ );
1898
+ }
1852
1899
  const parsed = parseMusicBody(raw);
1853
1900
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1854
1901
  try {
@@ -1922,6 +1969,15 @@ function createVoiceHandlers(config) {
1922
1969
  return new Response(result.audio, { headers: headers2 });
1923
1970
  }
1924
1971
  if (op.call === "composeMusic") {
1972
+ if (fields.provider === "google") {
1973
+ return json(
1974
+ {
1975
+ error: "provider_unavailable",
1976
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1977
+ },
1978
+ 400
1979
+ );
1980
+ }
1925
1981
  const { audio, contentType } = await el.composeMusic({
1926
1982
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1927
1983
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  defaultHandlers
3
- } from "../chunk-RALZRMFL.js";
4
- import "../chunk-U5UC7ZVS.js";
5
- import "../chunk-IOFWML5L.js";
3
+ } from "../chunk-VZ5MMFML.js";
4
+ import "../chunk-VC4WSDEL.js";
5
+ import "../chunk-ANUVG2N7.js";
6
6
  import "../chunk-Q3TJAJ54.js";
7
7
 
8
8
  // src/route/agent.ts
@@ -1015,10 +1015,45 @@ var VOICE_OPS = [
1015
1015
  },
1016
1016
  accept: "audio/mpeg",
1017
1017
  errorLabel: "Music generation failed",
1018
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1018
1019
  validate: (fields) => {
1019
1020
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1020
1021
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1021
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1022
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1023
+ const provider = fields.provider;
1024
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1025
+ return "`provider` must be 'elevenlabs' or 'google'";
1026
+ }
1027
+ if (provider !== "google") {
1028
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1029
+ if (googleOnly) {
1030
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1031
+ }
1032
+ return null;
1033
+ }
1034
+ const elevenLabsOnly = [
1035
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1036
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1037
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1038
+ ["forceInstrumental", "use `instrumental`"],
1039
+ ["outputFormat", "Lyria always returns mp3"]
1040
+ ];
1041
+ for (const [name, instead] of elevenLabsOnly) {
1042
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1043
+ }
1044
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1045
+ return "`lyrics` and `instrumental` cannot both be set";
1046
+ }
1047
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1048
+ return "`model` must be a non-empty string";
1049
+ }
1050
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1051
+ return "`lyrics` must be a string";
1052
+ }
1053
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1054
+ return "`instrumental` must be a boolean";
1055
+ }
1056
+ return null;
1022
1057
  }
1023
1058
  },
1024
1059
  {
@@ -1077,6 +1112,9 @@ function parseVoiceOpJsonBody(op, raw) {
1077
1112
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1078
1113
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1079
1114
  }
1115
+ for (const name of op.callerFields ?? []) {
1116
+ if (body[name] !== void 0) fields[name] = body[name];
1117
+ }
1080
1118
  for (const name of op.required ?? []) {
1081
1119
  const value = fields[name];
1082
1120
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1847,6 +1885,15 @@ function createVoiceHandlers(config) {
1847
1885
  if (u instanceof Response) return u;
1848
1886
  }
1849
1887
  const raw = await readJson(req);
1888
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1889
+ return json(
1890
+ {
1891
+ error: "provider_unavailable",
1892
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1893
+ },
1894
+ 400
1895
+ );
1896
+ }
1850
1897
  const parsed = parseMusicBody(raw);
1851
1898
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1852
1899
  try {
@@ -1920,6 +1967,15 @@ function createVoiceHandlers(config) {
1920
1967
  return new Response(result.audio, { headers: headers2 });
1921
1968
  }
1922
1969
  if (op.call === "composeMusic") {
1970
+ if (fields.provider === "google") {
1971
+ return json(
1972
+ {
1973
+ error: "provider_unavailable",
1974
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1975
+ },
1976
+ 400
1977
+ );
1978
+ }
1923
1979
  const { audio, contentType } = await el.composeMusic({
1924
1980
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1925
1981
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  defaultHandlers
3
- } from "../chunk-RALZRMFL.js";
4
- import "../chunk-U5UC7ZVS.js";
5
- import "../chunk-IOFWML5L.js";
3
+ } from "../chunk-VZ5MMFML.js";
4
+ import "../chunk-VC4WSDEL.js";
5
+ import "../chunk-ANUVG2N7.js";
6
6
  import "../chunk-Q3TJAJ54.js";
7
7
 
8
8
  // src/route/audio-tags.ts
@@ -1016,10 +1016,45 @@ var VOICE_OPS = [
1016
1016
  },
1017
1017
  accept: "audio/mpeg",
1018
1018
  errorLabel: "Music generation failed",
1019
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1019
1020
  validate: (fields) => {
1020
1021
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1021
1022
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1022
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1023
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1024
+ const provider = fields.provider;
1025
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1026
+ return "`provider` must be 'elevenlabs' or 'google'";
1027
+ }
1028
+ if (provider !== "google") {
1029
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1030
+ if (googleOnly) {
1031
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1032
+ }
1033
+ return null;
1034
+ }
1035
+ const elevenLabsOnly = [
1036
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1037
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1038
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1039
+ ["forceInstrumental", "use `instrumental`"],
1040
+ ["outputFormat", "Lyria always returns mp3"]
1041
+ ];
1042
+ for (const [name, instead] of elevenLabsOnly) {
1043
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1044
+ }
1045
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1046
+ return "`lyrics` and `instrumental` cannot both be set";
1047
+ }
1048
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1049
+ return "`model` must be a non-empty string";
1050
+ }
1051
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1052
+ return "`lyrics` must be a string";
1053
+ }
1054
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1055
+ return "`instrumental` must be a boolean";
1056
+ }
1057
+ return null;
1023
1058
  }
1024
1059
  },
1025
1060
  {
@@ -1078,6 +1113,9 @@ function parseVoiceOpJsonBody(op, raw) {
1078
1113
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1079
1114
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1080
1115
  }
1116
+ for (const name of op.callerFields ?? []) {
1117
+ if (body[name] !== void 0) fields[name] = body[name];
1118
+ }
1081
1119
  for (const name of op.required ?? []) {
1082
1120
  const value = fields[name];
1083
1121
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1848,6 +1886,15 @@ function createVoiceHandlers(config) {
1848
1886
  if (u instanceof Response) return u;
1849
1887
  }
1850
1888
  const raw = await readJson(req);
1889
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1890
+ return json(
1891
+ {
1892
+ error: "provider_unavailable",
1893
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1894
+ },
1895
+ 400
1896
+ );
1897
+ }
1851
1898
  const parsed = parseMusicBody(raw);
1852
1899
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1853
1900
  try {
@@ -1921,6 +1968,15 @@ function createVoiceHandlers(config) {
1921
1968
  return new Response(result.audio, { headers: headers2 });
1922
1969
  }
1923
1970
  if (op.call === "composeMusic") {
1971
+ if (fields.provider === "google") {
1972
+ return json(
1973
+ {
1974
+ error: "provider_unavailable",
1975
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1976
+ },
1977
+ 400
1978
+ );
1979
+ }
1924
1980
  const { audio, contentType } = await el.composeMusic({
1925
1981
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1926
1982
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createVoiceRoute,
3
3
  defaultHandlers
4
- } from "../chunk-RALZRMFL.js";
5
- import "../chunk-U5UC7ZVS.js";
6
- import "../chunk-IOFWML5L.js";
4
+ } from "../chunk-VZ5MMFML.js";
5
+ import "../chunk-VC4WSDEL.js";
6
+ import "../chunk-ANUVG2N7.js";
7
7
  import "../chunk-Q3TJAJ54.js";
8
8
  export {
9
9
  createVoiceRoute,
@@ -1015,10 +1015,45 @@ var VOICE_OPS = [
1015
1015
  },
1016
1016
  accept: "audio/mpeg",
1017
1017
  errorLabel: "Music generation failed",
1018
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1018
1019
  validate: (fields) => {
1019
1020
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1020
1021
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1021
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1022
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1023
+ const provider = fields.provider;
1024
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1025
+ return "`provider` must be 'elevenlabs' or 'google'";
1026
+ }
1027
+ if (provider !== "google") {
1028
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1029
+ if (googleOnly) {
1030
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1031
+ }
1032
+ return null;
1033
+ }
1034
+ const elevenLabsOnly = [
1035
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1036
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1037
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1038
+ ["forceInstrumental", "use `instrumental`"],
1039
+ ["outputFormat", "Lyria always returns mp3"]
1040
+ ];
1041
+ for (const [name, instead] of elevenLabsOnly) {
1042
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1043
+ }
1044
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1045
+ return "`lyrics` and `instrumental` cannot both be set";
1046
+ }
1047
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1048
+ return "`model` must be a non-empty string";
1049
+ }
1050
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1051
+ return "`lyrics` must be a string";
1052
+ }
1053
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1054
+ return "`instrumental` must be a boolean";
1055
+ }
1056
+ return null;
1022
1057
  }
1023
1058
  },
1024
1059
  {
@@ -1077,6 +1112,9 @@ function parseVoiceOpJsonBody(op, raw) {
1077
1112
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1078
1113
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1079
1114
  }
1115
+ for (const name of op.callerFields ?? []) {
1116
+ if (body[name] !== void 0) fields[name] = body[name];
1117
+ }
1080
1118
  for (const name of op.required ?? []) {
1081
1119
  const value = fields[name];
1082
1120
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1847,6 +1885,15 @@ function createVoiceHandlers(config) {
1847
1885
  if (u instanceof Response) return u;
1848
1886
  }
1849
1887
  const raw = await readJson(req);
1888
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1889
+ return json(
1890
+ {
1891
+ error: "provider_unavailable",
1892
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1893
+ },
1894
+ 400
1895
+ );
1896
+ }
1850
1897
  const parsed = parseMusicBody(raw);
1851
1898
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1852
1899
  try {
@@ -1920,6 +1967,15 @@ function createVoiceHandlers(config) {
1920
1967
  return new Response(result.audio, { headers: headers2 });
1921
1968
  }
1922
1969
  if (op.call === "composeMusic") {
1970
+ if (fields.provider === "google") {
1971
+ return json(
1972
+ {
1973
+ error: "provider_unavailable",
1974
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1975
+ },
1976
+ 400
1977
+ );
1978
+ }
1923
1979
  const { audio, contentType } = await el.composeMusic({
1924
1980
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1925
1981
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  defaultHandlers
3
- } from "../chunk-RALZRMFL.js";
4
- import "../chunk-U5UC7ZVS.js";
5
- import "../chunk-IOFWML5L.js";
3
+ } from "../chunk-VZ5MMFML.js";
4
+ import "../chunk-VC4WSDEL.js";
5
+ import "../chunk-ANUVG2N7.js";
6
6
  import "../chunk-Q3TJAJ54.js";
7
7
 
8
8
  // src/route/music.ts
package/dist/route/op.cjs CHANGED
@@ -1017,10 +1017,45 @@ var VOICE_OPS = [
1017
1017
  },
1018
1018
  accept: "audio/mpeg",
1019
1019
  errorLabel: "Music generation failed",
1020
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1020
1021
  validate: (fields) => {
1021
1022
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1022
1023
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1023
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1024
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1025
+ const provider = fields.provider;
1026
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1027
+ return "`provider` must be 'elevenlabs' or 'google'";
1028
+ }
1029
+ if (provider !== "google") {
1030
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1031
+ if (googleOnly) {
1032
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1033
+ }
1034
+ return null;
1035
+ }
1036
+ const elevenLabsOnly = [
1037
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1038
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1039
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1040
+ ["forceInstrumental", "use `instrumental`"],
1041
+ ["outputFormat", "Lyria always returns mp3"]
1042
+ ];
1043
+ for (const [name, instead] of elevenLabsOnly) {
1044
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1045
+ }
1046
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1047
+ return "`lyrics` and `instrumental` cannot both be set";
1048
+ }
1049
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1050
+ return "`model` must be a non-empty string";
1051
+ }
1052
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1053
+ return "`lyrics` must be a string";
1054
+ }
1055
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1056
+ return "`instrumental` must be a boolean";
1057
+ }
1058
+ return null;
1024
1059
  }
1025
1060
  },
1026
1061
  {
@@ -1079,6 +1114,9 @@ function parseVoiceOpJsonBody(op, raw) {
1079
1114
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1080
1115
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1081
1116
  }
1117
+ for (const name of op.callerFields ?? []) {
1118
+ if (body[name] !== void 0) fields[name] = body[name];
1119
+ }
1082
1120
  for (const name of op.required ?? []) {
1083
1121
  const value = fields[name];
1084
1122
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1849,6 +1887,15 @@ function createVoiceHandlers(config) {
1849
1887
  if (u instanceof Response) return u;
1850
1888
  }
1851
1889
  const raw = await readJson(req);
1890
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1891
+ return json(
1892
+ {
1893
+ error: "provider_unavailable",
1894
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1895
+ },
1896
+ 400
1897
+ );
1898
+ }
1852
1899
  const parsed = parseMusicBody(raw);
1853
1900
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1854
1901
  try {
@@ -1922,6 +1969,15 @@ function createVoiceHandlers(config) {
1922
1969
  return new Response(result.audio, { headers: headers2 });
1923
1970
  }
1924
1971
  if (op.call === "composeMusic") {
1972
+ if (fields.provider === "google") {
1973
+ return json(
1974
+ {
1975
+ error: "provider_unavailable",
1976
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1977
+ },
1978
+ 400
1979
+ );
1980
+ }
1925
1981
  const { audio, contentType } = await el.composeMusic({
1926
1982
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1927
1983
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
package/dist/route/op.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  defaultHandlers
3
- } from "../chunk-RALZRMFL.js";
4
- import "../chunk-U5UC7ZVS.js";
3
+ } from "../chunk-VZ5MMFML.js";
4
+ import "../chunk-VC4WSDEL.js";
5
5
  import {
6
6
  VOICE_OPS
7
- } from "../chunk-IOFWML5L.js";
7
+ } from "../chunk-ANUVG2N7.js";
8
8
  import "../chunk-Q3TJAJ54.js";
9
9
 
10
10
  // src/route/op.ts
@@ -1015,10 +1015,45 @@ var VOICE_OPS = [
1015
1015
  },
1016
1016
  accept: "audio/mpeg",
1017
1017
  errorLabel: "Music generation failed",
1018
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1018
1019
  validate: (fields) => {
1019
1020
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1020
1021
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1021
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1022
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1023
+ const provider = fields.provider;
1024
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1025
+ return "`provider` must be 'elevenlabs' or 'google'";
1026
+ }
1027
+ if (provider !== "google") {
1028
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1029
+ if (googleOnly) {
1030
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1031
+ }
1032
+ return null;
1033
+ }
1034
+ const elevenLabsOnly = [
1035
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1036
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1037
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1038
+ ["forceInstrumental", "use `instrumental`"],
1039
+ ["outputFormat", "Lyria always returns mp3"]
1040
+ ];
1041
+ for (const [name, instead] of elevenLabsOnly) {
1042
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1043
+ }
1044
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1045
+ return "`lyrics` and `instrumental` cannot both be set";
1046
+ }
1047
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1048
+ return "`model` must be a non-empty string";
1049
+ }
1050
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1051
+ return "`lyrics` must be a string";
1052
+ }
1053
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1054
+ return "`instrumental` must be a boolean";
1055
+ }
1056
+ return null;
1022
1057
  }
1023
1058
  },
1024
1059
  {
@@ -1077,6 +1112,9 @@ function parseVoiceOpJsonBody(op, raw) {
1077
1112
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1078
1113
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1079
1114
  }
1115
+ for (const name of op.callerFields ?? []) {
1116
+ if (body[name] !== void 0) fields[name] = body[name];
1117
+ }
1080
1118
  for (const name of op.required ?? []) {
1081
1119
  const value = fields[name];
1082
1120
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1847,6 +1885,15 @@ function createVoiceHandlers(config) {
1847
1885
  if (u instanceof Response) return u;
1848
1886
  }
1849
1887
  const raw = await readJson(req);
1888
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1889
+ return json(
1890
+ {
1891
+ error: "provider_unavailable",
1892
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1893
+ },
1894
+ 400
1895
+ );
1896
+ }
1850
1897
  const parsed = parseMusicBody(raw);
1851
1898
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1852
1899
  try {
@@ -1920,6 +1967,15 @@ function createVoiceHandlers(config) {
1920
1967
  return new Response(result.audio, { headers: headers2 });
1921
1968
  }
1922
1969
  if (op.call === "composeMusic") {
1970
+ if (fields.provider === "google") {
1971
+ return json(
1972
+ {
1973
+ error: "provider_unavailable",
1974
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1975
+ },
1976
+ 400
1977
+ );
1978
+ }
1923
1979
  const { audio, contentType } = await el.composeMusic({
1924
1980
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1925
1981
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  defaultHandlers
3
- } from "../chunk-RALZRMFL.js";
4
- import "../chunk-U5UC7ZVS.js";
5
- import "../chunk-IOFWML5L.js";
3
+ } from "../chunk-VZ5MMFML.js";
4
+ import "../chunk-VC4WSDEL.js";
5
+ import "../chunk-ANUVG2N7.js";
6
6
  import "../chunk-Q3TJAJ54.js";
7
7
 
8
8
  // src/route/signed-url.ts
@@ -1015,10 +1015,45 @@ var VOICE_OPS = [
1015
1015
  },
1016
1016
  accept: "audio/mpeg",
1017
1017
  errorLabel: "Music generation failed",
1018
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1018
1019
  validate: (fields) => {
1019
1020
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1020
1021
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1021
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1022
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1023
+ const provider = fields.provider;
1024
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1025
+ return "`provider` must be 'elevenlabs' or 'google'";
1026
+ }
1027
+ if (provider !== "google") {
1028
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1029
+ if (googleOnly) {
1030
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1031
+ }
1032
+ return null;
1033
+ }
1034
+ const elevenLabsOnly = [
1035
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1036
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1037
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1038
+ ["forceInstrumental", "use `instrumental`"],
1039
+ ["outputFormat", "Lyria always returns mp3"]
1040
+ ];
1041
+ for (const [name, instead] of elevenLabsOnly) {
1042
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1043
+ }
1044
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1045
+ return "`lyrics` and `instrumental` cannot both be set";
1046
+ }
1047
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1048
+ return "`model` must be a non-empty string";
1049
+ }
1050
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1051
+ return "`lyrics` must be a string";
1052
+ }
1053
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1054
+ return "`instrumental` must be a boolean";
1055
+ }
1056
+ return null;
1022
1057
  }
1023
1058
  },
1024
1059
  {
@@ -1077,6 +1112,9 @@ function parseVoiceOpJsonBody(op, raw) {
1077
1112
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1078
1113
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1079
1114
  }
1115
+ for (const name of op.callerFields ?? []) {
1116
+ if (body[name] !== void 0) fields[name] = body[name];
1117
+ }
1080
1118
  for (const name of op.required ?? []) {
1081
1119
  const value = fields[name];
1082
1120
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1847,6 +1885,15 @@ function createVoiceHandlers(config) {
1847
1885
  if (u instanceof Response) return u;
1848
1886
  }
1849
1887
  const raw = await readJson(req);
1888
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1889
+ return json(
1890
+ {
1891
+ error: "provider_unavailable",
1892
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1893
+ },
1894
+ 400
1895
+ );
1896
+ }
1850
1897
  const parsed = parseMusicBody(raw);
1851
1898
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1852
1899
  try {
@@ -1920,6 +1967,15 @@ function createVoiceHandlers(config) {
1920
1967
  return new Response(result.audio, { headers: headers2 });
1921
1968
  }
1922
1969
  if (op.call === "composeMusic") {
1970
+ if (fields.provider === "google") {
1971
+ return json(
1972
+ {
1973
+ error: "provider_unavailable",
1974
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1975
+ },
1976
+ 400
1977
+ );
1978
+ }
1923
1979
  const { audio, contentType } = await el.composeMusic({
1924
1980
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1925
1981
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  defaultHandlers
3
- } from "../chunk-RALZRMFL.js";
4
- import "../chunk-U5UC7ZVS.js";
5
- import "../chunk-IOFWML5L.js";
3
+ } from "../chunk-VZ5MMFML.js";
4
+ import "../chunk-VC4WSDEL.js";
5
+ import "../chunk-ANUVG2N7.js";
6
6
  import "../chunk-Q3TJAJ54.js";
7
7
 
8
8
  // src/route/tts-preview.ts
package/dist/server.cjs CHANGED
@@ -1016,10 +1016,45 @@ var VOICE_OPS = [
1016
1016
  },
1017
1017
  accept: "audio/mpeg",
1018
1018
  errorLabel: "Music generation failed",
1019
+ callerFields: ["provider", "model", "lyrics", "instrumental"],
1019
1020
  validate: (fields) => {
1020
1021
  const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
1021
1022
  const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
1022
- return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
1023
+ if (hasPrompt === hasPlan) return "Provide exactly one of `prompt` or `compositionPlan`";
1024
+ const provider = fields.provider;
1025
+ if (provider !== void 0 && provider !== "elevenlabs" && provider !== "google") {
1026
+ return "`provider` must be 'elevenlabs' or 'google'";
1027
+ }
1028
+ if (provider !== "google") {
1029
+ const googleOnly = ["model", "lyrics", "instrumental"].find((name) => fields[name] !== void 0);
1030
+ if (googleOnly) {
1031
+ return `\`${googleOnly}\` is Google-only; send \`provider: 'google'\` to use Lyria, or drop it`;
1032
+ }
1033
+ return null;
1034
+ }
1035
+ const elevenLabsOnly = [
1036
+ ["compositionPlan", "Lyria reads style and structure from `prompt`"],
1037
+ ["modelId", "use `model` (`lyria-3-clip` or `lyria-3.5`)"],
1038
+ ["musicLengthMs", "Lyria takes the length from the model \u2014 use `model: lyria-3.5` for a full song"],
1039
+ ["forceInstrumental", "use `instrumental`"],
1040
+ ["outputFormat", "Lyria always returns mp3"]
1041
+ ];
1042
+ for (const [name, instead] of elevenLabsOnly) {
1043
+ if (fields[name] !== void 0) return `\`${name}\` is ElevenLabs-only \u2014 ${instead}`;
1044
+ }
1045
+ if (fields.lyrics !== void 0 && fields.instrumental === true) {
1046
+ return "`lyrics` and `instrumental` cannot both be set";
1047
+ }
1048
+ if (fields.model !== void 0 && (typeof fields.model !== "string" || !fields.model.trim())) {
1049
+ return "`model` must be a non-empty string";
1050
+ }
1051
+ if (fields.lyrics !== void 0 && typeof fields.lyrics !== "string") {
1052
+ return "`lyrics` must be a string";
1053
+ }
1054
+ if (fields.instrumental !== void 0 && typeof fields.instrumental !== "boolean") {
1055
+ return "`instrumental` must be a boolean";
1056
+ }
1057
+ return null;
1023
1058
  }
1024
1059
  },
1025
1060
  {
@@ -1078,6 +1113,9 @@ function parseVoiceOpJsonBody(op, raw) {
1078
1113
  for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
1079
1114
  if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
1080
1115
  }
1116
+ for (const name of op.callerFields ?? []) {
1117
+ if (body[name] !== void 0) fields[name] = body[name];
1118
+ }
1081
1119
  for (const name of op.required ?? []) {
1082
1120
  const value = fields[name];
1083
1121
  if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
@@ -1848,6 +1886,15 @@ function createVoiceHandlers(config) {
1848
1886
  if (u instanceof Response) return u;
1849
1887
  }
1850
1888
  const raw = await readJson(req);
1889
+ if (raw && typeof raw === "object" && raw.provider === "google") {
1890
+ return json(
1891
+ {
1892
+ error: "provider_unavailable",
1893
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1894
+ },
1895
+ 400
1896
+ );
1897
+ }
1851
1898
  const parsed = parseMusicBody(raw);
1852
1899
  if ("error" in parsed) return json({ error: parsed.error }, 400);
1853
1900
  try {
@@ -1921,6 +1968,15 @@ function createVoiceHandlers(config) {
1921
1968
  return new Response(result.audio, { headers: headers2 });
1922
1969
  }
1923
1970
  if (op.call === "composeMusic") {
1971
+ if (fields.provider === "google") {
1972
+ return json(
1973
+ {
1974
+ error: "provider_unavailable",
1975
+ detail: "Lyria music runs through the Shogo API; this BYO handler only has an ElevenLabs key"
1976
+ },
1977
+ 400
1978
+ );
1979
+ }
1924
1980
  const { audio, contentType } = await el.composeMusic({
1925
1981
  ...typeof fields.prompt === "string" ? { prompt: fields.prompt } : {},
1926
1982
  ...fields.compositionPlan ? { compositionPlan: fields.compositionPlan } : {},
package/dist/server.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createVoiceHandlers,
3
3
  toNodeListener
4
- } from "./chunk-U5UC7ZVS.js";
5
- import "./chunk-IOFWML5L.js";
4
+ } from "./chunk-VC4WSDEL.js";
5
+ import "./chunk-ANUVG2N7.js";
6
6
  import "./chunk-Q3TJAJ54.js";
7
7
  export {
8
8
  createVoiceHandlers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shogo-ai/voice",
3
- "version": "2.4.3",
3
+ "version": "2.4.5",
4
4
  "description": "Shogo Voice — ElevenLabs/Twilio voice agent infrastructure with React + React Native UI primitives.",
5
5
  "license": "MIT",
6
6
  "type": "module",