@downcity/city 0.2.17

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 (142) hide show
  1. package/README.md +182 -0
  2. package/bin/core/auth/authenticator.d.ts +79 -0
  3. package/bin/core/auth/authenticator.d.ts.map +1 -0
  4. package/bin/core/auth/authenticator.js +125 -0
  5. package/bin/core/auth/authenticator.js.map +1 -0
  6. package/bin/core/auth/token-signer.d.ts +27 -0
  7. package/bin/core/auth/token-signer.d.ts.map +1 -0
  8. package/bin/core/auth/token-signer.js +116 -0
  9. package/bin/core/auth/token-signer.js.map +1 -0
  10. package/bin/core/auth/types.d.ts +94 -0
  11. package/bin/core/auth/types.d.ts.map +1 -0
  12. package/bin/core/auth/types.js +7 -0
  13. package/bin/core/auth/types.js.map +1 -0
  14. package/bin/core/base/base-env-catalog.d.ts +13 -0
  15. package/bin/core/base/base-env-catalog.d.ts.map +1 -0
  16. package/bin/core/base/base-env-catalog.js +85 -0
  17. package/bin/core/base/base-env-catalog.js.map +1 -0
  18. package/bin/core/base/base-init.d.ts +48 -0
  19. package/bin/core/base/base-init.d.ts.map +1 -0
  20. package/bin/core/base/base-init.js +105 -0
  21. package/bin/core/base/base-init.js.map +1 -0
  22. package/bin/core/base/base-instruction.d.ts +11 -0
  23. package/bin/core/base/base-instruction.d.ts.map +1 -0
  24. package/bin/core/base/base-instruction.js +37 -0
  25. package/bin/core/base/base-instruction.js.map +1 -0
  26. package/bin/core/base/base-router.d.ts +41 -0
  27. package/bin/core/base/base-router.d.ts.map +1 -0
  28. package/bin/core/base/base-router.js +222 -0
  29. package/bin/core/base/base-router.js.map +1 -0
  30. package/bin/core/base/base-runtime.d.ts +13 -0
  31. package/bin/core/base/base-runtime.d.ts.map +1 -0
  32. package/bin/core/base/base-runtime.js +116 -0
  33. package/bin/core/base/base-runtime.js.map +1 -0
  34. package/bin/core/base/base.d.ts +82 -0
  35. package/bin/core/base/base.d.ts.map +1 -0
  36. package/bin/core/base/base.js +156 -0
  37. package/bin/core/base/base.js.map +1 -0
  38. package/bin/core/runtime.d.ts +120 -0
  39. package/bin/core/runtime.d.ts.map +1 -0
  40. package/bin/core/runtime.js +13 -0
  41. package/bin/core/runtime.js.map +1 -0
  42. package/bin/core/types.d.ts +60 -0
  43. package/bin/core/types.d.ts.map +1 -0
  44. package/bin/core/types.js +7 -0
  45. package/bin/core/types.js.map +1 -0
  46. package/bin/index.d.ts +33 -0
  47. package/bin/index.d.ts.map +1 -0
  48. package/bin/index.js +37 -0
  49. package/bin/index.js.map +1 -0
  50. package/bin/service/action.d.ts +44 -0
  51. package/bin/service/action.d.ts.map +1 -0
  52. package/bin/service/action.js +48 -0
  53. package/bin/service/action.js.map +1 -0
  54. package/bin/service/ai/ai-service.d.ts +56 -0
  55. package/bin/service/ai/ai-service.d.ts.map +1 -0
  56. package/bin/service/ai/ai-service.js +262 -0
  57. package/bin/service/ai/ai-service.js.map +1 -0
  58. package/bin/service/ai/openai-compatible-provider.d.ts +54 -0
  59. package/bin/service/ai/openai-compatible-provider.d.ts.map +1 -0
  60. package/bin/service/ai/openai-compatible-provider.js +175 -0
  61. package/bin/service/ai/openai-compatible-provider.js.map +1 -0
  62. package/bin/service/ai/provider.d.ts +43 -0
  63. package/bin/service/ai/provider.d.ts.map +1 -0
  64. package/bin/service/ai/provider.js +50 -0
  65. package/bin/service/ai/provider.js.map +1 -0
  66. package/bin/service/ai/types.d.ts +94 -0
  67. package/bin/service/ai/types.d.ts.map +1 -0
  68. package/bin/service/ai/types.js +7 -0
  69. package/bin/service/ai/types.js.map +1 -0
  70. package/bin/service/env/env-service.d.ts +12 -0
  71. package/bin/service/env/env-service.d.ts.map +1 -0
  72. package/bin/service/env/env-service.js +39 -0
  73. package/bin/service/env/env-service.js.map +1 -0
  74. package/bin/service/env/env-store.d.ts +15 -0
  75. package/bin/service/env/env-store.d.ts.map +1 -0
  76. package/bin/service/env/env-store.js +35 -0
  77. package/bin/service/env/env-store.js.map +1 -0
  78. package/bin/service/env/schema.d.ts +171 -0
  79. package/bin/service/env/schema.d.ts.map +1 -0
  80. package/bin/service/env/schema.js +52 -0
  81. package/bin/service/env/schema.js.map +1 -0
  82. package/bin/service/env/types.d.ts +79 -0
  83. package/bin/service/env/types.d.ts.map +1 -0
  84. package/bin/service/env/types.js +8 -0
  85. package/bin/service/env/types.js.map +1 -0
  86. package/bin/service/hook.d.ts +23 -0
  87. package/bin/service/hook.d.ts.map +1 -0
  88. package/bin/service/hook.js +49 -0
  89. package/bin/service/hook.js.map +1 -0
  90. package/bin/service/installable-service.d.ts +75 -0
  91. package/bin/service/installable-service.d.ts.map +1 -0
  92. package/bin/service/installable-service.js +89 -0
  93. package/bin/service/installable-service.js.map +1 -0
  94. package/bin/service/instruction.d.ts +106 -0
  95. package/bin/service/instruction.d.ts.map +1 -0
  96. package/bin/service/instruction.js +70 -0
  97. package/bin/service/instruction.js.map +1 -0
  98. package/bin/service/service.d.ts +140 -0
  99. package/bin/service/service.d.ts.map +1 -0
  100. package/bin/service/service.js +94 -0
  101. package/bin/service/service.js.map +1 -0
  102. package/bin/service/studios/schema.d.ts +207 -0
  103. package/bin/service/studios/schema.d.ts.map +1 -0
  104. package/bin/service/studios/schema.js +60 -0
  105. package/bin/service/studios/schema.js.map +1 -0
  106. package/bin/service/studios/studio-store.d.ts +17 -0
  107. package/bin/service/studios/studio-store.d.ts.map +1 -0
  108. package/bin/service/studios/studio-store.js +49 -0
  109. package/bin/service/studios/studio-store.js.map +1 -0
  110. package/bin/service/studios/studios-service.d.ts +15 -0
  111. package/bin/service/studios/studios-service.d.ts.map +1 -0
  112. package/bin/service/studios/studios-service.js +51 -0
  113. package/bin/service/studios/studios-service.js.map +1 -0
  114. package/bin/service/studios/types.d.ts +51 -0
  115. package/bin/service/studios/types.d.ts.map +1 -0
  116. package/bin/service/studios/types.js +7 -0
  117. package/bin/service/studios/types.js.map +1 -0
  118. package/bin/service/types.d.ts +11 -0
  119. package/bin/service/types.d.ts.map +1 -0
  120. package/bin/service/types.js +5 -0
  121. package/bin/service/types.js.map +1 -0
  122. package/bin/store/db.d.ts +61 -0
  123. package/bin/store/db.d.ts.map +1 -0
  124. package/bin/store/db.js +29 -0
  125. package/bin/store/db.js.map +1 -0
  126. package/bin/store/table-api.d.ts +42 -0
  127. package/bin/store/table-api.d.ts.map +1 -0
  128. package/bin/store/table-api.js +100 -0
  129. package/bin/store/table-api.js.map +1 -0
  130. package/bin/store/types.d.ts +14 -0
  131. package/bin/store/types.d.ts.map +1 -0
  132. package/bin/store/types.js +7 -0
  133. package/bin/store/types.js.map +1 -0
  134. package/bin/utils/helpers.d.ts +101 -0
  135. package/bin/utils/helpers.d.ts.map +1 -0
  136. package/bin/utils/helpers.js +192 -0
  137. package/bin/utils/helpers.js.map +1 -0
  138. package/bin/utils/validation.d.ts +18 -0
  139. package/bin/utils/validation.d.ts.map +1 -0
  140. package/bin/utils/validation.js +28 -0
  141. package/bin/utils/validation.js.map +1 -0
  142. package/package.json +55 -0
package/bin/index.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @downcity/city 公共入口。
3
+ */
4
+ // ===========================================================================
5
+ // 场景 1:创建 City 实例
6
+ // ===========================================================================
7
+ export { City } from "./core/base/base.js";
8
+ // ===========================================================================
9
+ // 场景 2:注册 Service / InstallableService / AI 模型
10
+ // ===========================================================================
11
+ export { Service } from "./service/service.js";
12
+ export { InstallableService } from "./service/installable-service.js";
13
+ export { Action } from "./service/action.js";
14
+ export { AIService } from "./service/ai/ai-service.js";
15
+ export { Provider } from "./service/ai/provider.js";
16
+ export { createOpenAICompatibleProvider } from "./service/ai/openai-compatible-provider.js";
17
+ // ===========================================================================
18
+ // 场景 3:用户鉴权与 Token
19
+ // ===========================================================================
20
+ export { TokenSigner } from "./core/auth/token-signer.js";
21
+ // ===========================================================================
22
+ // 场景 4:管理 Studio 与环境变量(内置 Service)
23
+ // ===========================================================================
24
+ export { EnvService } from "./service/env/env-service.js";
25
+ export { EnvStore } from "./service/env/env-store.js";
26
+ export { StudiosService } from "./service/studios/studios-service.js";
27
+ export { executeDDL } from "./store/db.js";
28
+ // ===========================================================================
29
+ // 场景 6:内置表 Schema
30
+ // ===========================================================================
31
+ export { sqliteStudios, pgStudios } from "./service/studios/schema.js";
32
+ export { sqliteEnv, pgEnv } from "./service/env/schema.js";
33
+ // ===========================================================================
34
+ // 场景 7:工具函数
35
+ // ===========================================================================
36
+ export { randomSecret, base64UrlEncode, base64UrlDecode, base64UrlEncodeBytes, base64UrlDecodeBytes, timingSafeEqualBytes, httpError, normalizeEnvKey, bearerToken, parseDotenvEntries, } from "./utils/helpers.js";
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAI3C,8EAA8E;AAC9E,+CAA+C;AAC/C,8EAA8E;AAE9E,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA0B7C,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,8BAA8B,EAAE,MAAM,4CAA4C,CAAC;AAe5F,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAS1D,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAO1D,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAQtE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAE3D,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,EACT,eAAe,EACf,WAAW,EACX,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Action 模块。
3
+ *
4
+ * Action 是 Service 的一等能力单元。
5
+ * 每个 Action 有独立的 hook(before/after/onError),可独立被 client 调用。
6
+ *
7
+ * 使用方式:
8
+ * ```ts
9
+ * const zh2en = translateService.action("zh2en", async (ctx) => {
10
+ * return await translate(ctx.input.text, "zh", "en");
11
+ * });
12
+ * zh2en.before(checkBalance).after(deductFee);
13
+ * ```
14
+ */
15
+ import { Hook } from "./hook.js";
16
+ import type { Context } from "./service.js";
17
+ /** Action 的业务逻辑函数 */
18
+ export type ActionFn = (ctx: Context) => unknown | Promise<unknown>;
19
+ /** Action 实例 */
20
+ export declare class Action {
21
+ /** Action 唯一 ID */
22
+ readonly id: string;
23
+ /** Action 独立的 hook(before/after/onError) */
24
+ readonly hook: Hook;
25
+ /** 业务逻辑 */
26
+ private readonly _run;
27
+ constructor(id: string, fn: ActionFn);
28
+ /** 注册 before hook */
29
+ before(fn: HookFn): this;
30
+ /** 注册 after hook */
31
+ after(fn: HookFn): this;
32
+ /** 注册 onError hook */
33
+ onError(fn: HookFn): this;
34
+ /** 执行业务逻辑 */
35
+ run(ctx: Context): Promise<unknown>;
36
+ }
37
+ /**
38
+ * Hook 回调函数。
39
+ *
40
+ * 接收 Context,不返回值(返回 void 或 Promise<void>)。
41
+ * 如需修改 context,直接在 ctx 上改(如 ctx.output)。
42
+ */
43
+ export type HookFn = (ctx: Context) => void | Promise<void>;
44
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/service/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,qBAAqB;AACrB,MAAM,MAAM,QAAQ,GAAG,CACrB,GAAG,EAAE,OAAO,KACT,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC,gBAAgB;AAChB,qBAAa,MAAM;IACjB,mBAAmB;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,OAAc;IAE3B,WAAW;IACX,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAW;gBAEpB,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ;IAKpC,qBAAqB;IACrB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKxB,oBAAoB;IACpB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKvB,sBAAsB;IACtB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKzB,aAAa;IACP,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAG1C;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Action 模块。
3
+ *
4
+ * Action 是 Service 的一等能力单元。
5
+ * 每个 Action 有独立的 hook(before/after/onError),可独立被 client 调用。
6
+ *
7
+ * 使用方式:
8
+ * ```ts
9
+ * const zh2en = translateService.action("zh2en", async (ctx) => {
10
+ * return await translate(ctx.input.text, "zh", "en");
11
+ * });
12
+ * zh2en.before(checkBalance).after(deductFee);
13
+ * ```
14
+ */
15
+ import { Hook } from "./hook.js";
16
+ /** Action 实例 */
17
+ export class Action {
18
+ /** Action 唯一 ID */
19
+ id;
20
+ /** Action 独立的 hook(before/after/onError) */
21
+ hook = new Hook();
22
+ /** 业务逻辑 */
23
+ _run;
24
+ constructor(id, fn) {
25
+ this.id = id;
26
+ this._run = fn;
27
+ }
28
+ /** 注册 before hook */
29
+ before(fn) {
30
+ this.hook.before(fn);
31
+ return this;
32
+ }
33
+ /** 注册 after hook */
34
+ after(fn) {
35
+ this.hook.after(fn);
36
+ return this;
37
+ }
38
+ /** 注册 onError hook */
39
+ onError(fn) {
40
+ this.hook.onError(fn);
41
+ return this;
42
+ }
43
+ /** 执行业务逻辑 */
44
+ async run(ctx) {
45
+ return this._run(ctx);
46
+ }
47
+ }
48
+ //# sourceMappingURL=action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../../src/service/action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,gBAAgB;AAChB,MAAM,OAAO,MAAM;IACjB,mBAAmB;IACV,EAAE,CAAS;IACpB,4CAA4C;IACnC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IAE3B,WAAW;IACM,IAAI,CAAW;IAEhC,YAAY,EAAU,EAAE,EAAY;QAClC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;IACjB,CAAC;IAED,qBAAqB;IACrB,MAAM,CAAC,EAAU;QACf,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oBAAoB;IACpB,KAAK,CAAC,EAAU;QACd,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sBAAsB;IACtB,OAAO,CAAC,EAAU;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa;IACb,KAAK,CAAC,GAAG,CAAC,GAAY;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * AI Service 模块。
3
+ *
4
+ * AIService 处理所有 AI 通路(SDK 通路 + OpenAI 兼容通路)。
5
+ * 通过 action() 注册 modality action,通过 resolve() 匹配模型和 action。
6
+ *
7
+ * 鉴权由 City 在路由入口统一强制执行。
8
+ *
9
+ * 路由(City 自动生成):
10
+ * - POST /v1/ai/text — 文本生成
11
+ * - POST /v1/ai/stream — 流式生成
12
+ * - POST /v1/ai/image — 图片生成
13
+ * - POST /v1/ai/video — 视频生成
14
+ * - POST /v1/ai/chat/completions — OpenAI 兼容端点
15
+ * - GET /v1/ai/models — 模型列表
16
+ */
17
+ import { Service } from "../service.js";
18
+ import type { ActionFn } from "../action.js";
19
+ import type { ModelConfig, PublicModel } from "./types.js";
20
+ type EnvReader = (key: string) => string | undefined;
21
+ export declare class AIService extends Service {
22
+ /** 模型注册表 */
23
+ private modelMap;
24
+ /** SDK 通路 action 映射(modality → action) */
25
+ private modalityActions;
26
+ constructor();
27
+ use(...inputs: (ModelConfig | ModelConfig[])[]): this;
28
+ listModels(): ModelConfig[];
29
+ hasAction(): boolean;
30
+ resolve(query: {
31
+ model?: string;
32
+ mode?: string;
33
+ }, env?: EnvReader): {
34
+ model?: ModelConfig;
35
+ action: ActionFn;
36
+ };
37
+ private getAction;
38
+ private resolveOpenAIAction;
39
+ private createAutoPassthroughAction;
40
+ private listCandidateModels;
41
+ private compareModelPriority;
42
+ private getDefaultScore;
43
+ private getDefaultModes;
44
+ private getModelModalities;
45
+ private getModelEnvRequirements;
46
+ private getMissingEnv;
47
+ private handleModality;
48
+ private handleChatCompletions;
49
+ static listModels(aiService: AIService, options: {
50
+ env: EnvReader;
51
+ identity: "guest" | "user" | "admin";
52
+ }): PublicModel[];
53
+ private toPublicModel;
54
+ }
55
+ export {};
56
+ //# sourceMappingURL=ai-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-service.d.ts","sourceRoot":"","sources":["../../../src/service/ai/ai-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAgB,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAEV,WAAW,EAEX,WAAW,EACZ,MAAM,YAAY,CAAC;AAQpB,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AASrD,qBAAa,SAAU,SAAQ,OAAO;IACpC,YAAY;IACZ,OAAO,CAAC,QAAQ,CAAkC;IAElD,0CAA0C;IAC1C,OAAO,CAAC,eAAe,CAA+B;;IA4BtD,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,WAAW,GAAG,WAAW,EAAE,CAAC,EAAE,GAAG,IAAI;IAarD,UAAU,IAAI,WAAW,EAAE;IAI3B,SAAS,IAAI,OAAO;IAMpB,OAAO,CAAC,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG;QAAE,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,QAAQ,CAAA;KAAE;IA2B7G,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,2BAA2B;IAmCnC,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,aAAa;YAQP,cAAc;YAiBd,qBAAqB;IAmBnC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE;QAC/C,GAAG,EAAE,SAAS,CAAC;QACf,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;KACtC,GAAG,WAAW,EAAE;IAYjB,OAAO,CAAC,aAAa;CAgBtB"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * AI Service 模块。
3
+ *
4
+ * AIService 处理所有 AI 通路(SDK 通路 + OpenAI 兼容通路)。
5
+ * 通过 action() 注册 modality action,通过 resolve() 匹配模型和 action。
6
+ *
7
+ * 鉴权由 City 在路由入口统一强制执行。
8
+ *
9
+ * 路由(City 自动生成):
10
+ * - POST /v1/ai/text — 文本生成
11
+ * - POST /v1/ai/stream — 流式生成
12
+ * - POST /v1/ai/image — 图片生成
13
+ * - POST /v1/ai/video — 视频生成
14
+ * - POST /v1/ai/chat/completions — OpenAI 兼容端点
15
+ * - GET /v1/ai/models — 模型列表
16
+ */
17
+ import { Service } from "../service.js";
18
+ import { httpError } from "../../utils/helpers.js";
19
+ /** AIService 支持的 SDK 通路模态列表 */
20
+ const MODALITIES = ["text", "stream", "image", "video", "tts", "asr"];
21
+ /** 用户侧默认以 text 模态排序模型 */
22
+ const DEFAULT_MODEL_MODE = "text";
23
+ /**
24
+ * 判断一个值是否为 HTTP Response。
25
+ */
26
+ function isResponse(value) {
27
+ return typeof value === "object" && value !== null && "status" in value && "headers" in value;
28
+ }
29
+ export class AIService extends Service {
30
+ /** 模型注册表 */
31
+ modelMap = new Map();
32
+ /** SDK 通路 action 映射(modality → action) */
33
+ modalityActions = new Map();
34
+ constructor() {
35
+ super({ id: "ai", name: "AI" });
36
+ // 为每个 modality 注册 routing action
37
+ for (const modality of MODALITIES) {
38
+ this.action(modality, async (ctx) => this.handleModality(modality, ctx), {
39
+ auth: ["user", "admin"],
40
+ });
41
+ }
42
+ // OpenAI 兼容端点
43
+ this.action("chat/completions", async (ctx) => this.handleChatCompletions(ctx), {
44
+ auth: ["user", "admin"],
45
+ });
46
+ // 模型列表走同一路径,根据身份决定可见范围。
47
+ this.action("models", (ctx) => ({
48
+ items: AIService.listModels(this, {
49
+ env: ctx.env,
50
+ identity: ctx.identity?.kind ?? "guest",
51
+ }),
52
+ }), { method: "GET", auth: ["user", "admin"] });
53
+ }
54
+ // ========== 模型注册 ==========
55
+ use(...inputs) {
56
+ const configs = [];
57
+ for (const input of inputs) {
58
+ if (Array.isArray(input))
59
+ configs.push(...input);
60
+ else
61
+ configs.push(input);
62
+ }
63
+ for (const config of configs) {
64
+ if (this.modelMap.has(config.id))
65
+ throw new Error(`Duplicate model: ${config.id}`);
66
+ this.modelMap.set(config.id, config);
67
+ }
68
+ return this;
69
+ }
70
+ listModels() {
71
+ return [...this.modelMap.values()];
72
+ }
73
+ hasAction() {
74
+ return this.modelMap.size > 0;
75
+ }
76
+ // ========== 模型匹配 ==========
77
+ resolve(query, env) {
78
+ const { model: modelId, mode } = query;
79
+ const isOpenAIMode = mode === "openai";
80
+ if (modelId) {
81
+ const model = this.modelMap.get(modelId);
82
+ if (!model)
83
+ throw httpError(422, `Unknown model: ${modelId}`);
84
+ const action = isOpenAIMode ? this.resolveOpenAIAction(model) : this.getAction(model, mode);
85
+ if (!action)
86
+ throw httpError(422, `Model ${modelId} does not support mode: ${mode ?? "text"}`);
87
+ return { model, action };
88
+ }
89
+ const candidates = this.listCandidateModels(mode, env);
90
+ const defaultModel = candidates[0];
91
+ if (defaultModel) {
92
+ const action = isOpenAIMode ? this.resolveOpenAIAction(defaultModel) : this.getAction(defaultModel, mode);
93
+ if (action)
94
+ return { model: defaultModel, action };
95
+ }
96
+ if (this.modelMap.size > 0) {
97
+ if (env)
98
+ throw httpError(422, `No available model for mode: ${mode ?? DEFAULT_MODEL_MODE}`);
99
+ throw httpError(422, `No action for mode: ${mode ?? DEFAULT_MODEL_MODE}`);
100
+ }
101
+ throw httpError(422, `No model registered`);
102
+ }
103
+ getAction(model, mode) {
104
+ return model.actions[(mode ?? "text")];
105
+ }
106
+ resolveOpenAIAction(model) {
107
+ if (model.actions.openai)
108
+ return model.actions.openai;
109
+ if (model.baseURL && model.envKey)
110
+ return this.createAutoPassthroughAction(model);
111
+ return undefined;
112
+ }
113
+ createAutoPassthroughAction(model) {
114
+ const baseURL = model.baseURL;
115
+ const envKey = model.envKey;
116
+ const passthroughModel = model.passthroughModel;
117
+ return async (ctx) => {
118
+ const apiKey = ctx.env(envKey);
119
+ if (!apiKey) {
120
+ return new Response(JSON.stringify({
121
+ error: { message: `${envKey} is required`, type: "authentication_error" },
122
+ }), { status: 401, headers: { "content-type": "application/json" } });
123
+ }
124
+ const body = { ...ctx.input };
125
+ if (passthroughModel) {
126
+ body.model = passthroughModel;
127
+ }
128
+ const response = await fetch(`${baseURL}/chat/completions`, {
129
+ method: "POST",
130
+ headers: {
131
+ "Authorization": `Bearer ${apiKey}`,
132
+ "Content-Type": "application/json",
133
+ },
134
+ body: JSON.stringify(body),
135
+ });
136
+ return new Response(response.body, {
137
+ status: response.status,
138
+ statusText: response.statusText,
139
+ headers: response.headers,
140
+ });
141
+ };
142
+ }
143
+ listCandidateModels(mode, env) {
144
+ const candidates = [...this.modelMap.values()].filter((model) => {
145
+ const action = mode === "openai" ? this.resolveOpenAIAction(model) : this.getAction(model, mode);
146
+ return Boolean(action);
147
+ });
148
+ const readyCandidates = env
149
+ ? candidates.filter((model) => this.getMissingEnv(model, env).length === 0)
150
+ : candidates;
151
+ return readyCandidates.sort((a, b) => this.compareModelPriority(a, b, mode ?? DEFAULT_MODEL_MODE));
152
+ }
153
+ compareModelPriority(a, b, mode) {
154
+ const scoreDiff = this.getDefaultScore(b, mode) - this.getDefaultScore(a, mode);
155
+ if (scoreDiff !== 0)
156
+ return scoreDiff;
157
+ return 0;
158
+ }
159
+ getDefaultScore(model, mode) {
160
+ const defaultModes = this.getDefaultModes(model);
161
+ if (defaultModes.includes(mode))
162
+ return 3;
163
+ if (defaultModes.includes(DEFAULT_MODEL_MODE))
164
+ return 2;
165
+ if (defaultModes.length > 0)
166
+ return 1;
167
+ return 0;
168
+ }
169
+ getDefaultModes(model) {
170
+ const modalities = this.getModelModalities(model);
171
+ if (model.default === true)
172
+ return modalities;
173
+ if (Array.isArray(model.default))
174
+ return model.default.filter((mode) => modalities.includes(mode));
175
+ return [];
176
+ }
177
+ getModelModalities(model) {
178
+ const modalities = Object.keys(model.actions).filter((key) => model.actions[key] !== undefined);
179
+ if (!modalities.includes("openai") && this.resolveOpenAIAction(model))
180
+ modalities.push("openai");
181
+ return modalities;
182
+ }
183
+ getModelEnvRequirements(model) {
184
+ const requirements = model.env
185
+ ? Object.entries(model.env)
186
+ : model.envKey
187
+ ? [[model.envKey, `${model.id} API Key`]]
188
+ : [];
189
+ return requirements.map(([key, description]) => ({
190
+ key,
191
+ description,
192
+ required: true,
193
+ }));
194
+ }
195
+ getMissingEnv(model, env) {
196
+ return this.getModelEnvRequirements(model)
197
+ .filter((item) => item.required && !env(item.key))
198
+ .map((item) => item.key);
199
+ }
200
+ // ========== SDK 通路 ==========
201
+ async handleModality(modality, ctx) {
202
+ const resolved = this.resolve({ model: ctx.input.model, mode: modality }, ctx.env);
203
+ const aiCtx = { ...ctx, variant: resolved.model ? { id: resolved.model.id, name: resolved.model.name, meta: resolved.model.meta } : undefined };
204
+ try {
205
+ const output = await resolved.action(aiCtx);
206
+ if (isResponse(output))
207
+ return output;
208
+ return output;
209
+ }
210
+ catch (error) {
211
+ const message = error instanceof Error ? error.message : String(error);
212
+ const status = error.statusCode ?? 500;
213
+ return new Response(JSON.stringify({ error: message }), { status, headers: { "content-type": "application/json" } });
214
+ }
215
+ }
216
+ // ========== OpenAI 兼容通路 ==========
217
+ async handleChatCompletions(ctx) {
218
+ const body = ctx.input;
219
+ const modelId = body.model;
220
+ const resolved = this.resolve({ model: modelId, mode: "openai" }, ctx.env);
221
+ const aiCtx = { ...ctx, input: body, variant: resolved.model ? { id: resolved.model.id, name: resolved.model.name, meta: resolved.model.meta } : undefined };
222
+ try {
223
+ const output = await resolved.action(aiCtx);
224
+ if (isResponse(output))
225
+ return output;
226
+ return new Response(JSON.stringify(output), { status: 200, headers: { "content-type": "application/json" } });
227
+ }
228
+ catch (error) {
229
+ const message = error instanceof Error ? error.message : String(error);
230
+ const status = error.statusCode ?? 500;
231
+ return new Response(JSON.stringify({ error: { message, type: "server_error" } }), { status, headers: { "content-type": "application/json" } });
232
+ }
233
+ }
234
+ // ========== 模型列表 ==========
235
+ static listModels(aiService, options) {
236
+ const { env, identity } = options;
237
+ const includeAdminFields = identity === "admin";
238
+ const items = identity === "admin"
239
+ ? [...aiService.modelMap.values()]
240
+ : [...aiService.modelMap.values()].filter((config) => aiService.getMissingEnv(config, env).length === 0);
241
+ return items
242
+ .sort((a, b) => aiService.compareModelPriority(a, b, DEFAULT_MODEL_MODE))
243
+ .map((config) => aiService.toPublicModel(config, includeAdminFields));
244
+ }
245
+ toPublicModel(config, includeAdminFields = false) {
246
+ return {
247
+ id: config.id,
248
+ name: config.name,
249
+ description: config.description ?? "",
250
+ modalities: this.getModelModalities(config),
251
+ tags: config.tags ?? [],
252
+ meta: config.meta ?? {},
253
+ ...(includeAdminFields
254
+ ? {
255
+ env_requirements: this.getModelEnvRequirements(config),
256
+ default_modes: this.getDefaultModes(config),
257
+ }
258
+ : {}),
259
+ };
260
+ }
261
+ }
262
+ //# sourceMappingURL=ai-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-service.js","sourceRoot":"","sources":["../../../src/service/ai/ai-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAgB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AASnD,+BAA+B;AAC/B,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAC/E,yBAAyB;AACzB,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAKlC;;GAEG;AACH,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,IAAI,KAAK,IAAI,SAAS,IAAI,KAAK,CAAC;AAChG,CAAC;AAED,MAAM,OAAO,SAAU,SAAQ,OAAO;IACpC,YAAY;IACJ,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAElD,0CAA0C;IAClC,eAAe,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEtD;QACE,KAAK,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAEhC,iCAAiC;QACjC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE;gBACvE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;aACxB,CAAC,CAAC;QACL,CAAC;QAED,cAAc;QACd,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE;YAC9E,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;SACxB,CAAC,CAAC;QAEH,wBAAwB;QACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC9B,KAAK,EAAE,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE;gBAChC,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,IAAI,OAAO;aACxC,CAAC;SACH,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,6BAA6B;IAE7B,GAAG,CAAC,GAAG,MAAuC;QAC5C,MAAM,OAAO,GAAkB,EAAE,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;;gBAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACnF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,6BAA6B;IAE7B,OAAO,CAAC,KAAwC,EAAE,GAAe;QAC/D,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QACvC,MAAM,YAAY,GAAG,IAAI,KAAK,QAAQ,CAAC;QAEvC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,KAAK;gBAAE,MAAM,SAAS,CAAC,GAAG,EAAE,kBAAkB,OAAO,EAAE,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC5F,IAAI,CAAC,MAAM;gBAAE,MAAM,SAAS,CAAC,GAAG,EAAE,SAAS,OAAO,2BAA2B,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC;YAC/F,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC3B,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvD,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YAC1G,IAAI,MAAM;gBAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;QACrD,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,GAAG;gBAAE,MAAM,SAAS,CAAC,GAAG,EAAE,gCAAgC,IAAI,IAAI,kBAAkB,EAAE,CAAC,CAAC;YAC5F,MAAM,SAAS,CAAC,GAAG,EAAE,uBAAuB,IAAI,IAAI,kBAAkB,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,SAAS,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;IAC9C,CAAC;IAEO,SAAS,CAAC,KAAkB,EAAE,IAAa;QACjD,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,MAAM,CAAuB,CAAC,CAAC;IAC/D,CAAC;IAEO,mBAAmB,CAAC,KAAkB;QAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACtD,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,2BAA2B,CAAC,KAAK,CAAC,CAAC;QAClF,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,2BAA2B,CAAC,KAAkB;QACpD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAQ,CAAC;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAO,CAAC;QAC7B,MAAM,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAEhD,OAAO,KAAK,EAAE,GAAY,EAAqB,EAAE;YAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;oBACjC,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,cAAc,EAAE,IAAI,EAAE,sBAAsB,EAAE;iBAC1E,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;YACxE,CAAC;YAED,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,EAA6B,CAAC;YACzD,IAAI,gBAAgB,EAAE,CAAC;gBACrB,IAAI,CAAC,KAAK,GAAG,gBAAgB,CAAC;YAChC,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,mBAAmB,EAAE;gBAC1D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,eAAe,EAAE,UAAU,MAAM,EAAE;oBACnC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAC;YAEH,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;gBACjC,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,OAAO,EAAE,QAAQ,CAAC,OAAO;aAC1B,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC;IAEO,mBAAmB,CAAC,IAAa,EAAE,GAAe;QACxD,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YAC9D,MAAM,MAAM,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACjG,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,GAAG;YACzB,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;YAC3E,CAAC,CAAC,UAAU,CAAC;QAEf,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,IAAI,kBAAkB,CAAC,CAAC,CAAC;IACrG,CAAC;IAEO,oBAAoB,CAAC,CAAc,EAAE,CAAc,EAAE,IAAY;QACvE,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAChF,IAAI,SAAS,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACtC,OAAO,CAAC,CAAC;IACX,CAAC;IAEO,eAAe,CAAC,KAAkB,EAAE,IAAY;QACtD,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACjD,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,YAAY,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAAE,OAAO,CAAC,CAAC;QACxD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC;IACX,CAAC;IAEO,eAAe,CAAC,KAAkB;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,UAAU,CAAC;QAC9C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACnG,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,kBAAkB,CAAC,KAAkB;QAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;QAChG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjG,OAAO,UAAU,CAAC;IACpB,CAAC;IAEO,uBAAuB,CAAC,KAAkB;QAChD,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG;YAC5B,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;YAC3B,CAAC,CAAC,KAAK,CAAC,MAAM;gBACZ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;gBACzC,CAAC,CAAC,EAAE,CAAC;QAET,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/C,GAAG;YACH,WAAW;YACX,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC,CAAC;IACN,CAAC;IAEO,aAAa,CAAC,KAAkB,EAAE,GAAc;QACtD,OAAO,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC;aACvC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aACjD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,+BAA+B;IAEvB,KAAK,CAAC,cAAc,CAAC,QAAkB,EAAE,GAAY;QAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,KAA2B,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QACzG,MAAM,KAAK,GAAY,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAEzJ,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;YACtC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,MAAM,GAAI,KAAiC,CAAC,UAAU,IAAI,GAAG,CAAC;YACpE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;QACvH,CAAC;IACH,CAAC;IAED,oCAAoC;IAE5B,KAAK,CAAC,qBAAqB,CAAC,GAAY;QAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAgC,CAAC;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,KAA2B,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3E,MAAM,KAAK,GAAY,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAEtK,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;YACtC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAChH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,MAAM,GAAI,KAAiC,CAAC,UAAU,IAAI,GAAG,CAAC;YACpE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;QACjJ,CAAC;IACH,CAAC;IAED,6BAA6B;IAE7B,MAAM,CAAC,UAAU,CAAC,SAAoB,EAAE,OAGvC;QACC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAClC,MAAM,kBAAkB,GAAG,QAAQ,KAAK,OAAO,CAAC;QAChD,MAAM,KAAK,GAAG,QAAQ,KAAK,OAAO;YAChC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;QAE3G,OAAO,KAAK;aACT,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,kBAAkB,CAAC,CAAC;aACxE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC1E,CAAC;IAEO,aAAa,CAAC,MAAmB,EAAE,kBAAkB,GAAG,KAAK;QACnE,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;YACrC,UAAU,EAAE,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC;YAC3C,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;YACvB,GAAG,CAAC,kBAAkB;gBACpB,CAAC,CAAC;oBACE,gBAAgB,EAAE,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC;oBACtD,aAAa,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;iBAC5C;gBACH,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * OpenAI-compatible Provider 构造工具。
3
+ *
4
+ * 负责把 OpenAI 兼容上游包装成 Downcity 的 SDK 通路:
5
+ * - text → 返回 AI SDK `UIMessage`
6
+ * - stream → 返回 AI SDK `UIMessageStreamResponse`
7
+ *
8
+ * 关键说明(中文)
9
+ * - `/chat/completions` 仍由 AIService 的自动透传处理
10
+ * - 这里专门补齐 UserClient.ai.text() / stream() 所需的 server 侧 action
11
+ * - 不在请求热路径里做动态导入,减少首请求和冷启动额外开销
12
+ */
13
+ import type { LanguageModel } from "ai";
14
+ import { Provider } from "./provider.js";
15
+ /**
16
+ * OpenAI-compatible Provider 配置。
17
+ */
18
+ export interface OpenAICompatibleProviderOptions {
19
+ /** Provider 唯一 ID,用于模型注册和日志标识。 */
20
+ id: string;
21
+ /** 运行时 API Key 对应的环境变量 key。 */
22
+ envKey: string;
23
+ /** OpenAI-compatible 上游 baseURL。 */
24
+ baseURL: string;
25
+ /** 该 Provider 默认绑定的上游模型 ID。 */
26
+ defaultModelId: string;
27
+ }
28
+ /**
29
+ * OpenAI-compatible client 工厂入参。
30
+ */
31
+ export interface OpenAICompatibleClientConfig {
32
+ /** 上游 API Key。 */
33
+ apiKey: string;
34
+ /** 上游 OpenAI-compatible baseURL。 */
35
+ baseURL: string;
36
+ /** Provider 展示名称,通常用于底层 SDK 调试和埋点。 */
37
+ name: string;
38
+ }
39
+ /**
40
+ * OpenAI-compatible chat client 最小能力约束。
41
+ */
42
+ export interface OpenAICompatibleClient {
43
+ /** 根据模型 ID 创建可传给 AI SDK 的 chat model。 */
44
+ chat(modelId: string): LanguageModel;
45
+ }
46
+ /**
47
+ * OpenAI-compatible client 工厂签名。
48
+ */
49
+ export type OpenAICompatibleClientFactory = (config: OpenAICompatibleClientConfig) => OpenAICompatibleClient;
50
+ /**
51
+ * 创建 OpenAI-compatible Provider。
52
+ */
53
+ export declare function createOpenAICompatibleProvider(options: OpenAICompatibleProviderOptions, createClient: OpenAICompatibleClientFactory): Provider;
54
+ //# sourceMappingURL=openai-compatible-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-compatible-provider.d.ts","sourceRoot":"","sources":["../../../src/service/ai/openai-compatible-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAqB,aAAa,EAAsB,MAAM,IAAI,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAC1C,MAAM,EAAE,4BAA4B,KACjC,sBAAsB,CAAC;AAoC5B;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,EACxC,YAAY,EAAE,6BAA6B,GAC1C,QAAQ,CASV"}