@liujitcn/kratos-taro-app-system 0.0.61 → 0.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## AI 助手
17
17
 
18
- AI 页面支持会话创建与切换、历史会话搜索、消息加载、附件上传、复制、删除、再生成、工具提示和快捷入口。H5 使用 Fetch SSE,微信小程序使用 chunked 请求,两端共用增量 SSE 解析器和相同事件契约。
18
+ AI 页面支持会话创建与切换、历史会话搜索、消息加载、附件上传、复制、删除、再生成、工具提示和快捷入口。Provider 与模型通过独立的 `AiModelService` 选项接口加载。H5 使用 Fetch SSE,微信小程序使用 chunked 请求,两端共用增量 SSE 解析器和相同事件契约。
19
19
 
20
20
  请求统一封装在 `src/api`,RPC 类型由 backend Buf 模板生成。页面不能直接写 `Taro.request`,也不能手工修改 `src/rpc`。
21
21
 
package/dist/module.mjs CHANGED
@@ -51,6 +51,7 @@ var zh_CN_default = {
51
51
  "system.ai.delete_session_failed": "\u5220\u9664\u4F1A\u8BDD\u5931\u8D25",
52
52
  "system.ai.efficiency_assistant": "\u6548\u7387\u52A9\u624B",
53
53
  "system.ai.failed_response": "\u8FD9\u6B21\u56DE\u590D\u6CA1\u6709\u6210\u529F\u8FD4\u56DE\uFF0C\u4F60\u53EF\u4EE5\u76F4\u63A5\u91CD\u8BD5\u521A\u624D\u7684\u95EE\u9898\u3002",
54
+ "system.ai.chat.error.details_unavailable": "\u804A\u5929\u754C\u9762\u4E0D\u5C55\u793A\u5E95\u5C42\u670D\u52A1\u9519\u8BEF\u8BE6\u60C5\uFF0C\u8BF7\u67E5\u770B\u670D\u52A1\u65E5\u5FD7\u3002",
54
55
  "system.ai.general_assistant": "\u901A\u7528\u52A9\u624B",
55
56
  "system.ai.greeting": "\u60A8\u597D\uFF0C{period}\u597D\uFF01\u4ECA\u5929\u6709\u4EC0\u4E48\u9700\u8981\u6211\u534F\u52A9\u7684\u5417\uFF1F",
56
57
  "system.ai.greeting_hello": "\u60A8\u597D\uFF0CAI\u52A9\u624B\u4E3A\u60A8\u670D\u52A1\uFF01",
@@ -98,6 +99,7 @@ var zh_CN_default = {
98
99
  "system.ai.text_assistant": "\u6587\u672C\u52A9\u624B",
99
100
  "system.ai.thinking": "\u6B63\u5728\u56DE\u590D",
100
101
  "system.ai.tool_called": "\u5DF2\u8C03\u7528\uFF1A{tools}",
102
+ "system.ai.web_search": "\u8054\u7F51\u641C\u7D22",
101
103
  "system.ai.upload_failed": "\u9644\u4EF6\u4E0A\u4F20\u5931\u8D25",
102
104
  "system.profile.account": "\u8D26\u53F7",
103
105
  "system.profile.avatar_change": "\u70B9\u51FB\u4FEE\u6539\u5934\u50CF",
@@ -151,6 +153,7 @@ var zh_CN_default = {
151
153
  "system.settings.mfa_copy_uri": "\u590D\u5236\u7ED1\u5B9A\u5730\u5740",
152
154
  "system.settings.mfa_copy_success": "\u590D\u5236\u6210\u529F",
153
155
  "system.notification.title": "\u6D88\u606F\u4E2D\u5FC3",
156
+ "system.notification.sender.system": "\u7CFB\u7EDF",
154
157
  "system.notification.description": "\u67E5\u770B\u7CFB\u7EDF\u901A\u77E5\u548C\u4E1A\u52A1\u6D88\u606F",
155
158
  "system.notification.empty": "\u6682\u65E0\u6D88\u606F",
156
159
  "system.notification.action.view_all": "\u67E5\u770B\u5168\u90E8",
@@ -161,7 +164,10 @@ var zh_CN_default = {
161
164
  "system.notification.mark_all_read": "\u5168\u90E8\u5DF2\u8BFB",
162
165
  "system.notification.action.archive": "\u5F52\u6863",
163
166
  "system.notification.action.restore": "\u6062\u590D",
164
- "system.notification.action.delete": "\u5220\u9664"
167
+ "system.notification.action.delete": "\u5220\u9664",
168
+ "system.ai.model.provider_placeholder": "\u9009\u62E9\u4F9B\u5E94\u5546",
169
+ "system.ai.model.model_placeholder": "\u9009\u62E9\u6A21\u578B",
170
+ "system.ai.model.load_failed": "\u52A0\u8F7D\u4F9B\u5E94\u5546\u548C\u6A21\u578B\u5931\u8D25"
165
171
  };
166
172
 
167
173
  // src/locales/en-US.json
@@ -182,6 +188,7 @@ var en_US_default = {
182
188
  "system.ai.delete_session_failed": "Failed to delete session",
183
189
  "system.ai.efficiency_assistant": "Productivity assistant",
184
190
  "system.ai.failed_response": "The response did not complete. You can retry your previous question.",
191
+ "system.ai.chat.error.details_unavailable": "Provider error details are not exposed in chat. See the service logs.",
185
192
  "system.ai.general_assistant": "General assistant",
186
193
  "system.ai.greeting": "Good {period}, how can I help you today?",
187
194
  "system.ai.greeting_hello": "Hello, your AI assistant is ready to help.",
@@ -229,6 +236,7 @@ var en_US_default = {
229
236
  "system.ai.text_assistant": "Writing assistant",
230
237
  "system.ai.thinking": "Responding",
231
238
  "system.ai.tool_called": "Tools used: {tools}",
239
+ "system.ai.web_search": "Web search",
232
240
  "system.ai.upload_failed": "Attachment upload failed",
233
241
  "system.profile.account": "Account",
234
242
  "system.profile.avatar_change": "Tap to change avatar",
@@ -282,6 +290,7 @@ var en_US_default = {
282
290
  "system.settings.mfa_copy_uri": "Copy binding address",
283
291
  "system.settings.mfa_copy_success": "Copied",
284
292
  "system.notification.title": "Message Center",
293
+ "system.notification.sender.system": "System",
285
294
  "system.notification.description": "View system notifications and business messages",
286
295
  "system.notification.empty": "No messages",
287
296
  "system.notification.action.view_all": "View all",
@@ -292,7 +301,10 @@ var en_US_default = {
292
301
  "system.notification.mark_all_read": "Mark all read",
293
302
  "system.notification.action.archive": "Archive",
294
303
  "system.notification.action.restore": "Restore",
295
- "system.notification.action.delete": "Delete"
304
+ "system.notification.action.delete": "Delete",
305
+ "system.ai.model.provider_placeholder": "Select a provider",
306
+ "system.ai.model.model_placeholder": "Select a model",
307
+ "system.ai.model.load_failed": "Failed to load providers and models"
296
308
  };
297
309
 
298
310
  // src/locales/ja-JP.json
@@ -313,6 +325,7 @@ var ja_JP_default = {
313
325
  "system.ai.delete_session_failed": "\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
314
326
  "system.ai.efficiency_assistant": "\u52B9\u7387\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8",
315
327
  "system.ai.failed_response": "\u4ECA\u56DE\u306F\u5FDC\u7B54\u304C\u6B63\u5E38\u306B\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u8CEA\u554F\u3092\u76F4\u63A5\u518D\u8A66\u884C\u3067\u304D\u307E\u3059\u3002",
328
+ "system.ai.chat.error.details_unavailable": "\u30C1\u30E3\u30C3\u30C8\u3067\u306F\u5185\u90E8\u30A8\u30E9\u30FC\u306E\u8A73\u7D30\u306F\u8868\u793A\u3055\u308C\u307E\u305B\u3093\u3002\u30B5\u30FC\u30D3\u30B9\u30ED\u30B0\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
316
329
  "system.ai.general_assistant": "\u30E6\u30CB\u30D0\u30FC\u30B5\u30EB \u30A2\u30B7\u30B9\u30BF\u30F3\u30C8",
317
330
  "system.ai.greeting": "\u3053\u3093\u306B\u3061\u306F\u3001{period} \u3053\u3093\u306B\u3061\u306F\uFF01\u4ECA\u65E5\u306F\u4F55\u304B\u304A\u624B\u4F1D\u3044\u304C\u5FC5\u8981\u306A\u3053\u3068\u306F\u3042\u308A\u307E\u3059\u304B?",
318
331
  "system.ai.greeting_hello": "\u3053\u3093\u306B\u3061\u306F\u3001AI \u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u304C\u304A\u624B\u4F1D\u3044\u3057\u307E\u3059\u3002",
@@ -360,6 +373,7 @@ var ja_JP_default = {
360
373
  "system.ai.text_assistant": "\u30C6\u30AD\u30B9\u30C8 \u30A2\u30B7\u30B9\u30BF\u30F3\u30C8",
361
374
  "system.ai.thinking": "\u304C\u8FD4\u4FE1\u3057\u3066\u3044\u307E\u3059",
362
375
  "system.ai.tool_called": "\u547C\u3073\u51FA\u3057\u5148: {tools}",
376
+ "system.ai.web_search": "\u30A6\u30A7\u30D6\u691C\u7D22",
363
377
  "system.ai.upload_failed": "\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
364
378
  "system.profile.account": "\u30A2\u30AB\u30A6\u30F3\u30C8",
365
379
  "system.profile.avatar_change": "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30A2\u30D0\u30BF\u30FC\u3092\u5909\u66F4\u3057\u307E\u3059",
@@ -413,6 +427,7 @@ var ja_JP_default = {
413
427
  "system.settings.mfa_copy_uri": "\u767B\u9332\u30A2\u30C9\u30EC\u30B9\u3092\u30B3\u30D4\u30FC",
414
428
  "system.settings.mfa_copy_success": "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F",
415
429
  "system.notification.title": "\u30E1\u30C3\u30BB\u30FC\u30B8\u30BB\u30F3\u30BF\u30FC",
430
+ "system.notification.sender.system": "\u30B7\u30B9\u30C6\u30E0",
416
431
  "system.notification.description": "\u30B7\u30B9\u30C6\u30E0\u901A\u77E5\u3068\u696D\u52D9\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u78BA\u8A8D",
417
432
  "system.notification.empty": "\u30E1\u30C3\u30BB\u30FC\u30B8\u306F\u3042\u308A\u307E\u305B\u3093",
418
433
  "system.notification.action.view_all": "\u3059\u3079\u3066\u8868\u793A",
@@ -423,7 +438,10 @@ var ja_JP_default = {
423
438
  "system.notification.mark_all_read": "\u3059\u3079\u3066\u65E2\u8AAD",
424
439
  "system.notification.action.archive": "\u30A2\u30FC\u30AB\u30A4\u30D6",
425
440
  "system.notification.action.restore": "\u5FA9\u5143",
426
- "system.notification.action.delete": "\u524A\u9664"
441
+ "system.notification.action.delete": "\u524A\u9664",
442
+ "system.ai.model.provider_placeholder": "\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u3092\u9078\u629E",
443
+ "system.ai.model.model_placeholder": "\u30E2\u30C7\u30EB\u3092\u9078\u629E",
444
+ "system.ai.model.load_failed": "\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u3068\u30E2\u30C7\u30EB\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F"
427
445
  };
428
446
 
429
447
  // src/locales/zh-TW.json
@@ -444,6 +462,7 @@ var zh_TW_default = {
444
462
  "system.ai.delete_session_failed": "\u522A\u9664\u6703\u8A71\u5931\u6557",
445
463
  "system.ai.efficiency_assistant": "\u6548\u7387\u52A9\u624B",
446
464
  "system.ai.failed_response": "\u9019\u6B21\u56DE\u8986\u6C92\u6709\u6210\u529F\u8FD4\u56DE\uFF0C\u4F60\u53EF\u4EE5\u76F4\u63A5\u91CD\u8A66\u525B\u624D\u7684\u554F\u984C\u3002",
465
+ "system.ai.chat.error.details_unavailable": "\u804A\u5929\u4ECB\u9762\u4E0D\u6703\u986F\u793A\u5E95\u5C64\u670D\u52D9\u932F\u8AA4\u8A73\u60C5\uFF0C\u8ACB\u67E5\u770B\u670D\u52D9\u65E5\u8A8C\u3002",
447
466
  "system.ai.general_assistant": "\u901A\u7528\u52A9\u624B",
448
467
  "system.ai.greeting": "\u60A8\u597D\uFF0C{period}\u597D\uFF01\u4ECA\u5929\u6709\u4EC0\u9EBC\u9700\u8981\u6211\u5354\u52A9\u7684\u55CE\uFF1F",
449
468
  "system.ai.greeting_hello": "\u60A8\u597D\uFF0CAI\u52A9\u624B\u70BA\u60A8\u670D\u52D9\uFF01",
@@ -491,6 +510,7 @@ var zh_TW_default = {
491
510
  "system.ai.text_assistant": "\u6587\u5B57\u52A9\u624B",
492
511
  "system.ai.thinking": "\u6B63\u5728\u56DE\u8986",
493
512
  "system.ai.tool_called": "\u5DF2\u547C\u53EB\uFF1A{tools}",
513
+ "system.ai.web_search": "\u7DB2\u8DEF\u641C\u5C0B",
494
514
  "system.ai.upload_failed": "\u9644\u4EF6\u4E0A\u50B3\u5931\u6557",
495
515
  "system.profile.account": "\u8CEC\u865F",
496
516
  "system.profile.avatar_change": "\u9EDE\u9078\u4FEE\u6539\u982D\u50CF",
@@ -544,6 +564,7 @@ var zh_TW_default = {
544
564
  "system.settings.mfa_copy_uri": "\u8907\u88FD\u7D81\u5B9A\u5730\u5740",
545
565
  "system.settings.mfa_copy_success": "\u8907\u88FD\u6210\u529F",
546
566
  "system.notification.title": "\u6D88\u606F\u4E2D\u5FC3",
567
+ "system.notification.sender.system": "\u7CFB\u7D71",
547
568
  "system.notification.description": "\u67E5\u770B\u7CFB\u7D71\u901A\u77E5\u548C\u696D\u52D9\u6D88\u606F",
548
569
  "system.notification.empty": "\u66AB\u7121\u6D88\u606F",
549
570
  "system.notification.action.view_all": "\u67E5\u770B\u5168\u90E8",
@@ -554,7 +575,10 @@ var zh_TW_default = {
554
575
  "system.notification.mark_all_read": "\u5168\u90E8\u5DF2\u8B80",
555
576
  "system.notification.action.archive": "\u6B78\u6A94",
556
577
  "system.notification.action.restore": "\u6062\u5FA9",
557
- "system.notification.action.delete": "\u522A\u9664"
578
+ "system.notification.action.delete": "\u522A\u9664",
579
+ "system.ai.model.provider_placeholder": "\u9078\u64C7\u4F9B\u61C9\u5546",
580
+ "system.ai.model.model_placeholder": "\u9078\u64C7\u6A21\u578B",
581
+ "system.ai.model.load_failed": "\u8F09\u5165\u4F9B\u61C9\u5546\u8207\u6A21\u578B\u5931\u6557"
558
582
  };
559
583
 
560
584
  // src/locales/generated.ts
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "type": "git",
8
8
  "url": "git@github.com:liujitcn/kratos-admin.git"
9
9
  },
10
- "version": "0.0.61",
10
+ "version": "0.0.64",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
@@ -38,8 +38,8 @@
38
38
  "@tarojs/components": "4.2.1",
39
39
  "@tarojs/taro": "4.2.1",
40
40
  "react": "18.3.1",
41
- "@liujitcn/kratos-taro-app-ui": "^0.0.61",
42
- "@liujitcn/kratos-taro-app-core": "^0.0.61"
41
+ "@liujitcn/kratos-taro-app-ui": "^0.0.64",
42
+ "@liujitcn/kratos-taro-app-core": "^0.0.64"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "pnpm run build:entries && pnpm pack --pack-destination ../../../dist/npm",
@@ -0,0 +1,25 @@
1
+ import { http } from '@liujitcn/kratos-taro-app-core/utils/http'
2
+ import type {
3
+ AiModelService,
4
+ ListAiProviderModelOptionsRequest,
5
+ ListAiProviderModelOptionsResponse,
6
+ } from '../../../rpc/base/v1/ai_provider'
7
+
8
+ const AI_PROVIDER_MODELS_URL = '/v1/base/ai/provider-models'
9
+
10
+ /** AI Provider模型选项服务。 */
11
+ export class AiModelServiceImpl implements AiModelService {
12
+ /** 查询已启用Provider及其可用模型。 */
13
+ ListAiProviderModelOptions(
14
+ request: ListAiProviderModelOptionsRequest,
15
+ ): Promise<ListAiProviderModelOptionsResponse> {
16
+ return http<ListAiProviderModelOptionsResponse>({
17
+ url: AI_PROVIDER_MODELS_URL,
18
+ method: 'GET',
19
+ authMode: 'required',
20
+ data: request,
21
+ })
22
+ }
23
+ }
24
+
25
+ export const defAiModelService = new AiModelServiceImpl()
@@ -15,6 +15,7 @@
15
15
  "system.ai.delete_session_failed": "Failed to delete session",
16
16
  "system.ai.efficiency_assistant": "Productivity assistant",
17
17
  "system.ai.failed_response": "The response did not complete. You can retry your previous question.",
18
+ "system.ai.chat.error.details_unavailable": "Provider error details are not exposed in chat. See the service logs.",
18
19
  "system.ai.general_assistant": "General assistant",
19
20
  "system.ai.greeting": "Good {period}, how can I help you today?",
20
21
  "system.ai.greeting_hello": "Hello, your AI assistant is ready to help.",
@@ -62,6 +63,7 @@
62
63
  "system.ai.text_assistant": "Writing assistant",
63
64
  "system.ai.thinking": "Responding",
64
65
  "system.ai.tool_called": "Tools used: {tools}",
66
+ "system.ai.web_search": "Web search",
65
67
  "system.ai.upload_failed": "Attachment upload failed",
66
68
  "system.profile.account": "Account",
67
69
  "system.profile.avatar_change": "Tap to change avatar",
@@ -115,6 +117,7 @@
115
117
  "system.settings.mfa_copy_uri": "Copy binding address",
116
118
  "system.settings.mfa_copy_success": "Copied",
117
119
  "system.notification.title": "Message Center",
120
+ "system.notification.sender.system": "System",
118
121
  "system.notification.description": "View system notifications and business messages",
119
122
  "system.notification.empty": "No messages",
120
123
  "system.notification.action.view_all": "View all",
@@ -125,5 +128,8 @@
125
128
  "system.notification.mark_all_read": "Mark all read",
126
129
  "system.notification.action.archive": "Archive",
127
130
  "system.notification.action.restore": "Restore",
128
- "system.notification.action.delete": "Delete"
131
+ "system.notification.action.delete": "Delete",
132
+ "system.ai.model.provider_placeholder": "Select a provider",
133
+ "system.ai.model.model_placeholder": "Select a model",
134
+ "system.ai.model.load_failed": "Failed to load providers and models"
129
135
  }
@@ -15,6 +15,7 @@
15
15
  "system.ai.delete_session_failed": "セッションの削除に失敗しました",
16
16
  "system.ai.efficiency_assistant": "効率アシスタント",
17
17
  "system.ai.failed_response": "今回は応答が正常に返されませんでした。質問を直接再試行できます。",
18
+ "system.ai.chat.error.details_unavailable": "チャットでは内部エラーの詳細は表示されません。サービスログを確認してください。",
18
19
  "system.ai.general_assistant": "ユニバーサル アシスタント",
19
20
  "system.ai.greeting": "こんにちは、{period} こんにちは!今日は何かお手伝いが必要なことはありますか?",
20
21
  "system.ai.greeting_hello": "こんにちは、AI アシスタントがお手伝いします。",
@@ -62,6 +63,7 @@
62
63
  "system.ai.text_assistant": "テキスト アシスタント",
63
64
  "system.ai.thinking": "が返信しています",
64
65
  "system.ai.tool_called": "呼び出し先: {tools}",
66
+ "system.ai.web_search": "ウェブ検索",
65
67
  "system.ai.upload_failed": "添付ファイルのアップロードに失敗しました",
66
68
  "system.profile.account": "アカウント",
67
69
  "system.profile.avatar_change": "クリックしてアバターを変更します",
@@ -115,6 +117,7 @@
115
117
  "system.settings.mfa_copy_uri": "登録アドレスをコピー",
116
118
  "system.settings.mfa_copy_success": "コピーしました",
117
119
  "system.notification.title": "メッセージセンター",
120
+ "system.notification.sender.system": "システム",
118
121
  "system.notification.description": "システム通知と業務メッセージを確認",
119
122
  "system.notification.empty": "メッセージはありません",
120
123
  "system.notification.action.view_all": "すべて表示",
@@ -125,5 +128,8 @@
125
128
  "system.notification.mark_all_read": "すべて既読",
126
129
  "system.notification.action.archive": "アーカイブ",
127
130
  "system.notification.action.restore": "復元",
128
- "system.notification.action.delete": "削除"
131
+ "system.notification.action.delete": "削除",
132
+ "system.ai.model.provider_placeholder": "プロバイダーを選択",
133
+ "system.ai.model.model_placeholder": "モデルを選択",
134
+ "system.ai.model.load_failed": "プロバイダーとモデルを読み込めませんでした"
129
135
  }
@@ -15,6 +15,7 @@
15
15
  "system.ai.delete_session_failed": "删除会话失败",
16
16
  "system.ai.efficiency_assistant": "效率助手",
17
17
  "system.ai.failed_response": "这次回复没有成功返回,你可以直接重试刚才的问题。",
18
+ "system.ai.chat.error.details_unavailable": "聊天界面不展示底层服务错误详情,请查看服务日志。",
18
19
  "system.ai.general_assistant": "通用助手",
19
20
  "system.ai.greeting": "您好,{period}好!今天有什么需要我协助的吗?",
20
21
  "system.ai.greeting_hello": "您好,AI助手为您服务!",
@@ -62,6 +63,7 @@
62
63
  "system.ai.text_assistant": "文本助手",
63
64
  "system.ai.thinking": "正在回复",
64
65
  "system.ai.tool_called": "已调用:{tools}",
66
+ "system.ai.web_search": "联网搜索",
65
67
  "system.ai.upload_failed": "附件上传失败",
66
68
  "system.profile.account": "账号",
67
69
  "system.profile.avatar_change": "点击修改头像",
@@ -115,6 +117,7 @@
115
117
  "system.settings.mfa_copy_uri": "复制绑定地址",
116
118
  "system.settings.mfa_copy_success": "复制成功",
117
119
  "system.notification.title": "消息中心",
120
+ "system.notification.sender.system": "系统",
118
121
  "system.notification.description": "查看系统通知和业务消息",
119
122
  "system.notification.empty": "暂无消息",
120
123
  "system.notification.action.view_all": "查看全部",
@@ -125,5 +128,8 @@
125
128
  "system.notification.mark_all_read": "全部已读",
126
129
  "system.notification.action.archive": "归档",
127
130
  "system.notification.action.restore": "恢复",
128
- "system.notification.action.delete": "删除"
131
+ "system.notification.action.delete": "删除",
132
+ "system.ai.model.provider_placeholder": "选择供应商",
133
+ "system.ai.model.model_placeholder": "选择模型",
134
+ "system.ai.model.load_failed": "加载供应商和模型失败"
129
135
  }
@@ -15,6 +15,7 @@
15
15
  "system.ai.delete_session_failed": "刪除會話失敗",
16
16
  "system.ai.efficiency_assistant": "效率助手",
17
17
  "system.ai.failed_response": "這次回覆沒有成功返回,你可以直接重試剛才的問題。",
18
+ "system.ai.chat.error.details_unavailable": "聊天介面不會顯示底層服務錯誤詳情,請查看服務日誌。",
18
19
  "system.ai.general_assistant": "通用助手",
19
20
  "system.ai.greeting": "您好,{period}好!今天有什麼需要我協助的嗎?",
20
21
  "system.ai.greeting_hello": "您好,AI助手為您服務!",
@@ -62,6 +63,7 @@
62
63
  "system.ai.text_assistant": "文字助手",
63
64
  "system.ai.thinking": "正在回覆",
64
65
  "system.ai.tool_called": "已呼叫:{tools}",
66
+ "system.ai.web_search": "網路搜尋",
65
67
  "system.ai.upload_failed": "附件上傳失敗",
66
68
  "system.profile.account": "賬號",
67
69
  "system.profile.avatar_change": "點選修改頭像",
@@ -115,6 +117,7 @@
115
117
  "system.settings.mfa_copy_uri": "複製綁定地址",
116
118
  "system.settings.mfa_copy_success": "複製成功",
117
119
  "system.notification.title": "消息中心",
120
+ "system.notification.sender.system": "系統",
118
121
  "system.notification.description": "查看系統通知和業務消息",
119
122
  "system.notification.empty": "暫無消息",
120
123
  "system.notification.action.view_all": "查看全部",
@@ -125,5 +128,8 @@
125
128
  "system.notification.mark_all_read": "全部已讀",
126
129
  "system.notification.action.archive": "歸檔",
127
130
  "system.notification.action.restore": "恢復",
128
- "system.notification.action.delete": "刪除"
131
+ "system.notification.action.delete": "刪除",
132
+ "system.ai.model.provider_placeholder": "選擇供應商",
133
+ "system.ai.model.model_placeholder": "選擇模型",
134
+ "system.ai.model.load_failed": "載入供應商與模型失敗"
129
135
  }
@@ -46,7 +46,13 @@ export interface SendAiMessageRequest {
46
46
  /** 附件列表 */
47
47
  attachments: AiAttachment[];
48
48
  /** 助手动作 */
49
- action: AiAction | undefined;
49
+ action:
50
+ | AiAction
51
+ | undefined;
52
+ /** 所选供应商ID;留空时使用首个可用模型 */
53
+ provider_id: number;
54
+ /** 所选模型名称 */
55
+ model_name: string;
50
56
  }
51
57
 
52
58
  /** AI 助手失败消息重试请求 */
@@ -0,0 +1,33 @@
1
+ // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
2
+ // versions:
3
+ // protoc-gen-ts_proto v2.11.8
4
+ // protoc unknown
5
+ // source: base/v1/ai_provider.proto
6
+
7
+ /* eslint-disable */
8
+
9
+ /** AI Provider模型选项查询请求。 */
10
+ export interface ListAiProviderModelOptionsRequest {
11
+ }
12
+
13
+ /** 已启用的AI Provider及其模型选项。 */
14
+ export interface AiProviderModelOption {
15
+ /** Provider ID */
16
+ provider_id: number;
17
+ /** Provider名称 */
18
+ name: string;
19
+ /** 模型名称列表 */
20
+ models: string[];
21
+ }
22
+
23
+ /** AI Provider模型选项查询响应。 */
24
+ export interface ListAiProviderModelOptionsResponse {
25
+ /** Provider及其模型列表 */
26
+ providers: AiProviderModelOption[];
27
+ }
28
+
29
+ /** AI Provider 可选模型查询服务。 */
30
+ export interface AiModelService {
31
+ /** 查询已启用的AI Provider及其可用模型。 */
32
+ ListAiProviderModelOptions(request: ListAiProviderModelOptionsRequest): Promise<ListAiProviderModelOptionsResponse>;
33
+ }
@@ -108,6 +108,8 @@ export interface DeleteAiSessionResponse {
108
108
  export interface AiSession {
109
109
  /** 会话ID */
110
110
  id: string;
111
+ /** 租户ID */
112
+ tenant_id: number;
111
113
  /** 会话标题 */
112
114
  title: string;
113
115
  /** 会话摘要 */
@@ -124,6 +126,8 @@ export interface AiSession {
124
126
  export interface AiMessage {
125
127
  /** 消息ID */
126
128
  id: string;
129
+ /** 租户ID */
130
+ tenant_id: number;
127
131
  /** 输入内容 */
128
132
  input_content:
129
133
  | AiInputContent
@@ -278,6 +278,8 @@ export interface DeleteNotificationRequest {
278
278
  export interface Notification {
279
279
  /** 投递ID */
280
280
  id: number;
281
+ /** 租户ID */
282
+ tenant_id: number;
281
283
  /** 消息ID */
282
284
  message_id: number;
283
285
  /** 分类ID */
@@ -1,11 +1,21 @@
1
- import { Button, Text, Textarea, View } from '@tarojs/components'
1
+ import { Button, Picker, Text, Textarea, View } from '@tarojs/components'
2
2
  import { UniIcon } from '@liujitcn/kratos-taro-app-ui'
3
3
  import type { AiAttachment } from '../../../../rpc/base/v1/ai_session'
4
+ import type { AiProviderModelOption } from '../../../../rpc/base/v1/ai_provider'
4
5
  import './composer.scss'
5
6
 
7
+ /** AI 助手输入框与模型选择器参数。 */
6
8
  type ComposerProps = {
7
9
  value: string
8
10
  attachments: AiAttachment[]
11
+ /** 当前可供选择的供应商与模型。 */
12
+ modelProviders: AiProviderModelOption[]
13
+ /** 当前选中的供应商编号和模型名称。 */
14
+ providerId: number
15
+ modelName: string
16
+ /** 选择器未配置时显示的本地化占位文字。 */
17
+ providerPlaceholder: string
18
+ modelPlaceholder: string
9
19
  placeholder: string
10
20
  bottom: string
11
21
  recording: boolean
@@ -16,12 +26,41 @@ type ComposerProps = {
16
26
  onRecord: () => void
17
27
  onSend: () => void
18
28
  onRemoveAttachment: (attachment: AiAttachment) => void
29
+ onModelChange: (providerId: number, modelName: string) => void
19
30
  }
20
31
 
21
32
  /** AI 助手消息输入器。 */
22
33
  export default function Composer(props: ComposerProps) {
34
+ const selectedProvider = props.modelProviders.find((provider) => provider.provider_id === props.providerId)
35
+ const modelNames = selectedProvider?.models ?? []
36
+ const providerIndex = Math.max(props.modelProviders.findIndex((provider) => provider.provider_id === props.providerId), 0)
37
+ const modelIndex = Math.max(modelNames.indexOf(props.modelName), 0)
38
+
23
39
  return (
24
40
  <View className='composer' style={{ paddingBottom: props.bottom }}>
41
+ <View className='composer-model-selectors'>
42
+ <Picker
43
+ mode='selector'
44
+ range={props.modelProviders.map((provider) => provider.name)}
45
+ value={providerIndex}
46
+ disabled={props.sending || !props.modelProviders.length}
47
+ onChange={(event) => {
48
+ const provider = props.modelProviders[Number(event.detail.value)]
49
+ props.onModelChange(provider?.provider_id ?? 0, provider?.models[0] ?? '')
50
+ }}
51
+ >
52
+ <View className='composer-model-select'><Text>{selectedProvider?.name || props.providerPlaceholder}</Text></View>
53
+ </Picker>
54
+ <Picker
55
+ mode='selector'
56
+ range={modelNames}
57
+ value={modelIndex}
58
+ disabled={props.sending || !modelNames.length}
59
+ onChange={(event) => props.onModelChange(props.providerId, modelNames[Number(event.detail.value)] ?? '')}
60
+ >
61
+ <View className='composer-model-select'><Text>{props.modelName || props.modelPlaceholder}</Text></View>
62
+ </Picker>
63
+ </View>
25
64
  <View className='composer-main'>
26
65
  <Button className='attach-button' hoverClass='none' onClick={props.onAttach}>
27
66
  <UniIcon type='plusempty' size={30} color='#111' />
@@ -19,6 +19,29 @@
19
19
  box-sizing: border-box;
20
20
  }
21
21
 
22
+ .composer-model-selectors {
23
+ display: flex;
24
+ justify-content: flex-end;
25
+ gap: 12px;
26
+ min-width: 0;
27
+ margin: 0 8px 12px;
28
+ }
29
+
30
+ .composer-model-select {
31
+ max-width: 300px;
32
+ min-width: 120px;
33
+ padding: 8px 16px;
34
+ overflow: hidden;
35
+ color: #46515b;
36
+ font-size: 22px;
37
+ line-height: 32px;
38
+ text-overflow: ellipsis;
39
+ white-space: nowrap;
40
+ background: #fff;
41
+ border: 2px solid #d6dae2;
42
+ border-radius: 8px;
43
+ }
44
+
22
45
  .composer-main {
23
46
  display: flex;
24
47
  align-items: center;
@@ -13,7 +13,9 @@ import { UniIcon } from '@liujitcn/kratos-taro-app-ui'
13
13
  import { defAiMessageService, StreamAiMessageByChunkedRequest } from '../../../api/base/v1/ai_message'
14
14
  import { defAiSessionService } from '../../../api/base/v1/ai_session'
15
15
  import { defAiToolService } from '../../../api/base/v1/ai_tool'
16
+ import { defAiModelService } from '../../../api/base/v1/ai_provider'
16
17
  import type { AiAttachment, AiMessage, AiSession } from '../../../rpc/base/v1/ai_session'
18
+ import type { AiProviderModelOption } from '../../../rpc/base/v1/ai_provider'
17
19
  import type { AiShortcut, AiToolCall } from '../../../rpc/base/v1/ai_tool'
18
20
  import { AiMessageStatus } from '../../../rpc/base/v1/ai_session'
19
21
  import { Terminal } from '../../../rpc/base/v1/ai_tool'
@@ -92,6 +94,9 @@ export default function AiPage() {
92
94
  const [sessions, setSessionsState] = useState<AiSession[]>([])
93
95
  const [messages, setMessagesState] = useState<MessageMap>({})
94
96
  const [selectedAttachments, setSelectedAttachments] = useState<AiAttachment[]>([])
97
+ const [modelProviders, setModelProviders] = useState<AiProviderModelOption[]>([])
98
+ const [selectedProviderId, setSelectedProviderId] = useState(0)
99
+ const [selectedModelName, setSelectedModelName] = useState('')
95
100
  const [starterShortcuts, setStarterShortcuts] = useState<AiShortcut[]>(createDefaultShortcuts)
96
101
 
97
102
  const activeSessionIDRef = useRef('')
@@ -351,25 +356,50 @@ export default function AiPage() {
351
356
  if (loadingShortcutsRef.current) return
352
357
  setLoadingShortcuts(true)
353
358
  try {
354
- const response = await defAiToolService.ListAiShortcut({ terminal: AI_TERMINAL })
359
+ const [response, providerResponse] = await Promise.all([
360
+ defAiToolService.ListAiShortcut({ terminal: AI_TERMINAL }),
361
+ defAiModelService.ListAiProviderModelOptions({}),
362
+ ])
355
363
  const shortcuts = normalizeStarterShortcuts(response.shortcuts).filter((item) => !item.action)
364
+ const providers = providerResponse.providers ?? []
365
+ setModelProviders(providers)
366
+ if (!providers.some((item) => item.provider_id === selectedProviderId && item.models.includes(selectedModelName))) {
367
+ const firstProvider = providers[0]
368
+ setSelectedProviderId(firstProvider?.provider_id ?? 0)
369
+ setSelectedModelName(firstProvider?.models[0] ?? '')
370
+ }
356
371
  if (shortcuts.length) {
357
372
  setStarterShortcuts(shortcuts)
358
373
  setStarterPromptGroupIndex(0)
359
374
  }
360
375
  } catch (error) {
376
+ setModelProviders([])
377
+ setSelectedProviderId(0)
378
+ setSelectedModelName('')
361
379
  showError(error, t('system.ai.load_shortcuts_failed'))
362
380
  } finally {
363
381
  setLoadingShortcuts(false)
364
382
  }
365
383
  }
366
384
 
385
+ const handleModelChange = (providerId: number, modelName: string) => {
386
+ setSelectedProviderId(providerId)
387
+ setSelectedModelName(modelName)
388
+ }
389
+
367
390
  const runAiTask = async (
368
391
  sessionID: string,
369
392
  payload: { text: string; attachments: AiAttachment[] },
370
393
  ) => {
371
394
  let task: StreamTask | undefined
372
- const request = { session_id: sessionID, content: payload.text, attachments: payload.attachments, action: undefined }
395
+ const request = {
396
+ session_id: sessionID,
397
+ content: payload.text,
398
+ provider_id: selectedProviderId,
399
+ model_name: selectedModelName,
400
+ attachments: payload.attachments,
401
+ action: undefined,
402
+ }
373
403
  try {
374
404
  let handledByStream = false
375
405
  if (process.env.TARO_ENV === 'weapp') {
@@ -719,6 +749,11 @@ export default function AiPage() {
719
749
  <Composer
720
750
  value={inputText}
721
751
  attachments={selectedAttachments}
752
+ modelProviders={modelProviders}
753
+ providerId={selectedProviderId}
754
+ modelName={selectedModelName}
755
+ providerPlaceholder={t('system.ai.model.provider_placeholder')}
756
+ modelPlaceholder={t('system.ai.model.model_placeholder')}
722
757
  placeholder={composerPlaceholder}
723
758
  bottom={composerBottom}
724
759
  recording={isRecording}
@@ -736,6 +771,7 @@ export default function AiPage() {
736
771
  })
737
772
  }}
738
773
  onSend={() => void handleSend()}
774
+ onModelChange={handleModelChange}
739
775
  onRemoveAttachment={(attachment) => setSelectedAttachments((current) => current.filter((item) => item !== attachment))}
740
776
  />
741
777
  <SessionDrawer
@@ -758,6 +794,7 @@ export default function AiPage() {
758
794
  function normalizeSession(session?: Partial<AiSession> | null): AiSession {
759
795
  return {
760
796
  id: String(session?.id ?? ''),
797
+ tenant_id: Number(session?.tenant_id ?? 0),
761
798
  title: String(session?.title ?? t('system.ai.new_session')),
762
799
  summary: String(session?.summary ?? ''),
763
800
  updated_at: session?.updated_at,
@@ -848,6 +885,7 @@ function createLocalUserMessage(payload: { text: string; attachments: AiAttachme
848
885
  const now = Date.now()
849
886
  const message = mapMessageItem({
850
887
  id: `${LOCAL_USER_MESSAGE_PREFIX}-${now}`,
888
+ tenant_id: 0,
851
889
  input_content: { kind: 'text', content: payload.text },
852
890
  output_content: undefined,
853
891
  attachments: payload.attachments,
@@ -869,8 +907,9 @@ function createThinkingMessage(options?: { sessionID?: string; messageID?: strin
869
907
  ? buildStreamMessageKey(options.sessionID, options.messageID || PENDING_MESSAGE_ID)
870
908
  : undefined
871
909
  const message = mapMessageItem({
872
- id: streamKey || `ai-thinking-${now}`,
873
- input_content: undefined,
910
+ id: streamKey || `ai-thinking-${now}`,
911
+ tenant_id: 0,
912
+ input_content: undefined,
874
913
  output_content: {
875
914
  kind: 'text', content: t('system.ai.thinking'), reply_source: '', model: '', fallback: false,
876
915
  fallback_reason: '', flow: '', step: '', blocks_json: '',
@@ -997,7 +1036,7 @@ function formatAttachmentMeta(attachment: AiAttachment) {
997
1036
  }
998
1037
 
999
1038
  function formatTools(tools: AiToolCall[]) {
1000
- return tools.map((item) => item.title || item.name).filter(Boolean).join(' · ')
1039
+ return tools.map((item) => (item.name === 'web_search' ? t('system.ai.web_search') : item.title || item.name)).filter(Boolean).join(' · ')
1001
1040
  }
1002
1041
 
1003
1042
  function showError(error: unknown, fallback: string) {
@@ -25,6 +25,13 @@ export default function MessageDetailPage() {
25
25
  if (!value.read_at) await defNotificationService.MarkNotificationRead({ ids: [id] })
26
26
  }
27
27
 
28
+ function resolveSenderName(value?: string) {
29
+ if (value === '系统' || value === '__I18N__:system.notification.sender.system') {
30
+ return t('system.notification.sender.system')
31
+ }
32
+ return value || ''
33
+ }
34
+
28
35
  /** 执行通知携带的稳定 viewKey 动作。 */
29
36
  function openAction() {
30
37
  if (!detail || detail.action_type !== MessageActionType.MESSAGE_ACTION_TYPE_VIEW_KEY) return
@@ -60,7 +67,7 @@ export default function MessageDetailPage() {
60
67
  />
61
68
  <Text>{detail.category_name}</Text>
62
69
  </View>
63
- <Text>{detail.sender_name}</Text>
70
+ <Text>{resolveSenderName(detail.sender_name)}</Text>
64
71
  <Text>{detail.received_at}</Text>
65
72
  </View>
66
73
  {detail.content_format === MessageContentFormat.MESSAGE_CONTENT_FORMAT_RICH_TEXT ? (