@henjicc/ai-sdk 0.7.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0 - 2026-09-27
4
+
5
+ - TTS 音色参数统一支持选择器;公开 `listTtsVoices` 查询百炼 MiniMax、Qwen Audio 3.1、CosyVoice 3.5 和硅基流动 CosyVoice2 的账号音色。
6
+ - Henji-AI 首发宿主加入 Qwen Audio 3.1 与火山 Seed-TTS 2.0 的官方预置音色目录,并为其他 TTS 模型展示已核实的预置或示例音色。无法查询完整音色目录的渠道保留手填 ID 兜底。
7
+ - 迁移:上述 TTS 音色参数在目录中改为 `composite` 字符串字段;依赖旧 `dropdown.options` 渲染 Qwen 3.1 音色的 SDK 消费方需改用自己的音色选择器。原参数 ID、字符串值和请求构建保持不变。
8
+
3
9
  ## 0.7.0 - 2026-09-27
4
10
 
5
11
  - 新增 Fal、百炼、KIE、硅基流动和火山语音的 13 个 TTS 模型入口;保留派欧云 MiniMax Speech 2.8,并按平台契约隐藏未证实的情感参数。
package/README.md CHANGED
@@ -48,10 +48,10 @@ try {
48
48
 
49
49
  百炼工厂必须传 `baseUrl` 为实际地域/工作空间的 API 根地址,不包含端点路径。其他供应商可以覆盖根地址与 `credentialId`;SDK 不自动切换地域或账号。不同模型的向量不能混用;切换模型通常需要重建向量索引。Rerank 分数仅在本次请求内比较。
50
50
 
51
- SDK `0.7.0` 的正式分发渠道为公共 npm,无需配置 registry 或访问令牌:
51
+ SDK `0.8.0` 的正式分发渠道为公共 npm,无需配置 registry 或访问令牌:
52
52
 
53
53
  ```bash
54
- npm install @henjicc/ai-sdk@0.7.0
54
+ npm install @henjicc/ai-sdk@0.8.0
55
55
  ```
56
56
 
57
57
  然后提供 4 个宿主能力(`Transport` / `CredentialStore` / `MediaReader` / `Logger`),创建客户端:
@@ -8,7 +8,7 @@ export const bailianCosyVoice35Model = defineModel({
8
8
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
9
9
  params: [
10
10
  { id: 'bailianCosyVoiceSpec', type: 'dropdown', order: 1, default: 'flash', options: [{ value: 'flash' }, { value: 'plus' }] },
11
- { id: 'bailianCosyVoiceId', type: 'text', order: 2, required: true, default: '' },
11
+ { id: 'bailianCosyVoiceId', type: 'composite', valueType: 'string', order: 2, required: true, default: '' },
12
12
  { id: 'bailianCosyVoiceInstruction', type: 'text', order: 3, default: '' },
13
13
  { id: 'bailianCosyVoiceSpeed', type: 'number', order: 4, default: 1, min: 0.5, max: 2, step: 0.1 },
14
14
  ],
@@ -8,7 +8,7 @@ export const bailianMinimaxSpeech28Model = defineModel({
8
8
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
9
9
  params: [
10
10
  { id: 'bailianMinimaxSpeechSpec', type: 'dropdown', order: 1, default: 'hd', options: [{ value: 'hd' }, { value: 'turbo' }] },
11
- { id: 'bailianMinimaxSpeechVoice', type: 'text', order: 2, default: 'male-qn-qingse' },
11
+ { id: 'bailianMinimaxSpeechVoice', type: 'composite', valueType: 'string', order: 2, default: 'male-qn-qingse' },
12
12
  { id: 'bailianMinimaxSpeechSpeed', type: 'number', order: 3, default: 1, min: 0.5, max: 2, step: 0.1 },
13
13
  { id: 'bailianMinimaxSpeechEmotion', type: 'dropdown', order: 4, default: '', options: ['', 'happy', 'sad', 'angry', 'fearful', 'disgusted', 'surprised', 'calm'].map(value => ({ value })) },
14
14
  ],
@@ -8,7 +8,7 @@ export const bailianQwenAudio31TtsFlashModel = defineModel({
8
8
  },
9
9
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
10
10
  params: [
11
- { id: 'bailianQwenTtsVoice', type: 'dropdown', order: 1, default: 'longanhuan_v3.1', options: ['longanhuan_v3.1', 'longanlingxin_v3.1', 'longanfengyue_v3.1', 'xunanchuan_v3.1'].map(value => ({ value })) },
11
+ { id: 'bailianQwenTtsVoice', type: 'composite', valueType: 'string', order: 1, default: 'longanhuan_v3.1' },
12
12
  { id: 'bailianQwenTtsInstruction', type: 'text', order: 2, default: '' },
13
13
  { id: 'bailianQwenTtsSpeed', type: 'number', order: 3, default: 1, min: 0.5, max: 2, step: 0.1 },
14
14
  ],
@@ -8,7 +8,7 @@ export const falElevenV3TtsModel = defineModel({
8
8
  },
9
9
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
10
10
  params: [
11
- { id: 'falElevenV3Voice', type: 'text', order: 1, default: 'Rachel' },
11
+ { id: 'falElevenV3Voice', type: 'composite', valueType: 'string', order: 1, default: 'Rachel' },
12
12
  { id: 'falElevenV3Stability', type: 'number', order: 2, default: 0.5, min: 0, max: 1, step: 0.05 },
13
13
  { id: 'falElevenV3Language', type: 'text', order: 3, default: '' },
14
14
  ],
@@ -10,7 +10,7 @@ export const falMinimaxSpeech28Model = defineModel({
10
10
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
11
11
  params: [
12
12
  { id: 'falMinimaxSpeechSpec', type: 'dropdown', order: 1, default: 'hd', options: [{ value: 'hd' }, { value: 'turbo' }] },
13
- { id: 'falMinimaxSpeechVoice', type: 'text', order: 2, default: 'Wise_Woman' },
13
+ { id: 'falMinimaxSpeechVoice', type: 'composite', valueType: 'string', order: 2, default: 'Wise_Woman' },
14
14
  { id: 'falMinimaxSpeechSpeed', type: 'number', order: 3, default: 1, min: 0.5, max: 2, step: 0.1 },
15
15
  { id: 'falMinimaxSpeechEmotion', type: 'dropdown', order: 4, default: '', options: ['', 'happy', 'sad', 'angry', 'fearful', 'disgusted', 'surprised', 'neutral'].map(value => ({ value })) },
16
16
  ],
@@ -8,7 +8,7 @@ export const kieElevenTurbo25TtsModel = defineModel({
8
8
  },
9
9
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
10
10
  params: [
11
- { id: 'kieElevenTurboVoice', type: 'text', order: 1, default: 'Rachel' },
11
+ { id: 'kieElevenTurboVoice', type: 'composite', valueType: 'string', order: 1, default: 'Rachel' },
12
12
  { id: 'kieElevenTurboStability', type: 'number', order: 2, default: 0.5, min: 0, max: 1, step: 0.05 },
13
13
  { id: 'kieElevenTurboSpeed', type: 'number', order: 3, default: 1, min: 0.7, max: 1.2, step: 0.05 },
14
14
  ],
@@ -20,7 +20,7 @@ export const kieElevenV3DialogueModel = defineModel({
20
20
  },
21
21
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
22
22
  params: [
23
- { id: 'kieElevenDialogueVoice', type: 'text', order: 1, default: 'EkK5I93UQWFDigLMpZcX' },
23
+ { id: 'kieElevenDialogueVoice', type: 'composite', valueType: 'string', order: 1, default: 'EkK5I93UQWFDigLMpZcX' },
24
24
  ],
25
25
  endpoints: '/api/v1/jobs/createTask',
26
26
  request: {
@@ -8,7 +8,7 @@ export const kieGemini31FlashTtsModel = defineModel({
8
8
  },
9
9
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
10
10
  params: [
11
- { id: 'kieGeminiTtsVoice', type: 'text', order: 1, default: 'Fenrir' },
11
+ { id: 'kieGeminiTtsVoice', type: 'composite', valueType: 'string', order: 1, default: 'Fenrir' },
12
12
  { id: 'kieGeminiTtsScene', type: 'text', order: 2, default: '' },
13
13
  { id: 'kieGeminiTtsTemperature', type: 'number', order: 3, default: 1, min: 0, max: 2, step: 0.1 },
14
14
  ],
@@ -7,7 +7,7 @@ export const siliconflowCosyVoice2TtsModel = defineModel({
7
7
  },
8
8
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
9
9
  params: [
10
- { id: 'siliconflowCosyVoice', type: 'text', order: 1, default: 'FunAudioLLM/CosyVoice2-0.5B:alex' },
10
+ { id: 'siliconflowCosyVoice', type: 'composite', valueType: 'string', order: 1, default: 'FunAudioLLM/CosyVoice2-0.5B:alex' },
11
11
  { id: 'siliconflowCosySpeed', type: 'number', order: 2, default: 1, min: 0.25, max: 4, step: 0.05 },
12
12
  { id: 'siliconflowCosyFormat', type: 'dropdown', order: 3, default: 'mp3', options: ['mp3', 'opus', 'wav', 'pcm'].map(value => ({ value })) },
13
13
  ],
@@ -7,7 +7,7 @@ export const siliconflowMossTtsd05Model = defineModel({
7
7
  },
8
8
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
9
9
  params: [
10
- { id: 'siliconflowMossVoice', type: 'text', order: 1, default: 'fnlp/MOSS-TTSD-v0.5:alex' },
10
+ { id: 'siliconflowMossVoice', type: 'composite', valueType: 'string', order: 1, default: 'fnlp/MOSS-TTSD-v0.5:alex' },
11
11
  { id: 'siliconflowMossFormat', type: 'dropdown', order: 2, default: 'mp3', options: ['mp3', 'opus', 'wav', 'pcm'].map(value => ({ value })) },
12
12
  ],
13
13
  endpoints: '/v1/audio/speech',
@@ -7,7 +7,7 @@ export const volcengineSeedIcl20Model = defineModel({
7
7
  },
8
8
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
9
9
  params: [
10
- { id: 'volcSeedIclSpeaker', type: 'text', required: true, order: 1, default: '' },
10
+ { id: 'volcSeedIclSpeaker', type: 'composite', valueType: 'string', required: true, order: 1, default: '' },
11
11
  ],
12
12
  endpoints: '/api/v3/tts/unidirectional',
13
13
  request: {
@@ -7,7 +7,7 @@ export const volcengineSeedTts20Model = defineModel({
7
7
  },
8
8
  inputLimits: { images: { max: 0 }, videos: { max: 0 } },
9
9
  params: [
10
- { id: 'volcSeedTtsSpeaker', type: 'text', order: 1, default: 'zh_female_vv_uranus_bigtts' },
10
+ { id: 'volcSeedTtsSpeaker', type: 'composite', valueType: 'string', order: 1, default: 'zh_female_vv_uranus_bigtts' },
11
11
  ],
12
12
  endpoints: '/api/v3/tts/unidirectional',
13
13
  request: {
@@ -5,6 +5,7 @@ export { resolvePpioMediaRewriteMode, type PpioMediaRewriteMode } from './ppio-m
5
5
  export { PROVIDER_METADATA, findProviderMetadata, type ProviderMetadata, type ProviderMetadataLookupOptions, } from './metadata.js';
6
6
  export { testProviderConnection } from './connection.js';
7
7
  export { fetchProvider } from './provider-fetch.js';
8
+ export { listTtsVoices, type TtsVoice } from './tts-voices.js';
8
9
  export { buildApiMartEndpoints, markApiMartEndpointReachable, resetApiMartEndpointPreference, warmApiMartEndpointPreference, } from './endpoints/apimart.js';
9
10
  export { buildGrsaiEndpoints, markGrsaiEndpointReachable, resetGrsaiEndpointPreference, warmGrsaiEndpointPreference, } from './endpoints/grsai.js';
10
11
  export type { ProviderAdapter } from './types.js';
@@ -13,6 +13,7 @@ export { resolvePpioMediaRewriteMode } from './ppio-media.js';
13
13
  export { PROVIDER_METADATA, findProviderMetadata, } from './metadata.js';
14
14
  export { testProviderConnection } from './connection.js';
15
15
  export { fetchProvider } from './provider-fetch.js';
16
+ export { listTtsVoices } from './tts-voices.js';
16
17
  export { buildApiMartEndpoints, markApiMartEndpointReachable, resetApiMartEndpointPreference, warmApiMartEndpointPreference, } from './endpoints/apimart.js';
17
18
  export { buildGrsaiEndpoints, markGrsaiEndpointReachable, resetGrsaiEndpointPreference, warmGrsaiEndpointPreference, } from './endpoints/grsai.js';
18
19
  const registry = new Map();
@@ -0,0 +1,9 @@
1
+ import type { RuntimeContext } from '../runtime/RuntimeContext.js';
2
+ export interface TtsVoice {
3
+ id: string;
4
+ name: string;
5
+ description?: string;
6
+ source?: 'system' | 'clone';
7
+ }
8
+ /** 查询供应商账号中的可用音色;静态系统音色由宿主的展示目录提供。 */
9
+ export declare function listTtsVoices(modelId: string, runtime: RuntimeContext): Promise<TtsVoice[]>;
@@ -0,0 +1,139 @@
1
+ import { fetchProvider } from './provider-fetch.js';
2
+ const VOICE_PROVIDERS = {
3
+ 'bailian-minimax-speech-2.8': 'bailian',
4
+ 'bailian-cosyvoice-v3.5': 'bailian',
5
+ 'bailian-qwen-audio-3.1-tts-flash': 'bailian',
6
+ 'siliconflow-cosyvoice2-tts': 'siliconflow',
7
+ };
8
+ function object(value) {
9
+ return value && typeof value === 'object' && !Array.isArray(value)
10
+ ? value
11
+ : null;
12
+ }
13
+ function string(value) {
14
+ return typeof value === 'string' ? value.trim() : '';
15
+ }
16
+ function voiceDescription(value) {
17
+ if (typeof value === 'string')
18
+ return value.trim() || undefined;
19
+ if (Array.isArray(value)) {
20
+ const joined = value.filter((item) => typeof item === 'string' && item.trim().length > 0).join(' · ');
21
+ return joined || undefined;
22
+ }
23
+ return undefined;
24
+ }
25
+ function uniqueVoices(voices) {
26
+ return [...new Map(voices.map((voice) => [voice.id, voice])).values()];
27
+ }
28
+ async function requestJson(providerId, url, apiKey, runtime, body) {
29
+ const response = await fetchProvider(providerId, url, {
30
+ method: body ? 'POST' : 'GET',
31
+ headers: {
32
+ Authorization: `Bearer ${apiKey}`,
33
+ ...(body ? { 'Content-Type': 'application/json' } : {}),
34
+ },
35
+ ...(body ? { body: JSON.stringify(body) } : {}),
36
+ }, { transport: runtime.transport, retryPreconnectOnce: false });
37
+ if (!response.ok) {
38
+ throw new Error(`${providerId} 音色列表请求失败(HTTP ${response.status})`);
39
+ }
40
+ try {
41
+ return await response.json();
42
+ }
43
+ catch {
44
+ throw new Error(`${providerId} 音色列表响应格式错误`);
45
+ }
46
+ }
47
+ async function listBailianMinimax(apiKey, runtime) {
48
+ const payload = object(await requestJson('bailian', 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation', apiKey, runtime, { model: 'MiniMax/speech-2.8-turbo', input: { action: 'get_voice', voice_type: 'all' } }));
49
+ const output = object(payload?.output);
50
+ const baseResp = object(output?.base_resp);
51
+ if (!output || baseResp?.status_code !== 0 || (!Array.isArray(output.system_voice) && !Array.isArray(output.voice_cloning))) {
52
+ throw new Error('百炼 MiniMax 音色列表返回错误');
53
+ }
54
+ const voices = [];
55
+ for (const source of ['system_voice', 'voice_cloning']) {
56
+ const entries = output[source];
57
+ if (!Array.isArray(entries))
58
+ continue;
59
+ for (const item of entries) {
60
+ const entry = object(item);
61
+ const id = string(entry?.voice_id);
62
+ if (!id)
63
+ continue;
64
+ voices.push({
65
+ id,
66
+ name: string(entry?.voice_name) || id,
67
+ description: voiceDescription(entry?.description),
68
+ source: source === 'system_voice' ? 'system' : 'clone',
69
+ });
70
+ }
71
+ }
72
+ return uniqueVoices(voices);
73
+ }
74
+ async function listBailianEnrollment(modelId, apiKey, runtime) {
75
+ const prefix = modelId === 'bailian-cosyvoice-v3.5' ? 'cosyvoice-v3.5-' : 'qwen-audio-3.1-tts-flash-';
76
+ const voices = [];
77
+ const pageSize = 50;
78
+ for (let pageIndex = 0; pageIndex < 20; pageIndex += 1) {
79
+ const payload = object(await requestJson('bailian', 'https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization', apiKey, runtime, { model: 'voice-enrollment', input: { action: 'list_voice', page_index: pageIndex, page_size: pageSize } }));
80
+ const output = object(payload?.output);
81
+ const entries = output?.voice_list;
82
+ if (!Array.isArray(entries))
83
+ throw new Error('百炼复刻音色列表响应格式错误');
84
+ for (const item of entries) {
85
+ const entry = object(item);
86
+ const id = string(entry?.voice_id);
87
+ if (!id.startsWith(prefix) || entry?.status !== 'OK')
88
+ continue;
89
+ voices.push({ id, name: id, source: 'clone' });
90
+ }
91
+ if (entries.length < pageSize)
92
+ return uniqueVoices(voices);
93
+ }
94
+ throw new Error('百炼复刻音色超过查询页数限制');
95
+ }
96
+ async function listSiliconflow(apiKey, runtime) {
97
+ const payload = await requestJson('siliconflow', 'https://api.siliconflow.cn/v1/audio/voice/list', apiKey, runtime);
98
+ const root = object(payload);
99
+ const data = object(root?.data);
100
+ const entries = Array.isArray(payload) ? payload
101
+ : Array.isArray(root?.data) ? root.data
102
+ : Array.isArray(data?.voices) ? data.voices
103
+ : Array.isArray(root?.voices) ? root.voices
104
+ : null;
105
+ if (!entries)
106
+ throw new Error('硅基流动音色列表响应格式错误');
107
+ const voices = [];
108
+ for (const item of entries) {
109
+ const entry = object(item);
110
+ const id = string(entry?.uri);
111
+ if (!id)
112
+ continue;
113
+ voices.push({ id, name: string(entry?.customName) || string(entry?.name) || id, source: 'clone' });
114
+ }
115
+ return uniqueVoices(voices);
116
+ }
117
+ /** 查询供应商账号中的可用音色;静态系统音色由宿主的展示目录提供。 */
118
+ export async function listTtsVoices(modelId, runtime) {
119
+ if (!Object.prototype.hasOwnProperty.call(VOICE_PROVIDERS, modelId))
120
+ throw new Error(`不支持查询 ${modelId} 的音色`);
121
+ const providerId = VOICE_PROVIDERS[modelId];
122
+ const apiKey = await runtime.credentials.get('generation', providerId);
123
+ if (!apiKey)
124
+ throw new Error(`请先设置${providerId} API Key`);
125
+ runtime.logger?.info('tts_voice.list.start', { providerId, modelId });
126
+ try {
127
+ const voices = modelId === 'bailian-minimax-speech-2.8'
128
+ ? await listBailianMinimax(apiKey, runtime)
129
+ : modelId === 'siliconflow-cosyvoice2-tts'
130
+ ? await listSiliconflow(apiKey, runtime)
131
+ : await listBailianEnrollment(modelId, apiKey, runtime);
132
+ runtime.logger?.info('tts_voice.list.completed', { providerId, modelId, context: { count: voices.length } });
133
+ return voices;
134
+ }
135
+ catch (error) {
136
+ runtime.logger?.warn('tts_voice.list.failed', { providerId, modelId, error });
137
+ throw error;
138
+ }
139
+ }
package/docs/consumers.md CHANGED
@@ -3,7 +3,13 @@
3
3
  本清单是 `@henjicc/ai-sdk` 消费方的唯一维护入口,用于 SDK 发布后的跨仓升级协调。
4
4
  绝对路径仅描述当前开发机上的仓库位置,不进入 SDK 运行时代码、发布包或用户配置。
5
5
 
6
- 最后核对日期:2026-09-24
6
+ 最后核对日期:2026-09-27
7
+
8
+ `0.7.0` 已发布:新增 Fal、百炼、KIE、硅基流动和火山语音共 13 个 TTS 模型。发布源码 `9785e964` 的必需 CI [36316096650](https://github.com/henjicc/Henji-AI/actions/runs/36316096650) 全部成功;SDK 84 文件 / 1032 项测试、可移植性、打包、受限宿主、仓外 Vite 56 个入口与 Henji-AI Electron bundle / 宿主集成测试通过。火山截断帧校验撤销后定向测试失败、恢复后通过。固定候选包发布后,公共 npm 匿名安装和新供应商 pack 导入通过;未执行付费语音生成。Henji-AI 使用仓内 workspace `0.7.0`;现有外部消费者和三个示例未使用新增 TTS,不机械升级。
9
+
10
+ - `0.7.0` tarball:[公共 npm 包](https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.7.0.tgz)
11
+ - `0.7.0` shasum:`d1a8e84d16ef4a02cf81d1318e7af9a3d1debdd0`
12
+ - `0.7.0` integrity:`sha512-IP+0VChzt2XjNZjiG0/wtXkHWZOxciZcGA70q7wMhp7T9nw//OGlbnLSl6hvy1I3FrSZOl6qLXcgZtwriQWnrA==`
7
13
 
8
14
  `0.6.2` 已发布:聊天/翻译流终态、百炼/火山实时 ASR、共享会话终止与脱敏诊断已完成修复及撤销修复后的失败验证。SDK 83 文件 / 1023 项测试、可移植性、构建、49 个公开入口、严格类型和受限宿主门禁通过;Henji-AI 8 项相关宿主测试、主进程类型检查及 Electron bundle 通过。发布源码 `9e836def` 的必需 CI [35931001436](https://github.com/henjicc/Henji-AI/actions/runs/35931001436) 全部成功。固定候选包在仓外回装后发布,用户完成 npm 网页认证;待公共分发可读后,隔离配置、缓存与令牌的匿名重新安装通过,正式 tarball 和 lockfile integrity 与候选一致。Node ESM、严格 TypeScript、Vite 49 个公开入口、无 TextEncoder/TextDecoder 的受限宿主通过;llm-chat 类型构建、dry-run、bundle 边界及包内合法空事件/提前结束/后台故障与单次释放探针通过。本轮未执行真实付费模型请求。Henji-AI workspace 和 llm-chat 示例使用 `0.6.2`,minimal-node/form-renderer 保持 `0.6.0`。说吧仍为 `0.6.1`,本次未修改外部仓库,需在消费项目另行升级和验证;Photoshop 未在本机验证。
9
15
 
@@ -32,7 +38,7 @@
32
38
 
33
39
  Henji-AI 安装包的 GPT Image 2.5 高分辨率报错归属:宿主智能比例预处理曾忽略联动过滤,将 `smart` 转成 KIE 仅限 1K 的 `27:16`,SDK 在请求前正确拒绝。修复位于应用公共预处理,按当前分辨率/渠道的合法选项匹配;Flare/Sunburst 的 1K/2K/4K 及 APIMart、Grsai 同类筛选已有定向覆盖。无需放宽 SDK 契约或发布 SDK;安装包需随应用更新才包含修复,现有版本可手动选合法比例规避。
34
40
 
35
- 当前仓内及公共 npm SDK:`0.6.2`(2026-09-24 已发布并完成公共匿名回装)。以下为历史发布证据。
41
+ 当前仓内及公共 npm SDK:`0.7.0`(2026-09-27 已发布并完成公共匿名回装)。以下为历史发布证据。
36
42
 
37
43
  `0.5.2` 完善轻量 `@henjicc/ai-sdk/llm/streaming`:正式类型支持文本、JSON Object 与 JSON Schema,Chat Completions 的最终请求体写入 `response_format`,OpenAI 输出上限写入 `max_completion_tokens`;结构化输出、思考模式和模型能力的非法组合会在请求前返回结构化错误。流式正文与思考继续分离,保留外部 `AbortSignal`,不设置默认总时限;未显式配置输出上限时不再注入 4096,服务端以 `length` 等原因结束时返回 `truncated: true`。发布前 77 个测试文件 / 909 项测试、可移植性、构建、49 个 Vite 公开入口和受限宿主门禁通过;故意撤销结构化参数、输出上限、OpenAI 字段映射和截断标记后,8 项定向断言失败,恢复后通过。公共索引可读后,已使用隔离 npm 配置和缓存匿名安装精确版本,并从已发布的轻量入口验证最终请求体包含 JSON Schema、思考强度和 24,000 token 上限,SSE 正文/思考事件分离且未截断。
38
44
 
@@ -94,7 +100,7 @@ Henji-AI workspace 与三个仓内示例 manifest 均锁定 `0.4.0`;下表原
94
100
 
95
101
  | 仓库 / 开发路径 | 宿主类型 | 当前精确版本 | SDK 入口与构建方式 | 凭据 / transport 责任 | 需同步的变更类型 | 验证命令 | 同步证据与边界 | 最后核对 |
96
102
  |---|---|---|---|---|---|---|---|---|
97
- | `Henji-AI`<br>`D:/VibeCode/Henji-AI` | Electron 42 主进程 + React/Vite;SDK 主开发、首发验证宿主 | workspace 源码与公共 npm 均为 `0.6.2` | 包根、`provider-packs/*`、`tool-packs/*`;根构建先执行 `build:sdk`,再构建 Electron | Electron 主进程注入 HTTP transport、凭据、媒体读取、日志、trace、取消与落盘;渲染层不直接持有密钥 | 公共类型/目录、provider preset、凭据坐标、transport、媒体、包导出、LLM/生成执行协议 | SDK 发布门禁、相关宿主 Vitest、渲染层/主进程类型检查与模型 i18n | `9e836def`;必需 CI `35931001436` 成功,SDK 1023 项测试、8 项宿主测试、类型、Electron bundle、固定包及公共匿名回装验证通过;正式包完整性与候选一致 | 2026-09-24 |
103
+ | `Henji-AI`<br>`D:/VibeCode/Henji-AI` | Electron 42 主进程 + React/Vite;SDK 主开发、首发验证宿主 | workspace 源码与公共 npm 均为 `0.7.0` | 包根、`provider-packs/*`、`tool-packs/*`;根构建先执行 `build:sdk`,再构建 Electron | Electron 主进程注入 HTTP transport、凭据、媒体读取、日志、trace、取消与落盘;渲染层不直接持有密钥 | 公共类型/目录、provider preset、凭据坐标、transport、媒体、包导出、LLM/生成执行协议 | SDK 发布门禁、相关宿主 Vitest、渲染层/主进程类型检查与模型 i18n | `9785e964`;必需 CI `36316096650` 成功,SDK 1032 项测试、37 项宿主集成测试、类型、Electron bundle、固定包及公共匿名回装验证通过 | 2026-09-27 |
98
104
  | `henji-ai-ps`<br>`/Users/henji/Documents/VibeCode/henji-ai-ps` | Photoshop UXP 插件 + React/Vite IIFE(pnpm) | manifest/lock 均精确锁定公共 npm `0.2.8`;lock integrity 与上方正式产物一致 | `generation/core`、单模型/供应商 pack、LLM streaming;Vite 构建与 UXP smoke bundle | UXP 宿主注入受限 `fetch`、provider 凭据、媒体编码读取和脱敏日志;SDK 不读取 Node/文件系统 | 生成 pack/exports、受限环境可移植性、RuntimeContext、凭据 scope、媒体与流式 LLM;不因版本同步自动引入 GLM | `pnpm typecheck:uxp-smoke && pnpm check:uxp-sdk && pnpm smoke:uxp:build && pnpm check:uxp-smoke`;完整 `pnpm check` | `e509716`;无用户 npm 凭据的 frozen lock 安装通过;Node 22 下四项门禁通过,正式产品仍仅 39 个 generation/erase packs,网络调用与受限环境风险均为 0 | 2026-08-31 |
99
105
  | `say-it`<br>`D:/VibeCode/说吧` | Tauri 2 + Rust 管理 QuickJS;WebView 不运行 SDK | manifest/lock 精确锁定公共 npm `0.6.1`;bundle 版本、resolved、shasum、integrity 一致 | 按需打包 capability、Bailian/火山实时 ASR、SiliconFlow/Groq 文件 ASR、translation、LLM modules 为相互隔离 IIFE;Rust 加载 bundle | Rust Host API 注入 HTTP 字节流、WS、media-ref、CredentialStore、日志/trace、Abort/timeout/cancel;QuickJS/插件/WebView 不直取密钥 | capability/LLM 协议、按需 exports、descriptor source/坐标、QuickJS 可移植性、bundle 隔离;不因版本同步自动增加未采用模型 | `npm run sdk-runtime:typecheck && npm run sdk-runtime:build`;`npm run test:ui`;Rust 定向/全量测试;`npm run ui:build` | `18bad6d`;QuickJS/WS 空中间帧回归及 35 项 SDK 宿主本地测试通过,runtime 类型检查通过;听写/历史/UI 定向回归与前端类型构建通过,桌面开发应用已重启,实际麦克风交互待用户验证 | 2026-09-24 |
100
106
 
@@ -21,6 +21,8 @@
21
21
  ## 2026-09-27 TTS 适配
22
22
 
23
23
  [TTS 多供应商适配契约](TTS-多供应商-2026-09.md):新增 13 个文字转语音模型,覆盖 Fal、百炼、KIE、硅基流动与火山语音;派欧云 MiniMax Speech 2.8 继续使用已有接入。记录官方接口、价格、音色前提、流式响应和测试样本来源。
24
+
25
+ 2026-09-27 补充官方音色目录及账号查询接口:Qwen Audio 3.1、Seed-TTS 2.0 使用核对后的预置清单;百炼 MiniMax / Qwen / CosyVoice 与硅基流动 CosyVoice2 支持按账号查询可用音色。接口字段、适用模型和不可查询渠道的边界见上方契约。
24
26
 
25
27
  ## 2026-09-24 已适配更新
26
28
 
@@ -48,6 +48,20 @@
48
48
 
49
49
  ## 参数边界
50
50
 
51
+ ### 音色目录与账号音色查询(2026-09-27 核对)
52
+
53
+ | 模型范围 | 官方音色来源 | 接入方式与边界 |
54
+ | --- | --- | --- |
55
+ | 百炼 Qwen Audio 3.1 TTS Flash | [官方音色清单](https://help.aliyun.com/zh/model-studio/qwen-audio-tts-voice-list),当前 68 个预置 ID;[复刻音色列表 API](https://help.aliyun.com/en/model-studio/voice-clone-design-http-api) | 预置清单本地展示;以北京地域百炼 Key 请求 `POST https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization`,`model=voice-enrollment`、`input.action=list_voice`、`page_index` 从 0 起、`page_size=50`,读取 `output.voice_list[].voice_id/status`;只呈现 `status=OK` 且属于本模型的 ID。 |
56
+ | 百炼 CosyVoice 3.5 | [复刻音色列表 API](https://help.aliyun.com/en/model-studio/voice-clone-design-http-api) | 同上。无预置音色;只呈现 `cosyvoice-v3.5-` 前缀且状态为 `OK` 的音色。 |
57
+ | 百炼 MiniMax Speech 2.8 | [音色管理 API](https://help.aliyun.com/zh/model-studio/sound-management) | 以百炼 Key 请求 `POST https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation`,`model=MiniMax/speech-2.8-turbo`、`input.action=get_voice`、`voice_type=all`;读取 `output.system_voice[]` 和 `output.voice_cloning[]` 的 `voice_id/voice_name/description`,检查 `output.base_resp.status_code=0`。未配置 Key 时仅保留本模型已证实默认音色,不借用其他供应商目录。 |
58
+ | 硅基流动 CosyVoice2 | [官方音色文档](https://docs.siliconflow.cn/docs/userguide/capabilities/text-to-speech) | 本地展示 8 个预置音色;用账号 Key 请求 `GET https://api.siliconflow.cn/v1/audio/voice/list`,读取自定义音色 `uri` 和名称。官方文档未给出完整响应外壳,解析器接受直接数组、`data` 数组及 `voices` 数组;实测前不声称所有账号响应形态均已验证。 |
59
+ | 火山 Seed-TTS 2.0 | [官方音色清单](https://docs.volcengine.com/docs/DoubaoVoice/Tonelist-1?lang=zh) | 2026-09-27 页面去重后 230 个音色 ID,本地提供搜索和选择。 |
60
+ | 火山 Seed-ICL 2.0 | [新版按 ID 查询](https://docs.volcengine.com/docs/DoubaoVoice/tone-query-http?lang=zh)、[旧版音色管理说明](https://docs.volcengine.com/docs/DoubaoVoice/Soundreplicationorderingandusageguide?lang=zh) | 新版官方查询只按已知 `speaker_id` 查状态;旧版批量查询接口与当前 V3 凭据/协议不一致,未接入。暂保留手填复刻音色 ID。 |
61
+ | Fal Eleven v3 / MiniMax 2.8、KIE Eleven、Gemini 3.1、硅基流动 MOSS | [Fal Eleven v3](https://fal.ai/models/fal-ai/elevenlabs/tts/eleven-v3)、[Fal MiniMax](https://fal.ai/models/fal-ai/minimax/speech-2.8-hd/api)、[KIE Eleven Turbo](https://docs.kie.ai/market/elevenlabs/text-to-speech-turbo-2-5)、[KIE Eleven Dialogue](https://docs.kie.ai/market/elevenlabs/text-to-dialogue-v3)、[Google 预置音色](https://ai.google.dev/gemini-api/docs/speech-generation)、[硅基流动 MOSS](https://docs.siliconflow.cn/docs/userguide/capabilities/text-to-speech) | 收录各渠道官方明确列出的预置音色或示例 ID。渠道未公布完整账号音色查询接口的模型保留手填兜底;不可将 ElevenLabs 或其他供应商原生音色 API 直接当成转售渠道的可用列表。 |
62
+
63
+ 上述音色查询均为单次 HTTP JSON 读请求,官方没有流式事件、取消远端任务、分页总数或连接复用的特殊契约;HTTP 失败、供应商错误码、响应外壳缺失都应报错,不把错误当成空目录。分页仅用于百炼 `list_voice`,遇不足一页时结束;到达客户端有界页数仍满页时应提示目录未完整读取。自定义音色的名称若官方只返回 ID,则展示 ID。
64
+
51
65
  - 各模型的文本来自 SDK 统一 `prompt`/`text` 输入,转换到供应商要求的 `prompt`、`text` 或嵌套 `input.text`。
52
66
  - 音色使用各平台官方音色 ID;复刻音色仅允许已在供应商侧取得的 ID,不在本次为训练/上传建立新流程。
53
67
  - KIE Gemini 3.1 使用 `speakers[]` 与 `dialogue_turns[]`;Eleven v3 对话使用 `dialogue[]`。SDK 在仅有普通文本时明确构建单说话人/单台词数组;调用方传入结构化数组时保留多说话人能力。痕迹AI 当前通用参数面板只暴露单说话人文本入口。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henjicc/ai-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "痕迹AI 多供应商模型适配 SDK",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {