@henjicc/ai-sdk 0.4.1 → 0.5.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +36 -23
  2. package/README.md +61 -47
  3. package/dist/capabilities/media-request.d.ts +17 -0
  4. package/dist/capabilities/media-request.js +121 -0
  5. package/dist/capabilities/speech-recognition/bailian/module.js +21 -26
  6. package/dist/capabilities/speech-recognition/bailian/presets.js +5 -3
  7. package/dist/capabilities/speech-recognition/bailian/upload.js +32 -18
  8. package/dist/capabilities/speech-recognition/groq/module.js +15 -18
  9. package/dist/capabilities/speech-recognition/groq/types.d.ts +1 -1
  10. package/dist/capabilities/speech-recognition/siliconflow/module.js +7 -17
  11. package/dist/providers/provider-fetch.js +48 -14
  12. package/dist/runtime/AiRuntimeError.d.ts +2 -1
  13. package/dist/runtime/AiRuntimeError.js +3 -1
  14. package/dist/runtime/MediaReader.d.ts +24 -2
  15. package/dist/runtime/MediaReader.js +39 -1
  16. package/dist/runtime/Transport.d.ts +19 -0
  17. package/dist/runtime/index.d.ts +2 -1
  18. package/dist/runtime/index.js +1 -0
  19. package/dist/runtime/retry.js +5 -1
  20. package/docs/consumers.md +35 -18
  21. package/docs/model-adaptation/Fun-ASR/Fun-ASR_/347/231/276/347/202/274.md +5 -1
  22. package/docs/model-adaptation/Fun-ASR-Flash-2026-06-15/Fun-ASR-Flash-2026-06-15_/347/231/276/347/202/274.md +5 -1
  23. package/docs/model-adaptation/Qwen3-ASR-Flash/Qwen3-ASR-Flash_/347/231/276/347/202/274.md +5 -1
  24. package/docs/model-adaptation/Qwen3-ASR-Flash-2026-02-10/Qwen3-ASR-Flash-2026-02-10_/347/231/276/347/202/274.md +5 -1
  25. package/docs/model-adaptation/Qwen3-ASR-Flash-Filetrans/Qwen3-ASR-Flash-Filetrans_/347/231/276/347/202/274.md +5 -1
  26. package/docs/model-adaptation/README.md +22 -22
  27. package/docs/model-adaptation/SenseVoiceSmall/SenseVoiceSmall_/347/241/205/345/237/272/346/265/201/345/212/250.md +5 -1
  28. package/docs/model-adaptation/TeleSpeechASR/TeleSpeechASR_/347/241/205/345/237/272/346/265/201/345/212/250.md +5 -1
  29. package/docs/model-adaptation/Whisper-Large-v3/Whisper-Large-v3_Groq.md +5 -1
  30. package/docs/model-adaptation/Whisper-Large-v3-Turbo/Whisper-Large-v3-Turbo_Groq.md +5 -1
  31. package/docs/model-adaptation//344/276/233/345/272/224/345/225/206/APIMart.md +3 -1
  32. package/docs/model-adaptation//344/276/233/345/272/224/345/225/206//347/231/276/347/202/274.md +24 -10
  33. package/examples/form-renderer/package.json +1 -1
  34. package/examples/llm-chat/package.json +1 -1
  35. package/examples/minimal-node/package.json +1 -1
  36. package/package.json +8 -8
@@ -1,7 +1,7 @@
1
- import { readCapabilityMediaSource } from '../../media.js';
1
+ import { prepareMedia, multipartAudioRequest, sendMediaRequest } from '../../media-request.js';
2
2
  import { AiRuntimeError, cancelledError } from '../../../runtime/AiRuntimeError.js';
3
3
  const DEFAULT_API_BASE = 'https://api.siliconflow.cn/v1';
4
- const DEFAULT_MAX_FILE_BYTES = 50 * 1024 * 1024;
4
+ const DEFAULT_MAX_FILE_BYTES = 50_000_000;
5
5
  function endpoint(value) {
6
6
  const normalized = (value?.trim() || DEFAULT_API_BASE).replace(/\/+$/, '');
7
7
  let parsed;
@@ -141,18 +141,8 @@ async function formData(preset, input, maxFileBytes, context) {
141
141
  if (input.audio.kind === 'remote-url') {
142
142
  throw new AiRuntimeError('unsupported_media_source', 'SiliconFlow transcription requires uploaded audio bytes');
143
143
  }
144
- const media = await readCapabilityMediaSource(input.audio, context.runtime.media);
145
- if (media.bytes.byteLength === 0)
146
- throw new AiRuntimeError('invalid_media', 'SiliconFlow transcription audio is empty');
147
- if (media.bytes.byteLength > maxFileBytes) {
148
- throw new AiRuntimeError('media_too_large', `SiliconFlow transcription audio exceeds ${maxFileBytes} bytes`);
149
- }
150
- const uploadBytes = new Uint8Array(media.bytes.byteLength);
151
- uploadBytes.set(media.bytes);
152
- const form = new FormData();
153
- form.append('file', new Blob([uploadBytes], { type: media.mimeType }), media.filename);
154
- form.append('model', preset.modelId);
155
- return form;
144
+ const media = await prepareMedia(input.audio, context.runtime, maxFileBytes, context.signal);
145
+ return multipartAudioRequest(media, [['model', preset.modelId]]);
156
146
  }
157
147
  /** Create an on-demand SiliconFlow file transcription module. */
158
148
  export function createSiliconFlowAsrModule(preset, moduleOptions = {}) {
@@ -168,12 +158,12 @@ export function createSiliconFlowAsrModule(preset, moduleOptions = {}) {
168
158
  throw cancelledError(context.requestId);
169
159
  validateInput(input);
170
160
  const apiKey = await credential(context);
171
- const response = await context.runtime.transport.fetch(`${apiBaseUrl}/audio/transcriptions`, {
161
+ const request = await formData(preset, input, maxFileBytes, context);
162
+ const response = await sendMediaRequest(context.runtime, `${apiBaseUrl}/audio/transcriptions`, {
172
163
  method: 'POST',
173
164
  headers: { Authorization: `Bearer ${apiKey}` },
174
- body: await formData(preset, input, maxFileBytes, context),
175
165
  signal: context.signal,
176
- });
166
+ }, request);
177
167
  const output = await parseResponse(response);
178
168
  await context.emit({ type: 'final', text: output.text });
179
169
  await context.emit({ type: 'completed', output });
@@ -7,7 +7,22 @@ function isAbort(error, signal) {
7
7
  export async function fetchProvider(provider, endpoint, init, options) {
8
8
  const endpoints = [endpoint, ...(options.fallbackEndpoints ?? []).filter((value) => value !== endpoint)];
9
9
  let lastFailure;
10
+ const attempts = [];
11
+ const method = (init.method ?? 'GET').toUpperCase();
12
+ const readOnly = method === 'GET' || method === 'HEAD';
13
+ const recordFailure = (error, url, startedAt) => {
14
+ const failure = describeNetworkFailure(error);
15
+ // 不记录查询字符串、任务路径、请求头或正文,避免把密钥与用户内容写入诊断。
16
+ let host = 'invalid-url';
17
+ try {
18
+ host = new URL(url).hostname;
19
+ }
20
+ catch { /* 非标准地址只记分类。 */ }
21
+ attempts.push({ host, code: failure.code, stage: shouldRetry(error, 'safe-preconnect') ? 'before_send' : 'unknown', durationMs: Date.now() - startedAt });
22
+ return failure;
23
+ };
10
24
  for (let index = 0; index < endpoints.length; index += 1) {
25
+ const startedAt = Date.now();
11
26
  try {
12
27
  const response = await options.transport.fetch(endpoints[index], init);
13
28
  options.onEndpointReached?.(endpoints[index]);
@@ -16,27 +31,46 @@ export async function fetchProvider(provider, endpoint, init, options) {
16
31
  catch (error) {
17
32
  if (isAbort(error, init.signal ?? undefined))
18
33
  throw error;
19
- const failure = describeNetworkFailure(error);
34
+ const failure = recordFailure(error, endpoints[index], startedAt);
20
35
  lastFailure = failure;
21
36
  const isSafePreconnectFailure = shouldRetry(error, 'safe-preconnect');
22
- if (isSafePreconnectFailure && index < endpoints.length - 1)
37
+ const canRetry = isSafePreconnectFailure || (readOnly && ['ECONNRESET', 'UND_ERR_SOCKET', 'ETIMEDOUT'].includes(failure.code.toUpperCase()));
38
+ if (canRetry && index < endpoints.length - 1)
23
39
  continue;
24
- if (options.retryPreconnectOnce && isSafePreconnectFailure) {
25
- await new Promise((resolve) => setTimeout(resolve, 250));
26
- try {
27
- const response = await options.transport.fetch(endpoints[index], init);
28
- options.onEndpointReached?.(endpoints[index]);
29
- return response;
30
- }
31
- catch (retryError) {
32
- if (isAbort(retryError, init.signal ?? undefined))
33
- throw retryError;
34
- lastFailure = describeNetworkFailure(retryError);
40
+ if (options.retryPreconnectOnce && canRetry) {
41
+ // 保留内部兼容开关名;采用有界退避,每次失败重新核对是否仍可安全重放。
42
+ for (const delay of [1000, 3000, 8000]) {
43
+ await waitForRetry(delay, init.signal ?? undefined);
44
+ const retryStartedAt = Date.now();
45
+ try {
46
+ const response = await options.transport.fetch(endpoints[index], init);
47
+ options.onEndpointReached?.(endpoints[index]);
48
+ return response;
49
+ }
50
+ catch (retryError) {
51
+ if (isAbort(retryError, init.signal ?? undefined))
52
+ throw retryError;
53
+ lastFailure = recordFailure(retryError, endpoints[index], retryStartedAt);
54
+ const safe = shouldRetry(retryError, 'safe-preconnect')
55
+ || (readOnly && ['ECONNRESET', 'UND_ERR_SOCKET', 'ETIMEDOUT'].includes(lastFailure.code.toUpperCase()));
56
+ if (!safe)
57
+ break;
58
+ }
35
59
  }
36
60
  }
37
61
  break;
38
62
  }
39
63
  }
40
64
  const failure = lastFailure ?? { code: 'UNKNOWN_NETWORK_ERROR', message: 'Unknown network failure' };
41
- throw new AiRuntimeError('provider_network_error', `${provider} 网络连接失败(${failure.code}),请检查网络后重试`);
65
+ throw new AiRuntimeError('provider_network_error', `${provider} 网络连接失败(${failure.code}),${!readOnly && attempts.at(-1)?.stage === 'unknown' ? '提交结果尚不确定,请先核对任务状态,勿重复提交' : '请检查网络后重试'}`, { provider, method, attempts, submissionState: readOnly ? 'read_only' : attempts.at(-1)?.stage === 'before_send' ? 'not_sent' : 'unknown' });
66
+ }
67
+ /** 取消立即清理计时器与监听器,不等退避结束。 */
68
+ function waitForRetry(ms, signal) {
69
+ signal?.throwIfAborted();
70
+ return new Promise((resolve, reject) => {
71
+ const finish = () => { signal?.removeEventListener('abort', abort); resolve(); };
72
+ const timer = setTimeout(finish, ms);
73
+ const abort = () => { clearTimeout(timer); signal?.removeEventListener('abort', abort); reject(signal?.reason ?? new Error('Request aborted')); };
74
+ signal?.addEventListener('abort', abort, { once: true });
75
+ });
42
76
  }
@@ -1,5 +1,6 @@
1
1
  export declare class AiRuntimeError extends Error {
2
+ readonly details?: Readonly<Record<string, unknown>> | undefined;
2
3
  readonly code: string;
3
- constructor(code: string, message: string);
4
+ constructor(code: string, message: string, details?: Readonly<Record<string, unknown>> | undefined);
4
5
  }
5
6
  export declare function cancelledError(requestId: string): AiRuntimeError;
@@ -1,7 +1,9 @@
1
1
  export class AiRuntimeError extends Error {
2
+ details;
2
3
  code;
3
- constructor(code, message) {
4
+ constructor(code, message, details) {
4
5
  super(`[${code}] ${message}`);
6
+ this.details = details;
5
7
  this.name = 'AiRuntimeError';
6
8
  this.code = code;
7
9
  }
@@ -8,8 +8,9 @@ export interface MediaBinary {
8
8
  }
9
9
  /**
10
10
  * `MediaReader` 是 SDK 把「用户在界面上选中的媒体」转换成「可以放进供应商请求体的字节」
11
- * 的唯一入口。供应商适配器上传本地图片/视频/音频前,统一先经过这一步,再决定是转成
12
- * `data:` URI 内联、还是调用供应商的文件上传接口换一个公网 URL。
11
+ * 的读取入口。支持 describe/readChunk 的文件 ASR 宿主以有界分块生成请求体;
12
+ * 百炼异步文件使用 Transport.uploadFile 原生直传,完全绕过媒体读取通道。
13
+ * read() 保留给旧宿主与其他尚需完整字节的能力,不具有流式内存保证。
13
14
  *
14
15
  * 为什么必须由宿主提供:`ref` 指向的资源在三个目标运行时里对应完全不同的读取方式——
15
16
  * - **Electron**:`ref` 通常是本地文件系统绝对路径(如 `/Users/x/image.png`),读取即
@@ -34,6 +35,10 @@ export interface MediaBinary {
34
35
  * 是调用方(SDK 内部)的职责,不是 `MediaReader` 要处理的输入。
35
36
  */
36
37
  export interface MediaReader {
38
+ /** Metadata only: do not allocate/read the file. Size rejection must preserve media_too_large details. */
39
+ describe?(ref: string): Promise<MediaDescription>;
40
+ /** At most length bytes (<= 64 KiB); short nonempty reads are allowed. */
41
+ readChunk?(ref: string, offset: number, length: number): Promise<Uint8Array>;
37
42
  /**
38
43
  * 读取 `ref` 指向的媒体,返回字节与元信息。
39
44
  * @param ref 本地路径或 `data:` URI,语义见上方接口注释
@@ -43,3 +48,20 @@ export interface MediaReader {
43
48
  */
44
49
  read(ref: string): Promise<MediaBinary>;
45
50
  }
51
+ export interface MediaDescription {
52
+ size: number;
53
+ mimeType: string;
54
+ filename: string;
55
+ /** Decoded PCM metadata, when known. Never infer compressed duration from sample rate alone. */
56
+ audio?: {
57
+ sampleRateHz: number;
58
+ channels: number;
59
+ bitsPerSample: number;
60
+ pcmBytes?: number;
61
+ durationSeconds?: number;
62
+ };
63
+ }
64
+ /** Shared by native hosts and SDK preflight. Compressed audio needs a measured duration. */
65
+ export declare function assertMediaSize(media: MediaDescription, maxBytes: number): void;
66
+ /** Preserve structured native/RPC size errors across realms; never guess sizes from an error string. */
67
+ export declare function rethrowMediaError(error: unknown): never;
@@ -1 +1,39 @@
1
- export {};
1
+ import { AiRuntimeError } from './AiRuntimeError.js';
2
+ /** Shared by native hosts and SDK preflight. Compressed audio needs a measured duration. */
3
+ export function assertMediaSize(media, maxBytes) {
4
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
5
+ throw new AiRuntimeError('invalid_parameter', 'Media byte limit must be a positive safe integer');
6
+ }
7
+ if (!Number.isSafeInteger(media.size) || media.size < 0) {
8
+ throw new AiRuntimeError('invalid_media', 'Invalid media size');
9
+ }
10
+ if (media.size <= maxBytes)
11
+ return;
12
+ const audio = media.audio;
13
+ const bytesPerSecond = audio && audio.sampleRateHz * audio.channels * audio.bitsPerSample / 8;
14
+ const pcmBytes = audio?.pcmBytes ?? (/^audio\/(?:pcm|l16|l24)$/i.test(media.mimeType) ? media.size : undefined);
15
+ const duration = audio?.durationSeconds ?? (pcmBytes !== undefined && bytesPerSecond && bytesPerSecond > 0
16
+ ? pcmBytes / bytesPerSecond : undefined);
17
+ throw new AiRuntimeError('media_too_large', '录音文件过大,请分段后重试', {
18
+ actualBytes: media.size, maxBytes,
19
+ estimatedDurationSeconds: duration !== undefined && Number.isFinite(duration) && duration >= 0 ? duration : null,
20
+ sampleRateHz: audio?.sampleRateHz ?? null,
21
+ channels: audio?.channels ?? null,
22
+ bitsPerSample: audio?.bitsPerSample ?? null,
23
+ });
24
+ }
25
+ /** Preserve structured native/RPC size errors across realms; never guess sizes from an error string. */
26
+ export function rethrowMediaError(error) {
27
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'media_too_large'
28
+ && 'details' in error && error.details && typeof error.details === 'object') {
29
+ const details = error.details;
30
+ if (typeof details.actualBytes === 'number' && Number.isSafeInteger(details.actualBytes)
31
+ && typeof details.maxBytes === 'number' && Number.isSafeInteger(details.maxBytes)
32
+ && details.actualBytes > details.maxBytes && details.maxBytes > 0) {
33
+ throw new AiRuntimeError('media_too_large', '录音文件过大,请分段后重试', {
34
+ ...details, estimatedDurationSeconds: details.estimatedDurationSeconds ?? null,
35
+ });
36
+ }
37
+ }
38
+ throw error;
39
+ }
@@ -32,6 +32,25 @@
32
32
  * 返回一个 `Response`,任何网络层都能满足。
33
33
  */
34
34
  export interface Transport {
35
+ /** Consume lazily with backpressure; never collect the body. Close the iterator on failure/cancel.
36
+ * No automatic replay. Implementations may use native streams; no Web Streams global is required.
37
+ */
38
+ fetchStream?(url: string, init: Omit<RequestInit, 'body'> & {
39
+ body: AsyncIterable<Uint8Array>;
40
+ contentLength: number;
41
+ }): Promise<Response>;
42
+ /** Native scoped-file multipart upload. Stat and enforce maxBytes BEFORE opening the network;
43
+ * stream outside the JS heap, file part last. No media.read/describe/readChunk calls.
44
+ * On size rejection throw media_too_large with actualBytes, maxBytes and known audio metadata.
45
+ * Preserve cancellation, scope checks and file identity/size throughout upload. Never replay.
46
+ */
47
+ uploadFile?(url: string, init: {
48
+ ref: string;
49
+ fields: readonly (readonly [string, string])[];
50
+ fileField: string;
51
+ maxBytes: number;
52
+ signal: AbortSignal;
53
+ }): Promise<Response>;
35
54
  /**
36
55
  * 发起一次网络请求,语义与标准 `fetch(url, init)` 完全一致:
37
56
  * - 网络失败(DNS、连接被拒绝、超时等)应该 `throw`,而不是返回一个“失败态”的 `Response`。
@@ -7,6 +7,7 @@
7
7
  */
8
8
  export { modelProviderErrorCategorySchema, modelProviderErrorSchema, parseModelProviderError, ProviderModelStepError, serializeModelProviderError, } from './errors.js';
9
9
  export { AiRuntimeError, cancelledError } from './AiRuntimeError.js';
10
+ export { assertMediaSize } from './MediaReader.js';
10
11
  export type { ModelProviderError, ModelProviderErrorCategory, ProviderErrorContext, } from './errors.js';
11
12
  export { createCancelledError, createCredentialError, isAgentSemanticRetryable, normalizeProviderError, } from './error-classify.js';
12
13
  export { describeNetworkFailure, shouldRetry } from './retry.js';
@@ -16,7 +17,7 @@ export type { RealtimeConnectOptions, RealtimeConnection, RealtimeMessage, Realt
16
17
  export { resolveRuntimeContext } from './RuntimeContext.js';
17
18
  export type { ResolvedRuntimeContext, RuntimeContext } from './RuntimeContext.js';
18
19
  export type { CredentialScope, CredentialStore } from './CredentialStore.js';
19
- export type { MediaBinary, MediaReader } from './MediaReader.js';
20
+ export type { MediaBinary, MediaReader, MediaDescription } from './MediaReader.js';
20
21
  export type { LogContext, Logger } from './Logger.js';
21
22
  export { noopLogger } from './Logger.js';
22
23
  export type { TraceSpan, Tracer } from './Tracer.js';
@@ -7,6 +7,7 @@
7
7
  */
8
8
  export { modelProviderErrorCategorySchema, modelProviderErrorSchema, parseModelProviderError, ProviderModelStepError, serializeModelProviderError, } from './errors.js';
9
9
  export { AiRuntimeError, cancelledError } from './AiRuntimeError.js';
10
+ export { assertMediaSize } from './MediaReader.js';
10
11
  export { createCancelledError, createCredentialError, isAgentSemanticRetryable, normalizeProviderError, } from './error-classify.js';
11
12
  export { describeNetworkFailure, shouldRetry } from './retry.js';
12
13
  export { resolveRuntimeContext } from './RuntimeContext.js';
@@ -35,7 +35,11 @@ export function describeNetworkFailure(error) {
35
35
  }
36
36
  export function shouldRetry(error, mode = 'request') {
37
37
  if (mode === 'safe-preconnect') {
38
- return SAFE_PRECONNECT_RETRY_CODES.has(describeNetworkFailure(error).code.toUpperCase());
38
+ return SAFE_PRECONNECT_RETRY_CODES.has(describeNetworkFailure(error).code.toUpperCase())
39
+ // Node TLS onConnectEnd 在 secureConnect 之前产生此错误;普通 read ECONNRESET 不提供同等证据。
40
+ // 来源:https://github.com/nodejs/node/blob/v24.0.0/lib/_tls_wrap.js#L1573-L1585
41
+ || errorChain(error).some(record => providerCode(record)?.toUpperCase() === 'ECONNRESET'
42
+ && record.message === 'Client network socket disconnected before secure TLS connection was established');
39
43
  }
40
44
  const structuredRetryable = readStructuredRetryable(error);
41
45
  if (structuredRetryable !== undefined)
package/docs/consumers.md CHANGED
@@ -3,24 +3,41 @@
3
3
  本清单是 `@henjicc/ai-sdk` 消费方的唯一维护入口,用于 SDK 发布后的跨仓升级协调。
4
4
  绝对路径仅描述当前开发机上的仓库位置,不进入 SDK 运行时代码、发布包或用户配置。
5
5
 
6
- 最后核对日期:2026-09-11
7
-
8
- 当前 SDK 版本:`0.4.1`(候选版本,待 CI 与公共 npm 发布)
9
-
10
- `0.4.1`:修复 KIE GPT Image 2 / 2.5 的视频参考误标签,图片生成、编辑、多图输入及请求契约保持不变。109 个生成模型的跨模态视频标签检查、67 项定向测试、SDK 862 项全量测试、可移植性、类型构建及仓外公开入口/受限宿主回装通过。外部消费者登记版本为 `0.2.8`:say-it 不使用图片模型,不受影响;henji-ai-ps 的 KIE GPT Image 2 标签消费情况需在该项目可访问时核实,尚未升级。
11
-
12
- `0.4.0`:新增硅基流动聊天预设与分类模型发现,提交 `a678dab6` 已推送,必需 CI `34565921443` 全部通过。SDK 861 项测试、宿主配置 15 项测试、类型构建、49 个公开入口及受限宿主候选包回装通过;未执行真实付费推理。移除服务端分类参数的断牙验证使 4 项测试失败,恢复后全量通过。
13
-
14
- 账号网页二次验证已完成,正式包与候选包校验值一致。已在仓外隔离 npm 配置并移除令牌环境变量,从公共 npm 匿名安装精确版本:Node ESM、严格 TypeScriptVite 49 个入口及无 TextEncoder/TextDecoder 的受限宿主验证通过,包含硅基流动动态模型发现。仓内 workspace 与三个示例 manifest 已锁定 `0.4.0`;现有外部消费者未新增硅基流动聊天使用,暂不升级。
15
-
16
- - tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.0.tgz`
17
- - shasum:`29a267d8a9f8375ac08b5af94569939473ab6a17`
18
- - integrity:`sha512-4D2gHakuo7Fhhh3a987qrObqJd78k8mIhXPL9dgE3A5uywBi4ebHj7U4XVLUcbjv/HO34wH/+8kFlE5JMOGD0g==`
19
-
20
- `0.3.0` 已发布:新增四家 GPT Image 2.5 pack、五家文本 embedding / 四家 rerank,DeepSeek 官方默认模型更新为 `deepseek-flash`。发布提交 `a2dc5bc4` 的必需 CI 门禁全部通过(运行 `34532267703`),SDK 全量 839 项测试和候选包回装通过。正式包已在隔离 npm 配置、无用户令牌的仓外环境从公共 npm 安装,标准 Vite 48 个入口与受限宿主验证通过;正式包校验值与候选包一致。未运行真实付费模型请求。
21
-
22
- Henji-AI workspace 与三个仓内示例 manifest 均锁定 `0.4.0`;下表原有示例运行记录仅代表 `0.2.8` 历史证据,本次未完成三个示例的独立全套回装复验。`say-it` 已在 `D:/VibeCode/说吧` 定位,仍锁定公共 npm `0.2.8`,实际按需使用 ASR、translation 与 LLM modules,未使用本次新增硅基流动聊天能力,无需机械升级;未运行其真实宿主验收。`henji-ai-ps` 的下表路径为另一台开发机记录,本机未定位该路径,不声称本次完成外部升级。
23
-
6
+ 最后核对日期:2026-09-14
7
+
8
+ Henji-AI 安装包的 GPT Image 2.5 高分辨率报错归属:宿主智能比例预处理曾忽略联动过滤,将 `smart` 转成 KIE 仅限 1K 的 `27:16`,SDK 在请求前正确拒绝。修复位于应用公共预处理,按当前分辨率/渠道的合法选项匹配;Flare/Sunburst 的 1K/2K/4K 及 APIMart、Grsai 同类筛选已有定向覆盖。无需放宽 SDK 契约或发布 SDK;安装包需随应用更新才包含修复,现有版本可手动选合法比例规避。
9
+
10
+ 当前仓内 SDK 版本:`0.5.0`(2026-09-21 本地发布门禁通过,等待当前提交 CI 与公共发布;公共版本仍为 `0.4.1`)
11
+
12
+ `0.5.0` 文件 ASR:四族九模型已核对。SDK 新增 describe/readChunk + fetchStream 的有界请求体消费,百炼异步 media-ref 必须通过 uploadFile 原生直传 OSS;旧宿主 read 兼容路径不得提升原内存阈值。structured media_too_large 保留实际字节、上限与可得时长。官方 Qwen 编码后10 MB对应原始7,500,000字节,Fun Flash 2,000,000,000字节/5分钟,Groq附件25,000,000,硅基流动50,000,000,百炼临时上传取min(1,000,000,000,凭证MB×1,000,000)。
13
+
14
+ 本次完成SDK精确回归、10组修复撤销反向验证、全量发布门禁与真实QuickJS 64 MiB堆探针(50 MB multipart7.5/12 MB JSON);未执行付费请求。say-it 当前 Host API 虽有分块,适配器仍拼回完整Uint8Array,请求体也仍缓冲,必须接入新契约后才可调整分块总文件上限;原生OSS单独受凭证限制。当前任务交付SDK及宿主调整依据,未改写该外部仓库或声称完成Tauri验收。Henji-AI/Photoshop现有生成媒体读取未迁移到新ASR接口,不机械增加未使用能力。
15
+
16
+ 2026-09-14 候选增量:安全网络失败在端点尝试耗尽后按 1/3/8 秒退避,每次失败重新判断重放安全性,取消立即结束退避。SDK 873 项测试、主进程定向测试与类型检查、可移植性及仓外 Vite/受限宿主消费通过;故意移除重试安全复核后,未知提交状态测试失败,恢复后通过。真实 Electron 主进程对本地服务及 KIE 无凭据只读查询,Node 与 Chromium 两栈均得到 HTTP 200;只证明当时连通,不代表付费生成成功,也未复现之前断连的外部原因。宿主增加关联原请求的脱敏 DNS/代理诊断,不更换网络栈。本机 npm 身份仍返回 401,本次候选变更尚未公共发布或同步外部消费者;下方旧候选包校验值仅对应此前版本内容。
17
+
18
+ `0.4.2`:共享 transport 将 Node 明确的 TLS 建立前 ECONNRESET 识别为未发送,允许备用端点切换;GET/HEAD 的瞬态断连也允许切换,提交状态不明的写请求仍禁止重放。错误附带脱敏端点、阶段、耗时和提交状态,Henji-AI 生成与继续查询日志保留这些字段。SDK 870 项测试、宿主 7 项定向测试、可移植性、类型构建、仓外 Node ESM / 严格 TypeScript / Vite 49 入口与受限宿主回装通过;故意放行普通 ECONNRESET 重放会被 3 项写请求测试检测,恢复后全量通过。未执行真实付费请求。外部宿主使用不同 transport,需按其错误契约核对影响,不能将 Node 连接前分类视为所有宿主均已实测。
19
+
20
+ 修复提交 `5bf36dce` 已推送,CI `34758299265` 已启动。本机 npm 身份检查返回 `401 Unauthorized`,认证恢复且必需 CI 通过前不得发布;未声称完成公共回装或外部消费者升级。候选包 shasum `dafaae4787798c3f22e5615fe8f5d60f4545c0a2`。宿主 Electron 类型检查与重启通过。上一提交 `45bb50b5` CI 已存在两项画布能力引用相等断言失败,与本次网络修复无关,未夹带修改。
21
+
22
+ `0.4.1`:修复 KIE GPT Image 2 / 2.5 的视频参考误标签,图片生成、编辑、多图输入及请求契约保持不变。109 个生成模型的跨模态视频标签检查、67 项定向测试、SDK 862 项全量测试、可移植性、类型构建及仓外公开入口/受限宿主回装通过。外部消费者登记版本为 `0.2.8`:say-it 不使用图片模型,不受影响;henji-ai-ps 的 KIE GPT Image 2 标签消费情况需在该项目可访问时核实,尚未升级。
23
+
24
+ 修复提交 `ef2be83f` 的必需 CI `34575686946` 已全部通过。正式包与固定候选包 shasum 一致:`32b8dd00f3e320822999b626b8e95a50b6a8ff2f`。发布后在仓外隔离 npm 配置并移除令牌环境变量,已从公共 npm 匿名安装精确版本;Node ESM、严格 TypeScript、49 个 Vite 入口及无 TextEncoder/TextDecoder 的受限宿主验证通过。仓内 workspace 与三个示例 manifest 已锁定 `0.4.1`;未执行真实付费生成或外部消费项目升级。
25
+
26
+ - `0.4.1` tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.1.tgz`
27
+ - `0.4.1` integrity:`sha512-HtOoOlJWGR6m15wIvqI2VMJFk+ijHntXl2oZAqEA6Ri12gWqaa3JhPthisTj+epO7GwGcuGX4gW3a8qIDd4vAw==`
28
+
29
+ `0.4.0`:新增硅基流动聊天预设与分类模型发现,提交 `a678dab6` 已推送,必需 CI `34565921443` 全部通过。SDK 861 项测试、宿主配置 15 项测试、类型构建、49 个公开入口及受限宿主候选包回装通过;未执行真实付费推理。移除服务端分类参数的断牙验证使 4 项测试失败,恢复后全量通过。
30
+
31
+ 账号网页二次验证已完成,正式包与候选包校验值一致。已在仓外隔离 npm 配置并移除令牌环境变量,从公共 npm 匿名安装精确版本:Node ESM、严格 TypeScript、Vite 49 个入口及无 TextEncoder/TextDecoder 的受限宿主验证通过,包含硅基流动动态模型发现。仓内 workspace 与三个示例 manifest 已锁定 `0.4.0`;现有外部消费者未新增硅基流动聊天使用,暂不升级。
32
+
33
+ - tarball:`https://registry.npmjs.org/@henjicc/ai-sdk/-/ai-sdk-0.4.0.tgz`
34
+ - shasum:`29a267d8a9f8375ac08b5af94569939473ab6a17`
35
+ - integrity:`sha512-4D2gHakuo7Fhhh3a987qrObqJd78k8mIhXPL9dgE3A5uywBi4ebHj7U4XVLUcbjv/HO34wH/+8kFlE5JMOGD0g==`
36
+
37
+ `0.3.0` 已发布:新增四家 GPT Image 2.5 pack、五家文本 embedding / 四家 rerank,DeepSeek 官方默认模型更新为 `deepseek-flash`。发布提交 `a2dc5bc4` 的必需 CI 门禁全部通过(运行 `34532267703`),SDK 全量 839 项测试和候选包回装通过。正式包已在隔离 npm 配置、无用户令牌的仓外环境从公共 npm 安装,标准 Vite 48 个入口与受限宿主验证通过;正式包校验值与候选包一致。未运行真实付费模型请求。
38
+
39
+ Henji-AI workspace 与三个仓内示例 manifest 均锁定 `0.4.0`;下表原有示例运行记录仅代表 `0.2.8` 历史证据,本次未完成三个示例的独立全套回装复验。`say-it` 已在 `D:/VibeCode/说吧` 定位,仍锁定公共 npm `0.2.8`,实际按需使用 ASR、translation 与 LLM modules,未使用本次新增硅基流动聊天能力,无需机械升级;未运行其真实宿主验收。`henji-ai-ps` 的下表路径为另一台开发机记录,本机未定位该路径,不声称本次完成外部升级。
40
+
24
41
  ## 判定口径
25
42
 
26
43
  - **消费者**:package manifest / lockfile 声明 SDK,或源码、构建入口实际导入 SDK。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-28 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 能力 | 长音频/录音文件异步识别 |
7
7
  | 平台模型 ID | `fun-asr`(稳定别名,官方当前等同 `fun-asr-2025-11-07`) |
8
8
  | 输入上限 | 12 小时 / 2 GB;单次 1 个 URL |
@@ -41,3 +41,7 @@ GET https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/tasks/{task_id}
41
41
  | 临时上传 | https://help.aliyun.com/zh/model-studio/get-temporary-file-url | 否 |
42
42
  | 价格 | https://help.aliyun.com/zh/model-studio/model-pricing | 否 |
43
43
  | API Key | https://bailian.console.aliyun.com/?apiKey=1#/api-key | **是** |
44
+
45
+ ## 2026-09-21 文件传输边界复核
46
+
47
+ 模型 URL 输入限制为 12 小时 / 2 GB;百炼临时上传另有 1 GB 上限。SDK 本地文件直传上限取 min(1,000,000,000, floor(max_file_size_mb × 1,000,000)) 字节;凭证字段接受官方字段表的字符串及示例的数字。media-ref 只走 transport.uploadFile,完全不调用 media;上传后才提交 oss://。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-28 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 能力 | 短音频同步识别(HTTP) |
7
7
  | 平台模型 ID | `fun-asr-flash-2026-06-15` |
8
8
  | 输入上限 | 5 分钟 / 2 GB,单次 1 个音频 |
@@ -39,3 +39,7 @@ Content-Type: application/json
39
39
  | 临时上传 | https://help.aliyun.com/zh/model-studio/get-temporary-file-url | 否 |
40
40
  | 价格 | https://help.aliyun.com/zh/model-studio/model-pricing | 否 |
41
41
  | API Key | https://bailian.console.aliyun.com/?apiKey=1#/api-key | **是** |
42
+
43
+ ## 2026-09-21 文件传输边界复核
44
+
45
+ SDK 分块 JSON 路径按官方 2 GB 文件限制取 2,000,000,000 原始字节;仍受 5 分钟时长限制。不能以流式上传突破模型时长限制。HTTP 详细页:https://help.aliyun.com/zh/model-studio/fun-asr-flash-recorded-speech-recognition-http-api 。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-28 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 能力 | 短音频同步识别(OpenAI 兼容 / DashScope HTTP) |
7
7
  | 平台模型 ID | `qwen3-asr-flash`(稳定别名,官方当前等同 `qwen3-asr-flash-2025-09-08`) |
8
8
  | 输入上限 | 5 分钟 / 10 MB,单次 1 个音频 |
@@ -38,3 +38,7 @@ Authorization: Bearer <API Key>
38
38
  | 模型/音频规格 | https://help.aliyun.com/zh/model-studio/asr-model/ | 否 |
39
39
  | 价格 | https://help.aliyun.com/zh/model-studio/model-pricing | 否 |
40
40
  | API Key | https://bailian.console.aliyun.com/?apiKey=1#/api-key | **是** |
41
+
42
+ ## 2026-09-21 文件传输边界复核
43
+
44
+ 官方 API 的 Base64 输入说明明确要求编码后不超过 10 MB。SDK 按十进制保守折算原始音频最大 7,500,000 字节(4 × ceil(n/3) ≤ 10,000,000),仍受 5 分钟限制;不能把原始字节的 10 MiB 当作 Base64 上限。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-28 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 能力 | 短音频同步识别(OpenAI 兼容 / DashScope HTTP) |
7
7
  | 平台模型 ID | `qwen3-asr-flash-2026-02-10`(最新快照) |
8
8
  | 输入上限 | 5 分钟 / 10 MB,单次 1 个音频 |
@@ -31,3 +31,7 @@
31
31
  | 临时上传 | https://help.aliyun.com/zh/model-studio/get-temporary-file-url | 否 |
32
32
  | 价格 | https://help.aliyun.com/zh/model-studio/model-pricing | 否 |
33
33
  | API Key | https://bailian.console.aliyun.com/?apiKey=1#/api-key | **是** |
34
+
35
+ ## 2026-09-21 文件传输边界复核
36
+
37
+ 官方 API 的 Base64 输入说明要求编码后不超过 10 MB。SDK 原始音频上限取 7,500,000 字节,仍受 5 分钟限制;与 qwen3-asr-flash 共享分块 JSON 路径。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-28 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 能力 | 长音频/录音文件异步识别 |
7
7
  | 平台模型 ID | `qwen3-asr-flash-filetrans` |
8
8
  | 输入上限 | 12 小时 / 2 GB;单次 1 个 URL |
@@ -39,3 +39,7 @@ GET https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/tasks/{task_id}
39
39
  | 临时上传 | https://help.aliyun.com/zh/model-studio/get-temporary-file-url | 否 |
40
40
  | 价格 | https://help.aliyun.com/zh/model-studio/model-pricing | 否 |
41
41
  | API Key | https://bailian.console.aliyun.com/?apiKey=1#/api-key | **是** |
42
+
43
+ ## 2026-09-21 文件传输边界复核
44
+
45
+ 本地文件先原生直传临时 OSS,SDK media-ref 不走 media 通道。临时服务限制 1 GB,实际上限取 min(1,000,000,000, floor(max_file_size_mb × 1,000,000));模型自身仍受 12 小时 / 2 GB 约束。
@@ -5,7 +5,7 @@
5
5
 
6
6
  | 项目 | 内容 |
7
7
  |---|---|
8
- | 最后更新 | 2026-09-10 |
8
+ | 最后更新 | 2026-09-21 |
9
9
  | 模型数量 | 生成主清单 20(图片 11 / 视频 9)+ 主清单外存量 12 + SDK 跨项目能力模型 20(ASR 15 / 翻译 3 / LLM 2) |
10
10
  | 模型供应商文档数量 | 生成/存量 84 + SDK 跨项目能力 20(另有 Fal 工具 12) |
11
11
  | 覆盖供应商 | 火山引擎(官方生成/ASR)、百炼(官方)、硅基流动(官方 ASR)、智谱(官方 LLM)、Groq(官方 LLM/ASR)、APIMart、KIE、Fal、派欧云、魔搭、Grsai |
@@ -18,27 +18,27 @@
18
18
 
19
19
  工具均复用 Fal 队列、上传与结果解析;它们是独立的按需 SDK 入口,不是新的执行内核。Henji-AI 运行客户端显式装载三个 pack,但普通模型选择器仍固定为 109 个默认模型:六个图片实用工具通过图片能力菜单创建固定模型节点,三个多角度工具由专用多角度节点选择,消除工具保留给后续遮罩能力接入。真实质量、延迟和账单仍待获得付费授权后验证。
20
20
 
21
- ## 一、目录结构约定
22
-
23
- ### 文本 Embedding / Rerank(2026-09-11)
24
-
25
- SDK 使用独立 `capabilities/embedding/<provider>` 与 `capabilities/rerank/<provider>` 入口,显式注册到能力客户端和发现目录,不混入图片生成目录。
26
-
27
- | 供应商 | Embedding | Rerank | 协议资料 |
28
- |---|---|---|---|
29
- | 硅基流动 | Qwen3 8B/4B/0.6B、BGE-M3 | Qwen3 8B/4B/0.6B、BGE v2 M3 | [供应商资料](供应商/硅基流动.md) |
30
- | 百炼 | Qwen3.7、Qwen3.7 Flash、v4 | Qwen3.7、Qwen3、GTE v2 | [供应商资料](供应商/百炼.md) |
31
- | 派欧云 | BGE-M3 | BGE v2 M3 | [供应商资料](供应商/派欧云.md) |
32
- | 智谱 | embedding-3 | rerank | 见下方官方依据 |
33
- | 火山方舟 | Doubao Embedding Vision 251215(文本单条) | VikingDB 独立服务暂未适配 | [供应商资料](供应商/火山引擎.md) |
34
-
35
- 智谱直接依据:[Embedding API](https://docs.bigmodel.cn/api-reference/模型-api/文本嵌入)、[Embedding-3 模型](https://docs.bigmodel.cn/cn/guide/models/embedding/embedding-3)、[Rerank API](https://docs.bigmodel.cn/api-reference/模型-api/文本重排序)、[公开价格](https://bigmodel.cn/pricing)。国内根地址 `https://open.bigmodel.cn`,Bearer。Embedding POST `/api/paas/v4/embeddings`,model=embedding-3,input 字符串数组,最多 64 条,dimensions=256/512/1024/2048;返回 data 数组的 index/embedding。字段表限制每条 3072 tokens,概述 8K 表述存在冲突,不用字符数猜 token 限制。Rerank POST `/api/paas/v4/rerank`,model=rerank,query/documents/top_n;最多 128 条,每条及 query 最多 4096 字符;返回 results 的 index/relevance_score。省略 top_n 返回全部;SDK 显式 topN 必须为正整数,超出候选数裁到候选数。文本从原输入索引回填。Embedding 公开价 0.5 元/百万 tokens,Rerank 搜索可见官方价格 0.8 元/百万 tokens,价格页运行时需要 JavaScript,实际账单以控制台为准。
36
-
37
- 共同验证边界:非流式单次 HTTP;空结果、缺失/重复/越界索引、非数值向量/分数不当作成功。向量维度必须一致;显式要求维度必须匹配。HTTP 错误、JSON 错误、供应商 error/code、取消和超时均失败;不自动重试付费 POST,不记录原文或凭据。未做真实付费验证。
38
-
39
- 其他现有供应商也进行了检索核对:[APIMart 官方 AnythingLLM 指南](https://docs.apimart.ai/cn/integrations/chat/anythingllm) 提及 text-embedding-3-small/large,但本轮未取到独立端点字段表与价格,保留待核验;[魔搭 API 推理介绍](https://modelscope.cn/docs/model-service/API-Inference/intro) 本次无法读取正文,模型仓库存在权重不等于托管 API 已开放。Groq、DeepSeek、Fal、KIE、Grsai 的公开入口搜索未获得足以适配这两类能力的完整契约,不据此断言平台不支持。
40
-
41
- ```
21
+ ## 一、目录结构约定
22
+
23
+ ### 文本 Embedding / Rerank(2026-09-11)
24
+
25
+ SDK 使用独立 `capabilities/embedding/<provider>` 与 `capabilities/rerank/<provider>` 入口,显式注册到能力客户端和发现目录,不混入图片生成目录。
26
+
27
+ | 供应商 | Embedding | Rerank | 协议资料 |
28
+ |---|---|---|---|
29
+ | 硅基流动 | Qwen3 8B/4B/0.6B、BGE-M3 | Qwen3 8B/4B/0.6B、BGE v2 M3 | [供应商资料](供应商/硅基流动.md) |
30
+ | 百炼 | Qwen3.7、Qwen3.7 Flash、v4 | Qwen3.7、Qwen3、GTE v2 | [供应商资料](供应商/百炼.md) |
31
+ | 派欧云 | BGE-M3 | BGE v2 M3 | [供应商资料](供应商/派欧云.md) |
32
+ | 智谱 | embedding-3 | rerank | 见下方官方依据 |
33
+ | 火山方舟 | Doubao Embedding Vision 251215(文本单条) | VikingDB 独立服务暂未适配 | [供应商资料](供应商/火山引擎.md) |
34
+
35
+ 智谱直接依据:[Embedding API](https://docs.bigmodel.cn/api-reference/模型-api/文本嵌入)、[Embedding-3 模型](https://docs.bigmodel.cn/cn/guide/models/embedding/embedding-3)、[Rerank API](https://docs.bigmodel.cn/api-reference/模型-api/文本重排序)、[公开价格](https://bigmodel.cn/pricing)。国内根地址 `https://open.bigmodel.cn`,Bearer。Embedding POST `/api/paas/v4/embeddings`,model=embedding-3,input 字符串数组,最多 64 条,dimensions=256/512/1024/2048;返回 data 数组的 index/embedding。字段表限制每条 3072 tokens,概述 8K 表述存在冲突,不用字符数猜 token 限制。Rerank POST `/api/paas/v4/rerank`,model=rerank,query/documents/top_n;最多 128 条,每条及 query 最多 4096 字符;返回 results 的 index/relevance_score。省略 top_n 返回全部;SDK 显式 topN 必须为正整数,超出候选数裁到候选数。文本从原输入索引回填。Embedding 公开价 0.5 元/百万 tokens,Rerank 搜索可见官方价格 0.8 元/百万 tokens,价格页运行时需要 JavaScript,实际账单以控制台为准。
36
+
37
+ 共同验证边界:非流式单次 HTTP;空结果、缺失/重复/越界索引、非数值向量/分数不当作成功。向量维度必须一致;显式要求维度必须匹配。HTTP 错误、JSON 错误、供应商 error/code、取消和超时均失败;不自动重试付费 POST,不记录原文或凭据。未做真实付费验证。
38
+
39
+ 其他现有供应商也进行了检索核对:[APIMart 官方 AnythingLLM 指南](https://docs.apimart.ai/cn/integrations/chat/anythingllm) 提及 text-embedding-3-small/large,但本轮未取到独立端点字段表与价格,保留待核验;[魔搭 API 推理介绍](https://modelscope.cn/docs/model-service/API-Inference/intro) 本次无法读取正文,模型仓库存在权重不等于托管 API 已开放。Groq、DeepSeek、Fal、KIE、Grsai 的公开入口搜索未获得足以适配这两类能力的完整契约,不据此断言平台不支持。
40
+
41
+ ```
42
42
  docs/model-adaptation/
43
43
  ├── README.md # 本文件:总清单 + 索引
44
44
  ├── 文档采集手册.md # 官方调研、事件契约、fixture 与 SDK 首发验证的唯一详细规范
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-31 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 模态 | ASR(同步文件转写) |
7
7
  | 供应商 | SiliconFlow / SiliconCloud |
8
8
  | 平台模型 ID | `FunAudioLLM/SenseVoiceSmall` |
@@ -67,3 +67,7 @@ Say-It/SDK 接入是简单 Bearer + multipart HTTP,但不得透传硅基流动
67
67
  | 免费模型/实名/Rate Limits 规则 | https://docs.siliconflow.cn/cn/userguide/rate-limits/rate-limit-and-upgradation | 否 |
68
68
  | 错误处理 | https://docs.siliconflow.cn/cn/faqs/error-code | 否 |
69
69
  | API Key | https://cloud.siliconflow.cn/account/ak | **是** |
70
+
71
+ ## 2026-09-21 文件传输边界复核
72
+
73
+ SDK 本地 multipart 默认上限为 50,000,000 字节(十进制保守解释官方 50 MB),仍受 1 小时时长限制。宿主 describe/readChunk + fetchStream 仅改变传输内存,不改变文件格式。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-31 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 模态 | ASR(同步文件转写) |
7
7
  | 供应商 | SiliconFlow / SiliconCloud |
8
8
  | 平台模型 ID | `TeleAI/TeleSpeechASR` |
@@ -70,3 +70,7 @@ Say-It/SDK 接入是简单 Bearer + multipart HTTP,但只能提交 `file/model
70
70
  | 客户端免费服务证据 | https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-BiBiKeyboard | 否 |
71
71
  | 错误处理 | https://docs.siliconflow.cn/cn/faqs/error-code | 否 |
72
72
  | API Key | https://cloud.siliconflow.cn/account/ak | **是** |
73
+
74
+ ## 2026-09-21 文件传输边界复核
75
+
76
+ SDK 本地 multipart 默认上限为 50,000,000 字节,仍受官方 1 小时时长限制。与 SenseVoiceSmall 共享分块上传路径。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-31 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 模态 | ASR(同步文件转写 / 音频译英文) |
7
7
  | 供应商 | GroqCloud |
8
8
  | 平台模型 ID | `whisper-large-v3` |
@@ -74,3 +74,7 @@ HTTP 文件处理。
74
74
  | Free Plan 限流 | https://console.groq.com/docs/rate-limits | 否 |
75
75
  | 错误码 | https://console.groq.com/docs/errors | 否 |
76
76
  | API Key | https://console.groq.com/keys | **是** |
77
+
78
+ ## 2026-09-21 文件传输边界复核
79
+
80
+ SDK 本地 multipart 默认上限为 25,000,000 字节(十进制保守解释官方 25 MB),付费账号也不能据 100 MB URL 限额扩大附件上传限制。分块上传不改变格式或模型参数。
@@ -2,7 +2,7 @@
2
2
 
3
3
  | 项目 | 内容 |
4
4
  |---|---|
5
- | 最后更新 | 2026-08-31 |
5
+ | 最后更新 | 2026-09-21 |
6
6
  | 模态 | ASR(同步文件转写) |
7
7
  | 供应商 | GroqCloud |
8
8
  | 平台模型 ID | `whisper-large-v3-turbo` |
@@ -73,3 +73,7 @@ Limits 页为准。
73
73
  | Free Plan 限流 | https://console.groq.com/docs/rate-limits | 否 |
74
74
  | 错误码 | https://console.groq.com/docs/errors | 否 |
75
75
  | API Key | https://console.groq.com/keys | **是** |
76
+
77
+ ## 2026-09-21 文件传输边界复核
78
+
79
+ SDK 本地 multipart 默认上限为 25,000,000 字节。官方 100 MB 开发者限制不等于附件上传限制,超过 25 MB 应使用 URL。分块上传不转码。