Skip to content

Commit 2aa5c91

Browse files
authored
Merge pull request #80 from kense-lab/feat/r2r
Feat RAG enhancement
2 parents 5e13c36 + d9f87a7 commit 2aa5c91

File tree

32 files changed

+679
-99
lines changed

32 files changed

+679
-99
lines changed

.env.example

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ REDIS_PORT=6379
2525
REDIS_DB=0
2626
REDIS_PASSWORD=123456
2727

28-
# file service
29-
FILE_SERVICE_MODULE=app.services.file.impl.oss_file.OSSFileService
30-
3128
# s3 storage
3229
S3_ENDPOINT=http://minio:9000
3330
S3_BUCKET_NAME=oas
@@ -52,6 +49,15 @@ BING_SEARCH_URL=https://api.bing.microsoft.com/v7.0/search
5249
BING_SUBSCRIPTION_KEY=xxxx
5350
WEB_SEARCH_NUM_RESULTS=5
5451

52+
# file service
53+
FILE_SERVICE_MODULE=app.services.file.impl.oss_file.OSSFileService
54+
# FILE_SERVICE_MODULE=app.services.file.impl.r2r_file.R2RFileService
55+
56+
# file search tool
57+
R2R_BASE_URL=http://127.0.0.1:8000
58+
R2R_USERNAME=admin@example.com
59+
R2R_PASSWORD=change_me_immediately
60+
R2R_SEARCH_LIMIT=10
61+
5562
# secret
5663
APP_AES_ENCRYPTION_KEY=7700b2f9c8dd982dfaddf8b47a92f1d900507ee8ac335f96a64e9ca0f018b195
57-

README.md

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ LLM applications.
1919

2020
It supports [One API](https://github.com/songquanpeng/one-api) for integration with more commercial and private models.
2121

22+
It supports [R2R](https://github.com/SciPhi-AI/R2R) RAG engine。
23+
2224
## Usage
2325

2426
Below is an example of using the official OpenAI Python `openai` library:
@@ -40,21 +42,22 @@ assistant = client.beta.assistants.create(
4042

4143
## Why Choose Open Assistant API
4244

43-
| Feature | Open Assistant API | OpenAI Assistant API |
44-
|--------------------------|-----------------------|----------------------|
45-
| Ecosystem Strategy | Open Source | Closed Source |
46-
| RAG Engine | Simple Implementation | Supported |
47-
| Internet Search | Supported | Not Supported |
48-
| Custom Functions | Supported | Supported |
49-
| Built-in Tool | Extendable | Not Extendable |
50-
| Code Interpreter | Under Development | Supported |
51-
| LLM Support | Supports More LLMs | Only GPT |
52-
| Message Streaming Output | Supports | Supported |
53-
| Local Deployment | Supported | Not Supported |
45+
| Feature | Open Assistant API | OpenAI Assistant API |
46+
|--------------------------|--------------------|----------------------|
47+
| Ecosystem Strategy | Open Source | Closed Source |
48+
| RAG Engine | Support R2R | Supported |
49+
| Internet Search | Supported | Not Supported |
50+
| Custom Functions | Supported | Supported |
51+
| Built-in Tool | Extendable | Not Extendable |
52+
| Code Interpreter | Under Development | Supported |
53+
| Multimodal | Supported | Supported |
54+
| LLM Support | Supports More LLMs | Only GPT |
55+
| Message Streaming Output | Supports | Supported |
56+
| Local Deployment | Supported | Not Supported |
5457

5558
- **LLM Support**: Compared to the official OpenAI version, more models can be supported by integrating with One API.
5659
- **Tool**: Currently supports online search; can easily expand more tools.
57-
- **RAG Engine**: The currently supported file types are txt, pdf, html, markdown. We provide a preliminary
60+
- **RAG Engine**: The currently supported file types are txt, html, markdown, pdf, docx, pptx, xlsx, png, mp3, mp4, etc. We provide a preliminary
5861
implementation.
5962
- **Message Streaming Output**: Support message streaming output for a smoother user experience.
6063
- **Ecosystem Strategy**: Open source, you can deploy the service locally and expand the existing features.
@@ -76,6 +79,18 @@ OPENAI_API_KEY=<openai_api_key>
7679
BING_SUBSCRIPTION_KEY=<bing_subscription_key>
7780
```
7881

82+
It is recommended to configure the R2R RAG engine to replace the default RAG implementation to provide better RAG capabilities.
83+
You can learn about and use R2R through the [R2R Github repository](https://github.com/SciPhi-AI/R2R).
84+
85+
```sh
86+
# RAG config
87+
# FILE_SERVICE_MODULE=app.services.file.impl.oss_file.OSSFileService
88+
FILE_SERVICE_MODULE=app.services.file.impl.r2r_file.R2RFileService
89+
R2R_BASE_URL=http://<r2r_api_address>
90+
R2R_USERNAME=<r2r_username>
91+
R2R_PASSWORD=<r2r_password>
92+
```
93+
7994
### Run
8095

8196
#### Run with Docker Compose:
@@ -92,14 +107,14 @@ Interface documentation address: http://127.0.0.1:8086/docs
92107

93108
### Complete Usage Example
94109

95-
In this example, an AI assistant is created and run using the official OpenAI client library. If you need to explore other usage methods,
110+
In this example, an AI assistant is created and run using the official OpenAI client library. If you need to explore other usage methods,
96111
such as streaming output, tools (web_search, retrieval, function), etc., you can find the corresponding code under the examples directory.
97112
Before running, you need to run `pip install openai` to install the Python `openai` library.
98113

99114
```sh
100115
# !pip install openai
101-
export PYTHONPATH=$(pwd)
102-
python examples/run_assistant.py
116+
export PYTHONPATH=$(pwd)
117+
python examples/run_assistant.py
103118
```
104119

105120

@@ -135,6 +150,7 @@ We mainly referred to and relied on the following projects:
135150

136151
- [OpenOpenAI](https://github.com/transitive-bullshit/OpenOpenAI): Assistant API implemented in Node
137152
- [One API](https://github.com/songquanpeng/one-api): Multi-model management tool
153+
- [R2R](https://github.com/SciPhi-AI/R2R): RAG engine
138154
- [OpenAI-Python](https://github.com/openai/openai-python): OpenAI Python Client
139155
- [OpenAI API](https://github.com/openai/openai-openapi): OpenAI interface definition
140156
- [LangChain](https://github.com/langchain-ai/langchain): LLM application development library

README_CN.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Open Assistant API 是一个开源自托管的 AI 智能助手 API,兼容 Open
1818

1919
支持 [One API](https://github.com/songquanpeng/one-api) 可以用其接入更多商业和私有模型。
2020

21+
支持 [R2R](https://github.com/SciPhi-AI/R2R) RAG 引擎。
22+
2123
## 使用
2224

2325
以下是使用了 OpenAI 官方的 Python `openai` 库的使用示例:
@@ -42,18 +44,19 @@ assistant = client.beta.assistants.create(
4244
| 功能 | Open Assistant API | OpenAI Assistant API |
4345
|------------------|--------------------|----------------------|
4446
| 生态策略 | 开源 | 闭源 |
45-
| RAG 引擎 | 简单实现 | 支持 |
47+
| RAG 引擎 | 支持 R2R | 支持 |
4648
| 联网搜索 | 支持 | 不支持 |
4749
| 自定义 Functions | 支持 | 支持 |
4850
| 内置 Tool | 支持扩展 | 不支持扩展 |
4951
| Code Interpreter | 待开发 | 支持 |
52+
| 多模态识别 | 支持 | 支持 |
5053
| LLM 支持 | 支持更多的 LLM | 仅 GPT |
51-
| Message 流式输出 | 支持 | 支持 |
54+
| Message 流式输出 | 支持 | 支持 |
5255
| 本地部署 | 支持 | 不支持 |
5356

5457
- **LLM 支持**: 相较于 OpenAI 官方版本,可以通过接入 One API 来支持更多的模型。
5558
- **Tool**: 目前支持联网搜索;可以较容易扩展更多的 Tool。
56-
- **RAG 引擎**: 目前支持的文件类型有 txt、pdf、html、markdown。我们提供了一个初步的实现
59+
- **RAG 引擎**: 支持 R2R RAG 引擎,目前支持的文件类型有 txt、html、markdown、pdf、docx、pptx、xlsx、png、mp3、mp4 等
5760
- **Message 流式输出**: 支持 Message 流式输出,提供更流畅的用户体验。
5861
- **生态策略**: 开源,你可以将服务部署在本地,可以对已有功能进行扩展。
5962

@@ -71,6 +74,18 @@ OPENAI_API_KEY=<openai_api_key>
7174

7275
# bing search key (非必填)
7376
BING_SUBSCRIPTION_KEY=<bing_subscription_key>
77+
````
78+
79+
建议配置 R2R RAG 引擎替换默认的 RAG 实现,以提供更好的 RAG 能力。
80+
关于 R2R,可以通过 [R2R Github 仓库](https://github.com/SciPhi-AI/R2R) 了解和使用。
81+
82+
```sh
83+
# RAG 配置
84+
# FILE_SERVICE_MODULE=app.services.file.impl.oss_file.OSSFileService
85+
FILE_SERVICE_MODULE=app.services.file.impl.r2r_file.R2RFileService
86+
R2R_BASE_URL=http://<r2r_api_address>
87+
R2R_USERNAME=<r2r_username>
88+
R2R_PASSWORD=<r2r_password>
7489
```
7590

7691
### 运行
@@ -95,8 +110,8 @@ Api Base URL: http://127.0.0.1:8086/api/v1
95110

96111
```sh
97112
# !pip install openai
98-
export PYTHONPATH=$(pwd)
99-
python examples/run_assistant.py
113+
export PYTHONPATH=$(pwd)
114+
python examples/run_assistant.py
100115
```
101116

102117
### 权限
@@ -105,7 +120,7 @@ python examples/run_assistant.py
105120
![](docs/imgs/user.png)
106121

107122
1. 验证方式为 Bearer token,可在 Header 中填入 ```Authorization: Bearer ***``` 进行验证
108-
2. token 管理参考 api 文档中的 token 小节
123+
2. token 管理参考 api 文档中的 token 小节
109124
相关 api 需通过 admin token 验证,配置为 ```APP_AUTH_ADMIN_TOKEN```,默认为 admin
110125
3. 创建 token 需填入大模型 base_url 和 api_key,创建的 assistant 将使用相关配置访问大模型
111126
### 工具
@@ -130,6 +145,7 @@ python examples/run_assistant.py
130145

131146
- [OpenOpenAI](https://github.com/transitive-bullshit/OpenOpenAI): Node 实现的 Assistant API
132147
- [One API](https://github.com/songquanpeng/one-api): 多模型管理工具
148+
- [R2R](https://github.com/SciPhi-AI/R2R): RAG 引擎
133149
- [OpenAI-Python](https://github.com/openai/openai-python): OpenAI Python Client
134150
- [OpenAI API](https://github.com/openai/openai-openapi): OpenAI 接口定义
135151
- [LangChain](https://github.com/langchain-ai/langchain): LLM 应用开发库

app/api/v1/files.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
# 限制文件大小
1919
max_size = 512 * 1024 * 1024
2020
# 支持的文件类型
21-
file_ext = [".txt", ".md", ".pdf", ".html"]
21+
file_ext = [".csv", ".docx", ".html", ".json", ".md", ".pdf", ".pptx", ".txt",
22+
".xlsx", ".gif", ".png", ".jpg", ".jpeg", ".svg", ".mp3", ".mp4"]
2223

2324

2425
@router.get("", response_model=ListFilesResponse)

app/core/runner/pub_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def read_event(channel: str, x_index: str = None) -> Tuple[Optional[str], Option
5151

5252
def _data_adjust_tools(tools: List[dict]) -> List[dict]:
5353
def _adjust_tool(tool: dict):
54-
if tool["type"] not in {"code_interpreter", "retrieval", "function"}:
54+
if tool["type"] not in {"code_interpreter", "file_search", "function"}:
5555
return {
5656
"type": "function",
5757
"function": {

app/core/runner/thread_runner.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from config.config import settings
1111
from config.llm import llm_settings, tool_settings
1212

13-
from app.core.doc_loaders import doc_loader
1413
from app.core.runner.llm_backend import LLMBackend
1514
from app.core.runner.llm_callback_handler import LLMCallbackHandler
1615
from app.core.runner.memory import Memory, find_memory
@@ -28,8 +27,7 @@
2827
from app.models.message import Message, MessageUpdate
2928
from app.models.run import Run
3029
from app.models.run_step import RunStep
31-
from app.models.file import File
32-
from app.providers.storage import storage
30+
from app.models.token_relation import RelationType
3331
from app.services.assistant.assistant import AssistantService
3432
from app.services.file.file import FileService
3533
from app.services.message.message import MessageService
@@ -261,11 +259,6 @@ def __generate_chat_messages(self, messages: List[Message]):
261259
根据历史信息生成 chat message
262260
"""
263261

264-
def file_load(file: File):
265-
file_data = storage.load(file.key)
266-
content = doc_loader.load(file_data)
267-
return f"For reference, here is is the content of file {file.filename}: '{content}'"
268-
269262
chat_messages = []
270263
for message in messages:
271264
role = message.role
@@ -274,7 +267,7 @@ def file_load(file: File):
274267
if message.file_ids:
275268
files = FileService.get_file_list_by_ids(session=self.session, file_ids=message.file_ids)
276269
for file in files:
277-
chat_messages.append(msg_util.new_message(role, file_load(file)))
270+
chat_messages.append(msg_util.new_message(role, f'The file "{file.filename}" can be used as a reference'))
278271
else:
279272
for content in message.content:
280273
if content["type"] == "text":

app/core/runner/utils/tool_call_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{
66
"id": "tool_call_0",
77
"function": {
8-
"name": "retrieval",
8+
"name": "file_search",
99
"arguments": "{\"file_keys\": [\"file_0\", \"file_1\"], \"query\": \"query\"}"
1010
}
1111
}

app/core/tools/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@
99
from app.core.tools.base_tool import BaseTool
1010
from app.core.tools.external_function_tool import ExternalFunctionTool
1111
from app.core.tools.openapi_function_tool import OpenapiFunctionTool
12-
from app.core.tools.retrieval import RetrievalTool
12+
from app.core.tools.file_search_tool import FileSearchTool
1313
from app.core.tools.web_search import WebSearchTool
1414

1515

1616
class AvailableTools(str, Enum):
17-
RETRIEVAL = "retrieval"
17+
FILE_SEARCH = "file_search"
1818
WEB_SEARCH = "web_search"
1919

2020

2121
TOOLS = {
22-
AvailableTools.RETRIEVAL: RetrievalTool,
22+
AvailableTools.FILE_SEARCH: FileSearchTool,
2323
AvailableTools.WEB_SEARCH: WebSearchTool,
2424
}
2525

app/core/tools/retrieval.py renamed to app/core/tools/file_search_tool.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,24 @@
33
from pydantic import BaseModel, Field
44
from sqlalchemy.orm import Session
55

6-
from app.core.doc_loaders import doc_loader
76
from app.core.tools.base_tool import BaseTool
87
from app.models.run import Run
9-
from app.providers.storage import storage
108
from app.services.file.file import FileService
119

1210

13-
class RetrievalToolInput(BaseModel):
11+
class FileSearchToolInput(BaseModel):
1412
indexes: List[int] = Field(..., description="file index list to look up in retrieval")
1513
query: str = Field(..., description="query to look up in retrieval")
1614

1715

18-
class RetrievalTool(BaseTool):
19-
name: str = "retrieval"
16+
class FileSearchTool(BaseTool):
17+
name: str = "file_search"
2018
description: str = (
2119
"Can be used to look up information that was uploaded to this assistant."
2220
"If the user is referencing particular files, that is often a good hint that information may be here."
2321
)
2422

25-
args_schema: Type[BaseModel] = RetrievalToolInput
23+
args_schema: Type[BaseModel] = FileSearchToolInput
2624

2725
def __init__(self) -> None:
2826
super().__init__()
@@ -40,13 +38,12 @@ def configure(self, session: Session, run: Run, **kwargs):
4038
self.__keys.append(file.key)
4139

4240
def run(self, indexes: List[int], query: str) -> dict:
43-
files = {}
41+
file_keys = []
4442
for index in indexes:
4543
file_key = self.__keys[index]
46-
file_data = storage.load(file_key)
47-
# 截取前 5000 字符,防止超出 LLM 最大上下文限制
48-
files[file_key] = doc_loader.load(file_data)[:5000]
44+
file_keys.append(file_key)
4945

46+
files = FileService.search_in_files(query=query, file_keys=file_keys)
5047
return files
5148

5249
def instruction_supplement(self) -> str:

app/libs/util.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import uuid
22
from datetime import datetime
33

4+
import jwt
5+
46

57
def datetime2timestamp(value: datetime):
68
if not value:
@@ -26,3 +28,10 @@ def is_valid_datetime(date_str, format="%Y-%m-%d %H:%M:%S"):
2628

2729
def random_uuid() -> str:
2830
return "ml-" + str(uuid.uuid4()).replace("-", "")
31+
32+
33+
def verify_jwt_expiration(token):
34+
decoded_token = jwt.decode(token, options={"verify_signature": False, "verify_exp": False})
35+
expiration_time = datetime.fromtimestamp(decoded_token['exp'])
36+
current_time = datetime.now()
37+
return current_time < expiration_time

0 commit comments

Comments
 (0)