Fix bugs in agent api and update api document (#3996)

### What problem does this PR solve?

Fix bugs in agent api and update api document

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: liuhua <10215101452@stu.ecun.edu.cn>
This commit is contained in:
liuhua 2024-12-13 10:25:52 +08:00 committed by GitHub
parent 68d46b2a1e
commit 1ecb687c51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 351 additions and 57 deletions

View File

@ -185,6 +185,7 @@ class Canvas(ABC):
self.path.append(["begin"]) self.path.append(["begin"])
self.path.append([]) self.path.append([])
ran = -1 ran = -1
waiting = [] waiting = []
without_dependent_checking = [] without_dependent_checking = []

View File

@ -73,6 +73,8 @@ def create_agent_session(tenant_id, agent_id):
cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
canvas = Canvas(cvs.dsl, tenant_id) canvas = Canvas(cvs.dsl, tenant_id)
if canvas.get_preset_param():
return get_error_data_result("The agent can't create a session directly")
conv = { conv = {
"id": get_uuid(), "id": get_uuid(),
"dialog_id": cvs.id, "dialog_id": cvs.id,
@ -112,6 +114,8 @@ def update(tenant_id, chat_id, session_id):
@token_required @token_required
def chat_completion(tenant_id, chat_id): def chat_completion(tenant_id, chat_id):
req = request.json req = request.json
if not req or not req.get("session_id"):
req = {"question":""}
if not DialogService.query(tenant_id=tenant_id,id=chat_id,status=StatusEnum.VALID.value): if not DialogService.query(tenant_id=tenant_id,id=chat_id,status=StatusEnum.VALID.value):
return get_error_data_result(f"You don't own the chat {chat_id}") return get_error_data_result(f"You don't own the chat {chat_id}")
if req.get("session_id"): if req.get("session_id"):
@ -125,7 +129,6 @@ def chat_completion(tenant_id, chat_id):
resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
return resp return resp
else: else:
answer = None answer = None
for ans in rag_completion(tenant_id, chat_id, **req): for ans in rag_completion(tenant_id, chat_id, **req):
@ -137,22 +140,28 @@ def chat_completion(tenant_id, chat_id):
@manager.route('/agents/<agent_id>/completions', methods=['POST']) # noqa: F821 @manager.route('/agents/<agent_id>/completions', methods=['POST']) # noqa: F821
@token_required @token_required
def agent_completions(tenant_id, agent_id): def agent_completions(tenant_id, agent_id):
req = request.json req = request.json
if not UserCanvasService.query(user_id=tenant_id,id=agent_id): cvs = UserCanvasService.query(user_id=tenant_id, id=agent_id)
return get_error_data_result(f"You don't own the agent {agent_id}") if not cvs:
if req.get("session_id"): return get_error_data_result(f"You don't own the agent {agent_id}")
if not API4ConversationService.query(id=req["session_id"],dialog_id=agent_id): if req.get("session_id"):
return get_error_data_result(f"You don't own the session {req['session_id']}") conv = API4ConversationService.query(id=req["session_id"], dialog_id=agent_id)
if req.get("stream", True): if not conv:
resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream") return get_error_data_result(f"You don't own the session {req['session_id']}")
resp.headers.add_header("Cache-control", "no-cache") else:
resp.headers.add_header("Connection", "keep-alive") req["question"]=""
resp.headers.add_header("X-Accel-Buffering", "no") if req.get("stream", True):
resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream")
return resp resp.headers.add_header("Cache-control", "no-cache")
resp.headers.add_header("Connection", "keep-alive")
for answer in agent_completion(tenant_id, agent_id, **req): resp.headers.add_header("X-Accel-Buffering", "no")
return get_result(data=answer) resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
return resp
try:
for answer in agent_completion(tenant_id, agent_id, **req):
return get_result(data=answer)
except Exception as e:
return get_error_data_result(str(e))
@manager.route('/chats/<chat_id>/sessions', methods=['GET']) # noqa: F821 @manager.route('/chats/<chat_id>/sessions', methods=['GET']) # noqa: F821
@ -420,3 +429,5 @@ def agent_bot_completions(agent_id):
for answer in agent_completion(objs[0].tenant_id, agent_id, **req): for answer in agent_completion(objs[0].tenant_id, agent_id, **req):
return get_result(data=answer) return get_result(data=answer)

View File

@ -55,36 +55,39 @@ def completion(tenant_id, agent_id, question, session_id=None, stream=True, **kw
e, cvs = UserCanvasService.get_by_id(agent_id) e, cvs = UserCanvasService.get_by_id(agent_id)
assert e, "Agent not found." assert e, "Agent not found."
assert cvs.user_id == tenant_id, "You do not own the agent." assert cvs.user_id == tenant_id, "You do not own the agent."
if not isinstance(cvs.dsl,str):
if not isinstance(cvs.dsl, str):
cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
canvas = Canvas(cvs.dsl, tenant_id) canvas = Canvas(cvs.dsl, tenant_id)
canvas.reset() canvas.reset()
message_id = str(uuid4()) message_id = str(uuid4())
if not session_id: if not session_id:
query = canvas.get_preset_param()
if query:
for ele in query:
if not ele["optional"]:
if not kwargs.get(ele["key"]):
assert False, f"`{ele['key']}` is required"
ele["value"] = kwargs[ele["key"]]
if ele["optional"]:
if kwargs.get(ele["key"]):
ele["value"] = kwargs[ele['key']]
else:
if "value" in ele:
ele.pop("value")
cvs.dsl = json.loads(str(canvas))
temp_dsl = cvs.dsl
UserCanvasService.update_by_id(agent_id, cvs.to_dict())
else:
temp_dsl = json.loads(cvs.dsl)
session_id = get_uuid() session_id = get_uuid()
conv = { conv = {
"id": session_id, "id": session_id,
"dialog_id": cvs.id, "dialog_id": cvs.id,
"user_id": kwargs.get("user_id", ""), "user_id": kwargs.get("user_id", ""),
"source": "agent", "source": "agent",
"dsl": json.loads(cvs.dsl) "dsl": temp_dsl
} }
API4ConversationService.save(**conv) API4ConversationService.save(**conv)
if canvas.get_preset_param():
yield "data:" + json.dumps({"code": 0,
"message": "",
"data": {
"session_id": session_id,
"answer": "",
"reference": [],
"param": canvas.get_preset_param()
}
},
ensure_ascii=False) + "\n\n"
yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n"
return
conv = API4Conversation(**conv) conv = API4Conversation(**conv)
else: else:
e, conv = API4ConversationService.get_by_id(session_id) e, conv = API4ConversationService.get_by_id(session_id)
@ -104,7 +107,6 @@ def completion(tenant_id, agent_id, question, session_id=None, stream=True, **kw
conv.reference.append({"chunks": [], "doc_aggs": []}) conv.reference.append({"chunks": [], "doc_aggs": []})
final_ans = {"reference": [], "content": ""} final_ans = {"reference": [], "content": ""}
if stream: if stream:
try: try:
for ans in canvas.run(stream=stream): for ans in canvas.run(stream=stream):

View File

@ -86,8 +86,9 @@ def completion(tenant_id, chat_id, question, name="New session", session_id=None
assert dia, "You do not own the chat." assert dia, "You do not own the chat."
if not session_id: if not session_id:
session_id = get_uuid()
conv = { conv = {
"id": get_uuid(), "id":session_id ,
"dialog_id": chat_id, "dialog_id": chat_id,
"name": name, "name": name,
"message": [{"role": "assistant", "content": dia[0].prompt_config.get("prologue")}] "message": [{"role": "assistant", "content": dia[0].prompt_config.get("prologue")}]

View File

@ -2015,11 +2015,20 @@ curl --request POST \
--header 'Authorization: Bearer <YOUR_API_KEY>' \ --header 'Authorization: Bearer <YOUR_API_KEY>' \
--data-binary ' --data-binary '
{ {
"question": "What is RAGFlow?",
"stream": true
}' }'
``` ```
```bash
curl --request POST \
--url http://{address}/api/v1/chats/{chat_id}/completions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--data-binary '
{
"question": "Who are you",
"stream": true,
"session_id":"9fa7691cb85c11ef9c5f0242ac120005"
}'
```
#### Request Parameters #### Request Parameters
- `chat_id`: (*Path parameter*) - `chat_id`: (*Path parameter*)
@ -2034,10 +2043,29 @@ curl --request POST \
The ID of session. If it is not provided, a new session will be generated. The ID of session. If it is not provided, a new session will be generated.
### Response ### Response
Success without `session_id`:
```text
data:{
"code": 0,
"message": "",
"data": {
"answer": "Hi! I'm your assistant, what can I do for you?",
"reference": {},
"audio_binary": null,
"id": null,
"session_id": "b01eed84b85611efa0e90242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": true
}
```
Success: Success with `session_id`:
```json ```text
data:{ data:{
"code": 0, "code": 0,
"data": { "data": {
@ -2121,6 +2149,7 @@ Failure:
--- ---
## Create session with agent ## Create session with agent
*If there are parameters in the `begin` component, the session cannot be created in this way.*
**POST** `/api/v1/agents/{agent_id}/sessions` **POST** `/api/v1/agents/{agent_id}/sessions`
@ -2159,16 +2188,101 @@ Success:
{ {
"code": 0, "code": 0,
"data": { "data": {
"agent_id": "2e45b5209c1011efa3e90242ac120006", "agent_id": "b4a39922b76611efaa1a0242ac120006",
"id": "7869e9e49c1711ef92840242ac120006", "dsl": {
"answer": [],
"components": {
"Answer:GreenReadersDrum": {
"downstream": [],
"obj": {
"component_name": "Answer",
"inputs": [],
"output": null,
"params": {}
},
"upstream": []
},
"begin": {
"downstream": [],
"obj": {
"component_name": "Begin",
"inputs": [],
"output": {},
"params": {}
},
"upstream": []
}
},
"embed_id": "",
"graph": {
"edges": [],
"nodes": [
{
"data": {
"label": "Begin",
"name": "begin"
},
"dragging": false,
"height": 44,
"id": "begin",
"position": {
"x": 53.25688640427177,
"y": 198.37155679786412
},
"positionAbsolute": {
"x": 53.25688640427177,
"y": 198.37155679786412
},
"selected": false,
"sourcePosition": "left",
"targetPosition": "right",
"type": "beginNode",
"width": 200
},
{
"data": {
"form": {},
"label": "Answer",
"name": "对话_0"
},
"dragging": false,
"height": 44,
"id": "Answer:GreenReadersDrum",
"position": {
"x": 360.43473114516974,
"y": 207.29298425089348
},
"positionAbsolute": {
"x": 360.43473114516974,
"y": 207.29298425089348
},
"selected": false,
"sourcePosition": "right",
"targetPosition": "left",
"type": "logicNode",
"width": 200
}
]
},
"history": [],
"messages": [],
"path": [
[
"begin"
],
[]
],
"reference": []
},
"id": "2581031eb7a311efb5200242ac120005",
"message": [ "message": [
{ {
"content": "Hello! I am a recruiter at InfiniFlow. I learned that you are an expert in the field, and took the liberty of reaching out to you. There is an opportunity I would like to share with you. RAGFlow is currently looking for a senior engineer for your position. I was wondering if you might be interested?", "content": "Hi! I'm your smart assistant. What can I do for you?",
"role": "assistant" "role": "assistant"
} }
], ],
"source": "agent", "source": "agent",
"user_id": "" "user_id": "69736c5e723611efb51b0242ac120007"
} }
} }
``` ```
@ -2216,7 +2330,7 @@ Asks a specified agent a question to start an AI-powered conversation.
- `"question"`: `string` - `"question"`: `string`
- `"stream"`: `boolean` - `"stream"`: `boolean`
- `"session_id"`: `string` - `"session_id"`: `string`
- other parameters: `string`
#### Request example #### Request example
```bash ```bash
@ -2226,10 +2340,32 @@ curl --request POST \
--header 'Authorization: Bearer <YOUR_API_KEY>' \ --header 'Authorization: Bearer <YOUR_API_KEY>' \
--data-binary ' --data-binary '
{ {
"question": "What is RAGFlow?",
"stream": true
}' }'
``` ```
```bash
curl --request POST \
--url http://{address}/api/v1/agents/{agent_id}/completions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--data-binary '
{
"question": "Hello",
"stream": true,
"session_id": "cb2f385cb86211efa36e0242ac120005"
}'
```
```bash
curl --request POST \
--url http://{address}/api/v1/agents/{agent_id}/completions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--data-binary '
{
"lang":"English"
"file":"明天天气如何"
}'
```
#### Request Parameters #### Request Parameters
@ -2243,10 +2379,28 @@ curl --request POST \
- `false`: Disable streaming. - `false`: Disable streaming.
- `"session_id"`: (*Body Parameter*) - `"session_id"`: (*Body Parameter*)
The ID of the session. If it is not provided, a new session will be generated. The ID of the session. If it is not provided, a new session will be generated.
- Other parameters: (*Body Parameter*)
The parameters in the begin component.
### Response ### Response
success without `session_id` provided and with no parameters in the `begin` component:
Success: ```text
data:{
"code": 0,
"message": "",
"data": {
"answer": "Hi! I'm your smart assistant. What can I do for you?",
"reference": {},
"id": "31e6091d-88d4-441b-ac65-eae1c055be7b",
"session_id": "2987ad3eb85f11efb2a70242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": true
}
```
Success with `session_id` provided and with no parameters in the `begin` component:
```text ```text
data:{ data:{
@ -2354,6 +2508,85 @@ data:{
"data": true "data": true
} }
``` ```
Success with parameters in the `begin` component:
```text
data:{
"code": 0,
"message": "",
"data": {
"answer": "How",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": {
"answer": "How is",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": {
"answer": "How is the",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": {
"answer": "How is the weather",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": {
"answer": "How is the weather tomorrow",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": {
"answer": "How is the weather tomorrow?",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": {
"answer": "How is the weather tomorrow?",
"reference": {},
"id": "0379ac4c-b26b-4a44-8b77-99cebf313fdf",
"session_id": "4399c7d0b86311efac5b0242ac120005"
}
}
data:{
"code": 0,
"message": "",
"data": true
}
```
Failure: Failure:

View File

@ -14,6 +14,13 @@ Dataset Management
::: :::
--- ---
### Install the RAGFlow SDK
To install the RAGFlow SDK, run the following command in your terminal:
```bash
pip install ragflow-sdk
```
## Create dataset ## Create dataset
@ -1401,7 +1408,7 @@ while True:
--- ---
## Create session with agent ## Create session with agent
*If there are parameters in the `begin` component, the session cannot be created in this way.*
```python ```python
Agent.create_session(id,rag) -> Session Agent.create_session(id,rag) -> Session
``` ```
@ -1428,7 +1435,7 @@ session = create_session(AGENT_ID,rag_object)
--- ---
## Converse with agent ## Converse with agent without `begin` component
```python ```python
Session.ask(question: str, stream: bool = False) -> Optional[Message, iter[Message]] Session.ask(question: str, stream: bool = False) -> Optional[Message, iter[Message]]

View File

@ -1,7 +1,9 @@
from .base import Base from .base import Base
from .session import Session from .session import Session,Message
import requests import requests
from typing import List from typing import List
import json
class Agent(Base): class Agent(Base):
def __init__(self,rag,res_dict): def __init__(self,rag,res_dict):
@ -73,3 +75,30 @@ class Agent(Base):
result_list.append(temp_agent) result_list.append(temp_agent)
return result_list return result_list
raise Exception(res.get("message")) raise Exception(res.get("message"))
@staticmethod
def ask(agent_id,rag,stream=True,**kwargs):
url = f"{rag.api_url}/agents/{agent_id}/completions"
headers = {"Authorization": f"Bearer {rag.user_key}"}
res = requests.post(url=url, headers=headers, json=kwargs,stream=stream)
for line in res.iter_lines():
line = line.decode("utf-8")
if line.startswith("{"):
json_data = json.loads(line)
raise Exception(json_data["message"])
if line.startswith("data:"):
json_data = json.loads(line[5:])
if json_data["data"] is not True:
if json_data["data"].get("running_status"):
continue
answer = json_data["data"]["answer"]
reference = json_data["data"]["reference"]
temp_dict = {
"content": answer,
"role": "assistant"
}
if "chunks" in reference:
chunks = reference["chunks"]
temp_dict["reference"] = chunks
message = Message(rag, temp_dict)
yield message

View File

@ -29,7 +29,7 @@ class Session(Base):
raise Exception(json_data["message"]) raise Exception(json_data["message"])
if line.startswith("data:"): if line.startswith("data:"):
json_data = json.loads(line[5:]) json_data = json.loads(line[5:])
if not json_data["data"]: if json_data["data"] is not True:
answer = json_data["data"]["answer"] answer = json_data["data"]["answer"]
reference = json_data["data"]["reference"] reference = json_data["data"]["reference"]
temp_dict = { temp_dict = {

View File

@ -1,4 +1,4 @@
from ragflow_sdk import RAGFlow from ragflow_sdk import RAGFlow,Agent
from common import HOST_ADDRESS from common import HOST_ADDRESS
import pytest import pytest
@ -6,4 +6,14 @@ import pytest
def test_list_agents_with_success(get_api_key_fixture): def test_list_agents_with_success(get_api_key_fixture):
API_KEY=get_api_key_fixture API_KEY=get_api_key_fixture
rag = RAGFlow(API_KEY,HOST_ADDRESS) rag = RAGFlow(API_KEY,HOST_ADDRESS)
rag.list_agents() rag.list_agents()
@pytest.mark.skip(reason="")
def test_converse_with_agent_with_success(get_api_key_fixture):
API_KEY = "ragflow-BkOGNhYjIyN2JiODExZWY5MzVhMDI0Mm"
agent_id = "ebfada2eb2bc11ef968a0242ac120006"
rag = RAGFlow(API_KEY,HOST_ADDRESS)
lang = "Chinese"
file = "How is the weather tomorrow?"
Agent.ask(agent_id=agent_id,rag=rag,lang=lang,file=file)