From 747fab6a9879e2fc482e0b36b1c19ba2cbf77147 Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Mon, 25 Aug 2025 16:04:24 +0800 Subject: [PATCH 0001/1120] feat: Terminology Management --- frontend/src/api/professional.ts | 11 + frontend/src/components/layout/Menu.vue | 2 + frontend/src/i18n/en.json | 18 + frontend/src/i18n/zh-CN.json | 18 + frontend/src/router/index.ts | 7 + frontend/src/style.less | 5 + .../src/views/system/appearance/Person.vue | 18 +- .../src/views/system/professional/index.vue | 773 ++++++++++++++++++ 8 files changed, 842 insertions(+), 10 deletions(-) create mode 100644 frontend/src/api/professional.ts create mode 100644 frontend/src/views/system/professional/index.vue diff --git a/frontend/src/api/professional.ts b/frontend/src/api/professional.ts new file mode 100644 index 000000000..5b934e7e4 --- /dev/null +++ b/frontend/src/api/professional.ts @@ -0,0 +1,11 @@ +import { request } from '@/utils/request' + +export const professionalApi = { + getList: (pageNum: any, pageSize: any, params: any) => + request.get(`/system/terminology/page/${pageNum}/${pageSize}`, { + params, + }), + updateEmbedded: (data: any) => request.put('/system/terminology', data), + deleteEmbedded: (params: any) => request.delete('/system/terminology', { data: params }), + getOne: (id: any) => request.get(`/system/terminology/${id}`), +} diff --git a/frontend/src/components/layout/Menu.vue b/frontend/src/components/layout/Menu.vue index 8991599c1..dcd7e8910 100644 --- a/frontend/src/components/layout/Menu.vue +++ b/frontend/src/components/layout/Menu.vue @@ -49,6 +49,7 @@ const routerList = computed(() => { !route.path.includes('embeddedPage') && !route.path.includes('canvas') && !route.path.includes('member') && + !route.path.includes('professional') && !route.path.includes('permission') && !route.path.includes('preview') && route.path !== '/login' && @@ -92,6 +93,7 @@ const routerList = computed(() => { .ed-sub-menu.is-active { .ed-sub-menu__title { + background-color: #fff !important; color: var(--ed-color-primary) !important; } } diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index d7fe7d151..4a96e4994 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -5,6 +5,24 @@ "Dashboard": "Dashboard", "AI Model Configuration": "AI Model Configuration" }, + "professional": { + "editing_terminology": "Editing Terminology", + "professional_terminology": "Professional Terminology", + "term_name": "Term Name", + "term_description": "Term Description", + "search_term": "Search Term", + "no_term": "No Term", + "create_new_term": "Create New Term", + "export_all": "Export All", + "professional_term_details": "Professional Term Details", + "business_term": "Business Term", + "synonyms": "Synonyms", + "all_236_terms": "Export all {msg} terms?", + "export": "Export", + "selected_2_terms": "Delete the selected {msg} terms?", + "selected_2_terms_de": "Export the selected {msg} terms?", + "the_term_gmv": "Delete the term: {msg}?" + }, "common": { "zoom_in": "Zoom in", "zoom_out": "Zoom out", diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json index ef9eff09c..fedf5dd51 100644 --- a/frontend/src/i18n/zh-CN.json +++ b/frontend/src/i18n/zh-CN.json @@ -5,6 +5,24 @@ "Dashboard": "仪表盘", "AI Model Configuration": "模型配置" }, + "professional": { + "editing_terminology": "编辑术语", + "professional_terminology": "专业术语", + "term_name": "术语名称", + "term_description": "术语描述", + "search_term": "搜索术语", + "no_term": "暂无术语", + "create_new_term": "新建术语", + "export_all": "全部导出", + "professional_term_details": "专业术语详情", + "business_term": "业务术语", + "synonyms": "同义词", + "all_236_terms": "是否导出全部 {msg} 条术语?", + "export": "导出", + "selected_2_terms": "是否删除选中的 {msg} 条术语?", + "selected_2_terms_de": "是否导出选中的 {msg} 条术语?", + "the_term_gmv": "是否删除术语:{msg}?" + }, "common": { "zoom_in": "放大", "zoom_out": "缩小", diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index a2a5004a3..7e91bc7b8 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -15,6 +15,7 @@ import assistantTest from '@/views/system/embedded/Test.vue' import assistant from '@/views/embedded/index.vue' import EmbeddedPage from '@/views/embedded/page.vue' import Member from '@/views/system/member/index.vue' +import Professional from '@/views/system/professional/index.vue' import Appearance from '@/views/system/appearance/index.vue' import Permission from '@/views/system/permission/index.vue' import User from '@/views/system/user/User.vue' @@ -105,6 +106,12 @@ export const routes = [ component: Permission, meta: { title: t('workspace.permission_configuration') }, }, + { + path: '/set/professional', + name: 'professional', + component: Professional, + meta: { title: t('professional.professional_terminology') }, + }, ], }, { diff --git a/frontend/src/style.less b/frontend/src/style.less index 428d6945e..8063232c9 100644 --- a/frontend/src/style.less +++ b/frontend/src/style.less @@ -357,3 +357,8 @@ strong { .ed-checkbox { --ed-checkbox-font-weight: 400 !important; } + +.ed-form-item__label.ed-form-item__label.ed-form-item__label { + height: 22px; + line-height: 22px; +} diff --git a/frontend/src/views/system/appearance/Person.vue b/frontend/src/views/system/appearance/Person.vue index 732435f36..57fb6c6ca 100644 --- a/frontend/src/views/system/appearance/Person.vue +++ b/frontend/src/views/system/appearance/Person.vue @@ -1,6 +1,6 @@ + + + + + From 5e591e462de062f85a9d1cd59fcc91a1b74bb9df Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Mon, 25 Aug 2025 16:23:11 +0800 Subject: [PATCH 0002/1120] feat: Terminology Management --- frontend/src/views/system/professional/index.vue | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/frontend/src/views/system/professional/index.vue b/frontend/src/views/system/professional/index.vue index a8f212753..71bcc470e 100644 --- a/frontend/src/views/system/professional/index.vue +++ b/frontend/src/views/system/professional/index.vue @@ -22,7 +22,8 @@ defineProps({ interface Form { id?: string | null word: string | null - other_words?: string[] | null + other_words: string[] + description: string | null } const { t } = useI18n() @@ -51,6 +52,7 @@ const dialogTitle = ref('') const defaultForm = { id: null, word: null, + description: null, other_words: [], } const pageForm = ref
(cloneDeep(defaultForm)) @@ -537,13 +539,7 @@ const deleteHandlerItem = (idx: number) => { :before-close="onRowFormClose" modal-class="professional-term_drawer" > - +
{{ pageForm.word }} From 393fd8c5b5b94604256dc0cc1411b503f521df71 Mon Sep 17 00:00:00 2001 From: ulleo Date: Mon, 25 Aug 2025 16:34:44 +0800 Subject: [PATCH 0003/1120] feat: add terminology management --- backend/alembic/env.py | 3 +- .../versions/039_create_terminology.py | 41 ++++ backend/apps/ai_model/embedding.py | 59 ++++++ backend/apps/api.py | 3 +- backend/apps/terminology/__init__.py | 0 backend/apps/terminology/api/__init__.py | 0 backend/apps/terminology/api/terminology.py | 37 ++++ backend/apps/terminology/curd/__init__.py | 0 backend/apps/terminology/curd/terminology.py | 196 ++++++++++++++++++ backend/apps/terminology/models/__init__.py | 0 .../terminology/models/terminology_model.py | 27 +++ backend/common/core/config.py | 21 +- backend/pyproject.toml | 2 + frontend/src/i18n/en.json | 2 +- frontend/src/i18n/zh-CN.json | 2 +- .../src/views/system/professional/index.vue | 2 +- 16 files changed, 383 insertions(+), 12 deletions(-) create mode 100644 backend/alembic/versions/039_create_terminology.py create mode 100644 backend/apps/ai_model/embedding.py create mode 100644 backend/apps/terminology/__init__.py create mode 100644 backend/apps/terminology/api/__init__.py create mode 100644 backend/apps/terminology/api/terminology.py create mode 100644 backend/apps/terminology/curd/__init__.py create mode 100644 backend/apps/terminology/curd/terminology.py create mode 100644 backend/apps/terminology/models/__init__.py create mode 100644 backend/apps/terminology/models/terminology_model.py diff --git a/backend/alembic/env.py b/backend/alembic/env.py index db6e94130..3f72963ae 100755 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -24,7 +24,8 @@ # from apps.system.models.user import SQLModel # noqa # from apps.settings.models.setting_models import SQLModel -from apps.chat.models.chat_model import SQLModel +# from apps.chat.models.chat_model import SQLModel +from apps.terminology.models.terminology_model import SQLModel # from apps.dashboard.models.dashboard_model import SQLModel from common.core.config import settings # noqa diff --git a/backend/alembic/versions/039_create_terminology.py b/backend/alembic/versions/039_create_terminology.py new file mode 100644 index 000000000..42ab588b5 --- /dev/null +++ b/backend/alembic/versions/039_create_terminology.py @@ -0,0 +1,41 @@ +"""039_create_terminology + +Revision ID: 25cbc85766fd +Revises: fc23c4f3e755 +Create Date: 2025-08-25 11:38:32.990973 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes +import pgvector +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '25cbc85766fd' +down_revision = 'fc23c4f3e755' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute("CREATE EXTENSION IF NOT EXISTS vector;") + + op.create_table('terminology', + sa.Column('id', sa.BigInteger(), sa.Identity(always=True), nullable=False), + sa.Column('pid', sa.BigInteger(), nullable=True), + sa.Column('create_time', sa.DateTime(), nullable=True), + sa.Column('word', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('embedding', pgvector.sqlalchemy.vector.VECTOR(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('terminology') + # ### end Alembic commands ### diff --git a/backend/apps/ai_model/embedding.py b/backend/apps/ai_model/embedding.py new file mode 100644 index 000000000..dea3ffbe8 --- /dev/null +++ b/backend/apps/ai_model/embedding.py @@ -0,0 +1,59 @@ +import threading +from typing import Optional + +from langchain_core.embeddings import Embeddings +from langchain_huggingface import HuggingFaceEmbeddings +from pydantic import BaseModel + +from common.core.config import settings + + +class EmbeddingModelInfo(BaseModel): + folder: str + name: str + device: str = 'cpu' + + +local_embedding_model = EmbeddingModelInfo(folder=settings.LOCAL_MODEL_PATH, name=settings.DEFAULT_EMBEDDING_MODEL) + +_lock = threading.Lock() +locks = {} + +_embedding_model: dict[str, Optional[Embeddings]] = {} + + +class EmbeddingModelCache: + + @staticmethod + def _new_instance(config: EmbeddingModelInfo = local_embedding_model): + return HuggingFaceEmbeddings(model_name=config.name, cache_folder=config.folder, + model_kwargs={'device': config.device}, + encode_kwargs={'normalize_embeddings': True} + ) + + @staticmethod + def _get_lock(key: str = settings.DEFAULT_EMBEDDING_MODEL): + lock = locks.get(key) + if lock is None: + with _lock: + lock = locks.get(key) + if lock is None: + lock = threading.Lock() + locks[key] = lock + + return lock + + @staticmethod + def get_model(key: str = settings.DEFAULT_EMBEDDING_MODEL, + config: EmbeddingModelInfo = local_embedding_model) -> Embeddings: + global _embedding_model + model_instance = _embedding_model.get(key) + if model_instance is None: + lock = EmbeddingModelCache._get_lock(key) + with lock: + model_instance = _embedding_model.get(key) + if model_instance is None: + model_instance = EmbeddingModelCache._new_instance(config) + _embedding_model[key] = model_instance + + return model_instance diff --git a/backend/apps/api.py b/backend/apps/api.py index 4f573134b..14b07cea2 100644 --- a/backend/apps/api.py +++ b/backend/apps/api.py @@ -1,9 +1,9 @@ from fastapi import APIRouter +from apps.terminology.api import terminology from apps.chat.api import chat from apps.dashboard.api import dashboard_api from apps.datasource.api import datasource -from apps.settings.api import terminology from apps.system.api import login, user, aimodel, workspace, assistant from apps.mcp import mcp @@ -19,3 +19,4 @@ api_router.include_router(dashboard_api.router) api_router.include_router(mcp.router) + diff --git a/backend/apps/terminology/__init__.py b/backend/apps/terminology/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/apps/terminology/api/__init__.py b/backend/apps/terminology/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/apps/terminology/api/terminology.py b/backend/apps/terminology/api/terminology.py new file mode 100644 index 000000000..2467db3aa --- /dev/null +++ b/backend/apps/terminology/api/terminology.py @@ -0,0 +1,37 @@ +from typing import Optional + +from fastapi import APIRouter, Query + +from apps.terminology.curd.terminology import page_terminology, create_terminology, update_terminology, \ + delete_terminology +from apps.terminology.models.terminology_model import TerminologyInfo +from common.core.deps import SessionDep + +router = APIRouter(tags=["Terminology"], prefix="/system/terminology") + + +@router.get("/page/{current_page}/{page_size}") +async def pager(session: SessionDep, current_page: int, page_size: int, + word: Optional[str] = Query(None, description="搜索术语(可选)")): + current_page, page_size, total_count, total_pages, _list = page_terminology(session, current_page, page_size, word) + + return { + "current_page": current_page, + "page_size": page_size, + "total_count": total_count, + "total_pages": total_pages, + "data": _list + } + + +@router.put("") +async def create_or_update(session: SessionDep, info: TerminologyInfo): + if info.id: + return update_terminology(session, info) + else: + return create_terminology(session, info) + + +@router.delete("") +async def delete(session: SessionDep, id_list: list[int]): + delete_terminology(session, id_list) diff --git a/backend/apps/terminology/curd/__init__.py b/backend/apps/terminology/curd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/apps/terminology/curd/terminology.py b/backend/apps/terminology/curd/terminology.py new file mode 100644 index 000000000..ee1b146c0 --- /dev/null +++ b/backend/apps/terminology/curd/terminology.py @@ -0,0 +1,196 @@ +import datetime +from typing import List, Optional + +from sqlalchemy import and_, or_, select, func, delete, update +from sqlalchemy.dialects.postgresql import JSONB +from sqlalchemy.orm import aliased + +from apps.terminology.models.terminology_model import Terminology, TerminologyInfo +from common.core.deps import SessionDep + + +def page_terminology(session: SessionDep, current_page: int = 1, page_size: int = 10, name: Optional[str] = None): + _list: List[TerminologyInfo] = [] + + child = aliased(Terminology) + + current_page = max(1, current_page) + page_size = max(10, page_size) + + total_count = 0 + total_pages = 0 + + if name and name.strip() != "": + keyword_pattern = f"%{name.strip()}%" + # 步骤1:先找到所有匹配的节点ID(无论是父节点还是子节点) + matched_ids_subquery = ( + select(Terminology.id) + .where(Terminology.name.like(keyword_pattern)) # LIKE查询条件 + .subquery() + ) + + # 步骤2:找到这些匹配节点的所有父节点(包括自身如果是父节点) + parent_ids_subquery = ( + select(Terminology.id) + .where( + (Terminology.id.in_(matched_ids_subquery)) | + (Terminology.id.in_( + select(Terminology.pid) + .where(Terminology.id.in_(matched_ids_subquery)) + .where(Terminology.pid.isnot(None)) + )) + ) + .where(Terminology.pid.is_(None)) # 只取父节点 + ) + + count_stmt = select(func.count()).select_from(parent_ids_subquery.subquery()) + total_count = session.execute(count_stmt).scalar() + total_pages = (total_count + page_size - 1) // page_size + + # 步骤3:获取分页后的父节点ID + paginated_parent_ids = ( + parent_ids_subquery + .order_by(Terminology.create_time.desc()) + .offset((current_page - 1) * page_size) + .limit(page_size) + .subquery() + ) + + # 步骤4:获取这些父节点的childrenNames + children_subquery = ( + select( + child.pid, + func.jsonb_agg(child.name).order_by(child.create_time.desc()).label('children_names') + ) + .where(child.pid.isnot(None)) + .group_by(child.pid) + .subquery() + ) + + # 主查询 + stmt = ( + select( + Terminology.id, + Terminology.word, + Terminology.create_time, + Terminology.description, + func.coalesce( + children_subquery.c.children_names, + func.cast('[]', JSONB) + ).label('other_words') + ) + .outerjoin( + children_subquery, + Terminology.id == children_subquery.c.pid + ) + .where(Terminology.id.in_(paginated_parent_ids)) + .order_by(Terminology.create_time.desc()) + ) + print(str(stmt)) + else: + parent_ids_subquery = ( + select(Terminology.id) + .where(Terminology.pid.is_(None)) # 只取父节点 + ) + count_stmt = select(func.count()).select_from(parent_ids_subquery.subquery()) + total_count = session.execute(count_stmt).scalar() + total_pages = (total_count + page_size - 1) // page_size + + paginated_parent_ids = ( + parent_ids_subquery + .order_by(Terminology.create_time.desc()) + .offset((current_page - 1) * page_size) + .limit(page_size) + .subquery() + ) + + stmt = ( + select( + Terminology.id, + Terminology.word, + Terminology.create_time, + Terminology.description, + func.coalesce( + func.jsonb_agg(child.word), + func.cast('[]', JSONB) + ).label('other_words') + ) + .outerjoin(child, and_(Terminology.id == child.pid)) + .where(Terminology.id.in_(paginated_parent_ids)) + .group_by(Terminology.id, Terminology.word) + .order_by(Terminology.create_time.desc()) + ) + print(str(stmt)) + + result = session.execute(stmt) + + for row in result: + _list.append(TerminologyInfo( + id=row.id, + word=row.word, + create_time=row.create_time, + description=row.description, + other_words=row.other_words, + )) + + return current_page, page_size, total_count, total_pages, _list + + +def create_terminology(session: SessionDep, info: TerminologyInfo): + create_time = datetime.datetime.now() + parent = Terminology(word=info.word, create_time=create_time, description=info.description) + + result = Terminology(**parent.model_dump()) + + session.add(parent) + session.flush() + session.refresh(parent) + + result.id = parent.id + session.commit() + + _list: List[Terminology] = [] + if info.other_words: + for other_word in info.other_words: + _list.append( + Terminology(pid=result.id, word=other_word, create_time=create_time, description=info.description)) + session.bulk_save_objects(_list) + session.flush() + session.commit() + + # todo embedding + + return result.id + + +def update_terminology(session: SessionDep, info: TerminologyInfo): + stmt = update(Terminology).where(and_(Terminology.id == info.id)).values( + word=info.word, + description=info.description, + ) + session.execute(stmt) + session.commit() + + stmt = delete(Terminology).where(and_(Terminology.pid == info.id)) + session.execute(stmt) + session.commit() + + create_time = datetime.datetime.now() + _list: List[Terminology] = [] + if info.other_words: + for other_word in info.other_words: + _list.append( + Terminology(pid=info.id, word=other_word, create_time=create_time, description=info.description)) + session.bulk_save_objects(_list) + session.flush() + session.commit() + + # todo embedding + + return info.id + + +def delete_terminology(session: SessionDep, ids: list[int]): + stmt = delete(Terminology).where(or_(Terminology.id.in_(ids), Terminology.pid.in_(ids))) + session.execute(stmt) + session.commit() diff --git a/backend/apps/terminology/models/__init__.py b/backend/apps/terminology/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/apps/terminology/models/terminology_model.py b/backend/apps/terminology/models/terminology_model.py new file mode 100644 index 000000000..d467cc5c1 --- /dev/null +++ b/backend/apps/terminology/models/terminology_model.py @@ -0,0 +1,27 @@ +from datetime import datetime +from typing import List, Optional + +from pgvector.sqlalchemy import VECTOR +from pydantic import BaseModel +from sqlalchemy import Column, Text, BigInteger, DateTime, Identity +from sqlmodel import SQLModel, Field + + +class Terminology(SQLModel, table=True): + __tablename__ = "terminology" + id: Optional[int] = Field(sa_column=Column(BigInteger, Identity(always=True), primary_key=True)) + pid: Optional[int] = Field(sa_column=Column(BigInteger, nullable=True)) + create_time: Optional[datetime] = Field(sa_column=Column(DateTime(timezone=False), nullable=True)) + word: Optional[str] = Field(max_length=255) + description: Optional[str] = Field(sa_column=Column(Text, nullable=True)) + embedding: Optional[List[float]] = Field(sa_column=Column(VECTOR(), nullable=True)) + + +class TerminologyInfo(BaseModel): + id: Optional[int] = None + create_time: Optional[datetime] = None + word: Optional[str] = None + description: Optional[str] = None + other_words: Optional[List[str]] = [] + + diff --git a/backend/common/core/config.py b/backend/common/core/config.py index f32dad35a..793bd0bee 100644 --- a/backend/common/core/config.py +++ b/backend/common/core/config.py @@ -50,20 +50,20 @@ def all_cors_origins(self) -> list[str]: POSTGRES_PASSWORD: str = "123456" POSTGRES_DB: str = "sqlbot" SQLBOT_DB_URL: str = '' - #SQLBOT_DB_URL: str = 'mysql+pymysql://root:Password123%40mysql@127.0.0.1:3306/sqlbot' - - TOKEN_KEY: str = "X-SQLBOT-TOKEN" + # SQLBOT_DB_URL: str = 'mysql+pymysql://root:Password123%40mysql@127.0.0.1:3306/sqlbot' + + TOKEN_KEY: str = "X-SQLBOT-TOKEN" DEFAULT_PWD: str = "SQLBot@123456" ASSISTANT_TOKEN_KEY: str = "X-SQLBOT-ASSISTANT-TOKEN" - + CACHE_TYPE: Literal["redis", "memory", "None"] = "memory" - CACHE_REDIS_URL: str | None = None # Redis URL, e.g., "redis://[[username]:[password]]@localhost:6379/0" - + CACHE_REDIS_URL: str | None = None # Redis URL, e.g., "redis://[[username]:[password]]@localhost:6379/0" + LOG_LEVEL: str = "INFO" # DEBUG, INFO, WARNING, ERROR LOG_DIR: str = "logs" LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s:%(lineno)d - %(message)s" SQL_DEBUG: bool = False - + UPLOAD_DIR: str = "/opt/sqlbot/data/file" SQLBOT_KEY_EXPIRED: int = 100 # License key expiration timestamp, 0 means no expiration @@ -86,4 +86,11 @@ def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn | str: MCP_IMAGE_HOST: str = 'http://localhost:3000' SERVER_IMAGE_HOST: str = '' + LOCAL_MODEL_PATH: str = '/opt/sqlbot/models' + DEFAULT_EMBEDDING_MODEL: str = 'shibing624/text2vec-base-chinese' + + EMBEDDING_SIMILARITY: float = 0.6 + EMBEDDING_TOP_COUNT: int = 3 + + settings = Settings() # type: ignore diff --git a/backend/pyproject.toml b/backend/pyproject.toml index ef216ab33..5f6273e73 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -23,7 +23,9 @@ dependencies = [ "langchain-core>=0.3,<0.4", "langchain-openai>=0.3,<0.4", "langchain-community>=0.3,<0.4", + "langchain-huggingface>=0.2.0", "langgraph>=0.3,<0.4", + "pgvector>=0.4.1", "dashscope>=1.14.0,<2.0.0", "pymysql (>=1.1.1,<2.0.0)", "cryptography (>=44.0.3,<45.0.0)", diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 4a96e4994..a72014ee8 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -644,4 +644,4 @@ "setting_successfully": "Setting Successfully", "customize_theme_color": "Customize theme color" } -} \ No newline at end of file +} diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json index fedf5dd51..0dfae875c 100644 --- a/frontend/src/i18n/zh-CN.json +++ b/frontend/src/i18n/zh-CN.json @@ -644,4 +644,4 @@ "setting_successfully": "设置成功", "customize_theme_color": "自定义主题色" } -} \ No newline at end of file +} diff --git a/frontend/src/views/system/professional/index.vue b/frontend/src/views/system/professional/index.vue index 71bcc470e..8f57beee0 100644 --- a/frontend/src/views/system/professional/index.vue +++ b/frontend/src/views/system/professional/index.vue @@ -321,7 +321,7 @@ const deleteHandlerItem = (idx: number) => {