@planet-matrix/mobius-model 0.6.0 → 0.10.1

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 (258) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/oxlint.config.ts +1 -2
  3. package/package.json +29 -17
  4. package/scripts/build.ts +2 -52
  5. package/src/ai/README.md +1 -0
  6. package/src/ai/ai.ts +107 -0
  7. package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
  8. package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
  9. package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
  10. package/src/ai/chat-completion-ai/index.ts +7 -0
  11. package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
  12. package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
  13. package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
  14. package/src/ai/embedding-ai/embedding-ai.ts +63 -0
  15. package/src/ai/embedding-ai/embedding.ts +50 -0
  16. package/src/ai/embedding-ai/index.ts +4 -0
  17. package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
  18. package/src/ai/index.ts +4 -0
  19. package/src/aio/README.md +100 -0
  20. package/src/aio/content.ts +141 -0
  21. package/src/aio/index.ts +3 -0
  22. package/src/aio/json.ts +127 -0
  23. package/src/aio/prompt.ts +246 -0
  24. package/src/basic/README.md +20 -15
  25. package/src/basic/error.ts +19 -5
  26. package/src/basic/function.ts +2 -2
  27. package/src/basic/index.ts +1 -0
  28. package/src/basic/promise.ts +141 -71
  29. package/src/basic/schedule.ts +111 -0
  30. package/src/basic/stream.ts +135 -25
  31. package/src/credential/README.md +107 -0
  32. package/src/credential/api-key.ts +158 -0
  33. package/src/credential/bearer.ts +73 -0
  34. package/src/credential/index.ts +4 -0
  35. package/src/credential/json-web-token.ts +96 -0
  36. package/src/credential/password.ts +170 -0
  37. package/src/cron/README.md +86 -0
  38. package/src/cron/cron.ts +87 -0
  39. package/src/cron/index.ts +1 -0
  40. package/src/drizzle/README.md +1 -0
  41. package/src/drizzle/drizzle.ts +1 -0
  42. package/src/drizzle/helper.ts +47 -0
  43. package/src/drizzle/index.ts +5 -0
  44. package/src/drizzle/infer.ts +52 -0
  45. package/src/drizzle/kysely.ts +8 -0
  46. package/src/drizzle/pagination.ts +198 -0
  47. package/src/email/README.md +1 -0
  48. package/src/email/index.ts +1 -0
  49. package/src/email/resend.ts +25 -0
  50. package/src/event/class-event-proxy.ts +5 -6
  51. package/src/event/common.ts +13 -3
  52. package/src/event/event-manager.ts +3 -3
  53. package/src/event/instance-event-proxy.ts +5 -6
  54. package/src/event/internal.ts +4 -4
  55. package/src/exception/README.md +28 -19
  56. package/src/exception/error/error.ts +123 -0
  57. package/src/exception/error/index.ts +2 -0
  58. package/src/exception/error/match.ts +38 -0
  59. package/src/exception/error/must-fix.ts +17 -0
  60. package/src/exception/index.ts +2 -0
  61. package/src/file-system/find.ts +53 -0
  62. package/src/file-system/index.ts +2 -0
  63. package/src/file-system/path.ts +76 -0
  64. package/src/file-system/resolve.ts +22 -0
  65. package/src/form/README.md +25 -0
  66. package/src/form/index.ts +1 -0
  67. package/src/form/inputor-controller/base.ts +861 -0
  68. package/src/form/inputor-controller/boolean.ts +39 -0
  69. package/src/form/inputor-controller/file.ts +39 -0
  70. package/src/form/inputor-controller/form.ts +179 -0
  71. package/src/form/inputor-controller/helper.ts +117 -0
  72. package/src/form/inputor-controller/index.ts +17 -0
  73. package/src/form/inputor-controller/multi-select.ts +99 -0
  74. package/src/form/inputor-controller/number.ts +116 -0
  75. package/src/form/inputor-controller/select.ts +109 -0
  76. package/src/form/inputor-controller/text.ts +82 -0
  77. package/src/http/READMD.md +1 -0
  78. package/src/http/api/api-core.ts +84 -0
  79. package/src/http/api/api-handler.ts +79 -0
  80. package/src/http/api/api-host.ts +47 -0
  81. package/src/http/api/api-result.ts +56 -0
  82. package/src/http/api/api-schema.ts +154 -0
  83. package/src/http/api/api-server.ts +130 -0
  84. package/src/http/api/api-test.ts +142 -0
  85. package/src/http/api/api-type.ts +34 -0
  86. package/src/http/api/api.ts +81 -0
  87. package/src/http/api/index.ts +11 -0
  88. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  89. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  90. package/src/http/api-adapter/api-result-arktype.ts +294 -0
  91. package/src/http/api-adapter/api-result-zod.ts +286 -0
  92. package/src/http/api-adapter/index.ts +5 -0
  93. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  94. package/src/http/bin/gen-api-list/index.ts +1 -0
  95. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  96. package/src/http/bin/gen-api-test/index.ts +1 -0
  97. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  98. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  99. package/src/http/bin/gen-api-type/index.ts +2 -0
  100. package/src/http/bin/index.ts +2 -0
  101. package/src/http/index.ts +3 -0
  102. package/src/huawei/README.md +1 -0
  103. package/src/huawei/index.ts +2 -0
  104. package/src/huawei/moderation/index.ts +1 -0
  105. package/src/huawei/moderation/moderation.ts +355 -0
  106. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  107. package/src/huawei/obs/index.ts +1 -0
  108. package/src/huawei/obs/obs.ts +42 -0
  109. package/src/index.ts +21 -2
  110. package/src/json/README.md +92 -0
  111. package/src/json/index.ts +1 -0
  112. package/src/json/repair.ts +18 -0
  113. package/src/log/logger.ts +15 -4
  114. package/src/openai/README.md +1 -0
  115. package/src/openai/index.ts +1 -0
  116. package/src/openai/openai.ts +509 -0
  117. package/src/orchestration/README.md +9 -7
  118. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  119. package/src/orchestration/dispatching/index.ts +2 -0
  120. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  121. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  122. package/src/orchestration/dispatching/selector/index.ts +2 -0
  123. package/src/orchestration/index.ts +2 -0
  124. package/src/orchestration/scheduling/index.ts +2 -0
  125. package/src/orchestration/scheduling/scheduler.ts +103 -0
  126. package/src/orchestration/scheduling/task.ts +32 -0
  127. package/src/random/README.md +8 -7
  128. package/src/random/base.ts +66 -0
  129. package/src/random/index.ts +5 -1
  130. package/src/random/random-boolean.ts +40 -0
  131. package/src/random/random-integer.ts +60 -0
  132. package/src/random/random-number.ts +72 -0
  133. package/src/random/random-string.ts +66 -0
  134. package/src/request/README.md +108 -0
  135. package/src/request/fetch/base.ts +108 -0
  136. package/src/request/fetch/browser.ts +280 -0
  137. package/src/request/fetch/general.ts +20 -0
  138. package/src/request/fetch/index.ts +4 -0
  139. package/src/request/fetch/nodejs.ts +280 -0
  140. package/src/request/index.ts +2 -0
  141. package/src/request/request/base.ts +246 -0
  142. package/src/request/request/general.ts +63 -0
  143. package/src/request/request/index.ts +3 -0
  144. package/src/request/request/resource.ts +68 -0
  145. package/src/result/README.md +4 -0
  146. package/src/result/controller.ts +58 -0
  147. package/src/result/either.ts +363 -0
  148. package/src/result/generator.ts +168 -0
  149. package/src/result/index.ts +3 -0
  150. package/src/route/README.md +105 -0
  151. package/src/route/adapter/browser.ts +122 -0
  152. package/src/route/adapter/driver.ts +56 -0
  153. package/src/route/adapter/index.ts +2 -0
  154. package/src/route/index.ts +3 -0
  155. package/src/route/router/index.ts +2 -0
  156. package/src/route/router/route.ts +630 -0
  157. package/src/route/router/router.ts +1641 -0
  158. package/src/route/uri/hash.ts +307 -0
  159. package/src/route/uri/index.ts +7 -0
  160. package/src/route/uri/pathname.ts +376 -0
  161. package/src/route/uri/search.ts +412 -0
  162. package/src/service/README.md +1 -0
  163. package/src/service/index.ts +1 -0
  164. package/src/service/service.ts +110 -0
  165. package/src/socket/README.md +105 -0
  166. package/src/socket/client/index.ts +2 -0
  167. package/src/socket/client/socket-unit.ts +658 -0
  168. package/src/socket/client/socket.ts +203 -0
  169. package/src/socket/common/index.ts +2 -0
  170. package/src/socket/common/socket-unit-common.ts +23 -0
  171. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  172. package/src/socket/index.ts +3 -0
  173. package/src/socket/server/index.ts +3 -0
  174. package/src/socket/server/server.ts +183 -0
  175. package/src/socket/server/socket-unit.ts +448 -0
  176. package/src/socket/server/socket.ts +264 -0
  177. package/src/storage/table.ts +3 -3
  178. package/src/timer/expiration/expiration-manager.ts +3 -3
  179. package/src/timer/expiration/remaining-manager.ts +3 -3
  180. package/src/tube/README.md +99 -0
  181. package/src/tube/helper.ts +137 -0
  182. package/src/tube/index.ts +2 -0
  183. package/src/tube/tube.ts +880 -0
  184. package/src/weixin/README.md +1 -0
  185. package/src/weixin/index.ts +2 -0
  186. package/src/weixin/official-account/authorization.ts +157 -0
  187. package/src/weixin/official-account/index.ts +2 -0
  188. package/src/weixin/official-account/js-api.ts +132 -0
  189. package/src/weixin/open/index.ts +1 -0
  190. package/src/weixin/open/oauth2.ts +131 -0
  191. package/tests/unit/ai/ai.spec.ts +85 -0
  192. package/tests/unit/aio/content.spec.ts +105 -0
  193. package/tests/unit/aio/json.spec.ts +146 -0
  194. package/tests/unit/aio/prompt.spec.ts +111 -0
  195. package/tests/unit/basic/error.spec.ts +16 -4
  196. package/tests/unit/basic/promise.spec.ts +158 -50
  197. package/tests/unit/basic/schedule.spec.ts +74 -0
  198. package/tests/unit/basic/stream.spec.ts +90 -37
  199. package/tests/unit/credential/api-key.spec.ts +36 -0
  200. package/tests/unit/credential/bearer.spec.ts +23 -0
  201. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  202. package/tests/unit/credential/password.spec.ts +40 -0
  203. package/tests/unit/cron/cron.spec.ts +84 -0
  204. package/tests/unit/event/class-event-proxy.spec.ts +3 -3
  205. package/tests/unit/event/event-manager.spec.ts +3 -3
  206. package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
  207. package/tests/unit/exception/error/error.spec.ts +83 -0
  208. package/tests/unit/exception/error/match.spec.ts +81 -0
  209. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  210. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  211. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  212. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  213. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  214. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  215. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  216. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  217. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  218. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  219. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  220. package/tests/unit/http/api/api-server.spec.ts +363 -0
  221. package/tests/unit/http/api/api-test.spec.ts +117 -0
  222. package/tests/unit/http/api/api.spec.ts +121 -0
  223. package/tests/unit/http/api-adapter/node-http.spec.ts +187 -0
  224. package/tests/unit/identifier/uuid.spec.ts +0 -1
  225. package/tests/unit/json/repair.spec.ts +11 -0
  226. package/tests/unit/log/logger.spec.ts +19 -4
  227. package/tests/unit/openai/openai.spec.ts +64 -0
  228. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  229. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  230. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  231. package/tests/unit/random/base.spec.ts +58 -0
  232. package/tests/unit/random/random-boolean.spec.ts +25 -0
  233. package/tests/unit/random/random-integer.spec.ts +32 -0
  234. package/tests/unit/random/random-number.spec.ts +33 -0
  235. package/tests/unit/random/random-string.spec.ts +22 -0
  236. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  237. package/tests/unit/request/fetch/general.spec.ts +43 -0
  238. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  239. package/tests/unit/request/request/base.spec.ts +382 -0
  240. package/tests/unit/request/request/general.spec.ts +160 -0
  241. package/tests/unit/result/controller.spec.ts +82 -0
  242. package/tests/unit/result/either.spec.ts +377 -0
  243. package/tests/unit/result/generator.spec.ts +273 -0
  244. package/tests/unit/route/router/route.spec.ts +430 -0
  245. package/tests/unit/route/router/router.spec.ts +407 -0
  246. package/tests/unit/route/uri/hash.spec.ts +72 -0
  247. package/tests/unit/route/uri/pathname.spec.ts +146 -0
  248. package/tests/unit/route/uri/search.spec.ts +107 -0
  249. package/tests/unit/socket/client.spec.ts +208 -0
  250. package/tests/unit/socket/server.spec.ts +133 -0
  251. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  252. package/tests/unit/tube/helper.spec.ts +139 -0
  253. package/tests/unit/tube/tube.spec.ts +501 -0
  254. package/vite.config.ts +2 -1
  255. package/dist/index.js +0 -50
  256. package/dist/index.js.map +0 -209
  257. package/src/random/string.ts +0 -35
  258. package/tests/unit/random/string.spec.ts +0 -11
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @planet-matrix/mobius-model
2
2
 
3
+ ## 0.10.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d7c33d7: Add `dist` to `.npmignore`.
8
+
9
+ ## 0.10.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 6f99535: Add service model.
14
+ - 9520154: Add tagged error capabilities to exception model.
15
+
16
+ ## 0.9.0
17
+
18
+ ### Minor Changes
19
+
20
+ - f5cd790: Add weixin model.
21
+ - fae630f: Add ai model.
22
+ - 6e132e6: Add openai model.
23
+
24
+ ## 0.8.0
25
+
26
+ ### Minor Changes
27
+
28
+ - 3c736c0: Add http model.
29
+ - 5036bdb: Add drizzle model.
30
+ - fefbfef: 优化 basic steam,新增 basic schedule。
31
+ - 5a9fb9d: Add cron model.
32
+ - f101d98: Add http model.
33
+ - de21d1f: Add email model.
34
+ - 84e76bc: Enhance logger functionality with default names for various components.
35
+ - 8146954: Implement Scheduler and Task classes with logging functionality and tests.
36
+ - faa0f2d: Add huawei model, include moderation and obs functionalities.
37
+ - 8421836: Add tube model.
38
+
39
+ ## 0.7.0
40
+
41
+ ### Minor Changes
42
+
43
+ - cbe190c: Add request model.
44
+ - 730cb8b: Add socket model.
45
+ - 7511df7: Add result model.
46
+ - a97b489: Add form model.
47
+ - 44cbf55: Add aio model.
48
+ - 8ec2d59: Add route model.
49
+ - 73cb3ea: Implement dispatcher and down-count selector for orchestration model.
50
+ - 5452bbe: Add json model.
51
+ - c1afa6e: Add credential model.
52
+
3
53
  ## 0.6.0
4
54
 
5
55
  ### Minor Changes
package/oxlint.config.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { defineConfig } from "oxlint"
2
1
  import { Lint } from "@planet-matrix/mobius-mono"
3
2
 
4
- const config: Lint.OxlintConfig = defineConfig(Lint.defineOxlintConfig({}))
3
+ const config: Lint.OxlintConfig = Lint.defineOxlintConfig({})
5
4
 
6
5
  export default config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planet-matrix/mobius-model",
3
- "version": "0.6.0",
3
+ "version": "0.10.1",
4
4
  "description": "Mobius model.",
5
5
  "keywords": [
6
6
  "mobius",
@@ -32,19 +32,19 @@
32
32
  }
33
33
  },
34
34
  "imports": {
35
- "#Project/*": {
36
- "import": {
37
- "@planet-matrix/mobius-mono": "./*",
38
- "bun": "./*",
39
- "default": "./*"
40
- }
41
- },
42
35
  "#Source/*": {
43
36
  "import": {
44
37
  "@planet-matrix/mobius-mono": "./src/*",
45
38
  "bun": "./src/*",
46
39
  "default": "./src/*"
47
40
  }
41
+ },
42
+ "#Project/*": {
43
+ "import": {
44
+ "@planet-matrix/mobius-mono": "./*",
45
+ "bun": "./*",
46
+ "default": "./*"
47
+ }
48
48
  }
49
49
  },
50
50
  "repository": {
@@ -59,21 +59,33 @@
59
59
  "prepublishOnly": "bun run build"
60
60
  },
61
61
  "dependencies": {
62
+ "typescript": "^6.0.2",
62
63
  "ua-parser-js": "^2.0.9",
63
64
  "html-to-image": "^1.11.13",
65
+ "undici": "^7.24.6",
66
+ "ws": "^8.20.0",
67
+ "@types/ws": "^8.18.1",
64
68
  "@standard-schema/spec": "^v1.1.0",
65
69
  "arktype": "^2.2.0",
66
- "@dotenvx/dotenvx": "^1.54.1"
70
+ "zod": "^4.3.6",
71
+ "@dotenvx/dotenvx": "^1.59.1",
72
+ "jsonrepair": "^3.13.3",
73
+ "jose": "^6.2.2",
74
+ "croner": "^10.0.1",
75
+ "drizzle-orm": "^0.45.2",
76
+ "resend": "^6.9.4",
77
+ "esdk-obs-nodejs": "^3.26.2",
78
+ "openai": "^6.33.0"
67
79
  },
68
80
  "devDependencies": {
69
- "@planet-matrix/mobius-mono": "0.8.0",
70
- "@types/bun": "^1.3.10",
71
- "oxlint": "^1.55.0",
72
- "oxlint-tsgolint": "^0.16.0",
73
- "typescript": "^5.9.3",
74
- "@typescript/native-preview": "^7.0.0-dev.20260311.1",
75
- "vite": "^8.0.0",
76
- "vitest": "^4.1.0"
81
+ "@planet-matrix/mobius-mono": "0.10.1",
82
+ "@types/bun": "^1.3.11",
83
+ "@typescript/native-preview": "^7.0.0-dev.20260329.1",
84
+ "oxlint": "^1.57.0",
85
+ "oxlint-tsgolint": "^0.18.1",
86
+ "vitest": "^4.1.2",
87
+ "vite": "^8.0.3",
88
+ "drizzle-kit": "^0.31.10"
77
89
  },
78
90
  "peerDependencies": {},
79
91
  "peerDependenciesMeta": {},
package/scripts/build.ts CHANGED
@@ -1,53 +1,3 @@
1
- import { $, build } from "bun"
1
+ import { Build } from "@planet-matrix/mobius-mono"
2
2
 
3
- // ensure the outdir is existing and cleaned up before building
4
- console.log("Cleaning and preparing ./dist directory...")
5
- await $`rm -rf dist`
6
-
7
- // console.log("Building TypeScript declarations...")
8
- // await $`bun run tsgo --noEmit false`
9
-
10
- console.log("Building the library with Bun...")
11
- const builtOutput = await build({
12
- banner: "// Enjoy using Example Library!",
13
- bytecode: false,
14
- conditions: [],
15
- define: {},
16
- drop: [],
17
- emitDCEAnnotations: false,
18
- entrypoints: ["./src/index.ts"],
19
- env: "PUBLIC_*",
20
- external: [],
21
- footer: "// Made with ♥ by the Planet Matrix team!",
22
- format: "esm",
23
- ignoreDCEAnnotations: false,
24
- loader: {},
25
- minify: {
26
- identifiers: true,
27
- keepNames: false,
28
- syntax: true,
29
- whitespace: true,
30
- },
31
- naming: {
32
- entry: '[dir]/[name].[ext]',
33
- chunk: '[name]-[hash].[ext]',
34
- asset: '[name]-[hash].[ext]',
35
- },
36
- outdir: "./dist",
37
- packages: "bundle",
38
- plugins: [],
39
- // publicPath: undefined,
40
- // root: undefined,
41
- sourcemap: "linked",
42
- splitting: true,
43
- target: "bun",
44
- throw: false,
45
- tsconfig: "./tsconfig.json",
46
- })
47
-
48
- if (builtOutput.success === false) {
49
- console.error('Build failed:', builtOutput.logs);
50
- throw new Error('Build process failed.');
51
- } else {
52
- console.log("Build completed successfully.");
53
- }
3
+ await Build.Library.build({})
@@ -0,0 +1 @@
1
+ # Ai
package/src/ai/ai.ts ADDED
@@ -0,0 +1,107 @@
1
+ import type * as Openai from "#Source/openai/index.ts"
2
+ import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts"
3
+ import { Logger } from "#Source/log/index.ts"
4
+ import { randomIntBetween } from "#Source/basic/index.ts"
5
+
6
+ import * as ChatCompletionAi from "./chat-completion-ai/index.ts"
7
+ import * as EmbeddingAi from "./embedding-ai/index.ts"
8
+
9
+ export type ChatCompletionOptions = Openai.OriginalChatCompletionOptions
10
+ export type EmbeddingOptions = Openai.CustomEmbeddingOptions
11
+
12
+ export interface WithAi {
13
+ ai: Ai
14
+ }
15
+ export interface AiOptions extends LoggerFriendlyOptions {
16
+ chatCompletionAiOptions?: ChatCompletionAi.ChatCompletionAi.ChatCompletionAiOptions | undefined
17
+ embeddingAiOptions?: EmbeddingAi.EmbeddingAi.EmbeddingAiOptions | undefined
18
+ /**
19
+ * @deprecated
20
+ */
21
+ instancePool: Openai.Openai[]
22
+ /**
23
+ * @deprecated
24
+ */
25
+ indexChangeMode?: "random" | "poll"
26
+ }
27
+ export class Ai implements LoggerFriendly {
28
+ readonly logger: Logger
29
+
30
+ readonly chatCompletionAi: ChatCompletionAi.ChatCompletionAi.ChatCompletionAi
31
+ readonly embeddingAi: EmbeddingAi.EmbeddingAi.EmbeddingAi
32
+
33
+ protected instancePool: Openai.Openai[]
34
+ protected indexChangeMode: "random" | "poll"
35
+ protected index = 0
36
+
37
+ constructor(options: AiOptions) {
38
+ this.logger = Logger.fromOptions(options).setDefaultName("Ai")
39
+
40
+ this.chatCompletionAi = new ChatCompletionAi.ChatCompletionAi.ChatCompletionAi(options.chatCompletionAiOptions ?? {
41
+ chatCompletionInstanceList: [],
42
+ })
43
+ this.embeddingAi = new EmbeddingAi.EmbeddingAi.EmbeddingAi(options.embeddingAiOptions ?? {
44
+ embeddingInstanceList: [],
45
+ })
46
+
47
+ this.instancePool = options.instancePool
48
+ this.indexChangeMode = options.indexChangeMode ?? "random"
49
+ }
50
+
51
+ protected changeIndex(): void {
52
+ this.logger.info(`chang instance index, mode: ${this.indexChangeMode}`)
53
+
54
+ switch (this.indexChangeMode) {
55
+ case "random": {
56
+ this.index = randomIntBetween(0, this.instancePool.length - 1)
57
+ break
58
+ }
59
+ case "poll": {
60
+ this.index = this.index + 1
61
+ if (this.index >= this.instancePool.length - 1) {
62
+ this.index = 0
63
+ }
64
+ break
65
+ }
66
+ default: {
67
+ throw new Error(`unknown indexChangeMode: ${String(this.indexChangeMode)}`)
68
+ }
69
+ }
70
+
71
+ this.logger.info(`change instance index success: ${this.index}`)
72
+ }
73
+
74
+ protected async getInstance(): Promise<Openai.Openai> {
75
+ this.logger.info(`get instance index: ${this.index}`)
76
+
77
+ const instance = this.instancePool[this.index]
78
+ if (instance === undefined) {
79
+ throw new Error("can not find instance")
80
+ }
81
+
82
+ this.logger.info(`get instance success, index: ${this.index}`)
83
+ this.logger.info(`get instance success, baseUrl: ${instance.getBaseUrl()}`)
84
+ return await Promise.resolve(instance)
85
+ }
86
+
87
+ protected async changeIndexAndGetInstance(): Promise<Openai.Openai> {
88
+ this.changeIndex()
89
+ return await this.getInstance()
90
+ }
91
+
92
+ /**
93
+ * @deprecated
94
+ */
95
+ async originalChatCompletionNoStreaming(options: ChatCompletionOptions): Promise<Openai.OriginalChatCompletionResult> {
96
+ const instance = await this.changeIndexAndGetInstance()
97
+ return await instance.originalChatCompletionNonStreaming(options)
98
+ }
99
+
100
+ /**
101
+ * @deprecated
102
+ */
103
+ async originalChatCompletionStreaming(options: ChatCompletionOptions): Promise<Openai.Stream<Openai.OriginalChatCompletionChunkResult>> {
104
+ const instance = await this.changeIndexAndGetInstance()
105
+ return await instance.originalChatCompletionStreaming(options)
106
+ }
107
+ }
@@ -0,0 +1,78 @@
1
+ import type * as Openai from "#Source/openai/index.ts"
2
+
3
+ import type { BaseChatCompletionOptions, ChatCompletionOptions, ChatCompletionResult } from "./chat-completion.ts"
4
+ import { BaseChatCompletion } from "./chat-completion.ts"
5
+
6
+ export interface AihubmixChatCompletionOptions extends BaseChatCompletionOptions {
7
+ openai: Openai.Openai
8
+ }
9
+ export class AihubmixChatCompletion extends BaseChatCompletion {
10
+ protected readonly openai: Openai.Openai
11
+
12
+ constructor(options: AihubmixChatCompletionOptions) {
13
+ super(options)
14
+ this.logger.setDefaultName("AihubmixChatCompletion")
15
+
16
+ this.openai = options.openai
17
+ }
18
+
19
+ protected transformOptions(options: ChatCompletionOptions): Openai.CustomChatCompletionOptions {
20
+ const transformedOptions: Openai.CustomChatCompletionOptions = {
21
+ messages: options.messages.map((message) => {
22
+ const { role, content } = message
23
+ if (role === "system") {
24
+ return message
25
+ }
26
+ if (role === "user") {
27
+ if (typeof content === "string") {
28
+ return {
29
+ role,
30
+ content,
31
+ }
32
+ }
33
+ if (Array.isArray(content)) {
34
+ return {
35
+ role,
36
+ content: content.map((item) => {
37
+ if (item.type === "image_url") {
38
+ return {
39
+ type: item.type,
40
+ image_url: item.imageUrl,
41
+ }
42
+ }
43
+ else {
44
+ return item
45
+ }
46
+ }),
47
+ }
48
+ }
49
+ throw new Error("Invalid content")
50
+ }
51
+ if (role === "assistant") {
52
+ return message
53
+ }
54
+ throw new Error("Invalid role")
55
+ }),
56
+ model: options.model,
57
+ ...(options.frequencyPenalty !== undefined ? { frequency_penalty: options.frequencyPenalty } : {}),
58
+ ...(options.logitBias !== undefined ? { logit_bias: options.logitBias } : {}),
59
+ ...(options.logprobs !== undefined ? { logprobs: options.logprobs } : {}),
60
+ ...(options.maxCompletionTokens !== undefined ? { max_completion_tokens: options.maxCompletionTokens } : {}),
61
+ ...(options.maxTokens !== undefined ? { max_tokens: options.maxTokens } : {}),
62
+ ...(options.presencePenalty !== undefined ? { presence_penalty: options.presencePenalty } : {}),
63
+ ...(options.responseFormat !== undefined ? { response_format: options.responseFormat } : {}),
64
+ ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
65
+ ...(options.topLogprobs !== undefined ? { top_logprobs: options.topLogprobs } : {}),
66
+ ...(options.topP !== undefined ? { top_p: options.topP } : {}),
67
+ timeout: options.timeoutFirstChunk ?? undefined,
68
+ abortSignal: options.abortSignal,
69
+ }
70
+ return transformedOptions
71
+ }
72
+
73
+ async chatCompletion(options: ChatCompletionOptions): Promise<ChatCompletionResult> {
74
+ const transformedOptions = this.transformOptions(options)
75
+ const result = await this.openai.customChatCompletionStreaming(transformedOptions)
76
+ return result
77
+ }
78
+ }
@@ -0,0 +1,270 @@
1
+ import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts"
2
+ import type { AbortManager } from "#Source/abort/index.ts"
3
+
4
+ import { Logger } from "#Source/log/index.ts"
5
+ import { Dispatcher } from "#Source/orchestration/index.ts"
6
+ import { fromWithAbortSignal } from "#Source/abort/index.ts"
7
+ import { controllerFromEitherType, eitherToTuple } from "#Source/result/index.ts"
8
+ import { scheduleMacroTask } from "#Source/basic/index.ts"
9
+ import { connectTube, Tube } from "#Source/tube/index.ts"
10
+ import * as Aio from "#Source/aio/index.ts"
11
+
12
+ import type { BaseChatCompletion, ChatCompletionOptions, ChatCompletionResult, Completion } from "./chat-completion.ts"
13
+
14
+ export interface ChatCompletionAiOptions extends LoggerFriendlyOptions {
15
+ chatCompletionInstanceList: BaseChatCompletion[]
16
+ /**
17
+ * The timeout for the first chunk of data to be received, in milliseconds.
18
+ *
19
+ * @default timeoutPerChunk | 30_000
20
+ */
21
+ timeoutFirstChunk?: number | undefined
22
+ /**
23
+ * The timeout for each chunk of data to be received, in milliseconds.
24
+ *
25
+ * @default 30_000
26
+ */
27
+ timeoutPerChunk?: number | undefined
28
+ /**
29
+ * The maximum number of tries for the request.
30
+ *
31
+ * @default 3
32
+ */
33
+ maxTries?: number | undefined
34
+ }
35
+ interface ResolvedChatCompletionAiOptions {
36
+ chatCompletionInstanceList: BaseChatCompletion[]
37
+ timeoutFirstChunk: number
38
+ timeoutPerChunk: number
39
+ maxTries: number
40
+ }
41
+ export class ChatCompletionAi implements LoggerFriendly {
42
+ protected readonly options: ResolvedChatCompletionAiOptions
43
+
44
+ readonly logger: Logger
45
+ private dispatcher: Dispatcher<BaseChatCompletion>
46
+
47
+ constructor(options: ChatCompletionAiOptions) {
48
+ this.options = {
49
+ chatCompletionInstanceList: options.chatCompletionInstanceList,
50
+ timeoutFirstChunk: options.timeoutFirstChunk ?? options.timeoutPerChunk ?? 30_000,
51
+ timeoutPerChunk: options.timeoutPerChunk ?? 30_000,
52
+ maxTries: options.maxTries ?? 3,
53
+ }
54
+
55
+ this.logger = Logger.fromOptions(options).setDefaultName("ChatCompletionAi")
56
+
57
+ this.dispatcher = new Dispatcher({
58
+ itemList: this.options.chatCompletionInstanceList,
59
+ logger: this.logger,
60
+ })
61
+ }
62
+
63
+ async chatCompletion(
64
+ options: ChatCompletionOptions
65
+ ): Promise<ChatCompletionResult> {
66
+ const controller = controllerFromEitherType<ChatCompletionResult>()
67
+
68
+ const completionTube = new Tube<Completion>({
69
+ historyCount: Infinity,
70
+ replayHistory: true
71
+ })
72
+
73
+ scheduleMacroTask({
74
+ task: async () => {
75
+ await this.request(options, completionTube)
76
+ },
77
+ })
78
+
79
+ return await controller.returnRight({
80
+ completionTube,
81
+ })
82
+ }
83
+
84
+ private async request(
85
+ options: ChatCompletionOptions,
86
+ targetCompletionTube: Tube<Completion>,
87
+ ): Promise<void> {
88
+ const { selector, destroy } = this.dispatcher.getSelector({
89
+ filter: (item) => {
90
+ return item.isSupportModel(options.model) === true
91
+ },
92
+ })
93
+ targetCompletionTube.subscribeEndEvent({
94
+ subscriber: () => {
95
+ destroy()
96
+ },
97
+ })
98
+
99
+ const latestCompletion: Completion = {
100
+ content: {
101
+ deltaList: [],
102
+ total: "",
103
+ },
104
+ token: {
105
+ deltaList: [],
106
+ total: 0,
107
+ },
108
+ }
109
+
110
+ const maxTries = options.maxTries ?? this.options.maxTries
111
+
112
+ interface HandleTryErrorOptions {
113
+ tryIndex: number
114
+ abortManager: AbortManager
115
+ }
116
+ const handleTryError = async (options: HandleTryErrorOptions): Promise<void> => {
117
+ const { tryIndex, abortManager } = options
118
+
119
+ this.logger.log(`Handle try error, current failure count: ${tryIndex + 1}, max tries: ${maxTries}`)
120
+
121
+ if (abortManager.isAborted()) {
122
+ this.logger.log("Chat completion aborted by invoker.")
123
+ await targetCompletionTube.pushError(new Error("Chat completion aborted by invoker."))
124
+ abortManager.abort("Chat completion aborted by invoker.")
125
+ return
126
+ }
127
+ else {
128
+ this.logger.log("Chat completion not aborted by invoker.")
129
+ }
130
+ // 清理资源
131
+ abortManager.abort("Failed request")
132
+
133
+ // 重置数据
134
+ latestCompletion.content = Aio.applyDeltaToTextContent(latestCompletion.content, {
135
+ type: "reset",
136
+ })
137
+ latestCompletion.token = Aio.applyDeltaToNumberContent(latestCompletion.token, {
138
+ type: "reset",
139
+ })
140
+ await targetCompletionTube.pushData(structuredClone(latestCompletion))
141
+
142
+ // 如果尝试次数未达到上限,则继续尝试
143
+ const nextTryIndex = tryIndex + 1
144
+ if (nextTryIndex < maxTries) {
145
+ this.logger.log(`Not exceed maximum tries (${maxTries}) for chat completion, try again`)
146
+ scheduleMacroTask({
147
+ task: async () => {
148
+ await startTry(nextTryIndex)
149
+ },
150
+ timeout: 500,
151
+ })
152
+ }
153
+ else {
154
+ this.logger.log(`Exceeded maximum tries (${maxTries}) for chat completion`)
155
+ await targetCompletionTube.pushError(new Error("Exceeded maximum tries."))
156
+ }
157
+ }
158
+ const startTry = async (tryIndex: number): Promise<void> => {
159
+ this.logger.log(`Start try, tries ${tryIndex + 1} of ${maxTries}`)
160
+
161
+ const abortManager = fromWithAbortSignal(options)
162
+ const [getItemLeft, getItemRight] = eitherToTuple(await selector.getItem())
163
+ if (getItemLeft !== undefined) {
164
+ await handleTryError({ tryIndex, abortManager })
165
+ return
166
+ }
167
+ const { item, markUnavailable } = getItemRight
168
+ this.logger.log(`${item.logger.getName()} is selected from pool`)
169
+ const [leftResult, rightResult] = eitherToTuple(await this.requestAndConstrainSpeed(
170
+ {
171
+ ...options,
172
+ abortSignal: abortManager.abortSignal,
173
+ },
174
+ item,
175
+ ))
176
+ if (leftResult !== undefined) {
177
+ await handleTryError({ tryIndex, abortManager })
178
+ return
179
+ }
180
+ const { completionTube: sourceCompletionTube } = rightResult
181
+ sourceCompletionTube.subscribeEndEvent({
182
+ subscriber: async (): Promise<void> => {
183
+ if (sourceCompletionTube.isError()) {
184
+ this.logger.log("Source completion tube ended with error")
185
+ markUnavailable()
186
+ await handleTryError({ tryIndex, abortManager })
187
+ }
188
+ else {
189
+ this.logger.log("Source completion tube ended normally")
190
+ await targetCompletionTube.end()
191
+ }
192
+ },
193
+ })
194
+ sourceCompletionTube.subscribeData({
195
+ subscriber: async (data: Completion): Promise<void> => {
196
+ await targetCompletionTube.pushData(data)
197
+ },
198
+ })
199
+ }
200
+
201
+ await startTry(0)
202
+ }
203
+
204
+ /**
205
+ * 发起请求并对返回结果施加时间约束,当发生以下情况时,将被视为请求失败:
206
+ * 1. 首个数据块超时;
207
+ * 2. 相邻数据块超时;
208
+ */
209
+ private async requestAndConstrainSpeed(
210
+ options: ChatCompletionOptions,
211
+ instance: BaseChatCompletion,
212
+ ): Promise<ChatCompletionResult> {
213
+ const controller = controllerFromEitherType<ChatCompletionResult>()
214
+
215
+ const [chatCompletionLeft, chatCompletionRight] = eitherToTuple(await instance.chatCompletion(options))
216
+ if (chatCompletionLeft !== undefined) {
217
+ return await controller.returnLeft(chatCompletionLeft)
218
+ }
219
+ const { completionTube } = chatCompletionRight
220
+
221
+ const newCompletionTube = new Tube<Completion>({ historyCount: Infinity, replayHistory: true })
222
+ // 无异常的情况下,上游数据直接传递给下游即可
223
+ connectTube(completionTube, newCompletionTube)
224
+
225
+ const timeoutFirstChunk = options.timeoutFirstChunk ?? this.options.timeoutFirstChunk
226
+ const timeoutPerChunk = options.timeoutPerChunk ?? this.options.timeoutPerChunk
227
+
228
+ // 定义首个数据块超时的处理逻辑
229
+ let hasReceivedFirstChunk = false
230
+ const firstChunkTimeoutTimer: NodeJS.Timeout = setTimeout(() => {
231
+ if (hasReceivedFirstChunk === false) {
232
+ this.logger.addOnceTags(["ConstrainSpeed"]).log("First chunk timeout")
233
+ void newCompletionTube.pushError(new Error("FirstChunkTimeout"))
234
+ }
235
+ }, timeoutFirstChunk)
236
+
237
+ // 定义相邻数据块超时的处理逻辑
238
+ let perChunkTimeoutTimer: NodeJS.Timeout | undefined
239
+ completionTube.subscribeData({
240
+ subscriber: () => {
241
+ if (hasReceivedFirstChunk === false) {
242
+ this.logger.addOnceTags(["ConstrainSpeed"]).log("Received first chunk, clear first chunk timeout timer")
243
+ hasReceivedFirstChunk = true
244
+ // Clear the timeout timer for the first chunk
245
+ clearTimeout(firstChunkTimeoutTimer)
246
+ }
247
+ else {
248
+ // Reset the timeout timer for each chunk
249
+ clearTimeout(perChunkTimeoutTimer)
250
+ }
251
+ // Set a new timeout timer for the next chunk
252
+ perChunkTimeoutTimer = setTimeout(() => {
253
+ this.logger.addOnceTags(["ConstrainSpeed"]).log("Per chunk timeout")
254
+ void newCompletionTube.pushError(new Error("PerChunkTimeout"))
255
+ }, timeoutPerChunk)
256
+ },
257
+ })
258
+ completionTube.subscribeEndEvent({
259
+ subscriber: () => {
260
+ this.logger.addOnceTags(["ConstrainSpeed"]).log("Source completion tube ended")
261
+ clearTimeout(firstChunkTimeoutTimer)
262
+ clearTimeout(perChunkTimeoutTimer)
263
+ },
264
+ })
265
+
266
+ return await controller.returnRight({
267
+ completionTube: newCompletionTube,
268
+ })
269
+ }
270
+ }