{"_id":"@aihubmix/media-adapters","_rev":"4-a4ae211b1c47aaf824c2fc186c091860","name":"@aihubmix/media-adapters","dist-tags":{"latest":"0.2.2"},"versions":{"0.1.0":{"name":"@aihubmix/media-adapters","version":"0.1.0","license":"MIT","_id":"@aihubmix/media-adapters@0.1.0","maintainers":[{"name":"chenxue","email":"450907240@qq.com"}],"homepage":"https://github.com/AIhubmix/aihubmix-media-adapters#readme","bugs":{"url":"https://github.com/AIhubmix/aihubmix-media-adapters/issues"},"dist":{"shasum":"18d3b8a87113681ffb2375c941ae8e1a8ba202b1","tarball":"https://registry.npmjs.org/@aihubmix/media-adapters/-/media-adapters-0.1.0.tgz","fileCount":8,"integrity":"sha512-T3xQM3ydaOAbbFHtqEZXfLuuBTMOLXIKqYM0y/SnnH8FOz7qMKJL9a8FbJupA/kjkeJeq4AFxHDKdcesEUPl7g==","signatures":[{"sig":"MEUCIEvDwMyJ6OUDcvYTb9xSyHfaPUFBgc8PCHOWSEFrFYnXAiEA7yUWouh+PVpfyovDzdDW9Prjwv2rXQlhFKSsuMKZLro=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":170563},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"9daa187fb341b6b04cf756b321b702aff3c900b2","scripts":{"test":"vitest run","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"chenxue","email":"450907240@qq.com"},"repository":{"url":"git+https://github.com/AIhubmix/aihubmix-media-adapters.git","type":"git"},"_npmVersion":"10.9.8","description":"Pure, isomorphic interpreter that maps a unified media request to each vendor's native AIHubMix request shape (video; image/audio contracts stubbed). No transport — model capabilities are injected.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/media-adapters_0.1.0_1780648533798_0.6887997181213683","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aihubmix/media-adapters","version":"0.2.0","license":"MIT","_id":"@aihubmix/media-adapters@0.2.0","maintainers":[{"name":"chenxue","email":"450907240@qq.com"}],"homepage":"https://github.com/AIhubmix/aihubmix-media-adapters#readme","bugs":{"url":"https://github.com/AIhubmix/aihubmix-media-adapters/issues"},"dist":{"shasum":"5415caf16abe654c4632ff1e20c45bfd7dc25273","tarball":"https://registry.npmjs.org/@aihubmix/media-adapters/-/media-adapters-0.2.0.tgz","fileCount":9,"integrity":"sha512-Rr5xlQbSXC+ap9WOYmMh0SUN42bN8AZj1UkCNH6wzi5TIcwRhne8xkl5W7swJznV72KO6SAxXbif/NI5hHqEYA==","signatures":[{"sig":"MEUCIQCCnXIO8qpOmLkKCNUWHa02JYcObUQ5D3DPqhIPASsKQgIgA+4g63MJDdmswxa1OnvslZC/cBG/ZHwH7ZkXcuulfO0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":192924},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"d7ab3ccbfb0b89d9bdb9c3daefe72beaca483b4a","scripts":{"test":"vitest run","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"chenxue","email":"450907240@qq.com"},"repository":{"url":"git+https://github.com/AIhubmix/aihubmix-media-adapters.git","type":"git"},"_npmVersion":"10.9.8","description":"Pure, isomorphic interpreter that maps a unified media request to each vendor's native AIHubMix request shape (video; image/audio contracts stubbed). No transport — model capabilities are injected.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/media-adapters_0.2.0_1780654012874_0.002443817321097219","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aihubmix/media-adapters","version":"0.2.1","license":"MIT","_id":"@aihubmix/media-adapters@0.2.1","maintainers":[{"name":"chenxue","email":"450907240@qq.com"}],"homepage":"https://github.com/AIhubmix/aihubmix-media-adapters#readme","bugs":{"url":"https://github.com/AIhubmix/aihubmix-media-adapters/issues"},"dist":{"shasum":"cac5441bf7fcf4f6851dc64806815a12d4d30d42","tarball":"https://registry.npmjs.org/@aihubmix/media-adapters/-/media-adapters-0.2.1.tgz","fileCount":9,"integrity":"sha512-yl+5X3ZXgWyljzAdimLAf/TfDJ8Gx5BlVKNYvz/gNHKcwlQbIbV6+mMXmnzfIUf+l+Rqhqr9ao9FZ7Gow7HIqg==","signatures":[{"sig":"MEQCIFEMLZ7orb1rA6hRyc5MQRw82PUrw095EB2ASMKx2lOLAiAsAlptnaONafu881GKfD8uCM6cvSueCJB//MNRSo7g4A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":193706},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"feb859b099272a3a449fb22651b4518c8724aeb4","scripts":{"test":"vitest run","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"chenxue","email":"450907240@qq.com"},"repository":{"url":"git+https://github.com/AIhubmix/aihubmix-media-adapters.git","type":"git"},"_npmVersion":"10.9.8","description":"Pure, isomorphic interpreter that maps a unified media request to each vendor's native AIHubMix request shape (video; image/audio contracts stubbed). No transport — model capabilities are injected.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.6.3"},"_npmOperationalInternal":{"tmp":"tmp/media-adapters_0.2.1_1780654691367_0.023010707771482064","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@aihubmix/media-adapters","version":"0.2.2","description":"Pure, isomorphic interpreter that maps a unified media request to each vendor's native AIHubMix request shape (video; image/audio contracts stubbed). No transport — model capabilities are injected.","type":"module","license":"MIT","repository":{"type":"git","url":"git+https://github.com/AIhubmix/aihubmix-media-adapters.git"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"sideEffects":false,"publishConfig":{"access":"public"},"scripts":{"build":"tsup","prepare":"tsup","test":"vitest run","typecheck":"tsc --noEmit"},"devDependencies":{"tsup":"^8.3.5","typescript":"^5.6.3","vitest":"^2.1.8"},"_id":"@aihubmix/media-adapters@0.2.2","gitHead":"bef2715eb27f95f29378e1b12a314542ce856984","bugs":{"url":"https://github.com/AIhubmix/aihubmix-media-adapters/issues"},"homepage":"https://github.com/AIhubmix/aihubmix-media-adapters#readme","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-lHv41+u/5fgo+hFBHEAeaxxUcD/Veedizg/iBa45jyctxiNpgvWNM9xpnRsiOBKZYz4UIaaBxHfNrUGiFo4TtQ==","shasum":"bc5d4f30c27ee228a842acfbab42e1e9e3daa46a","tarball":"https://registry.npmjs.org/@aihubmix/media-adapters/-/media-adapters-0.2.2.tgz","fileCount":9,"unpackedSize":197641,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCMspGYv/ppFqaj8MYOgHUtOPcqTQhDO44KEC5iM8JSzgIhAO+7QNQcl3HcfoM6H+kv4MPePSJlE7US2I6QcUuD5+Hh"}]},"_npmUser":{"name":"chenxue","email":"450907240@qq.com"},"directories":{},"maintainers":[{"name":"chenxue","email":"450907240@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/media-adapters_0.2.2_1780657302017_0.4657128589201245"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-05T08:35:33.578Z","modified":"2026-06-05T11:01:42.289Z","0.1.0":"2026-06-05T08:35:33.963Z","0.2.0":"2026-06-05T10:06:53.004Z","0.2.1":"2026-06-05T10:18:11.510Z","0.2.2":"2026-06-05T11:01:42.169Z"},"bugs":{"url":"https://github.com/AIhubmix/aihubmix-media-adapters/issues"},"license":"MIT","homepage":"https://github.com/AIhubmix/aihubmix-media-adapters#readme","repository":{"type":"git","url":"git+https://github.com/AIhubmix/aihubmix-media-adapters.git"},"description":"Pure, isomorphic interpreter that maps a unified media request to each vendor's native AIHubMix request shape (video; image/audio contracts stubbed). No transport — model capabilities are injected.","maintainers":[{"name":"chenxue","email":"450907240@qq.com"}],"readme":"# @aihubmix/media-adapters\n\n[English](./README.md) | **中文**\n\n纯函数、同构的解释器,把**统一的媒体请求**映射为各厂商在 AIHubMix 上的**原生请求形态**。不含任何传输逻辑(fetch / 鉴权 / 轮询 / 下载 / 存储),也不内置任何专有模型数据——模型能力通过**注入**提供。被 [open-design](https://github.com/) 与 `aihubmix-video` playground 共同复用,使\"按厂商整形请求\"的逻辑只存在于一处。\n\n## 为什么需要它\n\nAIHubMix 是一个**透传**网关:每个上游厂商保留各自的原生请求体,因此客户端必须按厂商整形请求。AIHubMix 的 `contract.json` 虽已为统一的 `/v1/videos` 暴露了 OpenAPI schema,但目前只对 **OpenAI/Sora** 形态做了归一化——seedance / happyhorse / wan 的原生请求体并不在该 contract 内。在网关把每个厂商都归一化之前(那是终局,届时本解释器即可删除、只保留能力契约),本包承担客户端侧的映射。\n\n## 两层结构\n\n1. **能力契约**(`src/contracts/`)——描述每个模型的纯数据:`caps`、`supported{Durations,Sizes,AspectRatios,Resolutions,FrameImages}`、`allowedPassthroughParameters`、`apiModelI2V`、可选的 `xSource` 溯源。字段形态对齐 OpenRouter `/api/v1/videos/models` 与 AIHubMix `contract.json`,因此未来切换到实时 `/api/v1/models` 拉取只是换数据源,而非重构形态。面向 **video / image / audio** 设计;video 已实现,image/audio 契约为占位。\n2. **请求解释器**(`src/adapters/`)——把统一输入 + 能力契约转成厂商原生请求体的纯函数。目前仅 video。\n\n## 视频请求家族(family)\n\n| family | 模型 | 线上形态 |\n|---|---|---|\n| `seedance` | `doubao-seedance-*` | 多模态 `content[]`(text + `image_url{url,role}`);`duration` 数字、`resolution` token,可选 `ratio`/`watermark`/`camera_fixed`/`seed`/`generate_audio` |\n| `dashscope` | `happyhorse-*`(Alibaba ATH)、`wan2.7-i2v` | `{ input:{ prompt, media:[{type:first_frame\\|last_frame,url}] }, parameters:{ resolution(大写), duration, prompt_extend, watermark } }` |\n| `veo` | `veo-*` | 扁平 `{ prompt, seconds(数字), size }`;**仅 t2v**(Gemini predictLongRunning shim——i2v 端到端被拒) |\n| `generic` | `sora-*`、`wan2.x`、`jimeng-*`、… | 扁平 `{ prompt, seconds, size?, input_reference? }`;`seconds` 形态由 `secondsFormat` 决定;从不发送 `aspect_ratio` |\n\n`happyhorse(dashscope)` / `wan` / `veo` 已对 AIHubMix 线上网关验证、稳定。`seedance` / `generic` 是一个**同时服务两个调用方的超集**:snap 辅助函数会归一化偏宽的输入(由比例推出的像素 size、裸宽高比),但对**已归一化的输入是 no-op**(一个 `720p` token、一个已声明的 supported size)。因此 UI 已约束输入的调用方(aihubmix-video)字节原样通过,而传入原始聊天工具输入的调用方(open-design)会被纠正——两者走同一代码路径。见 `tests/parity-aihubmix-video.test.ts` 与 `tests/open-design-inputs.test.ts`。\n\n### 注入式归一化(能力字段驱动)\n\n归一化由能力上的数据驱动,而非按模型硬编码:\n\n- `supportedDurations: number[]` —— 枚举;`snapDuration` 取最近(并列取更短)。\n- `durationRange: { min, max }` —— 连续区间;`snapDuration` 钳制到区间(如 SeeDance 4–15s 保留 15s)。设了 `supportedDurations` 时忽略。\n- `supportedSizes: string[]` —— 声明的尺寸(WxH 和/或比例 token);`snapSizeToSupported` 命中则原样返回,否则按朝向映射(仅 `generic`)。\n- `secondsFormat: 'string' | 'number'` —— `generic` 的 `seconds` 线上形态(默认 `'number'`)。Sora 的字符串枚举还会**按 wire 名兜底识别**,即使不设此 flag 也成立。\n\n**开箱即用的线上保证**(上游硬性事实,本包按 wire 名兜底,消费方注入自己的目录、即使漏设对应 flag 也成立):\n\n- **Sora `input_reference`** 一律发对象 `{ image_url }`(字符串会 400);`referenceAsObject` 仅作其它厂商的显式开关。\n- **Sora `seconds`** 一律发字符串枚举。\n- **i2v 模型名**在缺 `apiModelI2V` 时从 `-t2v` 推导(如 `wan2.6-t2v`→`wan2.6-i2v`、`happyhorse-1.0-t2v`→`-i2v`);显式 `apiModelI2V` 优先;无 `-t2v` 段的不变(sora / seedance)。\n\n## 视频模型字段能力对照(官方 / 网关 / OR)\n\n本表对照三处来源的字段能力,作为接入与前端取值的依据:\n\n- **官方** —— 各厂商官方 API 的真实能力(火山方舟 Ark / 阿里云百炼 DashScope / Google Veo / OpenAI Sora)。\n- **网关** —— AIHubMix 网关(转发改写后)实际支持的能力。\n- **OR** —— OpenRouter `GET /api/v1/videos/models` 公布的字段枚举。\n\n网关将 OpenAI 风格请求重写为各厂商原生格式:通常官方能力最宽,网关因改写或未接而收窄,OR 为另一套抽象(可能与官方不一致)。本包字段取值**以官方为准、兼顾网关实际行为**。\n\n图例:**✓** 本包已对齐;**⚠️** 存在差异或限制(见说明);`✗` 不支持。\n\n### SeeDance 2.0 / 2.0 Fast\n\n| 字段 | 官方 | 网关 | OR | 状态 |\n|---|---|---|---|---|\n| frames | first_frame + last_frame + reference_image (+ reference_video) | 同官方(`content[].role`) | first_frame, last_frame | ✓ 取官方全集(保留 `reference_image`) |\n| resolution | 480p / 720p / 1080p | 由 size 短边推 480p/720p/1080p | 2.0:480/720/1080;Fast:480/720 | ✓ |\n| 比例 | 16:9 / 9:16 / 1:1 … | 吸附表无 `9:21` | 含 `9:21` | ⚠️ `9:21` 仅 `ratio` 直传;走 size 会被 GCD 约分成 `3:7` |\n| duration | 4–15s | clamp 2–15 | 4–15 | ✓ |\n| seed / audio | 支持 / 支持 | 透传 | true / true | ✓ |\n\n> **SeeDance 1.x**(1.0 Pro / 1.0 Pro Fast / 1.5 Pro):**仅** `first_frame`,不支持 `reference_image`。上游把 `reference_image` 映射为 `r2v`,而 1.x 不支持 r2v(`task_type r2v does not support model …`),只有 2.0 支持 r2v。网关与 OR 都不区分这个按版本的约束,本包通过 `supportedFrameImages: ['first_frame']` 表达。\n\n### Sora 2 Pro\n\n| 字段 | 官方 | 网关 | OR | 状态 |\n|---|---|---|---|---|\n| sizes | 720x1280 / 1280x720 / 1024x1792 / 1792x1024 | 纯透传 | 1280x720 / 720x1280 / 1080x1920 / 1920x1080 | ✓ 按官方(**与 OR 不同**) |\n| resolution | 720p / 1024p / 1080p | 透传 | 720p / 1080p | ✓ 按官方(含 `1024p`) |\n| durations | 4 / 8 / 12(新指南至 16/20) | 透传 | 4 / 8 / 12 / 16 / 20 | ✓ 取扩展集 |\n| frames | `input_reference` = 首帧 | 透传 | null | ✓ `first_frame`(OR 未纳入帧枚举) |\n| seed | 不支持 | — | false | ✓ |\n| audio | 支持 | 透传 | true | ✓ |\n\n> **Sora i2v 线上形态**:`input_reference` 必须是**对象** `{ image_url: <url> }` —— 传裸字符串会被 OpenAI/网关 400(`expected an object, but got a string`)。其它 `generic` 厂商(wan2.x)用裸字符串。本包对 Sora **按 wire 名自动发对象**,消费方注入自己的目录也成立;`referenceAsObject` 仅作其它厂商的显式开关。(网关和 OR 都不表达这一点,属线上格式差异。)\n\n### Wan 2.6 / 2.7\n\n| 字段 | 官方 | 网关 | OR | 状态 |\n|---|---|---|---|---|\n| resolution | 480P / 720P / 1080P | 透传 | 720p / 1080p | ✓ 按官方(含 `480p`) |\n| sizes | 像素,随首帧比例自适应 | `x`→`*` + 按模型填默认 | 2.6:4 个;2.7:10 个 | ✓ 同 OR 像素集 |\n| duration | 2–15s | 透传(不 clamp) | 2.6:[5,10];2.7:[2–10] | ✓ 按官方 2–15(网关透传) |\n| frames | first_frame + last_frame + 参考图 | 全支持 | 2.6:first;2.7:first+last | ✓ 同 OR(2.6 首帧、2.7 首尾帧) |\n| audio | wan2.5/2.6 默认有声 | 默认 audio=true | true | ✓ |\n\n### Veo 3.1 / 3.1 Lite\n\n| 字段 | 官方 | 网关 | OR | 状态 |\n|---|---|---|---|---|\n| frames | `image`(首帧)+ `lastFrame`(尾帧)+ `referenceImages`(asset) | **i2v 端到端被拒**(仅 t2v) | first_frame, last_frame | ⚠️ 本包**仅 t2v**:网关虽有 `input_reference`→`referenceImages` 映射,但 Veo 实测不接受,故不声明 `supportedFrameImages` |\n| resolution | 3.1:720p/1080p/4K;Lite:720p/1080p | 别名归一化 | 同官方 | ✓ |\n| 比例 | 16:9 / 9:16 | 透传 | 16:9 / 9:16 | ✓ |\n| duration | 4 / 6 / 8 | 透传 | 4 / 6 / 8 | ✓ |\n| seed / audio | 支持 / 支持 | 透传 | true / true | ✓ |\n\n> **前端注意**:① 默认 size/比例**各模型不同**(Sora 默认竖屏、Wan/Veo 默认横屏),不可用统一默认;② SeeDance 的 `9:21` 仅在以 `ratio` 直传时生效;③ Veo 在网关上**仅 t2v**,图生视频端到端被拒。\n>\n> 来源:[Ark Seedance](https://www.volcengine.com/docs/82379/1520757)、[万相图生视频](https://help.aliyun.com/zh/model-studio/image-to-video-api-reference/)、[OpenAI Sora](https://developers.openai.com/api/docs/guides/video-generation)、[Gemini Veo](https://ai.google.dev/gemini-api/docs/video);OpenRouter `/api/v1/videos/models`;AIHubMix 网关实测行为。\n\n## 用法\n\n```ts\nimport { aihubmixMediaRegistry, buildVideoRequest } from '@aihubmix/media-adapters';\n\nconst cap = aihubmixMediaRegistry.get('aihubmix-happyhorse-1.0-i2v'); // 前缀会被去除\nconst built = buildVideoRequest(cap!, {\n  prompt: 'a cat stretches',\n  durationSeconds: 5,\n  size: '1280x720',\n  imageRef: { dataUrl }, // 调用方先把参考图解析为 data URL\n});\n// 传输由调用方完成:\n// POST `${baseUrl}${built.pathSuffix}`,带鉴权头 + JSON.stringify(built.body)\n```\n\n本包**不做任何 I/O**:调用前先把参考图解析为 data URL,再用返回的 `{ pathSuffix, body }` 自行 fetch/轮询/下载。\n\n## 脚本\n\n- `pnpm build` —— tsup → `dist/{index.js,index.cjs,index.d.ts}`\n- `pnpm test` —— vitest\n- `pnpm typecheck` —— tsc --noEmit\n","readmeFilename":"README.zh-CN.md"}