Skip to content

Commit 43f836e

Browse files
committed
Merge branch 'feat/2.4.0' into feat/2.4.0-zl
2 parents ca15ad8 + fad2d2c commit 43f836e

90 files changed

Lines changed: 1178 additions & 862 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/backend/bisheng/common/errcode/tool.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,8 @@ class ToolSchemaServerError(BaseErrorCode):
4949
class ToolMcpSchemaError(BaseErrorCode):
5050
Code: int = 15024
5151
Msg: str = 'mcpTool configuration parsing failed, please check if the content matchesmcpConfigure Format: {exception}'
52+
53+
54+
class ToolMcpStdioError(BaseErrorCode):
55+
Code: int = 15025
56+
Msg: str = 'mcpTool not support stdio mode'

src/backend/bisheng/core/vectorstore/multi_retriever.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ async def _aget_relevant_documents(
4141

4242
def parse_doc_with_score(self, docs_with_score: List[tuple[Document, float]]) -> List[Document]:
4343
""" parse documents with score to documents only """
44-
docs_with_score.sort(key=lambda x: x[1])
44+
docs_with_score.sort(key=lambda x: x[1], reverse=True)
4545
if self.finally_k > 0:
4646
docs_with_score = docs_with_score[:self.finally_k]
4747
return [doc for doc, _ in docs_with_score]

src/backend/bisheng/knowledge/domain/services/knowledge_service.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import math
33
import os
4+
import time
45
from datetime import datetime
56
from typing import Any, Dict, List, Tuple
67
from urllib.parse import urlparse
@@ -57,6 +58,7 @@
5758
)
5859
from bisheng.knowledge.domain.repositories.interfaces.knowledge_file_repository import KnowledgeFileRepository
5960
from bisheng.knowledge.domain.repositories.interfaces.knowledge_repository import KnowledgeRepository
61+
from bisheng.knowledge.domain.schemas.knowledge_rag_schema import Metadata
6062
from bisheng.knowledge.domain.schemas.knowledge_schema import AddKnowledgeMetadataFieldsReq, \
6163
UpdateKnowledgeMetadataFieldsReq
6264
from bisheng.knowledge.domain.services.knowledge_audit_telemetry_service import KnowledgeAuditTelemetryService
@@ -247,6 +249,25 @@ def create_knowledge_base(cls, request, login_user: UserPayload, db_knowledge: K
247249
vector_client = KnowledgeRag.init_knowledge_milvus_vectorstore_sync(login_user.user_id,
248250
knowledge=db_knowledge,
249251
metadata_schemas=KNOWLEDGE_RAG_METADATA_SCHEMA)
252+
# Init Milvus schema avoiding SchemaNotReady concurrently
253+
# Need to provide non-nullable fields to satisfy Milvus schema constraints
254+
init_ids = vector_client.add_texts(
255+
texts=["init_schema"],
256+
metadatas=[Metadata(document_id=0,
257+
knowledge_id=db_knowledge.id,
258+
abstract="",
259+
chunk_index=1,
260+
bbox="",
261+
page=0,
262+
upload_time=int(time.time()),
263+
update_time=int(time.time()),
264+
uploader="",
265+
updater="",
266+
user_metadata={}).model_dump()]
267+
)
268+
if init_ids:
269+
vector_client.delete(ids=init_ids)
270+
250271
es_client = KnowledgeRag.init_knowledge_es_vectorstore_sync(knowledge=db_knowledge,
251272
metadata_schemas=KNOWLEDGE_RAG_METADATA_SCHEMA)
252273
es_client._store._create_index_if_not_exists()
@@ -1260,21 +1281,20 @@ async def copy_knowledge(
12601281
knowldge_dict.pop("id")
12611282
knowldge_dict.pop("create_time")
12621283
knowldge_dict.pop("update_time", None)
1263-
knowldge_dict["user_id"] = login_user.user_id
1264-
knowldge_dict["index_name"] = generate_knowledge_index_name()
1265-
knowldge_dict["collection_name"] = knowldge_dict["index_name"]
1284+
knowldge_dict.pop("collection_name", None)
1285+
knowldge_dict.pop("index_name", None)
12661286
knowldge_dict["name"] = f"{knowledge.name} Copy"[:200] if not knowledge_name else knowledge_name[:200]
12671287

12681288
knowldge_dict["state"] = KnowledgeState.UNPUBLISHED.value
12691289
knowledge_new = Knowledge(**knowldge_dict)
1270-
target_knowlege = await KnowledgeDao.async_insert_one(knowledge_new)
1271-
# celery not yetok
1290+
1291+
target_knowlege = cls.create_knowledge_base(request, login_user, knowledge_new)
1292+
12721293
params = {
12731294
"source_knowledge_id": knowledge.id,
12741295
"target_id": target_knowlege.id,
12751296
"login_user_id": login_user.user_id,
12761297
}
1277-
cls.create_knowledge_hook(request, login_user, target_knowlege)
12781298
file_worker.file_copy_celery.delay(params)
12791299
return target_knowlege
12801300

src/backend/bisheng/knowledge/rag/pipeline/transformer/preview_cache.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ def transform_documents(
2424
for key, val in all_chunk_info.items():
2525
one_metadata = val["metadata"]
2626
one_metadata.update(self.file_metadata)
27-
for k in one_metadata:
28-
if k not in knowledge_metadata_fields:
29-
del one_metadata[k]
27+
for k in one_metadata.keys() - knowledge_metadata_fields:
28+
del one_metadata[k]
3029
doc = Document(
3130
page_content=KnowledgeUtils.aggregate_chunk_metadata(val["text"], one_metadata),
3231
metadata=one_metadata,
@@ -35,9 +34,8 @@ def transform_documents(
3534
else:
3635
# aggregate chunk
3736
for doc in documents:
38-
for k in list(doc.metadata.keys()):
39-
if k not in knowledge_metadata_fields:
40-
doc.metadata.pop(k)
37+
for k in doc.metadata.keys() - knowledge_metadata_fields:
38+
del doc.metadata[k]
4139
doc.metadata.update(self.file_metadata)
4240
doc.page_content = KnowledgeUtils.aggregate_chunk_metadata(doc.page_content, doc.metadata)
4341

src/backend/bisheng/tool/domain/services/tool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from bisheng.common.errcode.http_error import UnAuthorizedError, NotFoundError
1616
from bisheng.common.errcode.tool import ToolTypeNotExistsError, ToolTypeRepeatError, ToolTypeNameError, \
1717
ToolTypeIsPresetError, ToolSchemaDownloadError, ToolSchemaEmptyError, ToolSchemaParseError, ToolSchemaServerError, \
18-
ToolMcpSchemaError
18+
ToolMcpSchemaError, ToolMcpStdioError
1919
from bisheng.common.services.config_service import settings
2020
from bisheng.database.models.group_resource import GroupResourceDao, ResourceTypeEnum, GroupResource
2121
from bisheng.database.models.role_access import AccessType
@@ -252,7 +252,7 @@ async def parse_mcp_schema(file_content: str) -> GptsToolsTypeRead:
252252
if not mcp_conf.enable_stdio:
253253
client_type, _ = ClientManager.parse_mcp_client_type(tool_type.openapi_schema)
254254
if client_type == McpClientType.STDIO.value:
255-
raise ToolMcpSchemaError(msg="mcp not support stdio mode")
255+
raise ToolMcpStdioError()
256256
# Instantiatemcpservice object, getting a list of tools
257257
client = await ClientManager.connect_mcp_from_json(result)
258258

src/backend/bisheng/worker/knowledge/file_worker.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def copy_vector(
200200

201201
es_db = KnowledgeRag.init_knowledge_es_vectorstore_sync(knowledge=target_knowledge)
202202
if es_db:
203-
insert_es(source_data, es_db)
203+
insert_es(source_data, es_db, index_name=target_knowledge.index_name)
204204

205205

206206
def create_milvus_col_and_es_index(source_konwledge: Knowledge, target_knowledge: Knowledge):
@@ -244,7 +244,7 @@ def insert_milvus(li: List, fields: list, target: Milvus):
244244
logger.info("copy_done pk_size={}", len(res_list))
245245

246246

247-
def insert_es(li: List, target: ElasticsearchStore):
247+
def insert_es(li: List, target: ElasticsearchStore, index_name: str):
248248
from elasticsearch.helpers import bulk
249249

250250
res_list = []
@@ -256,15 +256,15 @@ def insert_es(li: List, target: ElasticsearchStore):
256256
metadata = data
257257
request = {
258258
"_op_type": "index",
259-
"_index": target.index_name,
259+
"_index": index_name,
260260
"text": text,
261261
"metadata": metadata,
262262
"_id": ids[i],
263263
}
264264
requests.append(request)
265265
bulk(target.client, requests)
266266

267-
target.client.indices.refresh(index=target.index_name)
267+
target.client.indices.refresh(index=index_name)
268268
logger.info("copy_es_done pk_size={}", len(res_list))
269269

270270

src/backend/bisheng/workstation/domain/services/workstation_service.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ class WorkStationService(BaseService):
3333

3434
@classmethod
3535
def update_config(
36-
cls,
37-
request: Request,
38-
login_user: UserPayload,
39-
data: WorkstationConfig,
36+
cls,
37+
request: Request,
38+
login_user: UserPayload,
39+
data: WorkstationConfig,
4040
) -> WorkstationConfig:
4141
"""Update workstation default configuration."""
4242
config = ConfigDao.get_config(ConfigKeyEnum.WORKSTATION)
@@ -170,11 +170,11 @@ async def update_knowledge_space_config(cls, data: KnowledgeSpaceConfig) -> Know
170170

171171
@classmethod
172172
def uploadPersonalKnowledge(
173-
cls,
174-
request: Request,
175-
login_user: UserPayload,
176-
file_path,
177-
background_tasks: BackgroundTasks,
173+
cls,
174+
request: Request,
175+
login_user: UserPayload,
176+
file_path,
177+
background_tasks: BackgroundTasks,
178178
):
179179
knowledge = KnowledgeDao.get_user_knowledge(
180180
login_user.user_id,
@@ -204,11 +204,11 @@ def uploadPersonalKnowledge(
204204

205205
@classmethod
206206
def queryKnowledgeList(
207-
cls,
208-
request: Request,
209-
login_user: UserPayload,
210-
page: int,
211-
size: int,
207+
cls,
208+
request: Request,
209+
login_user: UserPayload,
210+
page: int,
211+
size: int,
212212
):
213213
knowledge = KnowledgeDao.get_user_knowledge(
214214
login_user.user_id,
@@ -228,11 +228,11 @@ def queryKnowledgeList(
228228

229229
@classmethod
230230
async def queryChunksFromDB(
231-
cls,
232-
question: str,
233-
use_knowledge_param: UseKnowledgeBaseParam,
234-
max_token: int,
235-
login_user: UserPayload,
231+
cls,
232+
question: str,
233+
use_knowledge_param: UseKnowledgeBaseParam,
234+
max_token: int,
235+
login_user: UserPayload,
236236
) -> tuple[list[Any], None] | tuple[list[Any], Any]:
237237
"""Query relevant knowledge blocks from the database."""
238238
try:
@@ -301,8 +301,7 @@ async def get_chat_history(cls, chat_id: str, size: int = 4):
301301
chat_history = []
302302
messages = await ChatMessageDao.aget_messages_by_chat_id(chat_id, ['question', 'answer'], size)
303303
for one in messages:
304-
extra = json.loads(one.extra) or {}
305-
content = extra['prompt'] if 'prompt' in extra else one.message
304+
content = one.message
306305
if one.category == MessageCategory.QUESTION.value:
307306
chat_history.append(HumanMessage(content=content))
308307
elif one.category == MessageCategory.ANSWER.value:
Lines changed: 14 additions & 0 deletions
Loading

src/frontend/client/src/api/chatApi.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ export function parseStreamHistoryItem(raw: StreamHistoryItem): ChatMessage {
203203
} else {
204204
// User messages: { query, tags? }
205205
displayText = parsed.query || parsed.text || raw.message;
206+
// Re-encode the first tag (if any) into the same `:::tag {...}:::`
207+
// prefix the live send path uses, so the user bubble can render
208+
// the chip after a history reload.
209+
const firstTag = Array.isArray(parsed.tags) ? parsed.tags[0] : null;
210+
if (firstTag && typeof firstTag.name === "string") {
211+
const tagJson = JSON.stringify({
212+
id: Number(firstTag.id) || 0,
213+
name: firstTag.name,
214+
});
215+
displayText = `:::tag ${tagJson}:::\n${displayText}`;
216+
}
206217
}
207218
} catch {
208219
displayText = raw.message || "";

src/frontend/client/src/components/AccountInfoDialog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ export function AccountInfoDialog({
404404
onClick={() => setShowOldPassword(!showOldPassword)}
405405
className="absolute right-3 top-1/2 -translate-y-1/2 text-[#86909c] hover:text-[#4e5969]"
406406
>
407-
{showOldPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
407+
{showOldPassword ? <Eye className="size-4" /> : <EyeOff className="size-4" />}
408408
</button>
409409
</div>
410410
</div>
@@ -427,7 +427,7 @@ export function AccountInfoDialog({
427427
onClick={() => setShowNewPassword(!showNewPassword)}
428428
className="absolute right-3 top-1/2 -translate-y-1/2 text-[#86909c] hover:text-[#4e5969]"
429429
>
430-
{showNewPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
430+
{showNewPassword ? <Eye className="size-4" /> : <EyeOff className="size-4" />}
431431
</button>
432432
</div>
433433

@@ -460,7 +460,7 @@ export function AccountInfoDialog({
460460
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
461461
className="absolute right-3 top-1/2 -translate-y-1/2 text-[#86909c] hover:text-[#4e5969]"
462462
>
463-
{showConfirmPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
463+
{showConfirmPassword ? <Eye className="size-4" /> : <EyeOff className="size-4" />}
464464
</button>
465465
</div>
466466
</div>

0 commit comments

Comments
 (0)