From e112fa4e4433801a45d561723a529420ed398b24 Mon Sep 17 00:00:00 2001 From: paituo <330435863@qq.com> Date: Tue, 13 Aug 2024 09:37:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/routers/events.py | 8 +- backend/app/api/routers/models.py | 4 +- backend/app/engine/__init__.py | 58 +++- backend/app/engine/constants.py | 1 + backend/app/engine/generate.py | 86 ++++-- backend/app/engine/index.py | 35 +-- backend/app/engine/loaders/__init__.py | 9 +- backend/app/engine/loaders/db.py | 173 ++++++++++- backend/app/engine/loaders/file.py | 9 + backend/app/engine/loaders/web.py | 3 +- backend/app/engine/tools/__init__.py | 14 +- backend/app/engine/vectordb.py | 71 +++++ backend/app/observability.py | 20 +- backend/app/settings.py | 221 ++++++++------ backend/config/loaders.yaml | 24 +- backend/config/tools.yaml | 5 +- backend/data/101.pdf | Bin 47931 -> 0 bytes ...电力造价工程文件格式_FeeCollectionTable.json | 61 ++++ .../博微电力造价工程文件格式_OtherFee.json | 76 +++++ ...微电力造价工程文件格式_ProjectDivision.json | 126 ++++++++ ...工程文件格式_ProjectDivisions_CostPreview.json | 201 +++++++++++++ ...电力造价工程文件格式_ProjectProperties.json | 31 ++ ...电力造价工程文件格式_ProjectQuantities.json | 281 ++++++++++++++++++ ...力造价工程文件格式_TotalCalculateTable.json | 86 ++++++ .../data/博微电力造价工程业务数据说明.docx | Bin 0 -> 14451 bytes backend/data/工程造价基础知识.doc | Bin 0 -> 141312 bytes backend/data/电力造价知识.docx | Bin 0 -> 17041 bytes backend/main.py | 77 ++--- backend/pyproject.toml | 30 +- backend/run-data.bat | 4 + backend/run-test.bat | 4 + backend/run.bat | 1 + backend/tests/query.py | 67 +++++ frontend/.env | 6 +- frontend/app/components/header.tsx | 10 +- .../app/components/ui/chat/chat-actions.tsx | 4 +- .../app/components/ui/chat/chat-input.tsx | 9 +- .../ui/chat/chat-message/chat-events.tsx | 2 +- .../ui/chat/chat-message/chat-sources.tsx | 36 ++- .../ui/chat/chat-message/chat-tools.tsx | 2 +- .../ui/chat/chat-message/codeblock.tsx | 6 +- .../app/components/ui/chat/chat.interface.ts | 2 +- .../app/components/ui/chat/hooks/use-file.ts | 4 +- frontend/app/components/ui/file-uploader.tsx | 4 +- frontend/app/layout.tsx | 8 +- frontend/app/page.tsx | 2 +- frontend/run.bat | 1 + phoenixserver/.env | 3 + phoenixserver/phoenixserver.py | 18 ++ phoenixserver/runphoenixserver.bat | 5 + 50 files changed, 1649 insertions(+), 259 deletions(-) create mode 100644 backend/app/engine/constants.py create mode 100644 backend/app/engine/vectordb.py delete mode 100644 backend/data/101.pdf create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_FeeCollectionTable.json create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_OtherFee.json create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_ProjectDivision.json create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_ProjectDivisions_CostPreview.json create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_ProjectProperties.json create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_ProjectQuantities.json create mode 100644 backend/data/projectstruct/博微电力造价工程文件格式_TotalCalculateTable.json create mode 100644 backend/data/博微电力造价工程业务数据说明.docx create mode 100644 backend/data/工程造价基础知识.doc create mode 100644 backend/data/电力造价知识.docx create mode 100644 backend/run-data.bat create mode 100644 backend/run-test.bat create mode 100644 backend/run.bat create mode 100644 backend/tests/query.py create mode 100644 frontend/run.bat create mode 100644 phoenixserver/.env create mode 100644 phoenixserver/phoenixserver.py create mode 100644 phoenixserver/runphoenixserver.bat diff --git a/backend/app/api/routers/events.py b/backend/app/api/routers/events.py index 94cc585..a1d2ea8 100644 --- a/backend/app/api/routers/events.py +++ b/backend/app/api/routers/events.py @@ -20,9 +20,9 @@ class CallbackEvent(BaseModel): if self.payload: nodes = self.payload.get("nodes") if nodes: - msg = f"Retrieved {len(nodes)} sources to use as context for the query" + msg = f"根据查询检索到 {len(nodes)} 源文件" else: - msg = f"Retrieving context for query: '{self.payload.get('query_str')}'" + msg = f"查询检索中: '{self.payload.get('query_str')}'" return { "type": "events", "data": {"title": msg}, @@ -37,7 +37,7 @@ class CallbackEvent(BaseModel): return { "type": "events", "data": { - "title": f"Calling tool: {tool.name} with inputs: {func_call_args}", + "title": f"调用工具 {tool.name} ,参数: {func_call_args}", }, } @@ -87,7 +87,7 @@ class CallbackEvent(BaseModel): case _: return None except Exception as e: - logger.error(f"Error in converting event to response: {e}") + logger.error(f"转换回应时间时发生错误,原因: {e}") return None diff --git a/backend/app/api/routers/models.py b/backend/app/api/routers/models.py index c9ea1ad..17a86b5 100644 --- a/backend/app/api/routers/models.py +++ b/backend/app/api/routers/models.py @@ -173,12 +173,12 @@ class SourceNodes(BaseModel): def from_source_node(cls, source_node: NodeWithScore): metadata = source_node.node.metadata url = cls.get_url_from_metadata(metadata) - + text = 'filename' in metadata and metadata['filename'] or source_node.node.node_id return cls( id=source_node.node.node_id, metadata=metadata, score=source_node.score, - text=source_node.node.text, # type: ignore + text=text, # type: ignore url=url, ) diff --git a/backend/app/engine/__init__.py b/backend/app/engine/__init__.py index fb8d410..def5e51 100644 --- a/backend/app/engine/__init__.py +++ b/backend/app/engine/__init__.py @@ -1,24 +1,67 @@ import os + +from llama_index.core import SQLDatabase, SummaryIndex, VectorStoreIndex +from llama_index.core.indices.struct_store import SQLTableRetrieverQueryEngine +from llama_index.core.objects import SQLTableNodeMapping, ObjectIndex from llama_index.core.settings import Settings -from llama_index.core.agent import AgentRunner +from llama_index.core.agent import AgentRunner, StructuredPlannerAgent, FunctionCallingAgentWorker from llama_index.core.tools.query_engine import QueryEngineTool +from sqlalchemy import create_engine, Engine + +from app.engine.loaders.db import makeDescriptionByEngine from app.engine.tools import ToolFactory from app.engine.index import get_index +sql_database = None +sql_obj_index = None def get_chat_engine(filters=None, params=None): system_prompt = os.getenv("SYSTEM_PROMPT") - top_k = os.getenv("TOP_K", "3") + top_k = int(os.getenv("TOP_K", "3")) tools = [] + global sql_obj_index + global sql_database + if sql_obj_index is None: + sqlengine = create_engine(os.getenv("SQL_DATABASE_URL", "")) + sql_database = SQLDatabase(sqlengine) + table_schema_objs = makeDescriptionByEngine(sql_database) + table_node_mapping = SQLTableNodeMapping(sql_database) + + sql_obj_index = ObjectIndex.from_objects( + table_schema_objs, + table_node_mapping, + index_cls=VectorStoreIndex, + ) + + # 创建SQL查询工具 + sql_query_engine = SQLTableRetrieverQueryEngine(sql_database, + sql_obj_index.as_retriever(similarity_top_k=top_k), + verbose=True,) + sql_query_tool = QueryEngineTool.from_defaults(query_engine=sql_query_engine, + name="zjdata_query_tool", + description="来源于一个由博微公司电力造价软件编制的造价工程文件。该文件以多张表格的形式存储存储了整个工程的全部数据内容。适用于以详细的自然语言查询表格数据方式查询造价工程各项具体属性、费用的数值。请先使用“zj_query_tool”无法解决才使用本工具") + # Add query tool if index exists index = get_index() if index is not None: + summary_index = SummaryIndex(index.vector_store.get_nodes(node_ids=None)) + summary_query_engine = summary_index.as_query_engine() + summary_query_tool = QueryEngineTool.from_defaults( query_engine=summary_query_engine, name="summary_query_tool", + description="适用于任何需要进行全面总结、概括的要求。", + #description="适用于任何需要对所有内容进行全面总结的请求。有关电力造价领域更具体部分的问题,请使用zj_query_engine_tool", + ) + + # 创建向量检索查询工具 query_engine = index.as_query_engine( - similarity_top_k=int(top_k), filters=filters + similarity_top_k=top_k, filters=filters ) - query_engine_tool = QueryEngineTool.from_defaults(query_engine=query_engine) + query_engine_tool = QueryEngineTool.from_defaults(query_engine=query_engine, name="zj_query_tool", + description="由博微公司编制的关于电力造价知识、电力造价编制软件知识和造价工程文件结构的知识库。适用于查询电力领域、电力造价领域、博微、博微电力、博微造价等业务等内容。如果本知识库没有直接答案但有解决思路的可以返回解决办法后建议使用“zjdata_query_tool”工具。", + ) + tools.append(summary_query_tool) tools.append(query_engine_tool) + #tools.append(sql_query_tool) # Add additional tools tools += ToolFactory.from_env() @@ -29,3 +72,10 @@ def get_chat_engine(filters=None, params=None): system_prompt=system_prompt, verbose=True, ) + # create the function calling worker for reasoning + # worker = FunctionCallingAgentWorker.from_tools( + # tools, verbose=True + # ) + # + # # wrap the worker in the top-level planner + # return StructuredPlannerAgent(worker, tools) diff --git a/backend/app/engine/constants.py b/backend/app/engine/constants.py new file mode 100644 index 0000000..bd93bb8 --- /dev/null +++ b/backend/app/engine/constants.py @@ -0,0 +1 @@ +STORAGE_DIR = "storage" # directory to cache the generated index \ No newline at end of file diff --git a/backend/app/engine/generate.py b/backend/app/engine/generate.py index 8bcf606..115c175 100644 --- a/backend/app/engine/generate.py +++ b/backend/app/engine/generate.py @@ -2,50 +2,84 @@ from dotenv import load_dotenv load_dotenv() -import os import logging -from app.settings import init_settings -from app.engine.loaders import get_documents -from llama_index.indices.managed.llama_cloud import LlamaCloudIndex +import os +from app.engine.loaders import get_documents +from app.engine.vectordb import get_vector_store +from app.settings import init_settings +from llama_index.core.ingestion import IngestionPipeline +from llama_index.core.node_parser import SentenceSplitter +from llama_index.core.settings import Settings +from llama_index.core.storage import StorageContext +from llama_index.core.storage.docstore import SimpleDocumentStore logging.basicConfig(level=logging.INFO) logger = logging.getLogger() +STORAGE_DIR = os.getenv("STORAGE_DIR", "storage") + + +def get_doc_store(): + + # If the storage directory is there, load the document store from it. + # If not, set up an in-memory document store since we can't load from a directory that doesn't exist. + if os.path.exists(STORAGE_DIR): + return SimpleDocumentStore.from_persist_dir(STORAGE_DIR) + else: + return SimpleDocumentStore() + + +def run_pipeline(docstore, vector_store, documents): + pipeline = IngestionPipeline( + transformations=[ + SentenceSplitter( + chunk_size=Settings.chunk_size, + chunk_overlap=Settings.chunk_overlap, + ), + Settings.embed_model, + ], + docstore=docstore, + docstore_strategy="upserts_and_delete", + vector_store=vector_store, + ) + + # Run the ingestion pipeline and store the results + nodes = pipeline.run(show_progress=True, documents=documents) + + return nodes + + +def persist_storage(docstore, vector_store): + storage_context = StorageContext.from_defaults( + docstore=docstore, + vector_store=vector_store, + ) + storage_context.persist(STORAGE_DIR) + def generate_datasource(): init_settings() logger.info("Generate index for the provided data") - name = os.getenv("LLAMA_CLOUD_INDEX_NAME") - project_name = os.getenv("LLAMA_CLOUD_PROJECT_NAME") - api_key = os.getenv("LLAMA_CLOUD_API_KEY") - base_url = os.getenv("LLAMA_CLOUD_BASE_URL") - organization_id = os.getenv("LLAMA_CLOUD_ORGANIZATION_ID") - - if name is None or project_name is None or api_key is None: - raise ValueError( - "Please set LLAMA_CLOUD_INDEX_NAME, LLAMA_CLOUD_PROJECT_NAME and LLAMA_CLOUD_API_KEY" - " to your environment variables or config them in .env file" - ) - + # Get the stores and documents or create new ones documents = get_documents() - # Set private=false to mark the document as public (required for filtering) for doc in documents: doc.metadata["private"] = "false" + docstore = get_doc_store() + vector_store = get_vector_store() - LlamaCloudIndex.from_documents( - documents=documents, - name=name, - project_name=project_name, - api_key=api_key, - base_url=base_url, - organization_id=organization_id - ) + # Run the ingestion pipeline + _ = run_pipeline(docstore, vector_store, documents) + + # Build the index and persist storage + persist_storage(docstore, vector_store) logger.info("Finished generating the index") if __name__ == "__main__": - generate_datasource() + from phoenix.trace import using_project + with using_project(os.getenv("PHOENIX_PROJECT_NAME") + "_generate") as obj: + generate_datasource() diff --git a/backend/app/engine/index.py b/backend/app/engine/index.py index e54e8ca..b21e695 100644 --- a/backend/app/engine/index.py +++ b/backend/app/engine/index.py @@ -1,31 +1,22 @@ import logging -import os -from llama_index.indices.managed.llama_cloud import LlamaCloudIndex +from llama_index.core.indices import VectorStoreIndex +from app.engine.vectordb import get_vector_store logger = logging.getLogger("uvicorn") +index = None + def get_index(params=None): - configParams = params or {} - pipelineConfig = configParams.get("llamaCloudPipeline", {}) - name = pipelineConfig.get("pipeline", os.getenv("LLAMA_CLOUD_INDEX_NAME")) - project_name = pipelineConfig.get("project", os.getenv("LLAMA_CLOUD_PROJECT_NAME")) - api_key = os.getenv("LLAMA_CLOUD_API_KEY") - base_url = os.getenv("LLAMA_CLOUD_BASE_URL") - organization_id = os.getenv("LLAMA_CLOUD_ORGANIZATION_ID") + global index + if index is None: + logger.info("Connecting vector store...") - if name is None or project_name is None or api_key is None: - raise ValueError( - "Please set LLAMA_CLOUD_INDEX_NAME, LLAMA_CLOUD_PROJECT_NAME and LLAMA_CLOUD_API_KEY" - " to your environment variables or config them in .env file" - ) - - index = LlamaCloudIndex( - name=name, - project_name=project_name, - api_key=api_key, - base_url=base_url, - organization_id=organization_id - ) + store = get_vector_store() + # Load the index from the vector store + # If you are using a vector store that doesn't store text, + # you must load the index from both the vector store and the document store + index = VectorStoreIndex.from_vector_store(store) + logger.info("Finished load index from vector store.") return index diff --git a/backend/app/engine/loaders/__init__.py b/backend/app/engine/loaders/__init__.py index 4a278a4..a220170 100644 --- a/backend/app/engine/loaders/__init__.py +++ b/backend/app/engine/loaders/__init__.py @@ -17,19 +17,22 @@ def load_configs(): def get_documents(): documents = [] config = load_configs() + if config is None or len(config.items()) == 0: + return documents + for loader_type, loader_config in config.items(): logger.info( f"Loading documents from loader: {loader_type}, config: {loader_config}" ) + + loader_config = loader_config or [] match loader_type: case "file": document = get_file_documents(FileLoaderConfig(**loader_config)) case "web": document = get_web_documents(WebLoaderConfig(**loader_config)) case "db": - document = get_db_documents( - configs=[DBLoaderConfig(**cfg) for cfg in loader_config] - ) + document = get_db_documents(configs=[DBLoaderConfig(**cfg) for cfg in loader_config]) case _: raise ValueError(f"Invalid loader type: {loader_type}") documents.extend(document) diff --git a/backend/app/engine/loaders/db.py b/backend/app/engine/loaders/db.py index d5c9ffd..69d3279 100644 --- a/backend/app/engine/loaders/db.py +++ b/backend/app/engine/loaders/db.py @@ -1,26 +1,187 @@ import os import logging from typing import List +from typing import Any, List, Optional + +from llama_index.core.readers.base import BaseReader +from llama_index.core.schema import Document +from llama_index.core.utilities.sql_wrapper import SQLDatabase +from sqlalchemy import text +from sqlalchemy.engine import Engine +from llama_index.core import SQLDatabase, Document +from llama_index.core.objects import SQLTableSchema, SQLTableNodeMapping +from llama_index.core.readers.base import BaseReader +from llama_index.readers.database import DatabaseReader from pydantic import BaseModel, validator from llama_index.core.indices.vector_store import VectorStoreIndex +from sqlalchemy import create_engine logger = logging.getLogger(__name__) +class CustomDatabaseReader(BaseReader): + """Simple Database reader. + + Concatenates each row into Document used by LlamaIndex. + + Args: + sql_database (Optional[SQLDatabase]): SQL database to use, + including table names to specify. + See :ref:`Ref-Struct-Store` for more details. + + OR + + engine (Optional[Engine]): SQLAlchemy Engine object of the database connection. + + OR + + uri (Optional[str]): uri of the database connection. + + OR + + scheme (Optional[str]): scheme of the database connection. + host (Optional[str]): host of the database connection. + port (Optional[int]): port of the database connection. + user (Optional[str]): user of the database connection. + password (Optional[str]): password of the database connection. + dbname (Optional[str]): dbname of the database connection. + + Returns: + DatabaseReader: A DatabaseReader object. + """ + + def __init__( + self, + sql_database: Optional[SQLDatabase] = None, + engine: Optional[Engine] = None, + uri: Optional[str] = None, + scheme: Optional[str] = None, + host: Optional[str] = None, + port: Optional[str] = None, + user: Optional[str] = None, + password: Optional[str] = None, + dbname: Optional[str] = None, + *args: Any, + **kwargs: Any, + ) -> None: + """Initialize with parameters.""" + if sql_database: + self.sql_database = sql_database + elif engine: + self.sql_database = SQLDatabase(engine, *args, **kwargs) + elif uri: + self.uri = uri + self.sql_database = SQLDatabase.from_uri(uri, *args, **kwargs) + elif scheme and host and port and user and password and dbname: + uri = f"{scheme}://{user}:{password}@{host}:{port}/{dbname}" + self.uri = uri + self.sql_database = SQLDatabase.from_uri(uri, *args, **kwargs) + else: + raise ValueError( + "You must provide either a SQLDatabase, " + "a SQL Alchemy Engine, a valid connection URI, or a valid " + "set of credentials." + ) + + def load_data(self, query: str) -> List[Document]: + """Query and load data from the Database, returning a list of Documents. + + Args: + query (str): Query parameter to filter tables and rows. + + Returns: + List[Document]: A list of Document objects. + """ + dco_str = "" + with self.sql_database.engine.connect() as connection: + if query is None: + raise ValueError("A query parameter is necessary to filter the data") + else: + result = connection.execute(text(query)) + + dco_str = ", ".join( + [f"{entry}" for entry in result.keys()] + ) + + for item in result.fetchall(): + # fetch each item + record_str = ", ".join( + [f"{entry}" for col, entry in zip(result.keys(), item)] + ) + dco_str += record_str + "\n" + + doc = Document(text=dco_str) + doc.metadata["name"] = query + doc.metadata["context"] = query + doc.metadata["file_type"] = "application/vnd.ms-excel" + return [doc] class DBLoaderConfig(BaseModel): uri: str queries: List[str] +def makeDescriptionByEngine(sql_database:SQLDatabase): + reader = DatabaseReader(sql_database) + + table_names = sql_database.get_usable_table_names() + table_schema_objs = [] + for table_name in table_names: + columns = sql_database.get_table_columns(table_name) + if len(columns) > 150: + continue + stats_txt = "" + + if table_name == 'gongchengshuxing': + stats_txt = '该表中有以下属性:' + documents = reader.load_data(query='select name from gongchengshuxing') + for index in range(len(documents) if len(documents) < 30 else 30): + if index == 0: + continue + elif index > 1: + stats_txt += ',' + stats_txt += documents[index].text.split(':')[1] + + tbSchema = (SQLTableSchema(table_name=table_name, context_str=stats_txt)) + table_schema_objs.append(tbSchema) + + return table_schema_objs def get_db_documents(configs: list[DBLoaderConfig]): - from llama_index.readers.database import DatabaseReader - docs = [] + + if len(configs) == 0 or configs[0].uri == "": + logger.warning( + f"Failed to load database, error message: uri is empty. Return as empty document list." + ) + return docs + + metadata = { + #'file_name':'', + 'file_type':'application/booway.document.zj', + #'file_path':'', + #'file_size':'', + #'creation_date':'', + #'last_modified_date':'', + } + + #from llama_index.readers.database import DatabaseReader for entry in configs: - loader = DatabaseReader(uri=entry.uri) - for query in entry.queries: + engine = create_engine(entry.uri) + sql_database = SQLDatabase(engine) + + table_schema_objs = makeDescriptionByEngine(sql_database) + table_node_mapping = SQLTableNodeMapping(sql_database) + + nodes = table_node_mapping.to_nodes(table_schema_objs) + for node in nodes: + node.metadata.update(metadata) + + docs.extend(nodes) + + queries = entry.queries or [] + loader = CustomDatabaseReader(sql_database) + for query in queries: logger.info(f"Loading data from database with query: {query}") documents = loader.load_data(query=query) - docs.extend(documents) - return documents + docs.extend(documents) + return docs diff --git a/backend/app/engine/loaders/file.py b/backend/app/engine/loaders/file.py index 4dea4f8..1db99ce 100644 --- a/backend/app/engine/loaders/file.py +++ b/backend/app/engine/loaders/file.py @@ -1,6 +1,9 @@ import os import logging from typing import Dict + +from llama_index.core.readers.base import BaseReader +from llama_index.core.readers.json import JSONReader from llama_parse import LlamaParse from pydantic import BaseModel, validator @@ -39,6 +42,9 @@ def llama_parse_extractor() -> Dict[str, LlamaParse]: parser = llama_parse_parser() return {file_type: parser for file_type in SUPPORTED_FILE_TYPES} +def llama_local_extractor() -> Dict[str, BaseReader]: + return {"json" : JSONReader} + def get_file_documents(config: FileLoaderConfig): from llama_index.core.readers import SimpleDirectoryReader @@ -53,6 +59,9 @@ def get_file_documents(config: FileLoaderConfig): nest_asyncio.apply() file_extractor = llama_parse_extractor() + else: + file_extractor = llama_local_extractor() + reader = SimpleDirectoryReader( config.data_dir, recursive=True, diff --git a/backend/app/engine/loaders/web.py b/backend/app/engine/loaders/web.py index 563e51b..e667a69 100644 --- a/backend/app/engine/loaders/web.py +++ b/backend/app/engine/loaders/web.py @@ -11,7 +11,7 @@ class CrawlUrl(BaseModel): class WebLoaderConfig(BaseModel): driver_arguments: list[str] = Field(default=None) - urls: list[CrawlUrl] + urls: list[CrawlUrl] = [] def get_web_documents(config: WebLoaderConfig): @@ -25,6 +25,7 @@ def get_web_documents(config: WebLoaderConfig): options.add_argument(arg) docs = [] + urls = config.urls or [] for url in config.urls: scraper = WholeSiteReader( prefix=url.prefix, diff --git a/backend/app/engine/tools/__init__.py b/backend/app/engine/tools/__init__.py index 111bee5..1aced70 100644 --- a/backend/app/engine/tools/__init__.py +++ b/backend/app/engine/tools/__init__.py @@ -48,9 +48,13 @@ class ToolFactory: if os.path.exists("config/tools.yaml"): with open("config/tools.yaml", "r") as f: tool_configs = yaml.safe_load(f) - for tool_type, config_entries in tool_configs.items(): - for tool_name, config in config_entries.items(): - tools.extend( - ToolFactory.load_tools(tool_type, tool_name, config) - ) + if tool_configs != None and len(tool_configs.items()) != 0: + for tool_type, config_entries in tool_configs.items(): + if config_entries == None or len(config_entries.items()) == 0: + continue + + for tool_name, config in config_entries.items(): + tools.extend( + ToolFactory.load_tools(tool_type, tool_name, config) + ) return tools diff --git a/backend/app/engine/vectordb.py b/backend/app/engine/vectordb.py new file mode 100644 index 0000000..f3f2a7d --- /dev/null +++ b/backend/app/engine/vectordb.py @@ -0,0 +1,71 @@ +import os +from llama_index.vector_stores.chroma import ChromaVectorStore +from llama_index.vector_stores.qdrant import QdrantVectorStore +from qdrant_client import qdrant_client + +qclient = None + +def get_qdrant_vector_store(): + collection_name = os.getenv("VECTOR_STORE_COLLECTION", "default") + vector_store_path = os.getenv("VECTOR_STORE_PATH") + host=os.getenv("VECTOR_STORE_HOST", "127.0.0.1"), + port=int(os.getenv("VECTOR_STORE_PORT", "6333")), + + if not vector_store_path or not host: + raise ValueError( + "Please provide either VECTOR_STORE_PATH or VECTOR_STORE_HOST and VECTOR_STORE_PORT" + ) + # if VECTOR_STORE_PATH is set, use a local QdrantVectorStore from the path + # otherwise, use a remote QdrantVectorStore + global qclient + if qclient == None: + if vector_store_path: + qclient = qdrant_client.QdrantClient( + path=vector_store_path, + ) + else: + qclient = qdrant_client.QdrantClient( + host=host, + port=port, + ) + + vector_store = QdrantVectorStore(client=qclient, collection_name=collection_name) + return vector_store + +def get_chroma_vector_store(): + collection_name = os.getenv("VECTOR_STORE_COLLECTION", "default") + vector_store_path = os.getenv("VECTOR_STORE_PATH") + # if VECTOR_STORE_PATH is set, use a local ChromaVectorStore from the path + # otherwise, use a remote ChromaVectorStore (ChromaDB Cloud is not supported yet) + if vector_store_path: + store = ChromaVectorStore.from_params( + persist_dir=vector_store_path, collection_name=collection_name, + collection_kwargs={"metadata":{"hnsw:space":"cosine"}}, + ) + else: + if not os.getenv("VECTOR_STORE_HOST") or not os.getenv("VECTOR_STORE_PORT"): + raise ValueError( + "Please provide either VECTOR_STORE_PATH or VECTOR_STORE_HOST and VECTOR_STORE_PORT" + ) + store = ChromaVectorStore.from_params( + host=os.getenv("VECTOR_STORE_HOST"), + port=int(os.getenv("VECTOR_STORE_PORT")), + collection_name=collection_name, + collection_kwargs={"metadata":{"hnsw:space":"cosine"}}, + ) + return store + +def get_vector_store(): + store_type=os.getenv("VECTOR_STORE_TYPE") + + store = None + + match store_type: + case "chroma": + store = get_chroma_vector_store() + case "qdrant": + store = get_qdrant_vector_store() + case _: + raise ValueError(f"Invalid vector store type: {store_type}") + + return store \ No newline at end of file diff --git a/backend/app/observability.py b/backend/app/observability.py index 28019c3..780ae04 100644 --- a/backend/app/observability.py +++ b/backend/app/observability.py @@ -1,2 +1,20 @@ +import os + +import llama_index.core + def init_observability(): - pass + + PHOENIX_API_KEY = os.getenv("PHOENIX_API_KEY") + if not PHOENIX_API_KEY: + raise ValueError("PHOENIX_API_KEY environment variable is not set") + os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"api_key={PHOENIX_API_KEY}" + PHOENIX_URL = os.getenv("PHOENIX_URL") + llama_index.core.set_global_handler( + "arize_phoenix", endpoint=PHOENIX_URL, eval_params={} + ) + + #debugHandle=[] + # llama_debug = LlamaDebugHandler(print_trace_on_end=True) + # debugHandle.append(llama_debug) + # callback_manager = CallbackManager(debugHandle) + # settings.Settings.callback_manager = callback_manager diff --git a/backend/app/settings.py b/backend/app/settings.py index b723bf3..0158074 100644 --- a/backend/app/settings.py +++ b/backend/app/settings.py @@ -1,6 +1,7 @@ import os from typing import Dict +from llama_index.core.constants import DEFAULT_TEMPERATURE from llama_index.core.settings import Settings @@ -9,6 +10,8 @@ def init_settings(): match model_provider: case "openai": init_openai() + case "dashscope": + init_dashscope() case "groq": init_groq() case "ollama": @@ -33,20 +36,21 @@ def init_settings(): def init_ollama(): - from llama_index.embeddings.ollama import OllamaEmbedding - from llama_index.llms.ollama.base import DEFAULT_REQUEST_TIMEOUT, Ollama - - base_url = os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434" - request_timeout = float( - os.getenv("OLLAMA_REQUEST_TIMEOUT", DEFAULT_REQUEST_TIMEOUT) - ) - Settings.embed_model = OllamaEmbedding( - base_url=base_url, - model_name=os.getenv("EMBEDDING_MODEL"), - ) - Settings.llm = Ollama( - base_url=base_url, model=os.getenv("MODEL"), request_timeout=request_timeout - ) + # from llama_index.embeddings.ollama import OllamaEmbedding + # from llama_index.llms.ollama.base import DEFAULT_REQUEST_TIMEOUT, Ollama + # + # base_url = os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434" + # request_timeout = float( + # os.getenv("OLLAMA_REQUEST_TIMEOUT", DEFAULT_REQUEST_TIMEOUT) + # ) + # Settings.embed_model = OllamaEmbedding( + # base_url=base_url, + # model_name=os.getenv("EMBEDDING_MODEL"), + # ) + # Settings.llm = Ollama( + # base_url=base_url, model=os.getenv("MODEL"), request_timeout=request_timeout + # ) + pass def init_openai(): @@ -69,104 +73,129 @@ def init_openai(): } Settings.embed_model = OpenAIEmbedding(**config) +def init_dashscope(): + from llama_index.llms.dashscope import DashScope,DashScopeGenerationModels + from llama_index.embeddings.dashscope import DashScopeEmbedding,DashScopeBatchTextEmbeddingModels,DashScopeTextEmbeddingType,DashScopeTextEmbeddingModels + + max_tokens = os.getenv("LLM_MAX_TOKENS") + config = { + "model": os.getenv("MODEL"), + "temperature": float(os.getenv("LLM_TEMPERATURE", DEFAULT_TEMPERATURE)), + "max_tokens": int(max_tokens) if max_tokens is not None else None, + } + Settings.llm = llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX) + + dimensions = os.getenv("EMBEDDING_DIM") + config = { + "model": os.getenv("EMBEDDING_MODEL"), + "dimensions": int(dimensions) if dimensions is not None else None, + } + Settings.embed_model = DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2, + text_type=DashScopeTextEmbeddingType.TEXT_TYPE_QUERY) + def init_azure_openai(): - from llama_index.core.constants import DEFAULT_TEMPERATURE - from llama_index.embeddings.azure_openai import AzureOpenAIEmbedding - from llama_index.llms.azure_openai import AzureOpenAI - - llm_deployment = os.environ["AZURE_OPENAI_LLM_DEPLOYMENT"] - embedding_deployment = os.environ["AZURE_OPENAI_EMBEDDING_DEPLOYMENT"] - max_tokens = os.getenv("LLM_MAX_TOKENS") - temperature = os.getenv("LLM_TEMPERATURE", DEFAULT_TEMPERATURE) - dimensions = os.getenv("EMBEDDING_DIM") - - azure_config = { - "api_key": os.environ["AZURE_OPENAI_KEY"], - "azure_endpoint": os.environ["AZURE_OPENAI_ENDPOINT"], - "api_version": os.getenv("AZURE_OPENAI_API_VERSION") - or os.getenv("OPENAI_API_VERSION"), - } - - Settings.llm = AzureOpenAI( - model=os.getenv("MODEL"), - max_tokens=int(max_tokens) if max_tokens is not None else None, - temperature=float(temperature), - deployment_name=llm_deployment, - **azure_config, - ) - - Settings.embed_model = AzureOpenAIEmbedding( - model=os.getenv("EMBEDDING_MODEL"), - dimensions=int(dimensions) if dimensions is not None else None, - deployment_name=embedding_deployment, - **azure_config, - ) + # from llama_index.core.constants import DEFAULT_TEMPERATURE + # from llama_index.embeddings.azure_openai import AzureOpenAIEmbedding + # from llama_index.llms.azure_openai import AzureOpenAI + # + # llm_deployment = os.environ["AZURE_OPENAI_LLM_DEPLOYMENT"] + # embedding_deployment = os.environ["AZURE_OPENAI_EMBEDDING_DEPLOYMENT"] + # max_tokens = os.getenv("LLM_MAX_TOKENS") + # temperature = os.getenv("LLM_TEMPERATURE", DEFAULT_TEMPERATURE) + # dimensions = os.getenv("EMBEDDING_DIM") + # + # azure_config = { + # "api_key": os.environ["AZURE_OPENAI_KEY"], + # "azure_endpoint": os.environ["AZURE_OPENAI_ENDPOINT"], + # "api_version": os.getenv("AZURE_OPENAI_API_VERSION") + # or os.getenv("OPENAI_API_VERSION"), + # } + # + # Settings.llm = AzureOpenAI( + # model=os.getenv("MODEL"), + # max_tokens=int(max_tokens) if max_tokens is not None else None, + # temperature=float(temperature), + # deployment_name=llm_deployment, + # **azure_config, + # ) + # + # Settings.embed_model = AzureOpenAIEmbedding( + # model=os.getenv("EMBEDDING_MODEL"), + # dimensions=int(dimensions) if dimensions is not None else None, + # deployment_name=embedding_deployment, + # **azure_config, + # ) + pass def init_fastembed(): """ Use Qdrant Fastembed as the local embedding provider. """ - from llama_index.embeddings.fastembed import FastEmbedEmbedding - - embed_model_map: Dict[str, str] = { - # Small and multilingual - "all-MiniLM-L6-v2": "sentence-transformers/all-MiniLM-L6-v2", - # Large and multilingual - "paraphrase-multilingual-mpnet-base-v2": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", # noqa: E501 - } - - # This will download the model automatically if it is not already downloaded - Settings.embed_model = FastEmbedEmbedding( - model_name=embed_model_map[os.getenv("EMBEDDING_MODEL")] - ) + # from llama_index.embeddings.fastembed import FastEmbedEmbedding + # + # embed_model_map: Dict[str, str] = { + # # Small and multilingual + # "all-MiniLM-L6-v2": "sentence-transformers/all-MiniLM-L6-v2", + # # Large and multilingual + # "paraphrase-multilingual-mpnet-base-v2": "sentence-transformers/paraphrase-multilingual-mpnet-base-v2", # noqa: E501 + # } + # + # # This will download the model automatically if it is not already downloaded + # Settings.embed_model = FastEmbedEmbedding( + # model_name=embed_model_map[os.getenv("EMBEDDING_MODEL")] + # ) + pass def init_groq(): - from llama_index.llms.groq import Groq - - model_map: Dict[str, str] = { - "llama3-8b": "llama3-8b-8192", - "llama3-70b": "llama3-70b-8192", - "mixtral-8x7b": "mixtral-8x7b-32768", - } - - Settings.llm = Groq(model=model_map[os.getenv("MODEL")]) - # Groq does not provide embeddings, so we use FastEmbed instead - init_fastembed() + # from llama_index.llms.groq import Groq + # + # model_map: Dict[str, str] = { + # "llama3-8b": "llama3-8b-8192", + # "llama3-70b": "llama3-70b-8192", + # "mixtral-8x7b": "mixtral-8x7b-32768", + # } + # + # Settings.llm = Groq(model=model_map[os.getenv("MODEL")]) + # # Groq does not provide embeddings, so we use FastEmbed instead + # init_fastembed() + pass def init_anthropic(): - from llama_index.llms.anthropic import Anthropic - - model_map: Dict[str, str] = { - "claude-3-opus": "claude-3-opus-20240229", - "claude-3-sonnet": "claude-3-sonnet-20240229", - "claude-3-haiku": "claude-3-haiku-20240307", - "claude-2.1": "claude-2.1", - "claude-instant-1.2": "claude-instant-1.2", - } - - Settings.llm = Anthropic(model=model_map[os.getenv("MODEL")]) - # Anthropic does not provide embeddings, so we use FastEmbed instead - init_fastembed() + # from llama_index.llms.anthropic import Anthropic + # + # model_map: Dict[str, str] = { + # "claude-3-opus": "claude-3-opus-20240229", + # "claude-3-sonnet": "claude-3-sonnet-20240229", + # "claude-3-haiku": "claude-3-haiku-20240307", + # "claude-2.1": "claude-2.1", + # "claude-instant-1.2": "claude-instant-1.2", + # } + # + # Settings.llm = Anthropic(model=model_map[os.getenv("MODEL")]) + # # Anthropic does not provide embeddings, so we use FastEmbed instead + # init_fastembed() + pass def init_gemini(): - from llama_index.embeddings.gemini import GeminiEmbedding - from llama_index.llms.gemini import Gemini - - model_name = f"models/{os.getenv('MODEL')}" - embed_model_name = f"models/{os.getenv('EMBEDDING_MODEL')}" - - Settings.llm = Gemini(model=model_name) - Settings.embed_model = GeminiEmbedding(model_name=embed_model_name) - + # from llama_index.embeddings.gemini import GeminiEmbedding + # from llama_index.llms.gemini import Gemini + # + # model_name = f"models/{os.getenv('MODEL')}" + # embed_model_name = f"models/{os.getenv('EMBEDDING_MODEL')}" + # + # Settings.llm = Gemini(model=model_name) + # Settings.embed_model = GeminiEmbedding(model_name=embed_model_name) + pass def init_mistral(): - from llama_index.embeddings.mistralai import MistralAIEmbedding - from llama_index.llms.mistralai import MistralAI - - Settings.llm = MistralAI(model=os.getenv("MODEL")) - Settings.embed_model = MistralAIEmbedding(model_name=os.getenv("EMBEDDING_MODEL")) + # from llama_index.embeddings.mistralai import MistralAIEmbedding + # from llama_index.llms.mistralai import MistralAI + # + # Settings.llm = MistralAI(model=os.getenv("MODEL")) + # Settings.embed_model = MistralAIEmbedding(model_name=os.getenv("EMBEDDING_MODEL")) + pass \ No newline at end of file diff --git a/backend/config/loaders.yaml b/backend/config/loaders.yaml index d746c61..1d4fdba 100644 --- a/backend/config/loaders.yaml +++ b/backend/config/loaders.yaml @@ -1,10 +1,30 @@ file: # use_llama_parse: Use LlamaParse if `true`. Needs a `LLAMA_CLOUD_API_KEY` from https://cloud.llamaindex.ai set as environment variable - use_llama_parse: true + use_llama_parse: false + db: # The configuration for the database loader, only supports MySQL and PostgreSQL databases for now. # uri: The URI for the database. E.g.: mysql+pymysql://user:password@localhost:3306/db or postgresql+psycopg2://user:password@localhost:5432/db # query: The query to fetch data from the database. E.g.: SELECT * FROM table - uri: mysql+pymysql://zjinfo1:Dy2Bcr53Hm5xRkba@110.42.234.166:3306/zjinfo1 + #- uri: mysql+pymysql://zjinfo:Y6EAjEEdSYmskA8B@110.42.234.166:3306/zjinfo +# - uri: mysql+pymysql://zjinfo2:GSKcziSdBixDXwcd@110.42.234.166:3306/zjinfo2 queries: - - SELECT * FROM mytable + - select * from ProjectProperties limit 30; + - select Name, Code, Amount, Amount_Total from TotalCalculateTable + - select SerialNumber, Name, Quantity, Rate, Sum_Price from ProjectDivision where Level = 1 limit 30; + - select Name, Code, Rate, Amount from OtherFee + +#web: +# driver_arguments: +# # The arguments to pass to the webdriver. E.g.: add --headless to run in headless mode +# - --no-sandbox +# - --disable-dev-shm-usage +# urls: +# # base_url: The URL to start crawling with +# # prefix: Only crawl URLs matching the specified prefix +# # depth: The maximum depth for BFS traversal +# # You can add more websites by adding more entries (don't forget the - prefix from YAML) +# - base_url: https://www.llamaindex.ai +# prefix: https://www.llamaindex.ai +# depth: 1 \ No newline at end of file diff --git a/backend/config/tools.yaml b/backend/config/tools.yaml index df5690c..1cde4f4 100644 --- a/backend/config/tools.yaml +++ b/backend/config/tools.yaml @@ -1,4 +1,5 @@ local: - weather: {} - interpreter: {} + #weather: {} + #interpreter: {} + #duckduckgo: {} llamahub: {} diff --git a/backend/data/101.pdf b/backend/data/101.pdf deleted file mode 100644 index ae5acffd5398b7c59e2df9e6dead2d99128b719c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47931 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 bcdU%O6xdJmhYmmoXuQlW(3iVW0O0=tvLaX5 literal 0 HcmV?d00001 diff --git a/backend/data/工程造价基础知识.doc b/backend/data/工程造价基础知识.doc new file mode 100644 index 0000000000000000000000000000000000000000..27d4d9fe1883bb8f3090d91ce3ee7b9f018aac79 GIT binary patch literal 141312 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

72xxbTQXe{r)37BM{ga_)rgrq>k)MaSZm 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 literal 0 HcmV?d00001 diff --git a/backend/data/电力造价知识.docx b/backend/data/电力造价知识.docx new file mode 100644 index 0000000000000000000000000000000000000000..45ff225cb47f38f38b88f4b2a9816e7a84bdfadc GIT binary patch literal 17041 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 literal 0 HcmV?d00001 diff --git a/backend/main.py b/backend/main.py index a72745e..cbf7969 100644 --- a/backend/main.py +++ b/backend/main.py @@ -15,50 +15,53 @@ from app.observability import init_observability from fastapi.staticfiles import StaticFiles -app = FastAPI() - -init_settings() -init_observability() - -environment = os.getenv("ENVIRONMENT", "dev") # Default to 'development' if not set logger = logging.getLogger("uvicorn") +app = None -if environment == "dev": - logger.warning("Running in development mode - allowing CORS for all origins") - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) +def init_webserver(): + global app + app = FastAPI() + environment = os.getenv("ENVIRONMENT", "dev") # Default to 'development' if not set + if environment == "dev": + logger.warning("Running in development mode - allowing CORS for all origins") + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + def mount_static_files(directory, path): + if os.path.exists(directory): + for dir, _, _ in os.walk(directory): + relative_path = os.path.relpath(dir, directory) + mount_path = path if relative_path == "." else f"{path}/{relative_path}" + logger.info(f"Mounting static files '{dir}' at {mount_path}") + app.mount(mount_path, StaticFiles(directory=dir), name=f"{dir}-static") + + # Mount the data files to serve the file viewer + mount_static_files("data", "/api/files/data") + # Mount the output files from tools + mount_static_files("data_output", "/api/files/output") + app.include_router(chat_router, prefix="/api/chat") + app.include_router(file_upload_router, prefix="/api/chat/upload") # Redirect to documentation page when accessing base URL @app.get("/") async def redirect_to_docs(): return RedirectResponse(url="/docs") - -def mount_static_files(directory, path): - if os.path.exists(directory): - for dir, _, _ in os.walk(directory): - relative_path = os.path.relpath(dir, directory) - mount_path = path if relative_path == "." else f"{path}/{relative_path}" - logger.info(f"Mounting static files '{dir}' at {mount_path}") - app.mount(mount_path, StaticFiles(directory=dir), name=f"{dir}-static") - - -# Mount the data files to serve the file viewer -mount_static_files("data", "/api/files/data") -# Mount the output files from tools -mount_static_files("output", "/api/files/output") - -app.include_router(chat_router, prefix="/api/chat") -app.include_router(file_upload_router, prefix="/api/chat/upload") - if __name__ == "__main__": - app_host = os.getenv("APP_HOST", "0.0.0.0") - app_port = int(os.getenv("APP_PORT", "8000")) - reload = True if environment == "dev" else False + from phoenix.trace import using_project + with using_project(os.getenv("PHOENIX_PROJECT_NAME")) as obj: - uvicorn.run(app="main:app", host=app_host, port=app_port, reload=reload) + init_settings() + init_observability() + init_webserver() + + app_host = os.getenv("APP_HOST", "0.0.0.0") + app_port = int(os.getenv("APP_PORT", "8000")) + #reload = True if environment == "dev" else False + reload = False + uvicorn.run(app=app, host=app_host, port=app_port, reload=reload) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 21715b3..527c0ef 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -11,11 +11,25 @@ generate = "app.engine.generate:generate_datasource" [tool.poetry.dependencies] python = "^3.11,<3.12" -fastapi = "^0.109.1" +fastapi = "^0.112.0" python-dotenv = "^1.0.0" -aiostream = "^0.5.2" -llama-index = "0.10.58" +aiostream = "^0.6.2" +llama-index = "0.10.63" cachetools = "^5.3.3" +protobuf = "4.25.4" +#arize-phoenix = "^4.12.0" +openinference-instrumentation-llama-index="2.2.3" +llama-index-callbacks-arize-phoenix = "^0.1.4" +llama-index-llms-dashscope = "^0.1.2" +llama-index-embeddings-dashscope = "^0.1.4" +llama-index-postprocessor-dashscope-rerank-custom = "0.1.0" +qdrant-client="^1.10.1" +llama-index-vector-stores-qdrant = "^0.2.14" +chroma="^0.5.5" +llama-index-vector-stores-chroma = "^0.1.10" +llama-index-readers-json = "^0.1.5" + +duckduckgo_search = "^6.2.6" [tool.poetry.dependencies.uvicorn] extras = [ "standard" ] @@ -28,11 +42,11 @@ version = "^0.1.3" version = "^1.1.0" extras = [ "rsa" ] -[tool.poetry.dependencies.psycopg2] -version = "^2.9.9" +#[tool.poetry.dependencies.psycopg2] +#version = "^2.9.9" -[tool.poetry.dependencies.llama-index-indices-managed-llama-cloud] -version = "^0.2.7" +#[tool.poetry.dependencies.llama-index-indices-managed-llama-cloud] +#version = "^0.2.7" [tool.poetry.dependencies.docx2txt] version = "^0.8" @@ -40,8 +54,6 @@ version = "^0.8" [tool.poetry.dependencies.e2b_code_interpreter] version = "0.0.7" -[tool.poetry.dependencies.llama-index-agent-openai] -version = "0.2.6" [build-system] requires = [ "poetry-core" ] diff --git a/backend/run-data.bat b/backend/run-data.bat new file mode 100644 index 0000000..8314019 --- /dev/null +++ b/backend/run-data.bat @@ -0,0 +1,4 @@ +rmdir /S /Q storage_vector +rmdir /S /Q storage + +C:\Users\liuyue\AppData\Local\pypoetry\Cache\virtualenvs\app-laEO4lY0-py3.11\Scripts\python app/engine/generate.py \ No newline at end of file diff --git a/backend/run-test.bat b/backend/run-test.bat new file mode 100644 index 0000000..12114d2 --- /dev/null +++ b/backend/run-test.bat @@ -0,0 +1,4 @@ +rmdir /S /Q storage_vector +rmdir /S /Q storage + +C:\Users\liuyue\AppData\Local\pypoetry\Cache\virtualenvs\app-laEO4lY0-py3.11\Scripts\python tests/query.py \ No newline at end of file diff --git a/backend/run.bat b/backend/run.bat new file mode 100644 index 0000000..0e0df02 --- /dev/null +++ b/backend/run.bat @@ -0,0 +1 @@ +C:\Users\liuyue\AppData\Local\pypoetry\Cache\virtualenvs\app-laEO4lY0-py3.11\Scripts\python main.py \ No newline at end of file diff --git a/backend/tests/query.py b/backend/tests/query.py new file mode 100644 index 0000000..48ca304 --- /dev/null +++ b/backend/tests/query.py @@ -0,0 +1,67 @@ +import os +from ctypes import cast + +from llama_index.core import VectorStoreIndex, SQLDatabase +from llama_index.core.indices.struct_store import SQLTableRetrieverQueryEngine +from llama_index.core.objects import SQLTableNodeMapping, ObjectIndex +from llama_index.readers.database import DatabaseReader +from sqlalchemy import create_engine + +from app.api.routers.chat import generate_filters +from app.engine import get_index, makeDescriptionByEngine +from app.engine.loaders.db import CustomDatabaseReader +from app.engine.vectordb import get_vector_store +from app.observability import init_observability +from app.settings import init_settings + + +def main(): + init_settings() + init_observability() + + index = get_index() + + top_k = 5 + filters = generate_filters([]) + #question = "从工程属性表中查找工程名称" + question = "总算表中名称等于架空输电线路本体工程的金额?" + # 创建向量检索查询工具 + query_engine = index.as_query_engine( + similarity_top_k=top_k, filters=filters + ) + query_result = query_engine.query(question) + print(query_result) + + engine = create_engine(os.getenv("SQL_DATABASE_URL", "")) + sql_database = SQLDatabase(engine) + + loader = CustomDatabaseReader(sql_database) + documents = loader.load_data(query="select * from ProjectProperties") + + table_schema_objs = makeDescriptionByEngine(sql_database) + table_node_mapping = SQLTableNodeMapping(sql_database) + + vectorIndex = VectorStoreIndex() + # 创建SQL查询工具 + sql_obj_index = ObjectIndex.from_objects( + table_schema_objs, + table_node_mapping, + index_cls=VectorStoreIndex, + ) + + query_result =vectorIndex.as_query_engine( + similarity_top_k=top_k, filters=filters + ).query(question) + print(query_result) + + sql_query_engine = SQLTableRetrieverQueryEngine(sql_database, + sql_obj_index.as_retriever(similarity_top_k=1)) + sql_query_result = sql_query_engine.query(question) + print(sql_query_result) + + +if __name__ == "__main__": + from phoenix.trace import using_project + + with using_project("ly_zjapp_test") as obj: + main() \ No newline at end of file diff --git a/frontend/.env b/frontend/.env index faf27f1..d52b5f0 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1,6 +1,8 @@ # The backend API for chat endpoint. NEXT_PUBLIC_CHAT_API=http://localhost:8000/api/chat -# Let's the user change indexes in LlamaCloud projects -NEXT_PUBLIC_USE_LLAMACLOUD=true +PHOENIX_SERVER_URL=http://localhost:6006/ + +# Let's the user change indexes in LlamaCloud projects +NEXT_PUBLIC_USE_LLAMACLOUD=false diff --git a/frontend/app/components/header.tsx b/frontend/app/components/header.tsx index f02ce73..c08a67c 100644 --- a/frontend/app/components/header.tsx +++ b/frontend/app/components/header.tsx @@ -1,18 +1,20 @@ import Image from "next/image"; +const phoenixUrl = process.env.PHOENIX_SERVER_URL; + export default function Header() { return (

)}
- )}
diff --git a/frontend/app/components/ui/chat/chat-message/chat-events.tsx b/frontend/app/components/ui/chat/chat-message/chat-events.tsx index 3dfad75..4000520 100644 --- a/frontend/app/components/ui/chat/chat-message/chat-events.tsx +++ b/frontend/app/components/ui/chat/chat-message/chat-events.tsx @@ -17,7 +17,7 @@ export function ChatEvents({ }) { const [isOpen, setIsOpen] = useState(false); - const buttonLabel = isOpen ? "Hide events" : "Show events"; + const buttonLabel = isOpen ? "隐藏" : "详情"; const EventIcon = isOpen ? ( diff --git a/frontend/app/components/ui/chat/chat-message/chat-sources.tsx b/frontend/app/components/ui/chat/chat-message/chat-sources.tsx index 1d4ccb6..8736375 100644 --- a/frontend/app/components/ui/chat/chat-message/chat-sources.tsx +++ b/frontend/app/components/ui/chat/chat-message/chat-sources.tsx @@ -9,23 +9,33 @@ import { import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard"; import { SourceData } from "../index"; import PdfDialog from "../widgets/PdfDialog"; +import { useClientConfig } from "../hooks/use-config"; const SCORE_THRESHOLD = 0.3; -function SourceNumberButton({ index }: { index: number }) { +function truncateNumber(num: number | undefined, precision: number): number { + if (num == undefined || num == 0) return 0; + const factor = Math.pow(10, precision); + return Math.trunc(num * factor) / factor; +} + +function SourceNumberButton({ index, score }: { index: number, score: number | undefined }) { return ( -
- {index + 1} +
+ {truncateNumber(score, 2)}
); } type NodeInfo = { id: string; + score?: number; + text: string; url?: string; }; export function ChatSources({ data }: { data: SourceData }) { + const { backend } = useClientConfig(); const sources: NodeInfo[] = useMemo(() => { // aggregate nodes by url or file_path (get the highest one by score) const nodesByPath: { [path: string]: NodeInfo } = {}; @@ -36,6 +46,8 @@ export function ChatSources({ data }: { data: SourceData }) { .forEach((node) => { const nodeInfo = { id: node.id, + score: node.score, + text: node.text, url: node.url, }; const key = nodeInfo.url ?? nodeInfo.id; // use id as key for UNKNOWN type @@ -51,7 +63,7 @@ export function ChatSources({ data }: { data: SourceData }) { return (
- Sources: + 来源:
{sources.map((nodeInfo: NodeInfo, index: number) => { if (nodeInfo.url?.endsWith(".pdf")) { @@ -59,8 +71,8 @@ export function ChatSources({ data }: { data: SourceData }) { } + url={backend+nodeInfo.url} + trigger={} /> ); } @@ -68,9 +80,9 @@ export function ChatSources({ data }: { data: SourceData }) {
- + - + @@ -83,6 +95,7 @@ export function ChatSources({ data }: { data: SourceData }) { } function NodeInfo({ nodeInfo }: { nodeInfo: NodeInfo }) { + const { backend } = useClientConfig(); const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 1000 }); if (nodeInfo.url) { @@ -92,10 +105,10 @@ function NodeInfo({ nodeInfo }: { nodeInfo: NodeInfo }) {
- {nodeInfo.url} + {nodeInfo.text}
diff --git a/frontend/app/components/ui/chat/chat.interface.ts b/frontend/app/components/ui/chat/chat.interface.ts index 6b74d4f..5483abd 100644 --- a/frontend/app/components/ui/chat/chat.interface.ts +++ b/frontend/app/components/ui/chat/chat.interface.ts @@ -10,7 +10,7 @@ export interface ChatHandler { data?: any; }, ) => void; - handleInputChange: (e: React.ChangeEvent) => void; + handleInputChange: (e: React.ChangeEvent) => void; reload?: () => void; stop?: () => void; onFileUpload?: (file: File) => Promise; diff --git a/frontend/app/components/ui/chat/hooks/use-file.ts b/frontend/app/components/ui/chat/hooks/use-file.ts index 2c2c34b..bf1c508 100644 --- a/frontend/app/components/ui/chat/hooks/use-file.ts +++ b/frontend/app/components/ui/chat/hooks/use-file.ts @@ -63,7 +63,7 @@ export function useFile() { ...requestParams, }), }); - if (!response.ok) throw new Error("Failed to upload document."); + if (!response.ok) throw new Error("上传文件时发生错误。"); return await response.json(); }; @@ -109,7 +109,7 @@ export function useFile() { } const filetype = docMineTypeMap[file.type]; - if (!filetype) throw new Error("Unsupported document type."); + if (!filetype) throw new Error("不支持的文件类型。"); const newDoc: Omit = { id: uuidv4(), filetype, diff --git a/frontend/app/components/ui/file-uploader.tsx b/frontend/app/components/ui/file-uploader.tsx index e42a267..fe4f062 100644 --- a/frontend/app/components/ui/file-uploader.tsx +++ b/frontend/app/components/ui/file-uploader.tsx @@ -32,7 +32,7 @@ export default function FileUploader({ const allowedExtensions = config?.allowedExtensions; const defaultCheckExtension = (extension: string) => { if (allowedExtensions && !allowedExtensions.includes(extension)) { - return `Invalid file type. Please select a file with one of these formats: ${allowedExtensions!.join( + return `无效的文件类型。请选择一个以下格式的文件: ${allowedExtensions!.join( ",", )}`; } @@ -69,7 +69,7 @@ export default function FileUploader({ if (isFileSizeExceeded(file)) { return onFileUploadError( - `File size exceeded. Limit is ${fileSizeLimit / 1024 / 1024} MB`, + `文件尺寸超标。请选择不大于 ${fileSizeLimit / 1024 / 1024} MB 的文件。`, ); } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 8f7cab9..9fdbff4 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -3,11 +3,11 @@ import { Inter } from "next/font/google"; import "./globals.css"; import "./markdown.css"; -const inter = Inter({ subsets: ["latin"] }); +const inter = Inter({ subsets: ["latin", "latin-ext"] }); export const metadata: Metadata = { - title: "Create Llama App", - description: "Generated by create-llama", + title: "博微造价工程文件知识问答", + description: "博微技术中心搭建的造价工程文件知识问答", }; export default function RootLayout({ @@ -16,7 +16,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + {children} ); diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 04d4302..44625f9 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -4,7 +4,7 @@ import ChatSection from "./components/chat-section"; export default function Home() { return (
-
+
diff --git a/frontend/run.bat b/frontend/run.bat new file mode 100644 index 0000000..b896a08 --- /dev/null +++ b/frontend/run.bat @@ -0,0 +1 @@ +npm run dev \ No newline at end of file diff --git a/phoenixserver/.env b/phoenixserver/.env new file mode 100644 index 0000000..1754812 --- /dev/null +++ b/phoenixserver/.env @@ -0,0 +1,3 @@ +ENV_PHOENIX_HOST=0.0.0.0 +ENV_PHOENIX_PORT=6006 +PHOENIX_HOST_ROOT_PATH=./.phoenix/ \ No newline at end of file diff --git a/phoenixserver/phoenixserver.py b/phoenixserver/phoenixserver.py new file mode 100644 index 0000000..7dd0e91 --- /dev/null +++ b/phoenixserver/phoenixserver.py @@ -0,0 +1,18 @@ +import os +import phoenix as px + + +os.environ['PHOENIX_HOST'] = "0.0.0.0" + +session = px.launch_app(use_temp_dir=False) + +import msvcrt + +def wait_for_keypress(): + print("Press any key to continue...") + msvcrt.getch() # 等待按键 + print("\nKey pressed!") + +wait_for_keypress() + +px.close_app() \ No newline at end of file diff --git a/phoenixserver/runphoenixserver.bat b/phoenixserver/runphoenixserver.bat new file mode 100644 index 0000000..a593c5d --- /dev/null +++ b/phoenixserver/runphoenixserver.bat @@ -0,0 +1,5 @@ +SET ENV_PHOENIX_HOST=0.0.0.0 +SET ENV_PHOENIX_PORT=6006 +SET PHOENIX_HOST_ROOT_PATH=./.phoenix/ + +C:\Users\liuyue\AppData\Local\pypoetry\Cache\virtualenvs\app-pCyqx0Uo-py3.11\Scripts\python phoenixserver.py \ No newline at end of file