diff --git a/agent/canvas.py b/agent/canvas.py index a0526b552e3..3d66cc436b9 100644 --- a/agent/canvas.py +++ b/agent/canvas.py @@ -185,6 +185,7 @@ def run(self, **kwargs): self.path.append(["begin"]) self.path.append([]) + ran = -1 waiting = [] without_dependent_checking = [] diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 5307a2a676f..e074462b939 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -73,6 +73,8 @@ def create_agent_session(tenant_id, agent_id): cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) 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 = { "id": get_uuid(), "dialog_id": cvs.id, @@ -112,6 +114,8 @@ def update(tenant_id, chat_id, session_id): @token_required def chat_completion(tenant_id, chat_id): 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): return get_error_data_result(f"You don't own the chat {chat_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") return resp - else: answer = None for ans in rag_completion(tenant_id, chat_id, **req): @@ -137,22 +140,28 @@ def chat_completion(tenant_id, chat_id): @manager.route('/agents//completions', methods=['POST']) # noqa: F821 @token_required def agent_completions(tenant_id, agent_id): - req = request.json - if not UserCanvasService.query(user_id=tenant_id,id=agent_id): - return get_error_data_result(f"You don't own the agent {agent_id}") - if req.get("session_id"): - if not API4ConversationService.query(id=req["session_id"],dialog_id=agent_id): - return get_error_data_result(f"You don't own the session {req['session_id']}") - if req.get("stream", True): - resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream") - resp.headers.add_header("Cache-control", "no-cache") - resp.headers.add_header("Connection", "keep-alive") - resp.headers.add_header("X-Accel-Buffering", "no") - resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") - return resp - - for answer in agent_completion(tenant_id, agent_id, **req): - return get_result(data=answer) + req = request.json + cvs = UserCanvasService.query(user_id=tenant_id, id=agent_id) + if not cvs: + return get_error_data_result(f"You don't own the agent {agent_id}") + if req.get("session_id"): + conv = API4ConversationService.query(id=req["session_id"], dialog_id=agent_id) + if not conv: + return get_error_data_result(f"You don't own the session {req['session_id']}") + else: + req["question"]="" + if req.get("stream", True): + resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream") + resp.headers.add_header("Cache-control", "no-cache") + resp.headers.add_header("Connection", "keep-alive") + resp.headers.add_header("X-Accel-Buffering", "no") + 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//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): return get_result(data=answer) + + diff --git a/api/db/services/canvas_service.py b/api/db/services/canvas_service.py index ac867334602..3da151e75aa 100644 --- a/api/db/services/canvas_service.py +++ b/api/db/services/canvas_service.py @@ -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) assert e, "Agent not found." 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) canvas = Canvas(cvs.dsl, tenant_id) canvas.reset() message_id = str(uuid4()) - 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() conv = { "id": session_id, "dialog_id": cvs.id, "user_id": kwargs.get("user_id", ""), "source": "agent", - "dsl": json.loads(cvs.dsl) + "dsl": temp_dsl } 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) else: 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": []}) final_ans = {"reference": [], "content": ""} - if stream: try: for ans in canvas.run(stream=stream): diff --git a/api/db/services/conversation_service.py b/api/db/services/conversation_service.py index b0a509b5a94..aeed139c29f 100644 --- a/api/db/services/conversation_service.py +++ b/api/db/services/conversation_service.py @@ -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." if not session_id: + session_id = get_uuid() conv = { - "id": get_uuid(), + "id":session_id , "dialog_id": chat_id, "name": name, "message": [{"role": "assistant", "content": dia[0].prompt_config.get("prologue")}] diff --git a/docs/references/http_api_reference.md b/docs/references/http_api_reference.md index b93f7435688..10505402d5e 100644 --- a/docs/references/http_api_reference.md +++ b/docs/references/http_api_reference.md @@ -2015,11 +2015,20 @@ curl --request POST \ --header 'Authorization: Bearer ' \ --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 ' \ + --data-binary ' + { + "question": "Who are you", + "stream": true, + "session_id":"9fa7691cb85c11ef9c5f0242ac120005" + }' +``` #### Request Parameters - `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. ### 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:{ "code": 0, "data": { @@ -2121,6 +2149,7 @@ Failure: --- ## 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` @@ -2159,16 +2188,101 @@ Success: { "code": 0, "data": { - "agent_id": "2e45b5209c1011efa3e90242ac120006", - "id": "7869e9e49c1711ef92840242ac120006", + "agent_id": "b4a39922b76611efaa1a0242ac120006", + "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": [ { - "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" } ], "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` - `"stream"`: `boolean` - `"session_id"`: `string` - + - other parameters: `string` #### Request example ```bash @@ -2226,11 +2340,33 @@ curl --request POST \ --header 'Authorization: Bearer ' \ --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 ' \ + --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 ' \ + --data-binary ' + { + "lang":"English" + "file":"明天天气如何" }' ``` + #### Request Parameters - `agent_id`: (*Path parameter*), `string` @@ -2243,10 +2379,28 @@ curl --request POST \ - `false`: Disable streaming. - `"session_id"`: (*Body Parameter*) 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 - -Success: +success without `session_id` provided and with no parameters in the `begin` component: +```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 data:{ @@ -2354,6 +2508,85 @@ data:{ "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: diff --git a/docs/references/python_api_reference.md b/docs/references/python_api_reference.md index e4cc46046fd..f1ac5ce05f0 100644 --- a/docs/references/python_api_reference.md +++ b/docs/references/python_api_reference.md @@ -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 @@ -1401,7 +1408,7 @@ while True: --- ## Create session with agent - +*If there are parameters in the `begin` component, the session cannot be created in this way.* ```python 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 Session.ask(question: str, stream: bool = False) -> Optional[Message, iter[Message]] diff --git a/sdk/python/ragflow_sdk/modules/agent.py b/sdk/python/ragflow_sdk/modules/agent.py index 3818bef6e2a..cb3a8cd11a2 100644 --- a/sdk/python/ragflow_sdk/modules/agent.py +++ b/sdk/python/ragflow_sdk/modules/agent.py @@ -1,7 +1,9 @@ from .base import Base -from .session import Session +from .session import Session,Message import requests from typing import List +import json + class Agent(Base): def __init__(self,rag,res_dict): @@ -73,3 +75,30 @@ def list_sessions(agent_id,rag,page: int = 1, page_size: int = 30, orderby: str result_list.append(temp_agent) return result_list 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 diff --git a/sdk/python/ragflow_sdk/modules/session.py b/sdk/python/ragflow_sdk/modules/session.py index 539c1ce89ba..ffb2b55cab4 100644 --- a/sdk/python/ragflow_sdk/modules/session.py +++ b/sdk/python/ragflow_sdk/modules/session.py @@ -29,7 +29,7 @@ def ask(self, question,stream=True): raise Exception(json_data["message"]) if line.startswith("data:"): json_data = json.loads(line[5:]) - if not json_data["data"]: + if json_data["data"] is not True: answer = json_data["data"]["answer"] reference = json_data["data"]["reference"] temp_dict = { diff --git a/sdk/python/test/test_sdk_api/t_agent.py b/sdk/python/test/test_sdk_api/t_agent.py index f9f29c7e32d..aa330e52bc9 100644 --- a/sdk/python/test/test_sdk_api/t_agent.py +++ b/sdk/python/test/test_sdk_api/t_agent.py @@ -1,4 +1,4 @@ -from ragflow_sdk import RAGFlow +from ragflow_sdk import RAGFlow,Agent from common import HOST_ADDRESS import pytest @@ -6,4 +6,14 @@ def test_list_agents_with_success(get_api_key_fixture): API_KEY=get_api_key_fixture rag = RAGFlow(API_KEY,HOST_ADDRESS) - rag.list_agents() \ No newline at end of file + 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)