@tanstack/ai-byteplus 0.0.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/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/esm/adapters/image.d.ts +89 -0
- package/dist/esm/adapters/image.js +229 -0
- package/dist/esm/adapters/image.js.map +1 -0
- package/dist/esm/adapters/text.d.ts +163 -0
- package/dist/esm/adapters/text.js +347 -0
- package/dist/esm/adapters/text.js.map +1 -0
- package/dist/esm/adapters/transcription.d.ts +102 -0
- package/dist/esm/adapters/transcription.js +274 -0
- package/dist/esm/adapters/transcription.js.map +1 -0
- package/dist/esm/adapters/tts.d.ts +143 -0
- package/dist/esm/adapters/tts.js +307 -0
- package/dist/esm/adapters/tts.js.map +1 -0
- package/dist/esm/adapters/video.d.ts +182 -0
- package/dist/esm/adapters/video.js +442 -0
- package/dist/esm/adapters/video.js.map +1 -0
- package/dist/esm/audio/transcription-provider-options.d.ts +46 -0
- package/dist/esm/audio/tts-provider-options.d.ts +114 -0
- package/dist/esm/audio/wire-types.d.ts +261 -0
- package/dist/esm/audio/wire-types.js +28 -0
- package/dist/esm/audio/wire-types.js.map +1 -0
- package/dist/esm/image/image-provider-options.d.ts +165 -0
- package/dist/esm/image/image-provider-options.js +134 -0
- package/dist/esm/image/image-provider-options.js.map +1 -0
- package/dist/esm/image/wire-types.d.ts +149 -0
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/message-types.d.ts +154 -0
- package/dist/esm/model-meta.d.ts +594 -0
- package/dist/esm/model-meta.js +619 -0
- package/dist/esm/model-meta.js.map +1 -0
- package/dist/esm/text/text-provider-options.d.ts +109 -0
- package/dist/esm/utils/client.d.ts +183 -0
- package/dist/esm/utils/client.js +253 -0
- package/dist/esm/utils/client.js.map +1 -0
- package/dist/esm/video/video-provider-options.d.ts +197 -0
- package/dist/esm/video/video-provider-options.js +191 -0
- package/dist/esm/video/video-provider-options.js.map +1 -0
- package/dist/esm/video/wire-types.d.ts +248 -0
- package/package.json +77 -0
- package/src/adapters/image.ts +409 -0
- package/src/adapters/text.ts +539 -0
- package/src/adapters/transcription.ts +479 -0
- package/src/adapters/tts.ts +447 -0
- package/src/adapters/video.ts +732 -0
- package/src/audio/transcription-provider-options.ts +46 -0
- package/src/audio/tts-provider-options.ts +122 -0
- package/src/audio/wire-types.ts +290 -0
- package/src/image/image-provider-options.ts +288 -0
- package/src/image/wire-types.ts +169 -0
- package/src/index.ts +222 -0
- package/src/message-types.ts +169 -0
- package/src/model-meta.ts +954 -0
- package/src/text/text-provider-options.ts +151 -0
- package/src/utils/client.ts +377 -0
- package/src/video/video-provider-options.ts +361 -0
- package/src/video/wire-types.ts +293 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-meta.js","names":[],"sources":["../../src/model-meta.ts"],"sourcesContent":["/**\n * BytePlus ModelArk model metadata.\n *\n * Every Ark model id in this file — chat, video and image — was verified live\n * against `https://ark.ap-southeast.bytepluses.com/api/v3` on 2026-07-31. The\n * two Seed Speech ids are the exception: they live on the voice host, which\n * needs a separate key that was not available, so they are docs-derived.\n * Capability metadata is a mix of probed and docs-derived facts; anything not\n * confirmed against the live API is annotated as such at its declaration.\n * BytePlus\n * deactivates model ids aggressively (the whole `seedance-1-0-lite-*` family,\n * `seed-1-6-lite-*`, `seedream-3-0-*`, and the `doubao-`/`skylark-` names are\n * all 404s internationally), so only dated, probe-confirmed ids are shipped.\n *\n * Prefix rules, also probe-confirmed:\n * - `dola-seed-2-1-turbo-260628` and `dola-seedream-5-0-pro-260628` are the\n * canonical ids; the bare forms resolve as aliases but the API echoes the\n * prefixed id back.\n * - The Seedance 2.0 family *requires* the `dreamina-` prefix.\n * - Older models reject the `dola-` prefix outright.\n */\nimport type { DurationOptions } from '@tanstack/ai/adapters'\nimport type { BytePlusTextProviderOptions } from './text/text-provider-options'\n\n/**\n * BytePlus exposes no server-side provider tools (no hosted web search, code\n * interpreter, …) on the international Ark endpoint, so every chat model\n * advertises an empty tool set. Typing it as `never` makes passing another\n * provider's `ProviderTool` to a BytePlus adapter a compile-time error.\n */\nexport type BytePlusProviderToolKind = never\n\n/**\n * Internal metadata structure describing a BytePlus model.\n */\ninterface ModelMeta {\n name: string\n supports: {\n input: ReadonlyArray<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: ReadonlyArray<'text' | 'image' | 'audio' | 'video'>\n capabilities?: ReadonlyArray<\n 'reasoning' | 'tool_calling' | 'structured_outputs'\n >\n tools?: ReadonlyArray<BytePlusProviderToolKind>\n }\n context_window?: number\n max_input_tokens?: number\n max_output_tokens?: number\n}\n\n// ============================================================================\n// Chat models (Seed / GLM / DeepSeek / gpt-oss on Ark)\n// ============================================================================\n\nconst DOLA_SEED_2_1_TURBO = {\n name: 'dola-seed-2-1-turbo-260628',\n context_window: 256_000,\n max_input_tokens: 256_000,\n max_output_tokens: 256_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_2_0_LITE_260428 = {\n name: 'seed-2-0-lite-260428',\n context_window: 256_000,\n max_input_tokens: 256_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text', 'image', 'video', 'audio'],\n output: ['text'],\n // Live-probed 2026-07-31: rejects both json_schema and json_object.\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_2_0_MINI_260428 = {\n name: 'seed-2-0-mini-260428',\n context_window: 256_000,\n max_input_tokens: 256_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text', 'image', 'video', 'audio'],\n output: ['text'],\n // Live-probed 2026-07-31: rejects both json_schema and json_object.\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_2_0_PRO_260328 = {\n name: 'seed-2-0-pro-260328',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n // Live-probed 2026-07-31: accepts json_schema, despite the docs table\n // saying otherwise.\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_2_0_LITE_260228 = {\n name: 'seed-2-0-lite-260228',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_2_0_MINI_260215 = {\n name: 'seed-2-0-mini-260215',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_2_0_CODE_PREVIEW_260328 = {\n name: 'seed-2-0-code-preview-260328',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_1_8_251228 = {\n name: 'seed-1-8-251228',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 64_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_1_6_250915 = {\n name: 'seed-1-6-250915',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 32_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_1_6_250615 = {\n name: 'seed-1-6-250615',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 32_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_1_6_FLASH_250715 = {\n name: 'seed-1-6-flash-250715',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 32_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst SEED_1_6_FLASH_250615 = {\n name: 'seed-1-6-flash-250615',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 32_000,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst GLM_5_2_260617 = {\n name: 'glm-5-2-260617',\n context_window: 1_024_000,\n max_input_tokens: 1_024_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text'],\n output: ['text'],\n // Live-probed 2026-07-31: accepts json_schema, despite the docs table\n // saying otherwise.\n capabilities: ['reasoning', 'tool_calling', 'structured_outputs'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst GLM_4_7_251222 = {\n name: 'glm-4-7-251222',\n context_window: 256_000,\n max_input_tokens: 224_000,\n max_output_tokens: 128_000,\n supports: {\n input: ['text'],\n output: ['text'],\n // Adherence-probed 2026-07-31: ACCEPTS a json_schema with 200 but ignores\n // it and answers in prose, so it is not a structured-output model. A\n // status-code-only probe reads this as support — see the note on\n // BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS.\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst DEEPSEEK_V4_PRO_260425 = {\n name: 'deepseek-v4-pro-260425',\n context_window: 1_024_000,\n max_input_tokens: 1_024_000,\n max_output_tokens: 384_000,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\nconst DEEPSEEK_V4_FLASH_260425 = {\n name: 'deepseek-v4-flash-260425',\n context_window: 1_024_000,\n max_input_tokens: 1_024_000,\n max_output_tokens: 384_000,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\n// The one model on Ark that defaults to `thinking: disabled`.\nconst DEEPSEEK_V3_2_251201 = {\n name: 'deepseek-v3-2-251201',\n context_window: 128_000,\n max_input_tokens: 128_000,\n max_output_tokens: 32_000,\n supports: {\n input: ['text'],\n output: ['text'],\n // Live-probed 2026-07-31: rejects both json_schema and json_object.\n capabilities: ['reasoning', 'tool_calling'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\n// The only model accepting `thinking: {type: 'auto'}`. Tool calling is\n// undocumented on Ark and unverified, so it is not advertised.\nconst GPT_OSS_120B_250805 = {\n name: 'gpt-oss-120b-250805',\n context_window: 128_000,\n max_input_tokens: 96_000,\n max_output_tokens: 64_000,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning'],\n tools: [] as const,\n },\n} as const satisfies ModelMeta\n\n/**\n * All supported BytePlus chat model identifiers.\n */\nexport const BYTEPLUS_CHAT_MODELS = [\n DOLA_SEED_2_1_TURBO.name,\n SEED_2_0_LITE_260428.name,\n SEED_2_0_MINI_260428.name,\n SEED_2_0_PRO_260328.name,\n SEED_2_0_LITE_260228.name,\n SEED_2_0_MINI_260215.name,\n SEED_2_0_CODE_PREVIEW_260328.name,\n SEED_1_8_251228.name,\n SEED_1_6_250915.name,\n SEED_1_6_250615.name,\n SEED_1_6_FLASH_250715.name,\n SEED_1_6_FLASH_250615.name,\n GLM_5_2_260617.name,\n GLM_4_7_251222.name,\n DEEPSEEK_V4_PRO_260425.name,\n DEEPSEEK_V4_FLASH_260425.name,\n DEEPSEEK_V3_2_251201.name,\n GPT_OSS_120B_250805.name,\n] as const\n\n/**\n * Union of all supported BytePlus chat model names.\n */\nexport type BytePlusChatModel = (typeof BYTEPLUS_CHAT_MODELS)[number]\n\n/**\n * Chat models that emit a `encrypted_content` blob alongside\n * `reasoning_content` when thinking is enabled (\"thinking summary\" models).\n *\n * The blob is an opaque signature over the reasoning trace: when it is\n * present it must be echoed back verbatim on the assistant message in the\n * next turn. Live probing showed omitting it did *not* fail a simple tool\n * round-trip, so adapters preserve and replay it when present but must never\n * treat its absence as an error.\n */\nexport const BYTEPLUS_THINKING_SUMMARY_MODELS = [\n DOLA_SEED_2_1_TURBO.name,\n SEED_2_0_LITE_260428.name,\n SEED_2_0_MINI_260428.name,\n SEED_2_0_PRO_260328.name,\n] as const\n\n/**\n * Union of chat models that emit `encrypted_content`.\n */\nexport type BytePlusThinkingSummaryModel =\n (typeof BYTEPLUS_THINKING_SUMMARY_MODELS)[number]\n\nconst THINKING_SUMMARY_MODEL_SET: ReadonlySet<string> = new Set(\n BYTEPLUS_THINKING_SUMMARY_MODELS,\n)\n\n/**\n * True when the model emits `encrypted_content` that should be round-tripped\n * on subsequent turns.\n */\nexport function emitsEncryptedContent(model: string): boolean {\n return THINKING_SUMMARY_MODEL_SET.has(model)\n}\n\n/**\n * Chat models that accept `response_format: {type: 'json_schema'}`.\n *\n * Live-probed against all 18 chat models on 2026-07-31, not docs-derived — the\n * BytePlus capability tables are wrong here in both directions.\n *\n * Membership needs TWO things, because the API has both failure modes:\n * 1. The request is accepted. Seven models (`seed-2-0-lite-260428`,\n * `seed-2-0-mini-260428`, `seed-2-0-code-preview-260328`, both\n * `deepseek-v4-*`, `deepseek-v3-2-251201`, `gpt-oss-120b-250805`) answer a\n * JSON schema with 400 InvalidParameter — and reject\n * `{type: 'json_object'}` too, so there is no JSON-mode fallback.\n * 2. The schema is actually honoured. `glm-4-7-251222` accepts the request\n * with 200 and then ignores the schema, answering in prose (reproduced\n * twice by the adherence probe). A status-code-only probe wrongly reads\n * that as support, so it is excluded.\n *\n * Models that fail either check need tool-shaped extraction instead.\n *\n * Note that the default chat model `seed-2-0-lite-260428` is one of the\n * rejecting models: structured-output work needs `seed-2-0-lite-260228` or\n * `dola-seed-2-1-turbo-260628`.\n */\nexport const BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS = [\n DOLA_SEED_2_1_TURBO.name,\n SEED_2_0_PRO_260328.name,\n SEED_2_0_LITE_260228.name,\n SEED_2_0_MINI_260215.name,\n SEED_1_8_251228.name,\n SEED_1_6_250915.name,\n SEED_1_6_250615.name,\n SEED_1_6_FLASH_250715.name,\n SEED_1_6_FLASH_250615.name,\n GLM_5_2_260617.name,\n] as const\n\nconst STRUCTURED_OUTPUT_MODEL_SET: ReadonlySet<string> = new Set(\n BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS,\n)\n\n/**\n * True when the model supports native JSON-schema structured output.\n */\nexport function supportsStructuredOutput(model: string): boolean {\n return STRUCTURED_OUTPUT_MODEL_SET.has(model)\n}\n\n/**\n * Type-only map from chat model name to whether it supports native\n * JSON-schema structured output.\n */\nexport type BytePlusChatModelStructuredOutputByName = {\n [K in BytePlusChatModel]: K extends BytePlusStructuredOutputChatModel\n ? true\n : false\n}\n\n/**\n * Union of chat models supporting native JSON-schema structured output.\n */\nexport type BytePlusStructuredOutputChatModel =\n (typeof BYTEPLUS_STRUCTURED_OUTPUT_CHAT_MODELS)[number]\n\n/**\n * Type-only map from chat model name to its supported input modalities.\n * Used for type inference when constructing multimodal messages.\n */\nexport type BytePlusModelInputModalitiesByName = {\n [DOLA_SEED_2_1_TURBO.name]: typeof DOLA_SEED_2_1_TURBO.supports.input\n [SEED_2_0_LITE_260428.name]: typeof SEED_2_0_LITE_260428.supports.input\n [SEED_2_0_MINI_260428.name]: typeof SEED_2_0_MINI_260428.supports.input\n [SEED_2_0_PRO_260328.name]: typeof SEED_2_0_PRO_260328.supports.input\n [SEED_2_0_LITE_260228.name]: typeof SEED_2_0_LITE_260228.supports.input\n [SEED_2_0_MINI_260215.name]: typeof SEED_2_0_MINI_260215.supports.input\n [SEED_2_0_CODE_PREVIEW_260328.name]: typeof SEED_2_0_CODE_PREVIEW_260328.supports.input\n [SEED_1_8_251228.name]: typeof SEED_1_8_251228.supports.input\n [SEED_1_6_250915.name]: typeof SEED_1_6_250915.supports.input\n [SEED_1_6_250615.name]: typeof SEED_1_6_250615.supports.input\n [SEED_1_6_FLASH_250715.name]: typeof SEED_1_6_FLASH_250715.supports.input\n [SEED_1_6_FLASH_250615.name]: typeof SEED_1_6_FLASH_250615.supports.input\n [GLM_5_2_260617.name]: typeof GLM_5_2_260617.supports.input\n [GLM_4_7_251222.name]: typeof GLM_4_7_251222.supports.input\n [DEEPSEEK_V4_PRO_260425.name]: typeof DEEPSEEK_V4_PRO_260425.supports.input\n [DEEPSEEK_V4_FLASH_260425.name]: typeof DEEPSEEK_V4_FLASH_260425.supports.input\n [DEEPSEEK_V3_2_251201.name]: typeof DEEPSEEK_V3_2_251201.supports.input\n [GPT_OSS_120B_250805.name]: typeof GPT_OSS_120B_250805.supports.input\n}\n\n/**\n * Type-only map from chat model name to its supported provider tools.\n * BytePlus exposes no provider-tool factories, so every model gets an empty\n * tuple — passing another provider's tool is then a compile-time error.\n */\nexport type BytePlusChatModelToolCapabilitiesByName = {\n [K in BytePlusChatModel]: ReadonlyArray<BytePlusProviderToolKind>\n}\n\n/**\n * Type-only map from chat model name to its provider options type.\n */\nexport type BytePlusChatModelProviderOptionsByName = {\n [K in BytePlusChatModel]: BytePlusTextProviderOptions\n}\n\n// ============================================================================\n// Video models (Seedance, async task API)\n// ============================================================================\n\n/**\n * Output aspect ratios accepted by the Seedance task API. `adaptive` is only\n * meaningful for image-to-video, where the ratio follows the input frame.\n */\nexport type BytePlusVideoRatio =\n | '16:9'\n | '9:16'\n | '4:3'\n | '3:4'\n | '1:1'\n | '21:9'\n | 'adaptive'\n\n/**\n * Resolution tiers accepted by the Seedance task API.\n *\n * All four are probe-verified per model (2026-07-31). Two findings contradict\n * the BytePlus prose docs: there is **no 2K tier on any Seedance model** —\n * `2k`/`2K` is rejected everywhere, including on the 2.0 flagship documented\n * as reaching 4K — and `4k` exists only on `dreamina-seedance-2-0-260128`.\n *\n * The API matches this field case-insensitively (`4K`, `4k` and `1080P` are\n * all accepted), so this package standardizes on the lowercase spelling.\n */\nexport type BytePlusVideoResolution = '480p' | '720p' | '1080p' | '4k'\n\n/**\n * Generic `size` template for Seedance models: either a bare aspect ratio\n * (\"16:9\") or `ratio_resolution` (\"16:9_720p\"). The Seedance API takes the\n * two as separate `ratio` / `resolution` fields; the adapter splits this\n * template back apart.\n */\nexport type BytePlusVideoSize<\n TResolution extends BytePlusVideoResolution = BytePlusVideoResolution,\n> = BytePlusVideoRatio | `${BytePlusVideoRatio}_${TResolution}`\n\n// The Seedance 2.0 family's `audio` input modality is docs-derived, not\n// live-probed: the docs' multimodal-reference caps list `reference_audio`\n// parts (up to 3, never sent without a visual reference). Every 2.0 model id\n// below is itself probe-verified live; only the audio-reference capability\n// rests on the docs.\nconst DREAMINA_SEEDANCE_2_0 = {\n name: 'dreamina-seedance-2-0-260128',\n supports: {\n input: ['text', 'image', 'video', 'audio'],\n output: ['video', 'audio'],\n },\n} as const satisfies ModelMeta\n\nconst DREAMINA_SEEDANCE_2_0_FAST = {\n name: 'dreamina-seedance-2-0-fast-260128',\n supports: {\n input: ['text', 'image', 'video', 'audio'],\n output: ['video', 'audio'],\n },\n} as const satisfies ModelMeta\n\nconst DREAMINA_SEEDANCE_2_0_MINI = {\n name: 'dreamina-seedance-2-0-mini-260615',\n supports: {\n input: ['text', 'image', 'video', 'audio'],\n output: ['video', 'audio'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDANCE_1_5_PRO = {\n name: 'seedance-1-5-pro-251215',\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDANCE_1_0_PRO = {\n name: 'seedance-1-0-pro-250528',\n supports: {\n input: ['text', 'image'],\n output: ['video'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDANCE_1_0_PRO_FAST = {\n name: 'seedance-1-0-pro-fast-251015',\n supports: {\n input: ['text', 'image'],\n output: ['video'],\n },\n} as const satisfies ModelMeta\n\n/**\n * All supported Seedance video model identifiers.\n */\nexport const BYTEPLUS_VIDEO_MODELS = [\n DREAMINA_SEEDANCE_2_0.name,\n DREAMINA_SEEDANCE_2_0_FAST.name,\n DREAMINA_SEEDANCE_2_0_MINI.name,\n SEEDANCE_1_5_PRO.name,\n SEEDANCE_1_0_PRO.name,\n SEEDANCE_1_0_PRO_FAST.name,\n] as const\n\n/**\n * Union of all supported Seedance video model names.\n */\nexport type BytePlusVideoModel = (typeof BYTEPLUS_VIDEO_MODELS)[number]\n\n/**\n * Type-only map from video model name to the non-text prompt modalities it\n * accepts. The Seedance 2.0 family takes multimodal references (start/end\n * frames, reference images, reference video and audio); the 1.x models take\n * start/end frames only.\n */\nexport type BytePlusVideoModelInputModalitiesByName = {\n [DREAMINA_SEEDANCE_2_0.name]: readonly ['image', 'video', 'audio']\n [DREAMINA_SEEDANCE_2_0_FAST.name]: readonly ['image', 'video', 'audio']\n [DREAMINA_SEEDANCE_2_0_MINI.name]: readonly ['image', 'video', 'audio']\n [SEEDANCE_1_5_PRO.name]: readonly ['image']\n [SEEDANCE_1_0_PRO.name]: readonly ['image']\n [SEEDANCE_1_0_PRO_FAST.name]: readonly ['image']\n}\n\n/**\n * Type-only map from video model name to the resolutions it accepts.\n *\n * Probe-verified per model on 2026-07-31. Note `seedance-1-0-pro-fast-251015`\n * does accept `1080p`, despite the BytePlus docs listing it as 480p/720p.\n */\nexport type BytePlusVideoModelResolutionByName = {\n [DREAMINA_SEEDANCE_2_0.name]: '480p' | '720p' | '1080p' | '4k'\n [DREAMINA_SEEDANCE_2_0_FAST.name]: '480p' | '720p'\n [DREAMINA_SEEDANCE_2_0_MINI.name]: '480p' | '720p'\n [SEEDANCE_1_5_PRO.name]: '480p' | '720p' | '1080p'\n [SEEDANCE_1_0_PRO.name]: '480p' | '720p' | '1080p'\n [SEEDANCE_1_0_PRO_FAST.name]: '480p' | '720p' | '1080p'\n}\n\n/**\n * Type-only map from video model name to its accepted `size` strings.\n */\nexport type BytePlusVideoModelSizeByName = {\n [K in BytePlusVideoModel]: BytePlusVideoSize<\n BytePlusVideoModelResolutionByName[K]\n >\n}\n\n/**\n * A Seedance model id: one this package knows, or any other string.\n *\n * The open half is a deliberate escape hatch for models BytePlus ships between\n * releases of this package. **Seedance 2.5 is the live example.** Its real id\n * is `dreamina-seedance-2-5-260628` — note the June date suffix, which is why\n * guessing ids around its 2026-07-31 announcement never landed. It is absent\n * from the table below because its capability cells are unverified, not\n * because it is unreachable: probing it returns 404 `ModelNotOpen` (\"your\n * account has not activated the model\"), so no capability question can be\n * answered until someone enables it in the Ark Console. Passing it through\n * the escape hatch works today for an account that has.\n *\n * Adding a model here *narrows* it — the adapter's guards switch on and reject\n * against this file's tables. For a model whose real limits are unknown that\n * is strictly worse than the open path, which lets Ark judge. So an id lands\n * here only once probed.\n *\n * Discovering ids: `GET /models` on the Ark data plane enumerates the catalog\n * (id, `task_type`, `modalities`, `status`) and is how 2.5 was found. It is\n * not exhaustive — `seedream-5-0-lite-260128` answers requests but is missing\n * from the listing — so absence there is not evidence of absence. The ModelArk\n * release notes (https://docs.byteplus.com/en/docs/ModelArk/1159178) are the\n * other watch surface.\n *\n * To probe an id, POST `/contents/generations/tasks` with only\n * `{\"model\": \"<id>\"}`. Three outcomes, all live-verified:\n * - 400 `MissingParameter` (about `content`) — live and usable.\n * - 404 `ModelNotOpen` — real, but not activated on this account.\n * - 404 `InvalidEndpointOrModel.NotFound` — no such model.\n *\n * Unknown ids trade compile-time narrowing for reach: the full size surface is\n * accepted, provider options are ungated, and the adapter's model-specific\n * runtime guards stand down so a new model's legitimate request reaches Ark.\n * Known ids keep their probe-verified narrowing.\n */\nexport type BytePlusVideoModelOrString = BytePlusVideoModel | (string & {})\n\n/**\n * Resolve the `size` type for a video model: the model's probe-verified\n * template union when known, otherwise the full template surface plus any\n * string (a future model may bring ratios or resolution tiers that do not\n * exist today).\n */\nexport type ResolveBytePlusVideoSize<TModel extends string> =\n TModel extends BytePlusVideoModel\n ? BytePlusVideoModelSizeByName[TModel]\n : BytePlusVideoSize | (string & {})\n\n/**\n * Resolve the accepted non-text prompt modalities for a video model. Unknown\n * models accept all three rather than none, so a new model's reference media\n * is not a compile error.\n */\nexport type ResolveBytePlusVideoInputModalities<TModel extends string> =\n TModel extends BytePlusVideoModel\n ? BytePlusVideoModelInputModalitiesByName[TModel]\n : readonly ['image', 'video', 'audio']\n\nconst VIDEO_MODEL_SET: ReadonlySet<string> = new Set(BYTEPLUS_VIDEO_MODELS)\n\n/**\n * True when the id is one this package has probe-verified metadata for.\n *\n * The adapter uses this to decide whether its model-specific guards apply:\n * see {@link BytePlusVideoModelOrString}.\n */\nexport function isKnownBytePlusVideoModel(\n model: string,\n): model is BytePlusVideoModel {\n return VIDEO_MODEL_SET.has(model)\n}\n\n/**\n * Per-model duration type. Seedance accepts any integer second inside the\n * model's range, so this is a continuous range expressed as `number` — a\n * literal union cannot represent it. (The API also accepts `duration: -1` on\n * Seedance 2.0 and 1.5-pro to let the model choose; that is reachable through\n * provider options, not through the generic `duration`.)\n */\nexport type BytePlusVideoModelDurationByName = {\n [K in BytePlusVideoModel]: number\n}\n\n/**\n * Runtime duration table backing `availableDurations()` / `snapDuration()`.\n */\nexport const BYTEPLUS_VIDEO_DURATIONS: {\n readonly [TModel in BytePlusVideoModel]: DurationOptions<\n BytePlusVideoModelDurationByName[TModel]\n >\n} = {\n 'dreamina-seedance-2-0-260128': {\n kind: 'range',\n min: 4,\n max: 15,\n step: 1,\n unit: 'seconds',\n },\n 'dreamina-seedance-2-0-fast-260128': {\n kind: 'range',\n min: 4,\n max: 15,\n step: 1,\n unit: 'seconds',\n },\n 'dreamina-seedance-2-0-mini-260615': {\n kind: 'range',\n min: 4,\n max: 15,\n step: 1,\n unit: 'seconds',\n },\n 'seedance-1-5-pro-251215': {\n kind: 'range',\n min: 4,\n max: 12,\n step: 1,\n unit: 'seconds',\n },\n 'seedance-1-0-pro-250528': {\n kind: 'range',\n min: 2,\n max: 12,\n step: 1,\n unit: 'seconds',\n },\n 'seedance-1-0-pro-fast-251015': {\n kind: 'range',\n min: 2,\n max: 12,\n step: 1,\n unit: 'seconds',\n },\n}\n\n/**\n * Duration hint for a model this package has no table for.\n *\n * Spans every range Seedance has shipped so far (2s on the 1.0 models through\n * 15s on the 2.0 family) so `availableDurations()` can still drive a UI. It is\n * a hint, not a contract: the adapter does **not** snap an unknown model's\n * duration against it, because clamping a future model's legitimate 20-second\n * request down to 15 would corrupt the request rather than protect it.\n */\nexport const BYTEPLUS_VIDEO_FALLBACK_DURATIONS: DurationOptions<number> = {\n kind: 'range',\n min: 2,\n max: 15,\n step: 1,\n unit: 'seconds',\n}\n\n/**\n * Look up the duration options for a Seedance video model, falling back to\n * {@link BYTEPLUS_VIDEO_FALLBACK_DURATIONS} for an id this package does not\n * know.\n */\nexport function getBytePlusVideoDurationOptions(\n model: BytePlusVideoModelOrString,\n): DurationOptions<number> {\n return isKnownBytePlusVideoModel(model)\n ? BYTEPLUS_VIDEO_DURATIONS[model]\n : BYTEPLUS_VIDEO_FALLBACK_DURATIONS\n}\n\n// ============================================================================\n// Image models (Seedream)\n// ============================================================================\n\n/**\n * Shorthand size tokens accepted by `/images/generations`. A request uses\n * either a token or an explicit `WxH` string — never both.\n */\nexport type BytePlusImageSizeToken = '1K' | '2K' | '4K'\n\n/**\n * Accepted `size` values for Seedream models: a shorthand token or an\n * explicit pixel size such as `2048x2048`.\n */\nexport type BytePlusImageSize = BytePlusImageSizeToken | `${number}x${number}`\n\nconst DOLA_SEEDREAM_5_0_PRO = {\n name: 'dola-seedream-5-0-pro-260628',\n supports: {\n input: ['text', 'image'],\n output: ['image'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDREAM_5_0 = {\n name: 'seedream-5-0-260128',\n supports: {\n input: ['text', 'image'],\n output: ['image'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDREAM_5_0_LITE = {\n name: 'seedream-5-0-lite-260128',\n supports: {\n input: ['text', 'image'],\n output: ['image'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDREAM_4_5 = {\n name: 'seedream-4-5-251128',\n supports: {\n input: ['text', 'image'],\n output: ['image'],\n },\n} as const satisfies ModelMeta\n\nconst SEEDREAM_4_0 = {\n name: 'seedream-4-0-250828',\n supports: {\n input: ['text', 'image'],\n output: ['image'],\n },\n} as const satisfies ModelMeta\n\n/**\n * All supported Seedream image model identifiers.\n */\nexport const BYTEPLUS_IMAGE_MODELS = [\n DOLA_SEEDREAM_5_0_PRO.name,\n SEEDREAM_5_0.name,\n SEEDREAM_5_0_LITE.name,\n SEEDREAM_4_5.name,\n SEEDREAM_4_0.name,\n] as const\n\n/**\n * Union of all supported Seedream image model names.\n */\nexport type BytePlusImageModel = (typeof BYTEPLUS_IMAGE_MODELS)[number]\n\n/**\n * Type-only map from image model name to its accepted `size` strings.\n */\nexport type BytePlusImageModelSizeByName = {\n [K in BytePlusImageModel]: BytePlusImageSize\n}\n\n/**\n * Maximum number of reference images accepted per editing request.\n * Seedream 5.0 Pro caps at 10 references; the other editing-capable models\n * accept up to 14.\n *\n * Docs-derived, not live-probed. The 14 for `seedream-5-0-260128` is weaker\n * still — the docs never state a cap for that model, so it is inferred from\n * the rest of the family.\n */\nexport const BYTEPLUS_IMAGE_MAX_REFERENCE_IMAGES: {\n readonly [K in BytePlusImageModel]: number\n} = {\n 'dola-seedream-5-0-pro-260628': 10,\n 'seedream-5-0-260128': 14,\n 'seedream-5-0-lite-260128': 14,\n 'seedream-4-5-251128': 14,\n 'seedream-4-0-250828': 14,\n}\n\n// ============================================================================\n// Seed Speech models (voice host — separate product and API key)\n// ============================================================================\n\nconst SEED_AUDIO_1_0 = {\n name: 'seed-audio-1.0',\n supports: {\n input: ['text', 'audio'],\n output: ['audio'],\n },\n} as const satisfies ModelMeta\n\n// Seed Speech ASR is endpoint-addressed: `POST /api/v3/auc/bigmodel/recognize/\n// flash` selects the model through the `X-Api-Resource-Id` header\n// (`volc.seedasr.auc_turbo`) and takes no `model` field in the body. This\n// synthetic identifier satisfies the SDK's `TranscriptionOptions.model`\n// contract and gives logging and fixture matching a stable value.\nconst SEED_ASR = {\n name: 'seed-asr',\n supports: {\n input: ['audio'],\n output: ['text'],\n },\n} as const satisfies ModelMeta\n\n/**\n * All supported Seed Speech TTS model identifiers.\n *\n * Note: TTS runs on `voice.ap-southeast-1.bytepluses.com` with an\n * `X-Api-Key` header and a *different* API key from Ark.\n */\nexport const BYTEPLUS_TTS_MODELS = [SEED_AUDIO_1_0.name] as const\n\n/**\n * All supported Seed Speech transcription model identifiers.\n */\nexport const BYTEPLUS_TRANSCRIPTION_MODELS = [SEED_ASR.name] as const\n\n/**\n * Union of all supported Seed Speech TTS model names.\n */\nexport type BytePlusTTSModel = (typeof BYTEPLUS_TTS_MODELS)[number]\n\n/**\n * Union of all supported Seed Speech transcription model names.\n */\nexport type BytePlusTranscriptionModel =\n (typeof BYTEPLUS_TRANSCRIPTION_MODELS)[number]\n\n// ============================================================================\n// Type resolution helpers\n// ============================================================================\n\n/**\n * Resolve provider options for a specific model. Models listed in the chat\n * map get their explicit options; anything else falls back to the base chat\n * options.\n */\nexport type ResolveProviderOptions<TModel extends string> =\n TModel extends keyof BytePlusChatModelProviderOptionsByName\n ? BytePlusChatModelProviderOptionsByName[TModel]\n : BytePlusTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model. Models missing from the map\n * are treated as text-only.\n */\nexport type ResolveInputModalities<TModel extends string> =\n TModel extends keyof BytePlusModelInputModalitiesByName\n ? BytePlusModelInputModalitiesByName[TModel]\n : readonly ['text']\n"],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BytePlus ModelArk chat provider options.
|
|
3
|
+
*
|
|
4
|
+
* Ark's `/chat/completions` is OpenAI-compatible, so most of this is the
|
|
5
|
+
* familiar sampling surface; `thinking`, `reasoning_effort`,
|
|
6
|
+
* `repetition_penalty` and `service_tier` are the Ark-only additions.
|
|
7
|
+
*
|
|
8
|
+
* Every field below was accepted by a live request against
|
|
9
|
+
* `https://ark.ap-southeast.bytepluses.com/api/v3` on 2026-07-31. Two probe
|
|
10
|
+
* results are encoded as TSDoc warnings rather than types because they are
|
|
11
|
+
* cross-field constraints TypeScript can't express: `max_tokens` and
|
|
12
|
+
* `max_completion_tokens` are mutually exclusive, and `reasoning_effort`
|
|
13
|
+
* combined with `thinking: {type: 'disabled'}` is a 400.
|
|
14
|
+
*
|
|
15
|
+
* `response_format` is deliberately absent — the chat activity owns it via
|
|
16
|
+
* `outputSchema` / structured output, and Ark rejects `json_object` outright
|
|
17
|
+
* on every model.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Reasoning ("deep thinking") switch.
|
|
21
|
+
*
|
|
22
|
+
* - `enabled` — the model reasons before answering (default on every model
|
|
23
|
+
* except `deepseek-v3-2-251201`, where reasoning defaults to off).
|
|
24
|
+
* - `disabled` — skip reasoning.
|
|
25
|
+
* - `auto` — let the model decide. Only accepted by `gpt-oss-120b-250805`.
|
|
26
|
+
*/
|
|
27
|
+
export interface BytePlusThinkingOption {
|
|
28
|
+
type: 'enabled' | 'disabled' | 'auto';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Reasoning budget hint. `none` and `xhigh` are only accepted by
|
|
32
|
+
* `glm-5-2-260617`; `max` by `glm-5-2-260617` and the `deepseek-v4-*` models.
|
|
33
|
+
*
|
|
34
|
+
* Cannot be combined with `thinking: {type: 'disabled'}` — Ark rejects the
|
|
35
|
+
* pair with `400 InvalidParameter` ("Invalid combination of reasoning_effort
|
|
36
|
+
* and thinking type").
|
|
37
|
+
*/
|
|
38
|
+
export type BytePlusReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
39
|
+
/**
|
|
40
|
+
* Request routing tier. `flex` routes to the batch queue at a lower price with
|
|
41
|
+
* no latency guarantee; `default` is the standard online tier.
|
|
42
|
+
*/
|
|
43
|
+
export type BytePlusServiceTier = 'default' | 'flex';
|
|
44
|
+
/**
|
|
45
|
+
* Forces the model to call one specific function.
|
|
46
|
+
*/
|
|
47
|
+
export interface BytePlusNamedToolChoice {
|
|
48
|
+
type: 'function';
|
|
49
|
+
function: {
|
|
50
|
+
name: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Controls which (if any) tool the model calls.
|
|
55
|
+
*/
|
|
56
|
+
export type BytePlusToolChoice = 'none' | 'auto' | 'required' | BytePlusNamedToolChoice;
|
|
57
|
+
/**
|
|
58
|
+
* Provider options for BytePlus chat models.
|
|
59
|
+
*/
|
|
60
|
+
export interface BytePlusTextProviderOptions {
|
|
61
|
+
/** Reasoning switch — see {@link BytePlusThinkingOption}. */
|
|
62
|
+
thinking?: BytePlusThinkingOption;
|
|
63
|
+
/** Reasoning budget hint — see {@link BytePlusReasoningEffort}. */
|
|
64
|
+
reasoning_effort?: BytePlusReasoningEffort;
|
|
65
|
+
/**
|
|
66
|
+
* Penalty applied to repeated tokens. Values above 1 discourage repetition.
|
|
67
|
+
*/
|
|
68
|
+
repetition_penalty?: number;
|
|
69
|
+
/** Request routing tier — see {@link BytePlusServiceTier}. */
|
|
70
|
+
service_tier?: BytePlusServiceTier;
|
|
71
|
+
/** Sampling temperature. Higher values produce more varied output. */
|
|
72
|
+
temperature?: number;
|
|
73
|
+
/** Nucleus sampling cutoff. */
|
|
74
|
+
top_p?: number;
|
|
75
|
+
/** Restricts sampling to the `k` most likely tokens. */
|
|
76
|
+
top_k?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Maximum tokens to generate. Mutually exclusive with
|
|
79
|
+
* `max_completion_tokens` — sending both is a 400.
|
|
80
|
+
*/
|
|
81
|
+
max_tokens?: number;
|
|
82
|
+
/**
|
|
83
|
+
* OpenAI's newer name for {@link BytePlusTextProviderOptions.max_tokens}.
|
|
84
|
+
* Mutually exclusive with it.
|
|
85
|
+
*/
|
|
86
|
+
max_completion_tokens?: number;
|
|
87
|
+
/** Penalizes tokens by how often they have already appeared. */
|
|
88
|
+
frequency_penalty?: number;
|
|
89
|
+
/** Penalizes tokens that have appeared at all, regardless of count. */
|
|
90
|
+
presence_penalty?: number;
|
|
91
|
+
/** Up to four strings that stop generation when produced. */
|
|
92
|
+
stop?: string | Array<string>;
|
|
93
|
+
/** Number of completions to generate. */
|
|
94
|
+
n?: number;
|
|
95
|
+
/** Best-effort determinism hint for repeated identical requests. */
|
|
96
|
+
seed?: number;
|
|
97
|
+
/** Return log probabilities for the generated tokens. */
|
|
98
|
+
logprobs?: boolean;
|
|
99
|
+
/** How many alternatives to report per token. Requires `logprobs`. */
|
|
100
|
+
top_logprobs?: number;
|
|
101
|
+
/** Additive bias per token id, applied before sampling. */
|
|
102
|
+
logit_bias?: Record<string, number>;
|
|
103
|
+
/** Opaque end-user identifier forwarded for abuse monitoring. */
|
|
104
|
+
user?: string;
|
|
105
|
+
/** Whether the model may emit several tool calls in one turn. */
|
|
106
|
+
parallel_tool_calls?: boolean;
|
|
107
|
+
/** Tool-selection strategy — see {@link BytePlusToolChoice}. */
|
|
108
|
+
tool_choice?: BytePlusToolChoice;
|
|
109
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { ClientOptions } from 'openai';
|
|
2
|
+
/**
|
|
3
|
+
* BytePlus splits its APIs across two hosts with two different products,
|
|
4
|
+
* two different auth headers, and two different API keys:
|
|
5
|
+
*
|
|
6
|
+
* - **Ark (ModelArk)** — chat, video (Seedance) and image (Seedream).
|
|
7
|
+
* `Authorization: Bearer $ARK_API_KEY`.
|
|
8
|
+
* - **Seed Speech** — TTS and ASR on the voice host.
|
|
9
|
+
* `X-Api-Key: $BYTEPLUS_VOICE_API_KEY`.
|
|
10
|
+
*
|
|
11
|
+
* Ark keys are region-isolated: a key issued for `ap-southeast` does not work
|
|
12
|
+
* against the EU host and vice versa.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Default Ark data-plane base URL (Asia-Pacific south-east).
|
|
16
|
+
*
|
|
17
|
+
* Per the BytePlus docs the EU endpoint
|
|
18
|
+
* (`https://ark.eu-west.bytepluses.com/api/v3`) serves chat and image only —
|
|
19
|
+
* Seedance video is not available there. Docs-derived: only the ap-southeast
|
|
20
|
+
* host was exercised live.
|
|
21
|
+
*/
|
|
22
|
+
export declare const BYTEPLUS_ARK_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
|
|
23
|
+
/**
|
|
24
|
+
* Default Seed Speech base URL. Endpoint paths are appended under
|
|
25
|
+
* `/api/v3` (e.g. `/api/v3/tts/create`).
|
|
26
|
+
*/
|
|
27
|
+
export declare const BYTEPLUS_VOICE_BASE_URL = "https://voice.ap-southeast-1.bytepluses.com";
|
|
28
|
+
/**
|
|
29
|
+
* Configuration for the Ark-hosted adapters (chat, video, image).
|
|
30
|
+
*
|
|
31
|
+
* Extends the OpenAI SDK's client options because the chat adapter drives the
|
|
32
|
+
* OpenAI-compatible `/chat/completions` endpoint through the shared
|
|
33
|
+
* `@tanstack/openai-base` adapter. `fetch` and `defaultHeaders` are inherited
|
|
34
|
+
* from `ClientOptions`, and the video/image adapters — which issue plain JSON
|
|
35
|
+
* requests rather than SDK calls — honour the same two fields so tests can
|
|
36
|
+
* inject a fetch instead of patching the global one.
|
|
37
|
+
*
|
|
38
|
+
* Two inherited fields differ in reach, because the fetch-based adapters have
|
|
39
|
+
* no SDK to delegate to:
|
|
40
|
+
* - `timeout` is honoured everywhere — the fetch-based adapters convert it to
|
|
41
|
+
* an `AbortSignal` (see {@link bytePlusTimeoutSignal}).
|
|
42
|
+
* - `maxRetries` applies to the **chat adapter only**. The video, image and
|
|
43
|
+
* speech adapters do not retry; video polling is driven by core's loop,
|
|
44
|
+
* which owns its own retry policy.
|
|
45
|
+
*/
|
|
46
|
+
export interface BytePlusArkConfig extends Omit<ClientOptions, 'apiKey'> {
|
|
47
|
+
apiKey: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Configuration for the Seed Speech adapters (TTS, ASR).
|
|
51
|
+
*
|
|
52
|
+
* Seed Speech is not OpenAI-compatible, so this is a minimal config for
|
|
53
|
+
* direct `fetch` calls rather than an OpenAI `ClientOptions` extension.
|
|
54
|
+
*/
|
|
55
|
+
export interface BytePlusVoiceConfig {
|
|
56
|
+
/** Seed Speech API key — *not* the Ark key. Sent as `X-Api-Key`. */
|
|
57
|
+
apiKey: string;
|
|
58
|
+
/** Overrides {@link BYTEPLUS_VOICE_BASE_URL}. */
|
|
59
|
+
baseURL?: string;
|
|
60
|
+
/** Additional headers merged into every request (e.g., test ids). */
|
|
61
|
+
defaultHeaders?: Record<string, string>;
|
|
62
|
+
/**
|
|
63
|
+
* Override the underlying fetch. Defaults to the global `fetch`. Useful for
|
|
64
|
+
* proxying, instrumentation, or pointing requests at a mock in tests.
|
|
65
|
+
*/
|
|
66
|
+
fetch?: typeof fetch;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Gets the BytePlus Ark API key from environment variables, preferring
|
|
70
|
+
* `ARK_API_KEY` and falling back to `BYTEPLUS_API_KEY`.
|
|
71
|
+
* @throws Error if neither variable is set
|
|
72
|
+
*/
|
|
73
|
+
export declare function getBytePlusArkApiKeyFromEnv(): string;
|
|
74
|
+
/**
|
|
75
|
+
* Gets the Seed Speech API key from environment variables.
|
|
76
|
+
*
|
|
77
|
+
* Seed Speech is a separate BytePlus product from Ark with its own key — an
|
|
78
|
+
* Ark key sent as `X-Api-Key` is rejected with `45000010 Invalid X-Api-Key`.
|
|
79
|
+
*
|
|
80
|
+
* @throws Error if BYTEPLUS_VOICE_API_KEY is not found
|
|
81
|
+
*/
|
|
82
|
+
export declare function getBytePlusVoiceApiKeyFromEnv(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Returns an Ark client config with the default Ark base URL applied when not
|
|
85
|
+
* already set, and any trailing slashes trimmed so path joins stay
|
|
86
|
+
* single-slashed.
|
|
87
|
+
*
|
|
88
|
+
* The returned `baseURL` is always a string: adapters that build request paths
|
|
89
|
+
* by interpolation can use it directly without re-applying a default (which
|
|
90
|
+
* would otherwise risk interpolating `undefined` into a URL). The config's own
|
|
91
|
+
* type is preserved, so adapter-specific config fields survive the call.
|
|
92
|
+
*/
|
|
93
|
+
export declare function withBytePlusArkDefaults<TConfig extends BytePlusArkConfig>(config: TConfig): Omit<TConfig, 'baseURL'> & {
|
|
94
|
+
baseURL: string;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Returns a Seed Speech config with the default voice base URL applied (and
|
|
98
|
+
* any trailing slashes trimmed) when not already set.
|
|
99
|
+
*
|
|
100
|
+
* As with {@link withBytePlusArkDefaults}, the returned `baseURL` is always a
|
|
101
|
+
* string, so adapters can interpolate it without re-applying a fallback, and
|
|
102
|
+
* the config's own type is preserved.
|
|
103
|
+
*/
|
|
104
|
+
export declare function withBytePlusVoiceDefaults<TConfig extends BytePlusVoiceConfig>(config: TConfig): Omit<TConfig, 'baseURL'> & {
|
|
105
|
+
baseURL: string;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Normalizes the OpenAI-shaped `defaultHeaders` config field (which accepts a
|
|
109
|
+
* `Headers` instance, an entry list, or a record with nullable values) into
|
|
110
|
+
* the plain record the header builders below take. Non-string values are
|
|
111
|
+
* dropped rather than serialized.
|
|
112
|
+
*
|
|
113
|
+
* Shared by every fetch-based adapter (image, video, speech): they all read
|
|
114
|
+
* `defaultHeaders` off a config typed by the OpenAI SDK but issue plain JSON
|
|
115
|
+
* requests.
|
|
116
|
+
*/
|
|
117
|
+
export declare function toHeaderRecord(headers: BytePlusArkConfig['defaultHeaders']): Record<string, string>;
|
|
118
|
+
/**
|
|
119
|
+
* Turns the OpenAI-shaped `timeout` config field (milliseconds) into the
|
|
120
|
+
* `signal` a plain `fetch` needs, or `undefined` when no timeout is set.
|
|
121
|
+
*
|
|
122
|
+
* `BytePlusArkConfig` extends the OpenAI SDK's `ClientOptions` because the
|
|
123
|
+
* chat adapter drives the SDK, which honours `timeout` and `maxRetries`
|
|
124
|
+
* itself. The video, image and speech adapters issue plain JSON requests, so
|
|
125
|
+
* without this they would accept a `timeout` and ignore it — a stalled Ark
|
|
126
|
+
* connection hanging the caller forever despite an explicit setting.
|
|
127
|
+
*
|
|
128
|
+
* `maxRetries` has no equivalent here and stays SDK-path-only; it is
|
|
129
|
+
* documented as such on {@link BytePlusArkConfig}.
|
|
130
|
+
*/
|
|
131
|
+
export declare function bytePlusTimeoutSignal(timeout: number | undefined): AbortSignal | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Headers for a JSON request against the Ark data plane.
|
|
134
|
+
*
|
|
135
|
+
* A caller-supplied `Authorization` or `Content-Type` in `defaultHeaders` is
|
|
136
|
+
* dropped in any casing — see {@link applyReservedHeaders}.
|
|
137
|
+
*/
|
|
138
|
+
export declare function bytePlusArkHeaders(apiKey: string, extraHeaders?: Record<string, string>): Record<string, string>;
|
|
139
|
+
/**
|
|
140
|
+
* Headers for a JSON request against the Seed Speech host.
|
|
141
|
+
*
|
|
142
|
+
* As with {@link bytePlusArkHeaders}, a caller-supplied `X-Api-Key` is dropped
|
|
143
|
+
* in any casing. Seed Speech answers a clobbered key with
|
|
144
|
+
* `45000010 Invalid X-Api-Key`, which reads as a misconfigured key rather than
|
|
145
|
+
* a header collision.
|
|
146
|
+
*/
|
|
147
|
+
export declare function bytePlusVoiceHeaders(apiKey: string, extraHeaders?: Record<string, string>): Record<string, string>;
|
|
148
|
+
/**
|
|
149
|
+
* Reads a response body as JSON, tolerating the non-JSON failures both
|
|
150
|
+
* BytePlus hosts can return (an empty body, or an HTML error page from a proxy
|
|
151
|
+
* in front of the API).
|
|
152
|
+
*
|
|
153
|
+
* Returns the parsed value, the raw text when it is not JSON, or `undefined`
|
|
154
|
+
* for an empty body — all three of which {@link bytePlusArkError} and
|
|
155
|
+
* {@link bytePlusVoiceError} know how to render.
|
|
156
|
+
*/
|
|
157
|
+
export declare function readJsonBody(response: Response): Promise<unknown>;
|
|
158
|
+
/**
|
|
159
|
+
* Best-effort human-readable rendering of a response body we could not pull a
|
|
160
|
+
* `message` out of — a raw string passes through, any other object is
|
|
161
|
+
* serialized so the detail reaches the error instead of being dropped.
|
|
162
|
+
*
|
|
163
|
+
* Exported for adapters that need to attach a body to an error they raise
|
|
164
|
+
* themselves rather than one derived from a non-OK response — e.g. the image
|
|
165
|
+
* adapter reporting a 200 whose `data[]` items match no known shape.
|
|
166
|
+
*/
|
|
167
|
+
export declare function describeBody(body: unknown): string | undefined;
|
|
168
|
+
/**
|
|
169
|
+
* Formats an Ark error response into an `Error`.
|
|
170
|
+
*
|
|
171
|
+
* Ark uses the OpenAI error envelope with dotted string codes:
|
|
172
|
+
* `{"error": {"code": "InvalidEndpointOrModel.NotFound", "message": "…"}}`.
|
|
173
|
+
* Bodies that don't match (HTML error pages, proxy responses) fall back to
|
|
174
|
+
* the raw text so the failure stays diagnosable.
|
|
175
|
+
*/
|
|
176
|
+
export declare function bytePlusArkError(status: number, body: unknown, context?: string): Error;
|
|
177
|
+
/**
|
|
178
|
+
* Formats a Seed Speech error response into an `Error`.
|
|
179
|
+
*
|
|
180
|
+
* Seed Speech does not use the Ark envelope — it returns a flat numeric code:
|
|
181
|
+
* `{"code": 45000010, "message": "Invalid X-Api-Key"}`.
|
|
182
|
+
*/
|
|
183
|
+
export declare function bytePlusVoiceError(status: number, body: unknown, context?: string): Error;
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { getApiKeyFromEnv } from "@tanstack/ai-utils";
|
|
2
|
+
//#region src/utils/client.ts
|
|
3
|
+
/**
|
|
4
|
+
* BytePlus splits its APIs across two hosts with two different products,
|
|
5
|
+
* two different auth headers, and two different API keys:
|
|
6
|
+
*
|
|
7
|
+
* - **Ark (ModelArk)** — chat, video (Seedance) and image (Seedream).
|
|
8
|
+
* `Authorization: Bearer $ARK_API_KEY`.
|
|
9
|
+
* - **Seed Speech** — TTS and ASR on the voice host.
|
|
10
|
+
* `X-Api-Key: $BYTEPLUS_VOICE_API_KEY`.
|
|
11
|
+
*
|
|
12
|
+
* Ark keys are region-isolated: a key issued for `ap-southeast` does not work
|
|
13
|
+
* against the EU host and vice versa.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Default Ark data-plane base URL (Asia-Pacific south-east).
|
|
17
|
+
*
|
|
18
|
+
* Per the BytePlus docs the EU endpoint
|
|
19
|
+
* (`https://ark.eu-west.bytepluses.com/api/v3`) serves chat and image only —
|
|
20
|
+
* Seedance video is not available there. Docs-derived: only the ap-southeast
|
|
21
|
+
* host was exercised live.
|
|
22
|
+
*/
|
|
23
|
+
var BYTEPLUS_ARK_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
|
|
24
|
+
/**
|
|
25
|
+
* Default Seed Speech base URL. Endpoint paths are appended under
|
|
26
|
+
* `/api/v3` (e.g. `/api/v3/tts/create`).
|
|
27
|
+
*/
|
|
28
|
+
var BYTEPLUS_VOICE_BASE_URL = "https://voice.ap-southeast-1.bytepluses.com";
|
|
29
|
+
/**
|
|
30
|
+
* Gets the BytePlus Ark API key from environment variables, preferring
|
|
31
|
+
* `ARK_API_KEY` and falling back to `BYTEPLUS_API_KEY`.
|
|
32
|
+
* @throws Error if neither variable is set
|
|
33
|
+
*/
|
|
34
|
+
function getBytePlusArkApiKeyFromEnv() {
|
|
35
|
+
try {
|
|
36
|
+
return getApiKeyFromEnv("ARK_API_KEY");
|
|
37
|
+
} catch {
|
|
38
|
+
try {
|
|
39
|
+
return getApiKeyFromEnv("BYTEPLUS_API_KEY");
|
|
40
|
+
} catch {
|
|
41
|
+
throw new Error("ARK_API_KEY or BYTEPLUS_API_KEY is required. Please set one of these environment variables or use the factory function with an explicit API key.");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Gets the Seed Speech API key from environment variables.
|
|
47
|
+
*
|
|
48
|
+
* Seed Speech is a separate BytePlus product from Ark with its own key — an
|
|
49
|
+
* Ark key sent as `X-Api-Key` is rejected with `45000010 Invalid X-Api-Key`.
|
|
50
|
+
*
|
|
51
|
+
* @throws Error if BYTEPLUS_VOICE_API_KEY is not found
|
|
52
|
+
*/
|
|
53
|
+
function getBytePlusVoiceApiKeyFromEnv() {
|
|
54
|
+
try {
|
|
55
|
+
return getApiKeyFromEnv("BYTEPLUS_VOICE_API_KEY");
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error("BYTEPLUS_VOICE_API_KEY is required for Seed Speech (TTS/ASR). This is a different key from ARK_API_KEY. Please set it in your environment variables or use the factory function with an explicit API key.");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Returns an Ark client config with the default Ark base URL applied when not
|
|
62
|
+
* already set, and any trailing slashes trimmed so path joins stay
|
|
63
|
+
* single-slashed.
|
|
64
|
+
*
|
|
65
|
+
* The returned `baseURL` is always a string: adapters that build request paths
|
|
66
|
+
* by interpolation can use it directly without re-applying a default (which
|
|
67
|
+
* would otherwise risk interpolating `undefined` into a URL). The config's own
|
|
68
|
+
* type is preserved, so adapter-specific config fields survive the call.
|
|
69
|
+
*/
|
|
70
|
+
function withBytePlusArkDefaults(config) {
|
|
71
|
+
return {
|
|
72
|
+
...config,
|
|
73
|
+
baseURL: (config.baseURL || "https://ark.ap-southeast.bytepluses.com/api/v3").replace(/\/+$/, "")
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns a Seed Speech config with the default voice base URL applied (and
|
|
78
|
+
* any trailing slashes trimmed) when not already set.
|
|
79
|
+
*
|
|
80
|
+
* As with {@link withBytePlusArkDefaults}, the returned `baseURL` is always a
|
|
81
|
+
* string, so adapters can interpolate it without re-applying a fallback, and
|
|
82
|
+
* the config's own type is preserved.
|
|
83
|
+
*/
|
|
84
|
+
function withBytePlusVoiceDefaults(config) {
|
|
85
|
+
return {
|
|
86
|
+
...config,
|
|
87
|
+
baseURL: (config.baseURL || "https://voice.ap-southeast-1.bytepluses.com").replace(/\/+$/, "")
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Normalizes the OpenAI-shaped `defaultHeaders` config field (which accepts a
|
|
92
|
+
* `Headers` instance, an entry list, or a record with nullable values) into
|
|
93
|
+
* the plain record the header builders below take. Non-string values are
|
|
94
|
+
* dropped rather than serialized.
|
|
95
|
+
*
|
|
96
|
+
* Shared by every fetch-based adapter (image, video, speech): they all read
|
|
97
|
+
* `defaultHeaders` off a config typed by the OpenAI SDK but issue plain JSON
|
|
98
|
+
* requests.
|
|
99
|
+
*/
|
|
100
|
+
function toHeaderRecord(headers) {
|
|
101
|
+
const record = {};
|
|
102
|
+
if (!headers) return record;
|
|
103
|
+
if (headers instanceof Headers) {
|
|
104
|
+
headers.forEach((value, key) => {
|
|
105
|
+
record[key] = value;
|
|
106
|
+
});
|
|
107
|
+
return record;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(headers)) {
|
|
110
|
+
for (const [key, value] of headers) if (typeof key === "string" && typeof value === "string") record[key] = value;
|
|
111
|
+
return record;
|
|
112
|
+
}
|
|
113
|
+
for (const [key, value] of Object.entries(headers)) if (typeof value === "string") record[key] = value;
|
|
114
|
+
return record;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Drops any caller-supplied header whose name case-insensitively collides with
|
|
118
|
+
* one the adapter sets itself, then applies the adapter's own.
|
|
119
|
+
*
|
|
120
|
+
* Spreading `reserved` last is not enough on its own: HTTP header names are
|
|
121
|
+
* case-insensitive, but plain object keys are not, so `authorization` and
|
|
122
|
+
* `Authorization` are two distinct properties that both survive the spread.
|
|
123
|
+
* `fetch` then feeds the object to the `Headers` constructor, which *appends*
|
|
124
|
+
* rather than replaces — turning the pair into
|
|
125
|
+
* `authorization: "Bearer wrong, Bearer right"` and 401ing every request with
|
|
126
|
+
* what reads like a bad key. `toHeaderRecord` lowercases names whenever
|
|
127
|
+
* `defaultHeaders` arrives as a `Headers` instance, so that collision is
|
|
128
|
+
* reachable through ordinary config, not just a hand-built record.
|
|
129
|
+
*/
|
|
130
|
+
function applyReservedHeaders(extraHeaders, reserved) {
|
|
131
|
+
const blocked = new Set(Object.keys(reserved).map((key) => key.toLowerCase()));
|
|
132
|
+
const merged = {};
|
|
133
|
+
for (const [key, value] of Object.entries(extraHeaders ?? {})) if (!blocked.has(key.toLowerCase())) merged[key] = value;
|
|
134
|
+
return {
|
|
135
|
+
...merged,
|
|
136
|
+
...reserved
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Turns the OpenAI-shaped `timeout` config field (milliseconds) into the
|
|
141
|
+
* `signal` a plain `fetch` needs, or `undefined` when no timeout is set.
|
|
142
|
+
*
|
|
143
|
+
* `BytePlusArkConfig` extends the OpenAI SDK's `ClientOptions` because the
|
|
144
|
+
* chat adapter drives the SDK, which honours `timeout` and `maxRetries`
|
|
145
|
+
* itself. The video, image and speech adapters issue plain JSON requests, so
|
|
146
|
+
* without this they would accept a `timeout` and ignore it — a stalled Ark
|
|
147
|
+
* connection hanging the caller forever despite an explicit setting.
|
|
148
|
+
*
|
|
149
|
+
* `maxRetries` has no equivalent here and stays SDK-path-only; it is
|
|
150
|
+
* documented as such on {@link BytePlusArkConfig}.
|
|
151
|
+
*/
|
|
152
|
+
function bytePlusTimeoutSignal(timeout) {
|
|
153
|
+
return typeof timeout === "number" && timeout > 0 ? AbortSignal.timeout(timeout) : void 0;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Headers for a JSON request against the Ark data plane.
|
|
157
|
+
*
|
|
158
|
+
* A caller-supplied `Authorization` or `Content-Type` in `defaultHeaders` is
|
|
159
|
+
* dropped in any casing — see {@link applyReservedHeaders}.
|
|
160
|
+
*/
|
|
161
|
+
function bytePlusArkHeaders(apiKey, extraHeaders) {
|
|
162
|
+
return applyReservedHeaders(extraHeaders, {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
Authorization: `Bearer ${apiKey}`
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Headers for a JSON request against the Seed Speech host.
|
|
169
|
+
*
|
|
170
|
+
* As with {@link bytePlusArkHeaders}, a caller-supplied `X-Api-Key` is dropped
|
|
171
|
+
* in any casing. Seed Speech answers a clobbered key with
|
|
172
|
+
* `45000010 Invalid X-Api-Key`, which reads as a misconfigured key rather than
|
|
173
|
+
* a header collision.
|
|
174
|
+
*/
|
|
175
|
+
function bytePlusVoiceHeaders(apiKey, extraHeaders) {
|
|
176
|
+
return applyReservedHeaders(extraHeaders, {
|
|
177
|
+
"Content-Type": "application/json",
|
|
178
|
+
"X-Api-Key": apiKey
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Reads a response body as JSON, tolerating the non-JSON failures both
|
|
183
|
+
* BytePlus hosts can return (an empty body, or an HTML error page from a proxy
|
|
184
|
+
* in front of the API).
|
|
185
|
+
*
|
|
186
|
+
* Returns the parsed value, the raw text when it is not JSON, or `undefined`
|
|
187
|
+
* for an empty body — all three of which {@link bytePlusArkError} and
|
|
188
|
+
* {@link bytePlusVoiceError} know how to render.
|
|
189
|
+
*/
|
|
190
|
+
async function readJsonBody(response) {
|
|
191
|
+
const text = await response.text();
|
|
192
|
+
if (!text) return void 0;
|
|
193
|
+
try {
|
|
194
|
+
return JSON.parse(text);
|
|
195
|
+
} catch {
|
|
196
|
+
return text;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Best-effort human-readable rendering of a response body we could not pull a
|
|
201
|
+
* `message` out of — a raw string passes through, any other object is
|
|
202
|
+
* serialized so the detail reaches the error instead of being dropped.
|
|
203
|
+
*
|
|
204
|
+
* Exported for adapters that need to attach a body to an error they raise
|
|
205
|
+
* themselves rather than one derived from a non-OK response — e.g. the image
|
|
206
|
+
* adapter reporting a 200 whose `data[]` items match no known shape.
|
|
207
|
+
*/
|
|
208
|
+
function describeBody(body) {
|
|
209
|
+
if (typeof body === "string") return body || void 0;
|
|
210
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
211
|
+
try {
|
|
212
|
+
return JSON.stringify(body);
|
|
213
|
+
} catch {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function readStringField(value, field) {
|
|
218
|
+
if (typeof value !== "object" || value === null || !(field in value)) return;
|
|
219
|
+
const candidate = Reflect.get(value, field);
|
|
220
|
+
if (typeof candidate === "string") return candidate;
|
|
221
|
+
if (typeof candidate === "number") return String(candidate);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Formats an Ark error response into an `Error`.
|
|
225
|
+
*
|
|
226
|
+
* Ark uses the OpenAI error envelope with dotted string codes:
|
|
227
|
+
* `{"error": {"code": "InvalidEndpointOrModel.NotFound", "message": "…"}}`.
|
|
228
|
+
* Bodies that don't match (HTML error pages, proxy responses) fall back to
|
|
229
|
+
* the raw text so the failure stays diagnosable.
|
|
230
|
+
*/
|
|
231
|
+
function bytePlusArkError(status, body, context) {
|
|
232
|
+
const prefix = context ? `BytePlus Ark ${context}` : "BytePlus Ark request";
|
|
233
|
+
const error = typeof body === "object" && body !== null && "error" in body ? Reflect.get(body, "error") : void 0;
|
|
234
|
+
const code = readStringField(error, "code");
|
|
235
|
+
const detail = readStringField(error, "message") ?? describeBody(body);
|
|
236
|
+
return /* @__PURE__ */ new Error(`${prefix} failed (${status}${code ? ` ${code}` : ""})${detail ? `: ${detail}` : ""}`);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Formats a Seed Speech error response into an `Error`.
|
|
240
|
+
*
|
|
241
|
+
* Seed Speech does not use the Ark envelope — it returns a flat numeric code:
|
|
242
|
+
* `{"code": 45000010, "message": "Invalid X-Api-Key"}`.
|
|
243
|
+
*/
|
|
244
|
+
function bytePlusVoiceError(status, body, context) {
|
|
245
|
+
const prefix = context ? `BytePlus Seed Speech ${context}` : "BytePlus Seed Speech request";
|
|
246
|
+
const code = readStringField(body, "code");
|
|
247
|
+
const detail = readStringField(body, "message") ?? describeBody(body);
|
|
248
|
+
return /* @__PURE__ */ new Error(`${prefix} failed (${status}${code ? ` ${code}` : ""})${detail ? `: ${detail}` : ""}`);
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
export { BYTEPLUS_ARK_BASE_URL, BYTEPLUS_VOICE_BASE_URL, bytePlusArkError, bytePlusArkHeaders, bytePlusTimeoutSignal, bytePlusVoiceError, bytePlusVoiceHeaders, describeBody, getBytePlusArkApiKeyFromEnv, getBytePlusVoiceApiKeyFromEnv, readJsonBody, toHeaderRecord, withBytePlusArkDefaults, withBytePlusVoiceDefaults };
|
|
252
|
+
|
|
253
|
+
//# sourceMappingURL=client.js.map
|