iflow-mcp_pingcy_app_chatppt 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- __init__.py +3 -0
- doubao.py +58 -0
- iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/METADATA +102 -0
- iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/RECORD +10 -0
- iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/WHEEL +5 -0
- iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/top_level.txt +5 -0
- mcp_ppt_server.py +274 -0
- multi_doc_rag_engine.py +1017 -0
- ppt_utils.py +266 -0
__init__.py
ADDED
doubao.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
from openai import OpenAI
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
class DoubaoVisionLLM:
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
model_name: str,
|
|
10
|
+
api_key: Optional[str] = None,
|
|
11
|
+
base_url: str = "https://ark.cn-beijing.volces.com/api/v3"
|
|
12
|
+
):
|
|
13
|
+
self.model_name = model_name
|
|
14
|
+
self.client = OpenAI(
|
|
15
|
+
base_url=base_url,
|
|
16
|
+
api_key=api_key or os.getenv("ARK_API_KEY")
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def _get_image_mime_type(self, image_path: str) -> str:
|
|
20
|
+
"""Get the MIME type based on the image file extension."""
|
|
21
|
+
ext = image_path.lower().split('.')[-1]
|
|
22
|
+
mime_types = {
|
|
23
|
+
'jpg': 'image/jpeg',
|
|
24
|
+
'jpeg': 'image/jpeg',
|
|
25
|
+
'png': 'image/png'
|
|
26
|
+
}
|
|
27
|
+
return mime_types.get(ext, 'image/jpeg')
|
|
28
|
+
|
|
29
|
+
def _image_to_base64(self, image_path: str) -> str:
|
|
30
|
+
with open(image_path, "rb") as image_file:
|
|
31
|
+
return base64.b64encode(image_file.read()).decode('utf-8')
|
|
32
|
+
|
|
33
|
+
def generate_response(
|
|
34
|
+
self,
|
|
35
|
+
prompt: str,
|
|
36
|
+
image_paths: List[str],
|
|
37
|
+
system_prompt: str = "你是一个结合文本与图片回答问题的AI助手。"
|
|
38
|
+
) -> str:
|
|
39
|
+
completion = self.client.chat.completions.create(
|
|
40
|
+
model=self.model_name,
|
|
41
|
+
messages=[
|
|
42
|
+
{"role": "system", "content": system_prompt},
|
|
43
|
+
{"role": "user",
|
|
44
|
+
"content": [
|
|
45
|
+
{"type": "text", "text": prompt},
|
|
46
|
+
*[
|
|
47
|
+
{
|
|
48
|
+
"type": "image_url",
|
|
49
|
+
"image_url": {
|
|
50
|
+
"url": image_path if image_path.startswith(("http://", "https://")) else f"data:{self._get_image_mime_type(image_path)};base64,{self._image_to_base64(image_path)}" ,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
for image_path in image_paths
|
|
54
|
+
]
|
|
55
|
+
]},
|
|
56
|
+
],
|
|
57
|
+
)
|
|
58
|
+
return completion.choices[0].message.content
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iflow-mcp_pingcy_app_chatppt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: asyncpg>=0.30.0
|
|
8
|
+
Requires-Dist: browser-use>=0.1.24
|
|
9
|
+
Requires-Dist: chroma>=0.2.0
|
|
10
|
+
Requires-Dist: fastapi>=0.115.12
|
|
11
|
+
Requires-Dist: langchain-core>=0.3.65
|
|
12
|
+
Requires-Dist: langchain-mcp-adapters>=0.1.7
|
|
13
|
+
Requires-Dist: langchain-openai>=0.3.14
|
|
14
|
+
Requires-Dist: langgraph>=0.4.1
|
|
15
|
+
Requires-Dist: llama-index-agent-openai>=0.4.6
|
|
16
|
+
Requires-Dist: llama-index-core>=0.12.34.post1
|
|
17
|
+
Requires-Dist: llama-index-embeddings-ollama>=0.6.0
|
|
18
|
+
Requires-Dist: llama-index-embeddings-openai>=0.3.1
|
|
19
|
+
Requires-Dist: llama-index-llms-openai>=0.3.38
|
|
20
|
+
Requires-Dist: llama-index-tools-mcp>=0.1.2
|
|
21
|
+
Requires-Dist: llama-index-vector-stores-chroma>=0.4.1
|
|
22
|
+
Requires-Dist: mcp[cli]>=1.9.4
|
|
23
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
24
|
+
Requires-Dist: pandas>=2.2.3
|
|
25
|
+
Requires-Dist: pdf2image>=1.17.0
|
|
26
|
+
Requires-Dist: pypdfium2>=4.30.1
|
|
27
|
+
Requires-Dist: python-socks>=2.7.1
|
|
28
|
+
Requires-Dist: redis>=6.1.0
|
|
29
|
+
Requires-Dist: requests>=2.32.4
|
|
30
|
+
Requires-Dist: streamlit>=1.45.0
|
|
31
|
+
Requires-Dist: uvicorn>=0.34.3
|
|
32
|
+
Requires-Dist: websockets>=15.0.1
|
|
33
|
+
|
|
34
|
+
# ChatPPT-MCP: 多文档RAG引擎的 MCP Server
|
|
35
|
+
|
|
36
|
+
一个基于MCP(Model Context Protocol)的多文档RAG(Retrieval-Augmented Generation)引擎应用,支持PPT文档的智能问答和分析。
|
|
37
|
+
|
|
38
|
+
## 功能特性
|
|
39
|
+
|
|
40
|
+
- 🔍 **多文档处理**: 支持索引多个PPT文档,基于视觉模型
|
|
41
|
+
- 🤖 **智能问答**: 基于RAG技术的文档问答
|
|
42
|
+
- 🔄 **MCP集成**: 使用Model Context Protocol进行工具调用
|
|
43
|
+
- 📊 **交互式测试**: 提供命令行交互测试界面
|
|
44
|
+
|
|
45
|
+
## 技术栈
|
|
46
|
+
|
|
47
|
+
- **后端框架**: FastAPI
|
|
48
|
+
- **向量数据库**: ChromaDB
|
|
49
|
+
- **LLM**: Doubao Vision
|
|
50
|
+
- **文档处理**: LibreOffice, pypdfium2
|
|
51
|
+
- **协议**: Model Context Protocol (MCP)
|
|
52
|
+
- **向量化**: OpenAI Embeddings
|
|
53
|
+
|
|
54
|
+
## 安装使用
|
|
55
|
+
|
|
56
|
+
### 1. 安装依赖
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install -e .
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. 环境配置
|
|
63
|
+
|
|
64
|
+
复制环境变量模板文件,并修改:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cp .env.example .env
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. 运行应用
|
|
71
|
+
|
|
72
|
+
#### RAG引擎测试模式
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python rag_interactive_test.py
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### MCP测试模式(先sse启动:python mcp_ppt_server.py --transport sse)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python mcp_interactive_test.py
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 项目结构
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
app_chatppt/
|
|
88
|
+
├── src/ # 源代码目录
|
|
89
|
+
│ ├── multi_doc_rag_engine.py # RAG引擎核心
|
|
90
|
+
│ ├── document_processor.py # 文档处理器
|
|
91
|
+
│ └── ...
|
|
92
|
+
├── data/ # 示例数据
|
|
93
|
+
├── rag_interactive_test.py # 交互式测试入口
|
|
94
|
+
├── mcp_interactive_test.py # MCP集成测试
|
|
95
|
+
├── pyproject.toml # 项目配置
|
|
96
|
+
├── .env.example # 环境变量模板
|
|
97
|
+
└── README.md # 项目文档
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 许可证
|
|
101
|
+
|
|
102
|
+
MIT License
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
__init__.py,sha256=ieOvA37tdFuQDWzKhClZcBa3_T-YecF2IClZmjvrBq4,99
|
|
2
|
+
doubao.py,sha256=DhGUfj9L5dAX67QporZlq5VYA8xd5B8Z913QTMZ56Ig,2044
|
|
3
|
+
mcp_ppt_server.py,sha256=eM6q8DMnQ-k6kd-Np-R8Sc_ZbahXicNgjNz1lcAHw1o,8784
|
|
4
|
+
multi_doc_rag_engine.py,sha256=pAtK6G20AcIyAyvHn4SvO-T6dYfzTp97TfVDhgw0YWs,40876
|
|
5
|
+
ppt_utils.py,sha256=Jl4ldtkLA149botrkpfLf5dVMbR0PufVwIpvN1fGcjg,9408
|
|
6
|
+
iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/METADATA,sha256=dYyDC933t89kMIjp1XYwmDVrkV4qTx-EahjyYWL9fKE,2834
|
|
7
|
+
iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/entry_points.txt,sha256=8QHU_i81rOwO1TWnVMVXpSM5B030HRvKc_I8v-XNEjw,59
|
|
9
|
+
iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/top_level.txt,sha256=KN7yztyd3Yq5ZZy_538BGbWffHA5I1Zleh_RQh8MpkA,62
|
|
10
|
+
iflow_mcp_pingcy_app_chatppt-0.1.0.dist-info/RECORD,,
|
mcp_ppt_server.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""
|
|
2
|
+
多文档PPT RAG引擎的MCP服务器
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional, AsyncIterator
|
|
11
|
+
import logging
|
|
12
|
+
from contextlib import asynccontextmanager
|
|
13
|
+
import argparse
|
|
14
|
+
|
|
15
|
+
# MCP FastMCP导入
|
|
16
|
+
from mcp.server.fastmcp.server import FastMCP, Context
|
|
17
|
+
|
|
18
|
+
# 加载环境变量
|
|
19
|
+
from dotenv import load_dotenv
|
|
20
|
+
load_dotenv()
|
|
21
|
+
|
|
22
|
+
# 本地导入
|
|
23
|
+
from multi_doc_rag_engine import MultiDocRAGEngine
|
|
24
|
+
|
|
25
|
+
# 配置日志
|
|
26
|
+
logging.basicConfig(level=logging.INFO,
|
|
27
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# 定义应用程序上下文类
|
|
32
|
+
class AppContext:
|
|
33
|
+
def __init__(self):
|
|
34
|
+
self.rag_engine: Optional[MultiDocRAGEngine] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# 创建生命周期管理器
|
|
38
|
+
@asynccontextmanager
|
|
39
|
+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
|
40
|
+
"""管理应用程序生命周期,包括多文档RAG引擎初始化
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
server: FastMCP服务器实例
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
应用程序上下文对象
|
|
47
|
+
"""
|
|
48
|
+
# 启动初始化
|
|
49
|
+
logger.info("应用程序正在启动,初始化资源...")
|
|
50
|
+
app_ctx = AppContext()
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
# 从环境变量获取配置
|
|
54
|
+
cache_dir = os.getenv("CACHE_DIRECTORY", "./cache")
|
|
55
|
+
chroma_dir = os.getenv("CHROMA_PERSIST_DIRECTORY", "./chroma_db")
|
|
56
|
+
doubao_model = os.getenv("DOUBAO_MODEL", "ep-20250205153642-hzqpj")
|
|
57
|
+
|
|
58
|
+
# 初始化多文档RAG引擎
|
|
59
|
+
app_ctx.rag_engine = MultiDocRAGEngine(
|
|
60
|
+
persist_dir=chroma_dir,
|
|
61
|
+
cache_dir=cache_dir,
|
|
62
|
+
doubao_model=doubao_model
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
logger.info("多文档RAG引擎初始化成功")
|
|
66
|
+
|
|
67
|
+
# 将上下文传递给应用程序
|
|
68
|
+
yield app_ctx
|
|
69
|
+
|
|
70
|
+
finally:
|
|
71
|
+
# 关闭时清理资源
|
|
72
|
+
logger.info("应用程序正在关闭,清理资源...")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# 创建MCP服务器实例
|
|
76
|
+
app = FastMCP("multi-doc-ppt-rag", port=5053, lifespan=app_lifespan)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@app.tool()
|
|
80
|
+
async def add_ppt(
|
|
81
|
+
ctx: Context,
|
|
82
|
+
file_path: str,
|
|
83
|
+
force_reprocess: bool = False
|
|
84
|
+
) -> str:
|
|
85
|
+
"""将指定的PPT文档添加到RAG索引中
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
ctx: 上下文对象
|
|
89
|
+
file_path: 要添加的PPT文件的绝对或相对路径
|
|
90
|
+
force_reprocess: 是否强制重新处理,即使文档已存在于索引中
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
操作结果的JSON字符串
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
if not file_path:
|
|
97
|
+
return json.dumps({"status": "error", "message": "需要提供file_path参数"}, ensure_ascii=False)
|
|
98
|
+
|
|
99
|
+
# 将相对路径转换为绝对路径
|
|
100
|
+
abs_path = str(Path(file_path).resolve())
|
|
101
|
+
if not os.path.exists(abs_path):
|
|
102
|
+
return json.dumps({"status": "error", "message": f"文件未找到: {abs_path}"}, ensure_ascii=False)
|
|
103
|
+
|
|
104
|
+
# 从上下文获取RAG引擎
|
|
105
|
+
rag_engine = ctx.request_context.lifespan_context.rag_engine
|
|
106
|
+
|
|
107
|
+
logger.info(f"开始添加PPT文档: {abs_path}")
|
|
108
|
+
await ctx.info(f"开始添加PPT文档: {abs_path}")
|
|
109
|
+
|
|
110
|
+
result = await rag_engine.add_ppt_document(abs_path, force_reprocess=force_reprocess)
|
|
111
|
+
|
|
112
|
+
logger.info(f"PPT文档添加完成: {result['status']}")
|
|
113
|
+
await ctx.info(f"PPT文档添加完成: {result['status']}")
|
|
114
|
+
|
|
115
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
error_msg = f"添加PPT文档时出错: {str(e)}"
|
|
119
|
+
logger.error(error_msg)
|
|
120
|
+
return json.dumps({"status": "error", "message": error_msg}, ensure_ascii=False)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@app.tool()
|
|
124
|
+
def delete_ppt(
|
|
125
|
+
ctx: Context,
|
|
126
|
+
file_path: str
|
|
127
|
+
) -> str:
|
|
128
|
+
"""从RAG索引中删除指定的PPT文档
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
ctx: 上下文对象
|
|
132
|
+
file_path: 要删除的PPT文件的绝对或相对路径
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
操作结果的JSON字符串
|
|
136
|
+
"""
|
|
137
|
+
try:
|
|
138
|
+
if not file_path:
|
|
139
|
+
return json.dumps({"status": "error", "message": "需要提供file_path参数"}, ensure_ascii=False)
|
|
140
|
+
|
|
141
|
+
# 将相对路径转换为绝对路径
|
|
142
|
+
abs_path = str(Path(file_path).resolve())
|
|
143
|
+
|
|
144
|
+
# 从上下文获取RAG引擎
|
|
145
|
+
rag_engine = ctx.request_context.lifespan_context.rag_engine
|
|
146
|
+
|
|
147
|
+
logger.info(f"开始删除PPT文档: {abs_path}")
|
|
148
|
+
|
|
149
|
+
if not rag_engine.is_document_indexed(abs_path):
|
|
150
|
+
return json.dumps({"status": "error", "message": f"文档不在索引中: {abs_path}"}, ensure_ascii=False)
|
|
151
|
+
|
|
152
|
+
result = rag_engine.remove_ppt_document(abs_path)
|
|
153
|
+
|
|
154
|
+
logger.info(f"PPT文档删除完成: {result['status']}")
|
|
155
|
+
|
|
156
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
157
|
+
|
|
158
|
+
except Exception as e:
|
|
159
|
+
error_msg = f"删除PPT文档时出错: {str(e)}"
|
|
160
|
+
logger.error(error_msg)
|
|
161
|
+
return json.dumps({"status": "error", "message": error_msg}, ensure_ascii=False)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@app.tool()
|
|
165
|
+
async def chat_with_ppt(
|
|
166
|
+
ctx: Context,
|
|
167
|
+
query: str,
|
|
168
|
+
file_path: Optional[str] = None,
|
|
169
|
+
doc_id: Optional[str] = None
|
|
170
|
+
) -> str:
|
|
171
|
+
"""与一个或所有PPT文档进行对话
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
ctx: 上下文对象
|
|
175
|
+
query: 用户提出的问题
|
|
176
|
+
file_path: (可选) 指定要查询的PPT文件路径。如果提供,则只在该文档中搜索
|
|
177
|
+
doc_id: (可选) 指定要查询的文档ID。如果提供,则只在该文档中搜索。如果同时提供了file_path和doc_id,则优先使用doc_id
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
查询结果的JSON字符串
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
if not query:
|
|
184
|
+
return json.dumps({"status": "error", "message": "需要提供query参数"}, ensure_ascii=False)
|
|
185
|
+
|
|
186
|
+
# 从上下文获取RAG引擎
|
|
187
|
+
rag_engine = ctx.request_context.lifespan_context.rag_engine
|
|
188
|
+
|
|
189
|
+
logger.info(f"开始查询: {query}")
|
|
190
|
+
await ctx.info(f"开始查询: {query}")
|
|
191
|
+
|
|
192
|
+
result = await rag_engine.query(query, file_path=file_path, doc_id=doc_id)
|
|
193
|
+
|
|
194
|
+
logger.info(f"查询完成: {result['status']}")
|
|
195
|
+
await ctx.info(f"查询完成: {result['status']}")
|
|
196
|
+
|
|
197
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
error_msg = f"查询时出错: {str(e)}"
|
|
201
|
+
logger.error(error_msg)
|
|
202
|
+
return json.dumps({"status": "error", "message": error_msg}, ensure_ascii=False)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@app.tool()
|
|
206
|
+
def index_status(
|
|
207
|
+
ctx: Context
|
|
208
|
+
) -> str:
|
|
209
|
+
"""获取当前RAG索引的状态和统计信息
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
ctx: 上下文对象
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
索引状态的JSON字符串
|
|
216
|
+
"""
|
|
217
|
+
try:
|
|
218
|
+
# 从上下文获取RAG引擎
|
|
219
|
+
rag_engine = ctx.request_context.lifespan_context.rag_engine
|
|
220
|
+
|
|
221
|
+
logger.info("获取索引状态")
|
|
222
|
+
|
|
223
|
+
result = rag_engine.get_index_status()
|
|
224
|
+
|
|
225
|
+
logger.info(f"索引状态: {result['status']}")
|
|
226
|
+
|
|
227
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
error_msg = f"获取索引状态时出错: {str(e)}"
|
|
231
|
+
logger.error(error_msg)
|
|
232
|
+
return json.dumps({"status": "error", "message": error_msg}, ensure_ascii=False)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def get_available_tools():
|
|
236
|
+
"""获取当前服务器提供的所有工具"""
|
|
237
|
+
tools = app._tool_manager.list_tools()
|
|
238
|
+
return tools
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def main():
|
|
242
|
+
"""MCP服务器的主入口点"""
|
|
243
|
+
# 命令行参数
|
|
244
|
+
parser = argparse.ArgumentParser(description="启动多文档PPT RAG MCP服务器")
|
|
245
|
+
parser.add_argument("--transport", choices=["sse", "stdio"], default="stdio",
|
|
246
|
+
help="选择传输方式: sse 或 stdio")
|
|
247
|
+
parser.add_argument("--port", type=int, default=5053, help="指定服务器端口号(仅用于SSE)")
|
|
248
|
+
args = parser.parse_args()
|
|
249
|
+
|
|
250
|
+
# 更新端口设置
|
|
251
|
+
if args.transport == "sse":
|
|
252
|
+
current_settings = app.settings
|
|
253
|
+
current_settings.port = args.port
|
|
254
|
+
app.settings = current_settings
|
|
255
|
+
|
|
256
|
+
# 列出可用工具
|
|
257
|
+
logger.info("\n")
|
|
258
|
+
logger.info("==== 多文档PPT RAG Server可用工具 ====")
|
|
259
|
+
tools = app._tool_manager.list_tools()
|
|
260
|
+
if tools:
|
|
261
|
+
for i, tool in enumerate(tools, 1):
|
|
262
|
+
description = tool.description.strip().split("\n")[0] if tool.description else "无描述"
|
|
263
|
+
logger.info(f"{i}. {tool.name}: {description}")
|
|
264
|
+
else:
|
|
265
|
+
logger.info("未发现工具函数")
|
|
266
|
+
logger.info("=================================\n")
|
|
267
|
+
|
|
268
|
+
# 启动服务器
|
|
269
|
+
logger.info(f"启动多文档PPT RAG服务器,传输方式: {args.transport}, 端口: {args.port if args.transport == 'sse' else 'N/A'}")
|
|
270
|
+
app.run(transport=args.transport)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
main()
|