@liujitcn/kratos-uni-app-system 0.0.65 → 0.0.66

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/dist/index.mjs CHANGED
@@ -372,7 +372,12 @@ var zh_CN_default = {
372
372
  "system.ai.model.load_failed": "\u52A0\u8F7D\u4F9B\u5E94\u5546\u548C\u6A21\u578B\u5931\u8D25",
373
373
  "system.ai.model.title": "\u9009\u62E9\u6A21\u578B",
374
374
  "system.ai.model.provider_model_count": "{providers} \u4E2A\u4F9B\u5E94\u5546 \xB7 {models} \u4E2A\u6A21\u578B",
375
- "system.ai.model.empty": "\u6682\u65E0\u53EF\u9009\u6A21\u578B"
375
+ "system.ai.model.empty": "\u6682\u65E0\u53EF\u9009\u6A21\u578B",
376
+ "system.ai.knowledge.title": "\u77E5\u8BC6\u5E93",
377
+ "system.ai.knowledge.selected_count": "\u5DF2\u9009 {count} \u4E2A\u77E5\u8BC6\u5E93",
378
+ "system.ai.knowledge.doc_count": "{count} \u7BC7\u6587\u6863",
379
+ "system.ai.knowledge.select_hint": "\u52FE\u9009\u8981\u6CE8\u5165\u4E0A\u4E0B\u6587\u7684\u77E5\u8BC6\u5E93",
380
+ "system.ai.knowledge.empty": "\u6682\u65E0\u77E5\u8BC6\u5E93"
376
381
  };
377
382
 
378
383
  // src/locales/en-US.json
@@ -494,7 +499,12 @@ var en_US_default = {
494
499
  "system.ai.model.load_failed": "Failed to load providers and models",
495
500
  "system.ai.model.title": "Select Model",
496
501
  "system.ai.model.provider_model_count": "{providers} providers \xB7 {models} models",
497
- "system.ai.model.empty": "No models available"
502
+ "system.ai.model.empty": "No models available",
503
+ "system.ai.knowledge.title": "Knowledge Base",
504
+ "system.ai.knowledge.selected_count": "{count} knowledge base(s) selected",
505
+ "system.ai.knowledge.doc_count": "{count} doc(s)",
506
+ "system.ai.knowledge.select_hint": "Select knowledge bases to inject into context",
507
+ "system.ai.knowledge.empty": "No knowledge base available"
498
508
  };
499
509
 
500
510
  // src/locales/ja-JP.json
@@ -616,7 +626,12 @@ var ja_JP_default = {
616
626
  "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",
617
627
  "system.ai.model.title": "\u30E2\u30C7\u30EB\u3092\u9078\u629E",
618
628
  "system.ai.model.provider_model_count": "{providers} \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC \xB7 {models} \u30E2\u30C7\u30EB",
619
- "system.ai.model.empty": "\u9078\u629E\u53EF\u80FD\u306A\u30E2\u30C7\u30EB\u304C\u3042\u308A\u307E\u305B\u3093"
629
+ "system.ai.model.empty": "\u9078\u629E\u53EF\u80FD\u306A\u30E2\u30C7\u30EB\u304C\u3042\u308A\u307E\u305B\u3093",
630
+ "system.ai.knowledge.title": "\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9",
631
+ "system.ai.knowledge.selected_count": "{count} \u4EF6\u306E\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u3092\u9078\u629E\u4E2D",
632
+ "system.ai.knowledge.doc_count": "{count} \u4EF6\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
633
+ "system.ai.knowledge.select_hint": "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u306B\u6CE8\u5165\u3059\u308B\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u3092\u9078\u629E",
634
+ "system.ai.knowledge.empty": "\u5229\u7528\u53EF\u80FD\u306A\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u304C\u3042\u308A\u307E\u305B\u3093"
620
635
  };
621
636
 
622
637
  // src/locales/zh-TW.json
@@ -738,7 +753,12 @@ var zh_TW_default = {
738
753
  "system.ai.model.load_failed": "\u8F09\u5165\u4F9B\u61C9\u5546\u8207\u6A21\u578B\u5931\u6557",
739
754
  "system.ai.model.title": "\u9078\u64C7\u6A21\u578B",
740
755
  "system.ai.model.provider_model_count": "{providers} \u500B\u4F9B\u61C9\u5546 \xB7 {models} \u500B\u6A21\u578B",
741
- "system.ai.model.empty": "\u66AB\u7121\u53EF\u9078\u6A21\u578B"
756
+ "system.ai.model.empty": "\u66AB\u7121\u53EF\u9078\u6A21\u578B",
757
+ "system.ai.knowledge.title": "\u77E5\u8B58\u5EAB",
758
+ "system.ai.knowledge.selected_count": "\u5DF2\u9078 {count} \u500B\u77E5\u8B58\u5EAB",
759
+ "system.ai.knowledge.doc_count": "{count} \u7BC7\u6587\u4EF6",
760
+ "system.ai.knowledge.select_hint": "\u52FE\u9078\u8981\u6CE8\u5165\u4E0A\u4E0B\u6587\u7684\u77E5\u8B58\u5EAB",
761
+ "system.ai.knowledge.empty": "\u66AB\u7121\u77E5\u8B58\u5EAB"
742
762
  };
743
763
 
744
764
  // src/locales/generated.ts
package/dist/module.mjs CHANGED
@@ -152,7 +152,12 @@ var zh_CN_default = {
152
152
  "system.ai.model.load_failed": "\u52A0\u8F7D\u4F9B\u5E94\u5546\u548C\u6A21\u578B\u5931\u8D25",
153
153
  "system.ai.model.title": "\u9009\u62E9\u6A21\u578B",
154
154
  "system.ai.model.provider_model_count": "{providers} \u4E2A\u4F9B\u5E94\u5546 \xB7 {models} \u4E2A\u6A21\u578B",
155
- "system.ai.model.empty": "\u6682\u65E0\u53EF\u9009\u6A21\u578B"
155
+ "system.ai.model.empty": "\u6682\u65E0\u53EF\u9009\u6A21\u578B",
156
+ "system.ai.knowledge.title": "\u77E5\u8BC6\u5E93",
157
+ "system.ai.knowledge.selected_count": "\u5DF2\u9009 {count} \u4E2A\u77E5\u8BC6\u5E93",
158
+ "system.ai.knowledge.doc_count": "{count} \u7BC7\u6587\u6863",
159
+ "system.ai.knowledge.select_hint": "\u52FE\u9009\u8981\u6CE8\u5165\u4E0A\u4E0B\u6587\u7684\u77E5\u8BC6\u5E93",
160
+ "system.ai.knowledge.empty": "\u6682\u65E0\u77E5\u8BC6\u5E93"
156
161
  };
157
162
 
158
163
  // src/locales/en-US.json
@@ -274,7 +279,12 @@ var en_US_default = {
274
279
  "system.ai.model.load_failed": "Failed to load providers and models",
275
280
  "system.ai.model.title": "Select Model",
276
281
  "system.ai.model.provider_model_count": "{providers} providers \xB7 {models} models",
277
- "system.ai.model.empty": "No models available"
282
+ "system.ai.model.empty": "No models available",
283
+ "system.ai.knowledge.title": "Knowledge Base",
284
+ "system.ai.knowledge.selected_count": "{count} knowledge base(s) selected",
285
+ "system.ai.knowledge.doc_count": "{count} doc(s)",
286
+ "system.ai.knowledge.select_hint": "Select knowledge bases to inject into context",
287
+ "system.ai.knowledge.empty": "No knowledge base available"
278
288
  };
279
289
 
280
290
  // src/locales/ja-JP.json
@@ -396,7 +406,12 @@ var ja_JP_default = {
396
406
  "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",
397
407
  "system.ai.model.title": "\u30E2\u30C7\u30EB\u3092\u9078\u629E",
398
408
  "system.ai.model.provider_model_count": "{providers} \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC \xB7 {models} \u30E2\u30C7\u30EB",
399
- "system.ai.model.empty": "\u9078\u629E\u53EF\u80FD\u306A\u30E2\u30C7\u30EB\u304C\u3042\u308A\u307E\u305B\u3093"
409
+ "system.ai.model.empty": "\u9078\u629E\u53EF\u80FD\u306A\u30E2\u30C7\u30EB\u304C\u3042\u308A\u307E\u305B\u3093",
410
+ "system.ai.knowledge.title": "\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9",
411
+ "system.ai.knowledge.selected_count": "{count} \u4EF6\u306E\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u3092\u9078\u629E\u4E2D",
412
+ "system.ai.knowledge.doc_count": "{count} \u4EF6\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
413
+ "system.ai.knowledge.select_hint": "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u306B\u6CE8\u5165\u3059\u308B\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u3092\u9078\u629E",
414
+ "system.ai.knowledge.empty": "\u5229\u7528\u53EF\u80FD\u306A\u30CA\u30EC\u30C3\u30B8\u30D9\u30FC\u30B9\u304C\u3042\u308A\u307E\u305B\u3093"
400
415
  };
401
416
 
402
417
  // src/locales/zh-TW.json
@@ -518,7 +533,12 @@ var zh_TW_default = {
518
533
  "system.ai.model.load_failed": "\u8F09\u5165\u4F9B\u61C9\u5546\u8207\u6A21\u578B\u5931\u6557",
519
534
  "system.ai.model.title": "\u9078\u64C7\u6A21\u578B",
520
535
  "system.ai.model.provider_model_count": "{providers} \u500B\u4F9B\u61C9\u5546 \xB7 {models} \u500B\u6A21\u578B",
521
- "system.ai.model.empty": "\u66AB\u7121\u53EF\u9078\u6A21\u578B"
536
+ "system.ai.model.empty": "\u66AB\u7121\u53EF\u9078\u6A21\u578B",
537
+ "system.ai.knowledge.title": "\u77E5\u8B58\u5EAB",
538
+ "system.ai.knowledge.selected_count": "\u5DF2\u9078 {count} \u500B\u77E5\u8B58\u5EAB",
539
+ "system.ai.knowledge.doc_count": "{count} \u7BC7\u6587\u4EF6",
540
+ "system.ai.knowledge.select_hint": "\u52FE\u9078\u8981\u6CE8\u5165\u4E0A\u4E0B\u6587\u7684\u77E5\u8B58\u5EAB",
541
+ "system.ai.knowledge.empty": "\u66AB\u7121\u77E5\u8B58\u5EAB"
522
542
  };
523
543
 
524
544
  // 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.65",
10
+ "version": "0.0.66",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
@@ -35,7 +35,7 @@
35
35
  "@dcloudio/uni-app": "3.0.0-5010520260709002",
36
36
  "@dcloudio/uni-ui": "^1.4.28",
37
37
  "vue": "^3.4.21",
38
- "@liujitcn/kratos-uni-app-core": "^0.0.65"
38
+ "@liujitcn/kratos-uni-app-core": "^0.0.66"
39
39
  },
40
40
  "devDependencies": {
41
41
  "esbuild": "^0.20.2",
@@ -0,0 +1,25 @@
1
+ import { http } from '@liujitcn/kratos-uni-app-core/utils/http'
2
+ import type {
3
+ AiKnowledgeService,
4
+ ListAiKnowledgeOptionsRequest,
5
+ ListAiKnowledgeOptionsResponse,
6
+ } from '../../../rpc/base/v1/ai_knowledge'
7
+
8
+ const AI_KNOWLEDGE_OPTIONS_URL = '/v1/base/ai/knowledge/options'
9
+
10
+ /** AI知识库选项服务。 */
11
+ export class AiKnowledgeServiceImpl implements AiKnowledgeService {
12
+ /** 查询可用的AI知识库选项。 */
13
+ ListAiKnowledgeOptions(
14
+ request: ListAiKnowledgeOptionsRequest,
15
+ ): Promise<ListAiKnowledgeOptionsResponse> {
16
+ return http<ListAiKnowledgeOptionsResponse>({
17
+ url: AI_KNOWLEDGE_OPTIONS_URL,
18
+ method: 'GET',
19
+ authMode: 'required',
20
+ data: request,
21
+ })
22
+ }
23
+ }
24
+
25
+ export const defAiKnowledgeService = new AiKnowledgeServiceImpl()
@@ -116,5 +116,10 @@
116
116
  "system.ai.model.load_failed": "Failed to load providers and models",
117
117
  "system.ai.model.title": "Select Model",
118
118
  "system.ai.model.provider_model_count": "{providers} providers · {models} models",
119
- "system.ai.model.empty": "No models available"
119
+ "system.ai.model.empty": "No models available",
120
+ "system.ai.knowledge.title": "Knowledge Base",
121
+ "system.ai.knowledge.selected_count": "{count} knowledge base(s) selected",
122
+ "system.ai.knowledge.doc_count": "{count} doc(s)",
123
+ "system.ai.knowledge.select_hint": "Select knowledge bases to inject into context",
124
+ "system.ai.knowledge.empty": "No knowledge base available"
120
125
  }
@@ -116,5 +116,10 @@
116
116
  "system.ai.model.load_failed": "プロバイダーとモデルを読み込めませんでした",
117
117
  "system.ai.model.title": "モデルを選択",
118
118
  "system.ai.model.provider_model_count": "{providers} プロバイダー · {models} モデル",
119
- "system.ai.model.empty": "選択可能なモデルがありません"
119
+ "system.ai.model.empty": "選択可能なモデルがありません",
120
+ "system.ai.knowledge.title": "ナレッジベース",
121
+ "system.ai.knowledge.selected_count": "{count} 件のナレッジベースを選択中",
122
+ "system.ai.knowledge.doc_count": "{count} 件のドキュメント",
123
+ "system.ai.knowledge.select_hint": "コンテキストに注入するナレッジベースを選択",
124
+ "system.ai.knowledge.empty": "利用可能なナレッジベースがありません"
120
125
  }
@@ -116,5 +116,10 @@
116
116
  "system.ai.model.load_failed": "加载供应商和模型失败",
117
117
  "system.ai.model.title": "选择模型",
118
118
  "system.ai.model.provider_model_count": "{providers} 个供应商 · {models} 个模型",
119
- "system.ai.model.empty": "暂无可选模型"
119
+ "system.ai.model.empty": "暂无可选模型",
120
+ "system.ai.knowledge.title": "知识库",
121
+ "system.ai.knowledge.selected_count": "已选 {count} 个知识库",
122
+ "system.ai.knowledge.doc_count": "{count} 篇文档",
123
+ "system.ai.knowledge.select_hint": "勾选要注入上下文的知识库",
124
+ "system.ai.knowledge.empty": "暂无知识库"
120
125
  }
@@ -116,5 +116,10 @@
116
116
  "system.ai.model.load_failed": "載入供應商與模型失敗",
117
117
  "system.ai.model.title": "選擇模型",
118
118
  "system.ai.model.provider_model_count": "{providers} 個供應商 · {models} 個模型",
119
- "system.ai.model.empty": "暫無可選模型"
119
+ "system.ai.model.empty": "暫無可選模型",
120
+ "system.ai.knowledge.title": "知識庫",
121
+ "system.ai.knowledge.selected_count": "已選 {count} 個知識庫",
122
+ "system.ai.knowledge.doc_count": "{count} 篇文件",
123
+ "system.ai.knowledge.select_hint": "勾選要注入上下文的知識庫",
124
+ "system.ai.knowledge.empty": "暫無知識庫"
120
125
  }
@@ -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_knowledge.proto
6
+
7
+ /* eslint-disable */
8
+
9
+ /** AI知识库选项查询请求。 */
10
+ export interface ListAiKnowledgeOptionsRequest {
11
+ }
12
+
13
+ /** AI知识库选项查询响应。 */
14
+ export interface ListAiKnowledgeOptionsResponse {
15
+ /** 知识库选项列表 */
16
+ options: AiKnowledgeOption[];
17
+ }
18
+
19
+ /** AI知识库选项。 */
20
+ export interface AiKnowledgeOption {
21
+ /** 知识库ID */
22
+ id: number;
23
+ /** 知识库名称 */
24
+ name: string;
25
+ /** 文档数量 */
26
+ doc_count: number;
27
+ }
28
+
29
+ /** AI 知识库选项查询服务。 */
30
+ export interface AiKnowledgeService {
31
+ /** 查询可用的AI知识库选项。 */
32
+ ListAiKnowledgeOptions(request: ListAiKnowledgeOptionsRequest): Promise<ListAiKnowledgeOptionsResponse>;
33
+ }
@@ -53,6 +53,8 @@ export interface SendAiMessageRequest {
53
53
  provider_id: number;
54
54
  /** 所选模型名称 */
55
55
  model_name: string;
56
+ /** 所选知识库ID列表 */
57
+ knowledge_base_ids: number[];
56
58
  }
57
59
 
58
60
  /** AI 助手失败消息重试请求 */
@@ -1,9 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref } from 'vue'
3
+ import { useI18n } from '@liujitcn/kratos-uni-app-core'
3
4
  import type { AiAttachment } from '../../../../rpc/base/v1/ai_session'
4
5
  import type { AiProviderModelOption } from '../../../../rpc/base/v1/ai_provider'
6
+ import type { AiKnowledgeOption } from '../../../../rpc/base/v1/ai_knowledge'
5
7
  import { fallbackModelDisplayName } from '../modelDisplay'
6
8
  import ModelPicker from './ModelPicker.vue'
9
+ import KnowledgePicker from './KnowledgePicker.vue'
10
+
11
+ const { t } = useI18n()
7
12
 
8
13
  type InputEventValue = {
9
14
  detail: {
@@ -19,6 +24,10 @@ const props = defineProps<{
19
24
  /** 当前选中的供应商编号和模型名称。 */
20
25
  providerId: number
21
26
  modelName: string
27
+ /** 当前可供选择的知识库。 */
28
+ knowledgeOptions: AiKnowledgeOption[]
29
+ /** 当前选择的知识库编号列表。 */
30
+ knowledgeIds: number[]
22
31
  modelPlaceholder: string
23
32
  placeholder: string
24
33
  bottom: string
@@ -34,9 +43,11 @@ const emit = defineEmits<{
34
43
  send: []
35
44
  'remove-attachment': [attachment: AiAttachment]
36
45
  'model-change': [providerId: number, modelName: string]
46
+ 'knowledge-change': [knowledgeIds: number[]]
37
47
  }>()
38
48
 
39
49
  const showModelPicker = ref(false)
50
+ const showKnowledgePicker = ref(false)
40
51
 
41
52
  const selectedModelDisplayName = computed(() => {
42
53
  const provider = props.modelProviders.find((item) => item.provider_id === props.providerId)
@@ -59,6 +70,23 @@ function handleModelSelect(providerId: number, modelName: string) {
59
70
  showModelPicker.value = false
60
71
  emit('model-change', providerId, modelName)
61
72
  }
73
+
74
+ function openKnowledgePicker() {
75
+ if (props.sending || !props.knowledgeOptions.length) {
76
+ return
77
+ }
78
+ showKnowledgePicker.value = true
79
+ }
80
+
81
+ function handleKnowledgeToggle(knowledgeId: number) {
82
+ const current = new Set(props.knowledgeIds)
83
+ if (current.has(knowledgeId)) {
84
+ current.delete(knowledgeId)
85
+ } else {
86
+ current.add(knowledgeId)
87
+ }
88
+ emit('knowledge-change', [...current])
89
+ }
62
90
  </script>
63
91
 
64
92
  <template>
@@ -72,6 +100,22 @@ function handleModelSelect(providerId: number, modelName: string) {
72
100
  <text class="composer-model-chip__label">{{ selectedModelDisplayName || modelPlaceholder }}</text>
73
101
  <uni-icons type="down" size="12" color="#8a8f99" />
74
102
  </view>
103
+ <view
104
+ class="composer-model-chip"
105
+ :class="{
106
+ 'is-disabled': sending || !knowledgeOptions.length,
107
+ 'is-selected': knowledgeIds.length > 0,
108
+ }"
109
+ @tap="openKnowledgePicker"
110
+ >
111
+ <text class="composer-model-chip__spark">📚</text>
112
+ <text class="composer-model-chip__label">{{
113
+ knowledgeIds.length > 0
114
+ ? t('system.ai.knowledge.selected_count', { count: knowledgeIds.length })
115
+ : t('system.ai.knowledge.title')
116
+ }}</text>
117
+ <uni-icons type="down" size="12" color="#8a8f99" />
118
+ </view>
75
119
  <view class="composer-main">
76
120
  <button class="attach-button" hover-class="none" @tap="emit('attach')">
77
121
  <uni-icons type="plusempty" size="30" color="#111" />
@@ -124,6 +168,14 @@ function handleModelSelect(providerId: number, modelName: string) {
124
168
  @select="handleModelSelect"
125
169
  @close="showModelPicker = false"
126
170
  />
171
+
172
+ <KnowledgePicker
173
+ v-if="showKnowledgePicker"
174
+ :options="knowledgeOptions"
175
+ :selected-ids="knowledgeIds"
176
+ @toggle="handleKnowledgeToggle"
177
+ @close="showKnowledgePicker = false"
178
+ />
127
179
  </view>
128
180
  </template>
129
181
 
@@ -0,0 +1,181 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useI18n } from '@liujitcn/kratos-uni-app-core'
4
+ import type { AiKnowledgeOption } from '../../../../rpc/base/v1/ai_knowledge'
5
+
6
+ const props = defineProps<{
7
+ options: AiKnowledgeOption[]
8
+ selectedIds: number[]
9
+ }>()
10
+
11
+ const emit = defineEmits<{
12
+ toggle: [knowledgeId: number]
13
+ close: []
14
+ }>()
15
+
16
+ const { t } = useI18n()
17
+
18
+ const selectedCount = computed(() => props.selectedIds.length)
19
+
20
+ function isSelected(knowledgeId: number) {
21
+ return props.selectedIds.includes(knowledgeId)
22
+ }
23
+
24
+ function handleToggle(knowledgeId: number) {
25
+ emit('toggle', knowledgeId)
26
+ }
27
+ </script>
28
+
29
+ <template>
30
+ <view class="knowledge-picker">
31
+ <view class="knowledge-picker__mask" @tap="emit('close')" />
32
+ <view class="knowledge-picker__sheet">
33
+ <view class="knowledge-picker__handle" />
34
+ <view class="knowledge-picker__head">
35
+ <text class="knowledge-picker__title">{{ t('system.ai.knowledge.title') }}</text>
36
+ <text class="knowledge-picker__count">{{
37
+ t('system.ai.knowledge.selected_count', { count: selectedCount })
38
+ }}</text>
39
+ <button class="knowledge-picker__close" hover-class="none" @tap="emit('close')">✕</button>
40
+ </view>
41
+ <text class="knowledge-picker__hint">{{ t('system.ai.knowledge.select_hint') }}</text>
42
+ <scroll-view class="knowledge-picker__body" scroll-y :show-scrollbar="true">
43
+ <view
44
+ v-for="option in options"
45
+ :key="option.id"
46
+ class="knowledge-picker__row"
47
+ :class="{ 'is-selected': isSelected(option.id) }"
48
+ @tap="handleToggle(option.id)"
49
+ >
50
+ <view class="knowledge-picker__row-main">
51
+ <text class="knowledge-picker__row-name">{{ option.name }}</text>
52
+ <text v-if="option.doc_count" class="knowledge-picker__row-meta">{{
53
+ t('system.ai.knowledge.doc_count', { count: option.doc_count })
54
+ }}</text>
55
+ </view>
56
+ <view class="knowledge-picker__check" :class="{ 'is-checked': isSelected(option.id) }">
57
+ <text v-if="isSelected(option.id)">✓</text>
58
+ </view>
59
+ </view>
60
+ </scroll-view>
61
+ </view>
62
+ </view>
63
+ </template>
64
+
65
+ <style lang="scss" scoped>
66
+ .knowledge-picker {
67
+ position: fixed;
68
+ inset: 0;
69
+ z-index: 1000;
70
+ display: flex;
71
+ align-items: flex-end;
72
+ }
73
+
74
+ .knowledge-picker__mask {
75
+ position: absolute;
76
+ inset: 0;
77
+ background: rgba(0, 0, 0, 0.45);
78
+ }
79
+
80
+ .knowledge-picker__sheet {
81
+ position: relative;
82
+ display: flex;
83
+ flex-direction: column;
84
+ width: 100%;
85
+ max-height: 62vh;
86
+ padding: 10px 20px calc(20px + env(safe-area-inset-bottom));
87
+ background: #fff;
88
+ border-radius: 20px 20px 0 0;
89
+ }
90
+
91
+ .knowledge-picker__handle {
92
+ width: 36px;
93
+ height: 4px;
94
+ margin: 0 auto 10px;
95
+ background: #e5e6eb;
96
+ border-radius: 2px;
97
+ }
98
+
99
+ .knowledge-picker__head {
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 10px;
103
+ }
104
+
105
+ .knowledge-picker__title {
106
+ font-size: 17px;
107
+ font-weight: 600;
108
+ color: #111;
109
+ }
110
+
111
+ .knowledge-picker__count {
112
+ flex: 1;
113
+ font-size: 12px;
114
+ color: #8a8f99;
115
+ }
116
+
117
+ .knowledge-picker__close {
118
+ padding: 0;
119
+ font-size: 16px;
120
+ line-height: 1;
121
+ color: #8a8f99;
122
+ background: transparent;
123
+ }
124
+
125
+ .knowledge-picker__close::after {
126
+ border: none;
127
+ }
128
+
129
+ .knowledge-picker__hint {
130
+ margin-top: 6px;
131
+ font-size: 12px;
132
+ color: #8a8f99;
133
+ }
134
+
135
+ .knowledge-picker__body {
136
+ margin-top: 10px;
137
+ }
138
+
139
+ .knowledge-picker__row {
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ gap: 12px;
144
+ padding: 12px 4px;
145
+ border-bottom: 1px solid #f2f3f5;
146
+ }
147
+
148
+ .knowledge-picker__row-main {
149
+ display: flex;
150
+ flex-direction: column;
151
+ gap: 2px;
152
+ min-width: 0;
153
+ }
154
+
155
+ .knowledge-picker__row-name {
156
+ font-size: 15px;
157
+ color: #111;
158
+ }
159
+
160
+ .knowledge-picker__row-meta {
161
+ font-size: 12px;
162
+ color: #8a8f99;
163
+ }
164
+
165
+ .knowledge-picker__check {
166
+ display: flex;
167
+ align-items: center;
168
+ justify-content: center;
169
+ width: 22px;
170
+ height: 22px;
171
+ border: 1px solid #d0d2d6;
172
+ border-radius: 50%;
173
+ font-size: 13px;
174
+ color: #fff;
175
+ }
176
+
177
+ .knowledge-picker__check.is-checked {
178
+ background: #00a96b;
179
+ border-color: #00a96b;
180
+ }
181
+ </style>
@@ -8,9 +8,11 @@ import {
8
8
  import { defAiSessionService } from '../../../api/base/v1/ai_session'
9
9
  import { defAiToolService } from '../../../api/base/v1/ai_tool'
10
10
  import { defAiModelService } from '../../../api/base/v1/ai_provider'
11
+ import { defAiKnowledgeService } from '../../../api/base/v1/ai_knowledge'
11
12
  import type { AiMessage } from '../../../rpc/base/v1/ai_session'
12
13
  import type { AiAttachment, AiSession } from '../../../rpc/base/v1/ai_session'
13
14
  import type { AiProviderModelOption } from '../../../rpc/base/v1/ai_provider'
15
+ import type { AiKnowledgeOption } from '../../../rpc/base/v1/ai_knowledge'
14
16
  import type { AiShortcut, AiToolCall } from '../../../rpc/base/v1/ai_tool'
15
17
  import { AiMessageStatus } from '../../../rpc/base/v1/ai_session'
16
18
  import { Terminal } from '../../../rpc/base/v1/ai_tool'
@@ -100,6 +102,8 @@ const selectedAttachments = ref<AiAttachment[]>([])
100
102
  const modelProviders = ref<AiProviderModelOption[]>([])
101
103
  const selectedProviderId = ref(0)
102
104
  const selectedModelName = ref('')
105
+ const knowledgeOptions = ref<AiKnowledgeOption[]>([])
106
+ const selectedKnowledgeIds = ref<number[]>([])
103
107
  const runningStreamTaskMap = new Map<string, StreamTask>()
104
108
  const pendingDeltaMap = new Map<string, AiStreamPayload>()
105
109
  let pendingDeltaTimer = 0
@@ -472,11 +476,16 @@ async function loadAiShortcuts() {
472
476
  }
473
477
  loadingShortcuts.value = true
474
478
  try {
475
- const [response, providerResponse] = await Promise.all([
479
+ const [response, providerResponse, knowledgeResponse] = await Promise.all([
476
480
  defAiToolService.ListAiShortcut({ terminal: AI_TERMINAL }),
477
481
  defAiModelService.ListAiProviderModelOptions({}),
482
+ defAiKnowledgeService.ListAiKnowledgeOptions({}),
478
483
  ])
479
484
  modelProviders.value = providerResponse.providers ?? []
485
+ knowledgeOptions.value = knowledgeResponse.options ?? []
486
+ selectedKnowledgeIds.value = selectedKnowledgeIds.value.filter((id) =>
487
+ knowledgeOptions.value.some((option) => option.id === id),
488
+ )
480
489
  applyModelSelection()
481
490
  const shortcuts = normalizeStarterShortcuts(response.shortcuts).filter((item) => !item.action)
482
491
  if (shortcuts.length) {
@@ -487,6 +496,7 @@ async function loadAiShortcuts() {
487
496
  modelProviders.value = []
488
497
  selectedProviderId.value = 0
489
498
  selectedModelName.value = ''
499
+ knowledgeOptions.value = []
490
500
  showError(error, t('system.ai.load_shortcuts_failed'))
491
501
  } finally {
492
502
  loadingShortcuts.value = false
@@ -520,6 +530,11 @@ function handleModelChange(providerId: number, modelName: string) {
520
530
  saveModelSelection({ provider_id: providerId, model_name: modelName })
521
531
  }
522
532
 
533
+ /** 更新当前聊天检索注入的知识库选择。 */
534
+ function handleKnowledgeChange(knowledgeIds: number[]) {
535
+ selectedKnowledgeIds.value = knowledgeIds
536
+ }
537
+
523
538
  async function sendAiPayload(payload: { text: string; attachments: AiAttachment[] }) {
524
539
  const sessionID = await ensureActiveSession()
525
540
  if (!sessionID || isSessionSending(sessionID)) {
@@ -550,6 +565,7 @@ async function runAiTask(
550
565
  content: payload.text,
551
566
  provider_id: selectedProviderId.value,
552
567
  model_name: selectedModelName.value,
568
+ knowledge_base_ids: selectedKnowledgeIds.value,
553
569
  attachments: payload.attachments,
554
570
  action: undefined,
555
571
  }
@@ -1332,6 +1348,8 @@ function showError(error: unknown, fallback: string) {
1332
1348
  :model-providers="modelProviders"
1333
1349
  :provider-id="selectedProviderId"
1334
1350
  :model-name="selectedModelName"
1351
+ :knowledge-options="knowledgeOptions"
1352
+ :knowledge-ids="selectedKnowledgeIds"
1335
1353
  :model-placeholder="t('system.ai.model.model_placeholder')"
1336
1354
  :placeholder="composerPlaceholder"
1337
1355
  :bottom="composerBottom"
@@ -1342,6 +1360,7 @@ function showError(error: unknown, fallback: string) {
1342
1360
  @record="handleToggleRecord"
1343
1361
  @send="handleSend"
1344
1362
  @model-change="handleModelChange"
1363
+ @knowledge-change="handleKnowledgeChange"
1345
1364
  @remove-attachment="removeSelectedAttachment"
1346
1365
  />
1347
1366