@cloudbase/ai 4.0.0 → 4.0.1-alpha.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 (83) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +1164 -0
  3. package/dist/cjs/AI.d.ts +6 -3
  4. package/dist/cjs/AI.js +67 -24
  5. package/dist/cjs/bot/index.js +1 -1
  6. package/dist/cjs/errors.d.ts +10 -0
  7. package/dist/cjs/errors.js +38 -0
  8. package/dist/cjs/index.d.ts +7 -3
  9. package/dist/cjs/index.js +22 -8
  10. package/dist/cjs/models/Ark/index.js +1 -1
  11. package/dist/cjs/models/DashScope/index.js +1 -1
  12. package/dist/cjs/models/DeepSeek/index.js +1 -1
  13. package/dist/cjs/models/Default/index.js +1 -1
  14. package/dist/cjs/models/HunYuan/index.js +1 -1
  15. package/dist/cjs/models/HunYuan/util.js +1 -1
  16. package/dist/cjs/models/HunYuanBeta/index.js +1 -1
  17. package/dist/cjs/models/HunYuanExp/index.js +1 -1
  18. package/dist/cjs/models/HunYuanOpen/index.js +1 -1
  19. package/dist/cjs/models/Moonshot/index.js +1 -1
  20. package/dist/cjs/models/Yi/index.js +1 -1
  21. package/dist/cjs/models/ZhiPu/index.js +1 -1
  22. package/dist/cjs/models/image-model.d.ts +19 -0
  23. package/dist/cjs/models/image-model.js +106 -0
  24. package/dist/cjs/models/index.d.ts +2 -1
  25. package/dist/cjs/models/index.js +4 -2
  26. package/dist/cjs/models/model.js +1 -1
  27. package/dist/cjs/type.d.ts +81 -0
  28. package/dist/cjs/type.js +1 -1
  29. package/dist/cjs/utils.d.ts +1 -1
  30. package/dist/cjs/utils.js +9 -4
  31. package/dist/esm/AI.d.ts +6 -3
  32. package/dist/esm/AI.js +67 -24
  33. package/dist/esm/bot/index.js +1 -1
  34. package/dist/esm/errors.d.ts +10 -0
  35. package/dist/esm/errors.js +35 -0
  36. package/dist/esm/index.d.ts +7 -3
  37. package/dist/esm/index.js +21 -8
  38. package/dist/esm/models/Ark/index.js +1 -1
  39. package/dist/esm/models/DashScope/index.js +1 -1
  40. package/dist/esm/models/DeepSeek/index.js +1 -1
  41. package/dist/esm/models/Default/index.js +1 -1
  42. package/dist/esm/models/HunYuan/index.js +1 -1
  43. package/dist/esm/models/HunYuan/util.js +1 -1
  44. package/dist/esm/models/HunYuanBeta/index.js +1 -1
  45. package/dist/esm/models/HunYuanExp/index.js +1 -1
  46. package/dist/esm/models/HunYuanOpen/index.js +1 -1
  47. package/dist/esm/models/Moonshot/index.js +1 -1
  48. package/dist/esm/models/Yi/index.js +1 -1
  49. package/dist/esm/models/ZhiPu/index.js +1 -1
  50. package/dist/esm/models/image-model.d.ts +19 -0
  51. package/dist/esm/models/image-model.js +103 -0
  52. package/dist/esm/models/index.d.ts +2 -1
  53. package/dist/esm/models/index.js +3 -2
  54. package/dist/esm/models/model.js +1 -1
  55. package/dist/esm/type.d.ts +81 -0
  56. package/dist/esm/type.js +1 -1
  57. package/dist/esm/utils.d.ts +1 -1
  58. package/dist/esm/utils.js +9 -4
  59. package/dist/miniprogram/index.js +1 -1
  60. package/dist/miniprogram/index.js.LICENSE.txt +2 -2
  61. package/package.json +4 -4
  62. package/src/AI.ts +64 -17
  63. package/src/bot/index.ts +2 -0
  64. package/src/errors.ts +23 -0
  65. package/src/index.ts +27 -7
  66. package/src/models/Ark/index.ts +2 -0
  67. package/src/models/DashScope/index.ts +2 -0
  68. package/src/models/DeepSeek/index.ts +2 -0
  69. package/src/models/Default/index.ts +2 -0
  70. package/src/models/HunYuan/index.ts +2 -0
  71. package/src/models/HunYuan/util.ts +2 -0
  72. package/src/models/HunYuanBeta/index.ts +2 -0
  73. package/src/models/HunYuanExp/index.ts +2 -0
  74. package/src/models/HunYuanOpen/index.ts +2 -0
  75. package/src/models/Moonshot/index.ts +3 -0
  76. package/src/models/Yi/index.ts +2 -0
  77. package/src/models/ZhiPu/index.ts +2 -0
  78. package/src/models/image-model.ts +100 -0
  79. package/src/models/index.ts +2 -0
  80. package/src/models/model.ts +2 -0
  81. package/src/type.ts +216 -1
  82. package/src/utils.ts +21 -4
  83. package/tsconfig.json +1 -1
@@ -15,8 +15,8 @@ PERFORMANCE OF THIS SOFTWARE.
15
15
 
16
16
  /**
17
17
  * @license
18
- * web-streams-polyfill v4.0.0
19
- * Copyright 2024 Mattias Buelens, Diwank Singh Tomer and other contributors.
18
+ * web-streams-polyfill v4.3.0
19
+ * Copyright 2026 Mattias Buelens, Diwank Singh Tomer and other contributors.
20
20
  * This code is released under the MIT license.
21
21
  * SPDX-License-Identifier: MIT
22
22
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/ai",
3
- "version": "4.0.0",
3
+ "version": "4.0.1-alpha.0",
4
4
  "description": "cloudbase js sdk ai module",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -30,8 +30,8 @@
30
30
  "author": "",
31
31
  "license": "Apache-2.0",
32
32
  "dependencies": {
33
- "@cloudbase/types": "4.0.0",
34
- "@cloudbase/utilities": "4.0.0",
33
+ "@cloudbase/types": "4.0.1-alpha.0",
34
+ "@cloudbase/utilities": "4.0.1-alpha.0",
35
35
  "@mattiasbuelens/web-streams-adapter": "^0.1.0",
36
36
  "text-encoding-shim": "^1.0.5",
37
37
  "web-streams-polyfill": "^4.0.0"
@@ -39,5 +39,5 @@
39
39
  "devDependencies": {
40
40
  "@cloudbase/adapter-interface": "^0.7.1"
41
41
  },
42
- "gitHead": "e489e90352537598cac4ce36b64ed3184b0d41b1"
42
+ "gitHead": "f893174f5888080382b515a0a3ba1f4dd8999662"
43
43
  }
package/src/AI.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /* eslint-disable eqeqeq */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
1
3
  import type { SDKRequestInterface } from '@cloudbase/adapter-interface'
2
4
  import { Bot } from './bot'
3
5
  import * as models from './models'
@@ -9,15 +11,28 @@ import { langEvent } from '@cloudbase/utilities'
9
11
  const { MODELS } = models
10
12
 
11
13
  class AI {
12
- public aiBaseUrl: string
13
- public aiBotBaseUrl: string
14
14
  public bot: Bot
15
15
  public i18n: ICloudbaseConfig['i18n']
16
16
  defaultHeaders: { [x: string]: any }
17
+ private _getBaseUrl?: () => string
17
18
 
18
- constructor(private req: SDKRequestInterface, public baseUrl: string, i18n: ICloudbaseConfig['i18n']) {
19
- this.aiBaseUrl = `${baseUrl}/ai`
20
- this.aiBotBaseUrl = `${baseUrl}/aibot`
19
+ /** 动态获取 aiBaseUrl,支持 registerEndPointWithKey 后端点变更生效 */
20
+ get aiBaseUrl(): string {
21
+ return `${this._getBaseUrl ? this._getBaseUrl() : this.baseUrl}/ai`
22
+ }
23
+
24
+ /** 动态获取 aiBotBaseUrl,支持 registerEndPointWithKey 后端点变更生效 */
25
+ get aiBotBaseUrl(): string {
26
+ return `${this._getBaseUrl ? this._getBaseUrl() : this.baseUrl}/aibot`
27
+ }
28
+
29
+ constructor(
30
+ private req: SDKRequestInterface,
31
+ public baseUrl: string,
32
+ i18n: ICloudbaseConfig['i18n'],
33
+ getBaseUrl?: () => string,
34
+ ) {
35
+ this._getBaseUrl = getBaseUrl
21
36
  this.bot = new Bot(this.botRequest, this.aiBotBaseUrl)
22
37
  this.i18n = i18n
23
38
 
@@ -29,25 +44,42 @@ class AI {
29
44
  async handleResponseData(responseData: Promise<unknown> | ReadableStream<Uint8Array>, header?: Headers) {
30
45
  const GO_TO_AI_TEXT = `${this.i18n.t('请检查调用方式,或前往云开发 AI+ 首页查看文档')}:https://tcb.cloud.tencent.com/dev#/ai`
31
46
 
32
- if (typeof responseData === 'object' && responseData && 'then' in responseData) {
33
- // 非流式请求,直接返回 json 数据
34
- const json = (await responseData) as Record<string, unknown>
35
- if (typeof json === 'object' && json && 'code' in json && json.code !== 'NORMAL') {
36
- throw new Error(`${this.i18n.t('AI+ 请求出错,错误码')}:${json.code},${this.i18n.t('错误信息')}:${
37
- json.message
47
+ const throwIfError = (json: unknown) => {
48
+ 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
+ (json as any).message
38
51
  }\n${GO_TO_AI_TEXT}\n${JSON.stringify(json, null, 2)}`,)
39
52
  }
53
+ }
40
54
 
55
+ // 非流式请求:
56
+ // - 小程序/Web 适配器返回 Promise(has 'then')
57
+ // - Node 适配器已将 body 解析为普通对象(无 'then'、无 getReader)
58
+ // 两种情况都按 JSON 校验错误码后原样返回
59
+ if (typeof responseData === 'object' && responseData && typeof (responseData as any).getReader !== 'function') {
60
+ const json = 'then' in responseData ? await responseData : responseData
61
+ throwIfError(json)
41
62
  return responseData
42
63
  }
43
- // 流式请求,如果接口出错,会变成传 json 格式的响应,通过 header 判断格式
44
- if (header?.get?.('content-type')?.includes('application/json')) {
45
- const json = await readableStream2JsonObject(responseData as ReadableStream<Uint8Array>)
64
+
65
+ // 判断 responseData 是否是 ReadableStream 实例(兼容 Node.js 适配器非流式场景已解析为 JSON 对象的情况)
66
+ const isStream = typeof (responseData as any)?.getReader === 'function'
67
+
68
+ if (!isStream) {
69
+ // responseData 已经是解析后的 JSON 对象(如 Node.js 适配器非流式返回),直接检查错误码
70
+ const json = responseData as unknown as Record<string, unknown>
46
71
  if (typeof json === 'object' && json && 'code' in json && json.code !== 'NORMAL') {
47
72
  throw new Error(`${this.i18n.t('AI+ 请求出错,错误码')}:${json.code},${this.i18n.t('错误信息')}:${
48
73
  json.message
49
74
  }\n${GO_TO_AI_TEXT}\n${JSON.stringify(json, null, 2)}`,)
50
75
  }
76
+ return responseData
77
+ }
78
+
79
+ // 流式请求,如果接口出错,会变成传 json 格式的响应,通过 header 判断格式
80
+ if (header?.get?.('content-type')?.includes('application/json')) {
81
+ const json = await readableStream2JsonObject(responseData as ReadableStream<Uint8Array>)
82
+ throwIfError(json)
51
83
  }
52
84
  return responseData
53
85
  }
@@ -81,6 +113,10 @@ class AI {
81
113
  MODELS[name] = model
82
114
  }
83
115
 
116
+ createImageModel<T extends 'hunyuan-image' | (string & {})>(provider: T) {
117
+ return new models.DefaultImageModel<T>(this.modelRequest, this.aiBaseUrl, provider)
118
+ }
119
+
84
120
  modelRequest: types.ModelReq = async ({ url, data, headers, stream, timeout }) => {
85
121
  const fetchHeaders = {
86
122
  'Content-Type': 'application/json',
@@ -92,7 +128,7 @@ class AI {
92
128
  method: 'post',
93
129
  headers: { [this.i18n?.LANG_HEADER_KEY]: this.i18n?.lang, ...fetchHeaders, ...headers },
94
130
  body: JSON.stringify(data),
95
- url,
131
+ url: this.resolveUrl(url),
96
132
  stream,
97
133
  timeout,
98
134
  })) as { data: Promise<unknown> | ReadableStream<Uint8Array>; header?: Headers }
@@ -101,8 +137,9 @@ class AI {
101
137
  }
102
138
 
103
139
  botRequest: types.BotReq = async ({ method, url, data = {}, headers, stream, timeout }) => {
140
+ const resolvedUrl = this.resolveUrl(url)
104
141
  if (method === 'get') {
105
- return this.handleResponseData((await this.req.fetch({ url: `${url}?${objectToParam(data)}`, method, headers, stream, timeout })).data,)
142
+ return this.handleResponseData((await this.req.fetch({ url: `${resolvedUrl}?${objectToParam(data)}`, method, headers, stream, timeout })).data,)
106
143
  }
107
144
 
108
145
  const fetchHeaders = {
@@ -112,7 +149,7 @@ class AI {
112
149
  stream && Object.assign(fetchHeaders, { Accept: 'text/event-stream' })
113
150
 
114
151
  const { data: responseData, header } = (await this.req.fetch({
115
- url,
152
+ url: resolvedUrl,
116
153
  body: JSON.stringify(data),
117
154
  headers: {
118
155
  [this.i18n?.LANG_HEADER_KEY]: this.i18n?.lang,
@@ -139,6 +176,16 @@ class AI {
139
176
  }
140
177
  models.toolMap.set(functionTool.name, functionTool.fn)
141
178
  }
179
+
180
+ /** 动态替换 URL 中的 origin 部分,支持 registerEndPointWithKey 后生效 */
181
+ private resolveUrl(url: string): string {
182
+ if (!this._getBaseUrl) return url
183
+ const currentBase = this._getBaseUrl()
184
+ if (currentBase !== this.baseUrl && url.startsWith(this.baseUrl)) {
185
+ return url.replace(this.baseUrl, currentBase)
186
+ }
187
+ return url
188
+ }
142
189
  }
143
190
 
144
191
  export { AI }
package/src/bot/index.ts CHANGED
@@ -34,6 +34,7 @@ export class Bot {
34
34
  req: BotReq
35
35
 
36
36
  constructor(req: BotReq, public baseUrl: string) {
37
+ // eslint-disable-next-line prefer-rest-params
37
38
  const token = arguments[2]
38
39
  if (typeof token === 'string') {
39
40
  this.req = ({ headers = {}, ...rest }) => req({ ...rest, headers: { ...headers, Authorization: `Bearer ${token}` } })
@@ -230,6 +231,7 @@ export class Bot {
230
231
  type BotEventStreamData = { content: string }
231
232
 
232
233
  class StreamResult {
234
+ // eslint-disable-next-line @typescript-eslint/naming-convention
233
235
  private _eventSourceStream: ReadableStream<ParsedEvent>
234
236
 
235
237
  constructor(_stream: ReadableStream<Uint8Array>) {
package/src/errors.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 参数校验错误类
3
+ * 用于统一参数校验错误的格式
4
+ */
5
+ export class InvalidParamsError extends Error {
6
+ public code: string
7
+ public field?: string
8
+
9
+ constructor(message: string, field?: string) {
10
+ super(message)
11
+ this.name = 'InvalidParamsError'
12
+ this.code = 'INVALID_PARAMS'
13
+ this.field = field
14
+ }
15
+
16
+ toJSON() {
17
+ return {
18
+ code: this.code,
19
+ message: this.message,
20
+ field: this.field,
21
+ }
22
+ }
23
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable eqeqeq */
1
2
  import type { IFetchOptions, SDKRequestInterface } from '@cloudbase/adapter-interface'
2
3
  import type { ICloudbase, ICloudbaseConfig } from '@cloudbase/types'
3
4
  import type { ICloudbaseComponent } from '@cloudbase/types/component'
@@ -54,9 +55,11 @@ interface AIInitOption {
54
55
  baseUrl?: string
55
56
  }
56
57
 
57
- interface ICreateAi {
58
+ interface ICreateAI {
58
59
  req: SDKRequestInterface
59
60
  baseUrl?: string
61
+ /** 动态获取 baseUrl 的函数,若提供则优先使用,支持端点变更后请求自动跟随 */
62
+ getBaseUrl?: () => string
60
63
  env?: string
61
64
  getAccessToken?: () => Promise<{ accessToken: string }>
62
65
  handleReqInstance?: HandleRequestInstanceFunc
@@ -73,8 +76,11 @@ interface ICreateAi {
73
76
  *
74
77
  * 其他参数可按需传入覆盖函数逻辑。
75
78
  */
76
- function createAi({ env, baseUrl, req, getAccessToken, handleReqInstance, i18n }: ICreateAi) {
79
+ function createAI({ env, baseUrl, getBaseUrl: getBaseUrlFunc, req, getAccessToken, handleReqInstance, i18n }: ICreateAI) {
77
80
  const getBaseUrl = () => {
81
+ if (getBaseUrlFunc != null) {
82
+ return getBaseUrlFunc()
83
+ }
78
84
  if (baseUrl != null) {
79
85
  return baseUrl
80
86
  }
@@ -94,13 +100,19 @@ function createAi({ env, baseUrl, req, getAccessToken, handleReqInstance, i18n }
94
100
  return handleReqInstance({ req })
95
101
  }
96
102
 
97
- return new AI(getReq(), getBaseUrl(), i18n)
103
+ return new AI(getReq(), getBaseUrl(), i18n, getBaseUrlFunc)
98
104
  }
99
105
 
106
+ // 兼容
107
+ /**
108
+ * @deprecated use `createAI`
109
+ */
110
+ const createAi = createAI
111
+
100
112
  /**
101
113
  * 创建 `AI` 实例。
102
114
  * 该函数挂载在 js sdk 的 Cloudbase 实例上。
103
- * 该函数调用了本模块暴露的 `createAi` 函数,但调用时通过传入参数做了逻辑覆盖:
115
+ * 该函数调用了本模块暴露的 `createAI` 函数,但调用时通过传入参数做了逻辑覆盖:
104
116
  *
105
117
  * - 传入从 Cloudbase 实例上的中心的 `req`
106
118
  * - 传入从 Cloudbase 实例上拿到的 `baseUrl`
@@ -123,9 +135,12 @@ function ai(
123
135
  return `${PROTOCOL}${BASE_URL}`
124
136
  }
125
137
 
126
- const baseUrl = options?.baseUrl ?? getUrlFromCloud()
138
+ // 若用户传了静态 baseUrl 则使用静态值,否则传入动态获取函数支持端点变更后生效
139
+ if (options?.baseUrl) {
140
+ return createAI({ req, baseUrl: options.baseUrl, handleReqInstance: ({ req }) => req, i18n: this.config.i18n })
141
+ }
127
142
 
128
- return createAi({ req, baseUrl, handleReqInstance: ({ req }) => req, i18n: this.config.i18n })
143
+ return createAI({ req, baseUrl: getUrlFromCloud(), getBaseUrl: getUrlFromCloud, handleReqInstance: ({ req }) => req, i18n: this.config.i18n })
129
144
  }
130
145
 
131
146
  const component: ICloudbaseComponent = {
@@ -141,5 +156,10 @@ function registerAi(app: ICloudbase) {
141
156
  }
142
157
  }
143
158
 
144
- export { AI, Bot, createAi, restModels as models, registerAi, utils }
159
+ export { AI, Bot, createAi, createAI, restModels as models, registerAi, utils }
145
160
  export * from './type'
161
+ export * from './errors'
162
+
163
+ try {
164
+ (window as any).registerAi = registerAi
165
+ } catch (e) {}
@@ -1,3 +1,5 @@
1
+ /* eslint-disable eqeqeq */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,
@@ -1,3 +1,5 @@
1
+ /* eslint-disable eqeqeq */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,
@@ -1,3 +1,4 @@
1
+ /* eslint-disable eqeqeq */
1
2
  import {
2
3
  createAsyncIterable,
3
4
  TransformStream,
@@ -42,6 +43,7 @@ export class DeepSeekSimpleModel implements SimpleChatModel {
42
43
 
43
44
  public async doStream(data: BaseChatModelInput, options?: ReqOptions): Promise<DoStreamOutput> {
44
45
  let isToolCall: null | boolean = null
46
+ // eslint-disable-next-line @typescript-eslint/naming-convention
45
47
  const _stream = await this.req({
46
48
  url: this.url,
47
49
  data: {
@@ -33,6 +33,7 @@ export class DefaultSimpleModel implements SimpleChatModel {
33
33
  public async doStream(data: BaseChatModelInput): Promise<DoStreamOutput> {
34
34
  let isToolCall: null | boolean = null
35
35
 
36
+ // eslint-disable-next-line @typescript-eslint/naming-convention
36
37
  const _stream = await this.req({
37
38
  url: this.url,
38
39
  data: {
@@ -48,6 +49,7 @@ export class DefaultSimpleModel implements SimpleChatModel {
48
49
  transform(chunk, controller) {
49
50
  const newChoices = chunk.choices.map((choice) => {
50
51
  const message = choice.delta
52
+ // eslint-disable-next-line eqeqeq
51
53
  if (isToolCall == null) isToolCall = isToolCallAssistantMessage(message)
52
54
  if (isToolCall) {
53
55
  return {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable eqeqeq */
1
2
  import {
2
3
  createAsyncIterable,
3
4
  TransformStream,
@@ -52,6 +53,7 @@ export class HunYuanSimpleModel implements SimpleChatModel {
52
53
  public async doStream(data: BaseChatModelInput, options?: ReqOptions): Promise<DoStreamOutput> {
53
54
  let isToolCall: null | boolean = null
54
55
 
56
+ // eslint-disable-next-line @typescript-eslint/naming-convention
55
57
  const _stream = await this.req({
56
58
  url: this.url,
57
59
  headers: {
@@ -1,3 +1,5 @@
1
+ /* eslint-disable eqeqeq */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
1
3
  import { HunYuanInputData } from './type'
2
4
  import { BaseChatModelInput } from '../../type'
3
5
 
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ /* eslint-disable eqeqeq */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ /* eslint-disable eqeqeq */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,
@@ -1,3 +1,4 @@
1
+ /* eslint-disable eqeqeq */
1
2
  import {
2
3
  createAsyncIterable,
3
4
  TransformStream,
@@ -45,6 +46,7 @@ export class HunYuanOpenSimpleModel implements SimpleChatModel {
45
46
  public async doStream(data: BaseChatModelInput, options?: ReqOptions): Promise<DoStreamOutput> {
46
47
  let isToolCall: null | boolean = null
47
48
 
49
+ // eslint-disable-next-line @typescript-eslint/naming-convention
48
50
  const _stream = await this.req({
49
51
  url: this.url,
50
52
  data: {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable eqeqeq */
1
2
  import {
2
3
  createAsyncIterable,
3
4
  TransformStream,
@@ -17,6 +18,7 @@ import type {
17
18
  } from '../../type'
18
19
 
19
20
  function processInput(input: BaseChatModelInput): MoonshotInputData {
21
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20
22
  const { messages, model, temperature, tools, top_p } = input
21
23
 
22
24
  return {
@@ -56,6 +58,7 @@ export class MoonshotSimpleModel implements SimpleChatModel {
56
58
 
57
59
  public async doStream(data: BaseChatModelInput, options?: ReqOptions): Promise<DoStreamOutput> {
58
60
  let isToolCall: null | boolean = null
61
+ // eslint-disable-next-line @typescript-eslint/naming-convention
59
62
  const _stream = await this.req({
60
63
  url: this.url,
61
64
  data: {
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ /* eslint-disable eqeqeq */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,
@@ -1,3 +1,5 @@
1
+ /* eslint-disable eqeqeq */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,
@@ -0,0 +1,100 @@
1
+ import type {
2
+ ModelReq,
3
+ // ImageEditInput,
4
+ HunyuanGenerateImageInput,
5
+ HunyuanGenerateImageOutput,
6
+ HunyuanARGenerateImageInput,
7
+ HunyuanARGenerateImageOutput,
8
+ // HunyuanEditImageInput,
9
+ // HunyuanEditImageOutput,
10
+ } from '../type'
11
+ import { InvalidParamsError } from '../errors'
12
+
13
+ interface HunyuanImageGenerateFn {
14
+ (input: HunyuanARGenerateImageInput): Promise<HunyuanARGenerateImageOutput>
15
+ (input: HunyuanGenerateImageInput): Promise<HunyuanGenerateImageOutput>
16
+ }
17
+
18
+ type GenerateImageFn<TProvider> = TProvider extends 'hunyuan-image'
19
+ ? HunyuanImageGenerateFn
20
+ : (input: unknown) => Promise<unknown>
21
+
22
+ export class DefaultImageModel<TProvider extends string> {
23
+ public defaultGenerateImageSubUrl = 'images/ar/generations'
24
+
25
+ public generateImageSubUrlConfig: Record<string, Array<[RegExp, string]>> = {
26
+ 'hunyuan-image': [
27
+ [/^hunyuan-image(-v[12](\..*)?)?$/, 'images/generations'],
28
+ ],
29
+ }
30
+
31
+ /** 生成图片 */
32
+ public generateImage: GenerateImageFn<TProvider>
33
+
34
+ constructor(
35
+ private req: ModelReq,
36
+ public baseUrl: string,
37
+ public provider: TProvider,
38
+ ) {
39
+ this.generateImage = this.generateImageImpl.bind(this) as GenerateImageFn<TProvider>
40
+ }
41
+
42
+ /** 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
67
+
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
+ }
72
+
73
+ const res = (await this.req({
74
+ url: this.getGenerateUrl(subUrl),
75
+ data: input,
76
+ stream: false,
77
+ })) as HunyuanARGenerateImageOutput | HunyuanGenerateImageOutput
78
+
79
+ return res
80
+ }
81
+
82
+ private getGenerateUrl(subUrl: string) {
83
+ return `${this.baseUrl}/${this.provider}/${subUrl}`
84
+ }
85
+
86
+ /**
87
+ * 根据 provider 与 model 解析生成图片的子路径。
88
+ * 按配置顺序用 RegExp 匹配,命中即返回对应 subUrl。
89
+ */
90
+ private getGenerateImageSubUrl(provider: string, model: string): string | undefined {
91
+ const rules = this.generateImageSubUrlConfig[provider]
92
+ if (!rules) return undefined
93
+
94
+ for (const [pattern, subUrl] of rules) {
95
+ if (pattern.test(model)) return subUrl
96
+ }
97
+
98
+ return undefined
99
+ }
100
+ }
@@ -9,6 +9,7 @@ import { HunYuanExpSimpleModel } from './HunYuanExp/index'
9
9
  import { HunYuanOpenSimpleModel } from './HunYuanOpen/index'
10
10
  import { DeepSeekSimpleModel } from './DeepSeek'
11
11
  import { DefaultSimpleModel } from './Default'
12
+ import { DefaultImageModel } from './image-model'
12
13
 
13
14
  export const MODELS = {
14
15
  hunyuan: HunYuanSimpleModel,
@@ -34,6 +35,7 @@ export {
34
35
  HunYuanExpSimpleModel,
35
36
  HunYuanOpenSimpleModel,
36
37
  DefaultSimpleModel,
38
+ DefaultImageModel,
37
39
  }
38
40
 
39
41
  export { ReactModel, toolMap } from './model'
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ /* eslint-disable eqeqeq */
1
3
  import {
2
4
  createAsyncIterable,
3
5
  TransformStream,