@cloudbase/ai 3.9.2 → 3.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/ai",
3
- "version": "3.9.2",
3
+ "version": "3.10.0",
4
4
  "description": "cloudbase js sdk ai module",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -39,5 +39,5 @@
39
39
  "devDependencies": {
40
40
  "@cloudbase/adapter-interface": "~0.7.1"
41
41
  },
42
- "gitHead": "518cce4fe49bb6af9f23d092cd5e350449220b08"
42
+ "gitHead": "adfb119f5756f8574c2042a6ad6e883d6468e689"
43
43
  }
package/src/AI.ts CHANGED
@@ -42,11 +42,12 @@ class AI {
42
42
  }
43
43
 
44
44
  async handleResponseData(responseData: Promise<unknown> | ReadableStream<Uint8Array>, header?: Headers) {
45
- const GO_TO_AI_TEXT = `${this.i18n.t('请检查调用方式,或前往云开发 AI+ 首页查看文档')}:https://tcb.cloud.tencent.com/dev#/ai`
45
+ const t = (key: string) => this.i18n?.t?.(key) ?? key
46
+ const GO_TO_AI_TEXT = `${t('请检查调用方式,或前往云开发 AI+ 首页查看文档')}:https://tcb.cloud.tencent.com/dev#/ai`
46
47
 
47
48
  const throwIfError = (json: unknown) => {
48
49
  if (typeof json === 'object' && json && 'code' in json && (json as any).code !== 'NORMAL') {
49
- throw new Error(`${this.i18n.t('AI+ 请求出错,错误码')}:${(json as any).code},${this.i18n.t('错误信息')}:${
50
+ throw new Error(`${t('AI+ 请求出错,错误码')}:${(json as any).code},${t('错误信息')}:${
50
51
  (json as any).message
51
52
  }\n${GO_TO_AI_TEXT}\n${JSON.stringify(json, null, 2)}`,)
52
53
  }
@@ -69,7 +70,7 @@ class AI {
69
70
  // responseData 已经是解析后的 JSON 对象(如 Node.js 适配器非流式返回),直接检查错误码
70
71
  const json = responseData as unknown as Record<string, unknown>
71
72
  if (typeof json === 'object' && json && 'code' in json && json.code !== 'NORMAL') {
72
- throw new Error(`${this.i18n.t('AI+ 请求出错,错误码')}:${json.code},${this.i18n.t('错误信息')}:${
73
+ throw new Error(`${t('AI+ 请求出错,错误码')}:${json.code},${t('错误信息')}:${
73
74
  json.message
74
75
  }\n${GO_TO_AI_TEXT}\n${JSON.stringify(json, null, 2)}`,)
75
76
  }
@@ -113,7 +114,7 @@ class AI {
113
114
  MODELS[name] = model
114
115
  }
115
116
 
116
- createImageModel<T extends 'hunyuan-image' | (string & {})>(provider: T) {
117
+ createImageModel<T extends 'hunyuan-image' | 'cloudbase' | (string & {})>(provider: T) {
117
118
  return new models.DefaultImageModel<T>(this.modelRequest, this.aiBaseUrl, provider)
118
119
  }
119
120
 
@@ -5,6 +5,8 @@ import type {
5
5
  HunyuanGenerateImageOutput,
6
6
  HunyuanARGenerateImageInput,
7
7
  HunyuanARGenerateImageOutput,
8
+ CloudbaseGenerateImageInput,
9
+ CloudbaseGenerateImageOutput,
8
10
  // HunyuanEditImageInput,
9
11
  // HunyuanEditImageOutput,
10
12
  } from '../type'
@@ -15,17 +17,35 @@ interface HunyuanImageGenerateFn {
15
17
  (input: HunyuanGenerateImageInput): Promise<HunyuanGenerateImageOutput>
16
18
  }
17
19
 
20
+ type CloudbaseImageGenerateFn = (input: CloudbaseGenerateImageInput) => Promise<CloudbaseGenerateImageOutput>
21
+
22
+ type GenerateImageInput =
23
+ | HunyuanARGenerateImageInput
24
+ | HunyuanGenerateImageInput
25
+ | CloudbaseGenerateImageInput
26
+
27
+ type GenerateImageOutput =
28
+ | HunyuanARGenerateImageOutput
29
+ | HunyuanGenerateImageOutput
30
+ | CloudbaseGenerateImageOutput
31
+
18
32
  type GenerateImageFn<TProvider> = TProvider extends 'hunyuan-image'
19
33
  ? HunyuanImageGenerateFn
20
- : (input: unknown) => Promise<unknown>
34
+ : TProvider extends 'cloudbase'
35
+ ? CloudbaseImageGenerateFn
36
+ : (input: unknown) => Promise<unknown>
21
37
 
22
38
  export class DefaultImageModel<TProvider extends string> {
39
+ /** 默认子路径,仅作为老 provider(''/hunyuan-image)的兜底,cloudbase 等新 provider 不支持兜底 */
23
40
  public defaultGenerateImageSubUrl = 'images/ar/generations'
24
41
 
25
42
  public generateImageSubUrlConfig: Record<string, Array<[RegExp, string]>> = {
26
43
  'hunyuan-image': [
27
44
  [/^hunyuan-image(-v[12](\..*)?)?$/, 'images/generations'],
28
45
  ],
46
+ cloudbase: [
47
+ [/^hy-image-v3\.5-preview$/, 'wand/hunyuan-image/v35-generation'],
48
+ ],
29
49
  }
30
50
 
31
51
  /** 生成图片 */
@@ -40,45 +60,68 @@ export class DefaultImageModel<TProvider extends string> {
40
60
  }
41
61
 
42
62
  /** generateImage 的实际实现 */
43
- private async generateImageImpl(input: HunyuanARGenerateImageInput | HunyuanGenerateImageInput,): Promise<HunyuanARGenerateImageOutput | HunyuanGenerateImageOutput> {
44
- // 当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 且 provider 为空或为 hunyuan-image 时校验参数
45
- if (
46
- typeof input === 'object' && input && 'model' in input && input.model === 'HY-Image-v3.0-I2I-ToB-v1.0.1' && (this.provider === '' || this.provider === 'hunyuan-image')
47
- ) {
48
- const typedInput = input
49
- const imageUrls = typedInput.image_urls as string[] | undefined
50
- const images = typedInput.images as string[] | undefined
51
-
52
- if (imageUrls && imageUrls.length > 1) {
53
- throw new InvalidParamsError(
54
- '当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,image_urls 最多只能有 1 个元素',
55
- 'image_urls',
56
- )
57
- }
58
- if (images && images.length > 1) {
59
- throw new InvalidParamsError(
60
- '当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,images 最多只能有 1 个元素',
61
- 'images',
62
- )
63
- }
64
- }
65
-
66
- let subUrl = this.defaultGenerateImageSubUrl
63
+ private async generateImageImpl(input: GenerateImageInput): Promise<GenerateImageOutput> {
64
+ this.validateInput(input)
67
65
 
68
- if (typeof input === 'object' && input && 'model' in input && typeof input.model === 'string') {
69
- const newSubUrl = this.getGenerateImageSubUrl(this.provider, input.model)
70
- if (newSubUrl) subUrl = newSubUrl
71
- }
66
+ const subUrl = this.resolveSubUrl(input)
72
67
 
73
68
  const res = (await this.req({
74
69
  url: this.getGenerateUrl(subUrl),
75
70
  data: input,
76
71
  stream: false,
77
- })) as HunyuanARGenerateImageOutput | HunyuanGenerateImageOutput
72
+ })) as GenerateImageOutput
78
73
 
79
74
  return res
80
75
  }
81
76
 
77
+ /** 按 provider 执行各自的参数校验 */
78
+ private validateInput(input: GenerateImageInput) {
79
+ if (this.provider === '' || this.provider === 'hunyuan-image') {
80
+ this.assertHunyuanInput(input)
81
+ }
82
+ }
83
+
84
+ /** 当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,image_urls / images 最多只能有 1 个元素 */
85
+ private assertHunyuanInput(input: GenerateImageInput) {
86
+ if (!input || input.model !== 'HY-Image-v3.0-I2I-ToB-v1.0.1') return
87
+
88
+ const { image_urls: imageUrls, images } = input
89
+
90
+ if (imageUrls && imageUrls.length > 1) {
91
+ throw new InvalidParamsError(
92
+ '当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,image_urls 最多只能有 1 个元素',
93
+ 'image_urls',
94
+ )
95
+ }
96
+ if (images && images.length > 1) {
97
+ throw new InvalidParamsError(
98
+ '当模型为 HY-Image-v3.0-I2I-ToB-v1.0.1 时,images 最多只能有 1 个元素',
99
+ 'images',
100
+ )
101
+ }
102
+ }
103
+
104
+ /**
105
+ * 解析生成图片的子路径。
106
+ * 老 provider(''/hunyuan-image)未命中路由时兜底到默认子路径;
107
+ * cloudbase 等新 provider 不支持默认子路径,模型未配置路由时直接报错。
108
+ */
109
+ private resolveSubUrl(input: GenerateImageInput): string {
110
+ const model = input && typeof input.model === 'string' ? input.model : undefined
111
+ const subUrl = model ? this.getGenerateImageSubUrl(this.provider, model) : undefined
112
+
113
+ if (subUrl) return subUrl
114
+
115
+ if (this.provider === 'cloudbase') {
116
+ throw new InvalidParamsError(
117
+ `cloudbase provider 不支持的模型:${model ?? '(未指定)'}`,
118
+ 'model',
119
+ )
120
+ }
121
+
122
+ return this.defaultGenerateImageSubUrl
123
+ }
124
+
82
125
  private getGenerateUrl(subUrl: string) {
83
126
  return `${this.baseUrl}/${this.provider}/${subUrl}`
84
127
  }
package/src/type.ts CHANGED
@@ -277,6 +277,117 @@ export interface HunyuanARGenerateImageOutput {
277
277
  }>
278
278
  }
279
279
 
280
+ // #region Image Chat Types (OpenAI Chat Completions 风格 messages)
281
+ // 用于 Hy-Image-3.5-preview 等以 Chat/Messages 协议提交的生图模型。
282
+ // 注意:这是 OpenAI Chat Completions 风格的协议,并非 OpenAI 完整标准,
283
+ // 其中 reasoning、is_generate 为服务端扩展字段。
284
+
285
+ /** Chat messages 中的文本内容块 */
286
+ export interface ChatContentPartText {
287
+ type: 'text'
288
+ /** 具体文本内容 */
289
+ text: string
290
+ }
291
+
292
+ /** Chat messages 中的图片内容块 */
293
+ export interface ChatContentPartImageUrl {
294
+ type: 'image_url'
295
+ /** 支持 http(s) 公网 URL 或 data:image/...;base64,... */
296
+ image_url: {
297
+ url: string
298
+ }
299
+ /** 仅 role=tool 回灌上一轮生图结果时为 true */
300
+ is_generate?: boolean
301
+ }
302
+
303
+ export type ChatContentPart = ChatContentPartText | ChatContentPartImageUrl
304
+
305
+ /** OpenAI Chat Completions 风格的会话消息 */
306
+ export interface ChatMessage {
307
+ /** 角色。tool 仅在把上一轮生图结果回灌作为下一轮上下文时出现 */
308
+ role: 'user' | 'assistant' | 'tool'
309
+ /** 该轮的具体内容,assistant 仅触发工具调用、不发文本时可省略 */
310
+ content?: ChatContentPart[]
311
+ /** 仅 role=assistant 多轮回灌时使用,透传上一轮响应中的思维链原文 */
312
+ reasoning?: string
313
+ /** 仅 role=assistant 多轮回灌时使用,元素结构见 ToolCall */
314
+ tool_calls?: ToolCall[]
315
+ /** 仅 role=tool 多轮回灌时使用,须与上一条 assistant.tool_calls[i].id 一致 */
316
+ tool_call_id?: string
317
+ }
318
+
319
+ export interface CloudbaseGenerateImageInput {
320
+ /** 模型名称:Hy-Image-3.5-preview,协议取值为 hy-image-v3.5-preview */
321
+ model: 'hy-image-v3.5-preview'
322
+ /** 多轮会话内容,时间从旧到新排列。最后一个 role=user 的消息作为本轮生图指令 */
323
+ messages: ChatMessage[]
324
+ /**
325
+ * 生成尺寸,格式 "${宽}x${高}"
326
+ * 宽高均为 [256, 8192],面积不超过 16777216(最高 4096x4096)
327
+ * 不传或传空串时由模型基于 prompt 语义与 generate_max_pixels 自主决定
328
+ */
329
+ size?: string
330
+ /** 生成种子,int64 范围 [0, 2^63-1];为 0 或不传时服务端随机分配 */
331
+ seed?: number
332
+ /**
333
+ * 生成图片的目标面积(像素数),仅在未传 size 时生效
334
+ * 支持 1048576(1K)、2359296(1.5K,默认)、4194304(2K)
335
+ */
336
+ generate_max_pixels?: number
337
+ /** 输入参考图面积上限(像素数),默认 1048576 */
338
+ resize_max_pixels?: number
339
+ /** 会话 ID,用于推理服务一致性哈希调度 */
340
+ session?: string
341
+ /** 业务自定义水印内容,最长 16 字符,生成在图片右下角 */
342
+ footnote?: string
343
+ /** 外部搜索增强开关,默认关闭 */
344
+ use_search_tool?: { value: boolean }
345
+ }
346
+
347
+ export interface CloudbaseGenerateImageOutput {
348
+ /** 此次请求的 traceID */
349
+ id: string
350
+ /** 固定 image.chat.completion.chunk */
351
+ object: string
352
+ /** Unix 秒级时间戳 */
353
+ created: number
354
+ /** 原厂实际服务返回的内部版本标识,不建议用于业务分支判断 */
355
+ model: string
356
+ /** 本帧所属 LLM 轮次,从 0 起算 */
357
+ round: number
358
+ choices: Array<{
359
+ index: number
360
+ /** 主图载体,成功时优先读取 delta.image.url 作为最终交付 URL */
361
+ delta: {
362
+ type?: string
363
+ image?: {
364
+ /** 本次生图的最终交付 URL,有效期 12 小时 */
365
+ url: string
366
+ width: number
367
+ height: number
368
+ source: string
369
+ tool_call_id: string
370
+ }
371
+ }
372
+ /** 成功终态下往往为 null;内容安全拦截/业务错误时为 error 等非空值 */
373
+ finish_reason?: string | null
374
+ }>
375
+ usage?: { total_tokens: number }
376
+ tokenhub_usage?: { total_tokens: number }
377
+ request_id?: string
378
+ /** 服务端拼好的「下一轮回灌消息序列」,多轮编辑时原样追加到下一轮 messages */
379
+ assembled_history?: ChatMessage[]
380
+ /** 仅失败终态帧才带 */
381
+ error?: {
382
+ type: string
383
+ code: string
384
+ message: string
385
+ request_id?: string
386
+ }
387
+ }
388
+
389
+ // #endregion
390
+
280
391
  export type HunyuanEditImageInput = {
281
392
  /** 模型名称,本期为 hunyuan-image,即为商品图编辑场景 */
282
393
  model: 'hunyuan-image' | (string & {})