mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-11-10 21:32:43 +08:00
Feat: Agent api (#14157)
### What problem does this PR solve?
1. **List agents**
**Prev API**:
- `/v1/canvas/list GET`
- `/api/v1/agents GET`
**Current API**: `/api/v2/agents GET`
2. **Get canvas template**
**Prev API**: `/v1/canvas/templates GET`
**Current API**: `/api/v2/agents/templates GET`
3. **Delete an agent**
**Prev API**:
- `/v1/canvas/rm POST`
- `/api/v1/agents/<agent_id> DELETE`
**Current API**: `/api/v2/agents/<agent_id> DELETE`
4. **Update an agent**
**Prev API**:
- `/api/v1/agents/<agent_id> PUT`
- `/v1/canvas/setting POST `
**Current API**: `/api/v2/agents/<agent_id> PATCH`
5. **Create an agent**
**Prev API**:
- `/v1/canvas/set POST`
- `/api/v1/agents POST`
**Current API**: `/api/v2/agents POST`
6. **Get an agent**
**Prev API**:
- `/v1/canvas/get/<canvas_id> GET `
**Current API**: `/api/v2/agents/<agent_id> GET`
7. **Reset an agent**
**Prev API**:
- `/v1/canvas/reset POST`
**Current API**: `/api/v2/agents/<agent_id>/reset POST`
8. **Upload a file to an agent**
**Prev API**:
- `/v1/canvas/upload/<canvas_id> POST`
**Current API**: `/api/v2/agents/<agent_id>/upload POST`
9. **Input form**
**Prev API**:
- `/v1/canvas/input_form GET`
**Current API**:
`/api/v2/agents/<agent_id>/components/<component_id>/input-form GET`
10. **Debug an agent**
**Prev API**:
- `/v1/canvas/debug POST`
**Current API**:
`/api/v2/agents/<agent_id>/components/<component_id>/debug POST`
11. **Trace an agent**
**Prev API**:
- `/v1/canvas/trace GET`
**Current API**: `/api/v2/agents/<agent_id>/logs/<message_id> GET`
12. **Get an agent version list**
**Prev API**:
- `/v1/canvas/getlistversion/<canvas_id>`
**Current API**: `/api/v2/agents/<agent_id>/versions GET`
13. **Get a version of agent**
**Prev API**:
- `/v1/canvas/getversion/<version_id>`
**Current API**: `/api/v2/agents/<agent_id>/versions/<version_id> GET`
14. **Test db connection**
**Prev API**:
- `/v1/canvas/test_db_connect POST`
**Current API**: `/api/v2/agents/test_db_connection`
15. **Rerun the agent**
**Prev API**:
- `/v1/canvas/rerun POST`
**Current API**: `/api/v2/agents/rerun POST`
16. **Get prompts**
**Prev API**:
- `/v1/canvas/prompts GET`
**Current API**: `/api/v2/agents/prompts GET`
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: chanx <1243304602@qq.com>
This commit is contained in:
1 parent
d84438fd53
commit
c74aece63c
34 files changed
+1819
-4671
No files matched your search
+2
-728
@@ -13,330 +13,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
import copy
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
from functools import partial
|
||||
from quart import request, Response, make_response
|
||||
from agent.component import LLM
|
||||
from api.db import CanvasCategory
|
||||
from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService, API4ConversationService
|
||||
from api.db.services.document_service import DocumentService
|
||||
from api.db.services.file_service import FileService
|
||||
from api.db.services.knowledgebase_service import KnowledgebaseService
|
||||
from api.db.services.pipeline_operation_log_service import PipelineOperationLogService
|
||||
from api.db.services.task_service import queue_dataflow, CANVAS_DEBUG_DOC_ID, TaskService
|
||||
from api.db.services.user_service import TenantService
|
||||
from api.db.services.user_canvas_version import UserCanvasVersionService
|
||||
from common.constants import RetCode
|
||||
from common.misc_utils import get_uuid, thread_pool_exec
|
||||
from api.utils.api_utils import (
|
||||
get_json_result,
|
||||
server_error_response,
|
||||
validate_request,
|
||||
get_data_error_result,
|
||||
get_request_json,
|
||||
)
|
||||
from agent.canvas import Canvas
|
||||
from agent.dsl_migration import normalize_chunker_dsl
|
||||
from peewee import MySQLDatabase, PostgresqlDatabase
|
||||
from api.db.db_models import APIToken, Task
|
||||
|
||||
from rag.flow.pipeline import Pipeline
|
||||
from rag.nlp import search
|
||||
from api.utils.api_utils import get_json_result
|
||||
from rag.utils.redis_conn import REDIS_CONN
|
||||
from common import settings
|
||||
from api.apps import login_required, current_user
|
||||
from api.apps.services.canvas_replica_service import CanvasReplicaService
|
||||
from api.db.services.canvas_service import completion as agent_completion
|
||||
|
||||
|
||||
@manager.route('/templates', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def templates():
|
||||
return get_json_result(data=[c.to_dict() for c in CanvasTemplateService.get_all()])
|
||||
|
||||
|
||||
@manager.route('/rm', methods=['POST']) # noqa: F821
|
||||
@validate_request("canvas_ids")
|
||||
@login_required
|
||||
async def rm():
|
||||
req = await get_request_json()
|
||||
for i in req["canvas_ids"]:
|
||||
if not UserCanvasService.accessible(i, current_user.id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
UserCanvasService.delete_by_id(i)
|
||||
return get_json_result(data=True)
|
||||
|
||||
|
||||
@manager.route('/set', methods=['POST']) # noqa: F821
|
||||
@validate_request("dsl", "title")
|
||||
@login_required
|
||||
async def save():
|
||||
req = await get_request_json()
|
||||
req['release'] = bool(req.get("release", ""))
|
||||
try:
|
||||
req["dsl"] = CanvasReplicaService.normalize_dsl(req["dsl"])
|
||||
except ValueError as e:
|
||||
return get_data_error_result(message=str(e))
|
||||
cate = req.get("canvas_category", CanvasCategory.Agent)
|
||||
if "id" not in req:
|
||||
req["user_id"] = current_user.id
|
||||
if UserCanvasService.query(user_id=current_user.id, title=req["title"].strip(), canvas_category=cate):
|
||||
return get_data_error_result(message=f"{req['title'].strip()} already exists.")
|
||||
req["id"] = get_uuid()
|
||||
if not UserCanvasService.save(**req):
|
||||
return get_data_error_result(message="Fail to save canvas.")
|
||||
else:
|
||||
if not UserCanvasService.accessible(req["id"], current_user.id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
UserCanvasService.update_by_id(req["id"], req)
|
||||
# save version
|
||||
UserCanvasVersionService.save_or_replace_latest(
|
||||
user_canvas_id=req["id"],
|
||||
dsl=req["dsl"],
|
||||
title=UserCanvasVersionService.build_version_title(getattr(current_user, "nickname", current_user.id), req.get("title")),
|
||||
release=req.get("release"),
|
||||
)
|
||||
replica_ok = CanvasReplicaService.replace_for_set(
|
||||
canvas_id=req["id"],
|
||||
tenant_id=str(current_user.id),
|
||||
runtime_user_id=str(current_user.id),
|
||||
dsl=req["dsl"],
|
||||
canvas_category=req.get("canvas_category", cate),
|
||||
title=req.get("title", ""),
|
||||
)
|
||||
if not replica_ok:
|
||||
return get_data_error_result(message="canvas saved, but replica sync failed.")
|
||||
return get_json_result(data=req)
|
||||
|
||||
|
||||
@manager.route('/get/<canvas_id>', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def get(canvas_id):
|
||||
if not UserCanvasService.accessible(canvas_id, current_user.id):
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
e, c = UserCanvasService.get_by_canvas_id(canvas_id)
|
||||
if not e:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
try:
|
||||
# DELETE
|
||||
CanvasReplicaService.bootstrap(
|
||||
canvas_id=canvas_id,
|
||||
tenant_id=str(current_user.id),
|
||||
runtime_user_id=str(current_user.id),
|
||||
dsl=c.get("dsl"),
|
||||
canvas_category=c.get("canvas_category", CanvasCategory.Agent),
|
||||
title=c.get("title", ""),
|
||||
)
|
||||
except ValueError as e:
|
||||
return get_data_error_result(message=str(e))
|
||||
|
||||
# Get the last publication time (latest released version's update_time)
|
||||
last_publish_time = None
|
||||
versions = UserCanvasVersionService.list_by_canvas_id(canvas_id)
|
||||
if versions:
|
||||
released_versions = [v for v in versions if v.release]
|
||||
if released_versions:
|
||||
# Sort by update_time descending and get the latest
|
||||
released_versions.sort(key=lambda x: x.update_time, reverse=True)
|
||||
last_publish_time = released_versions[0].update_time
|
||||
|
||||
# Add last_publish_time to response data
|
||||
if isinstance(c, dict):
|
||||
c["dsl"] = normalize_chunker_dsl(c.get("dsl", {}))
|
||||
c["last_publish_time"] = last_publish_time
|
||||
else:
|
||||
# If c is a model object, convert to dict first
|
||||
c = c.to_dict()
|
||||
c["dsl"] = normalize_chunker_dsl(c.get("dsl", {}))
|
||||
c["last_publish_time"] = last_publish_time
|
||||
|
||||
# For pipeline type, get associated datasets
|
||||
if c.get("canvas_category") == CanvasCategory.DataFlow:
|
||||
datasets = list(KnowledgebaseService.query(pipeline_id=canvas_id))
|
||||
c["datasets"] = [{"id": d.id, "name": d.name, "avatar": d.avatar} for d in datasets]
|
||||
|
||||
return get_json_result(data=c)
|
||||
|
||||
|
||||
@manager.route('/getsse/<canvas_id>', methods=['GET']) # type: ignore # noqa: F821
|
||||
def getsse(canvas_id):
|
||||
token = request.headers.get('Authorization').split()
|
||||
if len(token) != 2:
|
||||
return get_data_error_result(message='Authorization is not valid!')
|
||||
token = token[1]
|
||||
objs = APIToken.query(beta=token)
|
||||
if not objs:
|
||||
return get_data_error_result(message='Authentication error: API key is invalid!"')
|
||||
tenant_id = objs[0].tenant_id
|
||||
if not UserCanvasService.query(user_id=tenant_id, id=canvas_id):
|
||||
return get_json_result(
|
||||
data=False,
|
||||
message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR
|
||||
)
|
||||
e, c = UserCanvasService.get_by_id(canvas_id)
|
||||
if not e or c.user_id != tenant_id:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
return get_json_result(data=c.to_dict())
|
||||
|
||||
|
||||
@manager.route('/completion', methods=['POST']) # noqa: F821
|
||||
@validate_request("id")
|
||||
@login_required
|
||||
async def run():
|
||||
req = await get_request_json()
|
||||
query = req.get("query", "")
|
||||
files = req.get("files", [])
|
||||
inputs = req.get("inputs", {})
|
||||
tenant_id = str(current_user.id)
|
||||
runtime_user_id = req.get("user_id") or tenant_id
|
||||
user_id = str(runtime_user_id)
|
||||
if not await thread_pool_exec(UserCanvasService.accessible, req["id"], tenant_id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
|
||||
replica_payload = CanvasReplicaService.load_for_run(
|
||||
canvas_id=req["id"],
|
||||
tenant_id=tenant_id,
|
||||
runtime_user_id=user_id,
|
||||
)
|
||||
|
||||
if not replica_payload:
|
||||
return get_data_error_result(message="canvas replica not found, please call /get/<canvas_id> first.")
|
||||
|
||||
replica_dsl = replica_payload.get("dsl", {})
|
||||
canvas_title = replica_payload.get("title", "")
|
||||
canvas_category = replica_payload.get("canvas_category", CanvasCategory.Agent)
|
||||
dsl_str = json.dumps(replica_dsl, ensure_ascii=False)
|
||||
|
||||
_, cvs = await thread_pool_exec(UserCanvasService.get_by_id, req["id"])
|
||||
if cvs.canvas_category == CanvasCategory.DataFlow:
|
||||
task_id = get_uuid()
|
||||
Pipeline(dsl_str, tenant_id=tenant_id, doc_id=CANVAS_DEBUG_DOC_ID, task_id=task_id, flow_id=req["id"])
|
||||
ok, error_message = await thread_pool_exec(queue_dataflow, user_id, req["id"], task_id, CANVAS_DEBUG_DOC_ID, files[0], 0)
|
||||
if not ok:
|
||||
return get_data_error_result(message=error_message)
|
||||
return get_json_result(data={"message_id": task_id})
|
||||
|
||||
try:
|
||||
canvas = Canvas(dsl_str, tenant_id, canvas_id=req["id"])
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
async def sse():
|
||||
nonlocal canvas, user_id
|
||||
try:
|
||||
async for ans in canvas.run(query=query, files=files, user_id=user_id, inputs=inputs):
|
||||
yield "data:" + json.dumps(ans, ensure_ascii=False) + "\n\n"
|
||||
|
||||
commit_ok = CanvasReplicaService.commit_after_run(
|
||||
canvas_id=req["id"],
|
||||
tenant_id=tenant_id,
|
||||
runtime_user_id=user_id,
|
||||
dsl=json.loads(str(canvas)),
|
||||
canvas_category=canvas_category,
|
||||
title=canvas_title,
|
||||
)
|
||||
if not commit_ok:
|
||||
logging.error(
|
||||
"Canvas runtime replica commit failed: canvas_id=%s tenant_id=%s runtime_user_id=%s",
|
||||
req["id"],
|
||||
tenant_id,
|
||||
user_id,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
canvas.cancel_task()
|
||||
yield "data:" + json.dumps({"code": 500, "message": str(e), "data": False}, ensure_ascii=False) + "\n\n"
|
||||
|
||||
resp = Response(sse(), 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")
|
||||
#resp.call_on_close(lambda: canvas.cancel_task())
|
||||
return resp
|
||||
|
||||
|
||||
@manager.route("/<canvas_id>/completion", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
async def exp_agent_completion(canvas_id):
|
||||
tenant_id = current_user.id
|
||||
req = await get_request_json()
|
||||
return_trace = bool(req.get("return_trace", False))
|
||||
async def generate():
|
||||
trace_items = []
|
||||
async for answer in agent_completion(tenant_id=tenant_id, agent_id=canvas_id, **req):
|
||||
if isinstance(answer, str):
|
||||
try:
|
||||
ans = json.loads(answer[5:]) # remove "data:"
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
event = ans.get("event")
|
||||
if event == "node_finished":
|
||||
if return_trace:
|
||||
data = ans.get("data", {})
|
||||
trace_items.append(
|
||||
{
|
||||
"component_id": data.get("component_id"),
|
||||
"trace": [copy.deepcopy(data)],
|
||||
}
|
||||
)
|
||||
ans.setdefault("data", {})["trace"] = trace_items
|
||||
answer = "data:" + json.dumps(ans, ensure_ascii=False) + "\n\n"
|
||||
yield answer
|
||||
|
||||
if event not in ["message", "message_end"]:
|
||||
continue
|
||||
|
||||
yield answer
|
||||
|
||||
yield "data:[DONE]\n\n"
|
||||
|
||||
resp = Response(generate(), 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
|
||||
|
||||
|
||||
@manager.route('/rerun', methods=['POST']) # noqa: F821
|
||||
@validate_request("id", "dsl", "component_id")
|
||||
@login_required
|
||||
async def rerun():
|
||||
req = await get_request_json()
|
||||
doc = PipelineOperationLogService.get_documents_info(req["id"])
|
||||
if not doc:
|
||||
return get_data_error_result(message="Document not found.")
|
||||
doc = doc[0]
|
||||
if 0 < doc["progress"] < 1:
|
||||
return get_data_error_result(message=f"`{doc['name']}` is processing...")
|
||||
|
||||
if settings.docStoreConn.index_exist(search.index_name(current_user.id), doc["kb_id"]):
|
||||
settings.docStoreConn.delete({"doc_id": doc["id"]}, search.index_name(current_user.id), doc["kb_id"])
|
||||
doc["progress_msg"] = ""
|
||||
doc["chunk_num"] = 0
|
||||
doc["token_num"] = 0
|
||||
DocumentService.clear_chunk_num_when_rerun(doc["id"])
|
||||
DocumentService.update_by_id(id, doc)
|
||||
TaskService.filter_delete([Task.doc_id == id])
|
||||
|
||||
dsl = req["dsl"]
|
||||
dsl["path"] = [req["component_id"]]
|
||||
PipelineOperationLogService.update_by_id(req["id"], {"dsl": dsl})
|
||||
queue_dataflow(tenant_id=current_user.id, flow_id=req["id"], task_id=get_uuid(), doc_id=doc["id"], priority=0, rerun=True)
|
||||
return get_json_result(data=True)
|
||||
from api.apps import login_required
|
||||
|
||||
|
||||
@manager.route('/cancel/<task_id>', methods=['PUT']) # noqa: F821
|
||||
@@ -347,409 +27,3 @@ def cancel(task_id):
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
return get_json_result(data=True)
|
||||
|
||||
|
||||
@manager.route('/reset', methods=['POST']) # noqa: F821
|
||||
@validate_request("id")
|
||||
@login_required
|
||||
async def reset():
|
||||
req = await get_request_json()
|
||||
if not UserCanvasService.accessible(req["id"], current_user.id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
try:
|
||||
e, user_canvas = UserCanvasService.get_by_id(req["id"])
|
||||
if not e:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
|
||||
canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id, canvas_id=user_canvas.id)
|
||||
canvas.reset()
|
||||
req["dsl"] = json.loads(str(canvas))
|
||||
UserCanvasService.update_by_id(req["id"], {"dsl": req["dsl"]})
|
||||
return get_json_result(data=req["dsl"])
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route("/upload/<canvas_id>", methods=["POST"]) # noqa: F821
|
||||
async def upload(canvas_id):
|
||||
e, cvs = UserCanvasService.get_by_canvas_id(canvas_id)
|
||||
if not e:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
|
||||
user_id = cvs["user_id"]
|
||||
files = await request.files
|
||||
file_objs = files.getlist("file") if files and files.get("file") else []
|
||||
try:
|
||||
if len(file_objs) == 1:
|
||||
return get_json_result(data=FileService.upload_info(user_id, file_objs[0], request.args.get("url")))
|
||||
results = [FileService.upload_info(user_id, f) for f in file_objs]
|
||||
return get_json_result(data=results)
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route('/input_form', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def input_form():
|
||||
cvs_id = request.args.get("id")
|
||||
cpn_id = request.args.get("component_id")
|
||||
try:
|
||||
e, user_canvas = UserCanvasService.get_by_id(cvs_id)
|
||||
if not e:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
if not UserCanvasService.query(user_id=current_user.id, id=cvs_id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
|
||||
canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id, canvas_id=user_canvas.id)
|
||||
return get_json_result(data=canvas.get_component_input_form(cpn_id))
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route('/debug', methods=['POST']) # noqa: F821
|
||||
@validate_request("id", "component_id", "params")
|
||||
@login_required
|
||||
async def debug():
|
||||
req = await get_request_json()
|
||||
if not UserCanvasService.accessible(req["id"], current_user.id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
try:
|
||||
e, user_canvas = UserCanvasService.get_by_id(req["id"])
|
||||
canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id, canvas_id=user_canvas.id)
|
||||
canvas.reset()
|
||||
canvas.message_id = get_uuid()
|
||||
component = canvas.get_component(req["component_id"])["obj"]
|
||||
component.reset()
|
||||
|
||||
if isinstance(component, LLM):
|
||||
component.set_debug_inputs(req["params"])
|
||||
component.invoke(**{k: o["value"] for k,o in req["params"].items()})
|
||||
outputs = component.output()
|
||||
for k in outputs.keys():
|
||||
if isinstance(outputs[k], partial):
|
||||
txt = ""
|
||||
iter_obj = outputs[k]()
|
||||
if inspect.isasyncgen(iter_obj):
|
||||
async for c in iter_obj:
|
||||
txt += c
|
||||
else:
|
||||
for c in iter_obj:
|
||||
txt += c
|
||||
outputs[k] = txt
|
||||
return get_json_result(data=outputs)
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route('/test_db_connect', methods=['POST']) # noqa: F821
|
||||
@validate_request("db_type", "database", "username", "host", "port", "password")
|
||||
@login_required
|
||||
async def test_db_connect():
|
||||
req = await get_request_json()
|
||||
try:
|
||||
if req["db_type"] in ["mysql", "mariadb"]:
|
||||
db = MySQLDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
|
||||
password=req["password"])
|
||||
elif req["db_type"] == "oceanbase":
|
||||
db = MySQLDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
|
||||
password=req["password"], charset="utf8mb4")
|
||||
elif req["db_type"] == 'postgres':
|
||||
db = PostgresqlDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
|
||||
password=req["password"])
|
||||
elif req["db_type"] == 'mssql':
|
||||
import pyodbc
|
||||
connection_string = (
|
||||
f"DRIVER={{ODBC Driver 17 for SQL Server}};"
|
||||
f"SERVER={req['host']},{req['port']};"
|
||||
f"DATABASE={req['database']};"
|
||||
f"UID={req['username']};"
|
||||
f"PWD={req['password']};"
|
||||
)
|
||||
db = pyodbc.connect(connection_string)
|
||||
cursor = db.cursor()
|
||||
cursor.execute("SELECT 1")
|
||||
cursor.close()
|
||||
elif req["db_type"] == 'IBM DB2':
|
||||
import ibm_db
|
||||
conn_str = (
|
||||
f"DATABASE={req['database']};"
|
||||
f"HOSTNAME={req['host']};"
|
||||
f"PORT={req['port']};"
|
||||
f"PROTOCOL=TCPIP;"
|
||||
f"UID={req['username']};"
|
||||
f"PWD={req['password']};"
|
||||
)
|
||||
redacted_conn_str = (
|
||||
f"DATABASE={req['database']};"
|
||||
f"HOSTNAME={req['host']};"
|
||||
f"PORT={req['port']};"
|
||||
f"PROTOCOL=TCPIP;"
|
||||
f"UID={req['username']};"
|
||||
f"PWD=****;"
|
||||
)
|
||||
logging.info(redacted_conn_str)
|
||||
conn = ibm_db.connect(conn_str, "", "")
|
||||
stmt = ibm_db.exec_immediate(conn, "SELECT 1 FROM sysibm.sysdummy1")
|
||||
ibm_db.fetch_assoc(stmt)
|
||||
ibm_db.close(conn)
|
||||
return get_json_result(data="Database Connection Successful!")
|
||||
elif req["db_type"] == 'trino':
|
||||
def _parse_catalog_schema(db_name: str):
|
||||
if not db_name:
|
||||
return None, None
|
||||
if "." in db_name:
|
||||
catalog_name, schema_name = db_name.split(".", 1)
|
||||
elif "/" in db_name:
|
||||
catalog_name, schema_name = db_name.split("/", 1)
|
||||
else:
|
||||
catalog_name, schema_name = db_name, "default"
|
||||
return catalog_name, schema_name
|
||||
try:
|
||||
import trino
|
||||
import os
|
||||
except Exception as e:
|
||||
return server_error_response(f"Missing dependency 'trino'. Please install: pip install trino, detail: {e}")
|
||||
|
||||
catalog, schema = _parse_catalog_schema(req["database"])
|
||||
if not catalog:
|
||||
return server_error_response("For Trino, 'database' must be 'catalog.schema' or at least 'catalog'.")
|
||||
|
||||
http_scheme = "https" if os.environ.get("TRINO_USE_TLS", "0") == "1" else "http"
|
||||
|
||||
auth = None
|
||||
if http_scheme == "https" and req.get("password"):
|
||||
auth = trino.BasicAuthentication(req.get("username") or "ragflow", req["password"])
|
||||
|
||||
conn = trino.dbapi.connect(
|
||||
host=req["host"],
|
||||
port=int(req["port"] or 8080),
|
||||
user=req["username"] or "ragflow",
|
||||
catalog=catalog,
|
||||
schema=schema or "default",
|
||||
http_scheme=http_scheme,
|
||||
auth=auth
|
||||
)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT 1")
|
||||
cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return get_json_result(data="Database Connection Successful!")
|
||||
else:
|
||||
return server_error_response("Unsupported database type.")
|
||||
if req["db_type"] != 'mssql':
|
||||
db.connect()
|
||||
db.close()
|
||||
|
||||
return get_json_result(data="Database Connection Successful!")
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
#api get list version dsl of canvas
|
||||
@manager.route('/getlistversion/<canvas_id>', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def getlistversion(canvas_id):
|
||||
try:
|
||||
versions =sorted([c.to_dict() for c in UserCanvasVersionService.list_by_canvas_id(canvas_id)], key=lambda x: x["update_time"]*-1)
|
||||
return get_json_result(data=versions)
|
||||
except Exception as e:
|
||||
return get_data_error_result(message=f"Error getting history files: {e}")
|
||||
|
||||
|
||||
#api get version dsl of canvas
|
||||
@manager.route('/getversion/<version_id>', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def getversion( version_id):
|
||||
try:
|
||||
e, version = UserCanvasVersionService.get_by_id(version_id)
|
||||
if version:
|
||||
return get_json_result(data=version.to_dict())
|
||||
except Exception as e:
|
||||
return get_json_result(data=f"Error getting history file: {e}")
|
||||
|
||||
|
||||
@manager.route('/list', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def list_canvas():
|
||||
keywords = request.args.get("keywords", "")
|
||||
page_number = int(request.args.get("page", 0))
|
||||
items_per_page = int(request.args.get("page_size", 0))
|
||||
orderby = request.args.get("orderby", "create_time")
|
||||
canvas_category = request.args.get("canvas_category")
|
||||
if request.args.get("desc", "true").lower() == "false":
|
||||
desc = False
|
||||
else:
|
||||
desc = True
|
||||
owner_ids = [id for id in request.args.get("owner_ids", "").strip().split(",") if id]
|
||||
if not owner_ids:
|
||||
tenants = TenantService.get_joined_tenants_by_user_id(current_user.id)
|
||||
tenants = [m["tenant_id"] for m in tenants]
|
||||
tenants.append(current_user.id)
|
||||
canvas, total = UserCanvasService.get_by_tenant_ids(
|
||||
tenants, current_user.id, page_number,
|
||||
items_per_page, orderby, desc, keywords, canvas_category)
|
||||
else:
|
||||
tenants = owner_ids
|
||||
canvas, total = UserCanvasService.get_by_tenant_ids(
|
||||
tenants, current_user.id, 0,
|
||||
0, orderby, desc, keywords, canvas_category)
|
||||
return get_json_result(data={"canvas": canvas, "total": total})
|
||||
|
||||
|
||||
@manager.route('/setting', methods=['POST']) # noqa: F821
|
||||
@validate_request("id", "title", "permission")
|
||||
@login_required
|
||||
async def setting():
|
||||
req = await get_request_json()
|
||||
req["user_id"] = current_user.id
|
||||
|
||||
if not UserCanvasService.accessible(req["id"], current_user.id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
|
||||
e,flow = UserCanvasService.get_by_id(req["id"])
|
||||
if not e:
|
||||
return get_data_error_result(message="canvas not found.")
|
||||
flow = flow.to_dict()
|
||||
flow["title"] = req["title"]
|
||||
|
||||
for key in ["description", "permission", "avatar"]:
|
||||
if value := req.get(key):
|
||||
flow[key] = value
|
||||
|
||||
num= UserCanvasService.update_by_id(req["id"], flow)
|
||||
return get_json_result(data=num)
|
||||
|
||||
|
||||
@manager.route('/trace', methods=['GET']) # noqa: F821
|
||||
def trace():
|
||||
cvs_id = request.args.get("canvas_id")
|
||||
msg_id = request.args.get("message_id")
|
||||
try:
|
||||
binary = REDIS_CONN.get(f"{cvs_id}-{msg_id}-logs")
|
||||
if not binary:
|
||||
return get_json_result(data={})
|
||||
|
||||
return get_json_result(data=json.loads(binary.encode("utf-8")))
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
|
||||
|
||||
@manager.route('/<canvas_id>/sessions', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def sessions(canvas_id):
|
||||
tenant_id = current_user.id
|
||||
if not UserCanvasService.accessible(canvas_id, tenant_id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
|
||||
user_id = request.args.get("user_id")
|
||||
page_number = int(request.args.get("page", 1))
|
||||
items_per_page = int(request.args.get("page_size", 30))
|
||||
keywords = request.args.get("keywords")
|
||||
from_date = request.args.get("from_date")
|
||||
to_date = request.args.get("to_date")
|
||||
orderby = request.args.get("orderby", "update_time")
|
||||
exp_user_id = request.args.get("exp_user_id")
|
||||
if request.args.get("desc") == "False" or request.args.get("desc") == "false":
|
||||
desc = False
|
||||
else:
|
||||
desc = True
|
||||
|
||||
if exp_user_id:
|
||||
sess = API4ConversationService.get_names(canvas_id, exp_user_id)
|
||||
return get_json_result(data={"total": len(sess), "sessions": sess})
|
||||
|
||||
# dsl defaults to True in all cases except for False and false
|
||||
include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false"
|
||||
total, sess = API4ConversationService.get_list(canvas_id, tenant_id, page_number, items_per_page, orderby, desc,
|
||||
None, user_id, include_dsl, keywords, from_date, to_date, exp_user_id=exp_user_id)
|
||||
try:
|
||||
return get_json_result(data={"total": total, "sessions": sess})
|
||||
except Exception as e:
|
||||
return server_error_response(e)
|
||||
|
||||
|
||||
@manager.route('/<canvas_id>/sessions', methods=['PUT']) # noqa: F821
|
||||
@login_required
|
||||
async def set_session(canvas_id):
|
||||
req = await get_request_json()
|
||||
tenant_id = current_user.id
|
||||
e, cvs = UserCanvasService.get_by_id(canvas_id)
|
||||
assert e, "Agent not found."
|
||||
if not isinstance(cvs.dsl, str):
|
||||
cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
|
||||
session_id=get_uuid()
|
||||
canvas = Canvas(cvs.dsl, tenant_id, canvas_id, canvas_id=cvs.id)
|
||||
canvas.reset()
|
||||
# Get the version title for this canvas (using latest, not necessarily released)
|
||||
version_title = UserCanvasVersionService.get_latest_version_title(cvs.id, release_mode=False)
|
||||
conv = {
|
||||
"id": session_id,
|
||||
"name": req.get("name", ""),
|
||||
"dialog_id": cvs.id,
|
||||
"user_id": tenant_id,
|
||||
"exp_user_id": tenant_id,
|
||||
"message": [],
|
||||
"source": "agent",
|
||||
"dsl": cvs.dsl,
|
||||
"reference": [],
|
||||
"version_title": version_title
|
||||
}
|
||||
API4ConversationService.save(**conv)
|
||||
return get_json_result(data=conv)
|
||||
|
||||
|
||||
@manager.route('/<canvas_id>/sessions/<session_id>', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def get_session(canvas_id, session_id):
|
||||
tenant_id = current_user.id
|
||||
if not UserCanvasService.accessible(canvas_id, tenant_id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
_, conv = API4ConversationService.get_by_id(session_id)
|
||||
return get_json_result(data=conv.to_dict())
|
||||
|
||||
|
||||
@manager.route('/<canvas_id>/sessions/<session_id>', methods=['DELETE']) # noqa: F821
|
||||
@login_required
|
||||
def del_session(canvas_id, session_id):
|
||||
tenant_id = current_user.id
|
||||
if not UserCanvasService.accessible(canvas_id, tenant_id):
|
||||
return get_json_result(
|
||||
data=False, message='Only owner of canvas authorized for this operation.',
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
return get_json_result(data=API4ConversationService.delete_by_id(session_id))
|
||||
|
||||
|
||||
@manager.route('/prompts', methods=['GET']) # noqa: F821
|
||||
@login_required
|
||||
def prompts():
|
||||
from rag.prompts.generator import ANALYZE_TASK_SYSTEM, ANALYZE_TASK_USER, NEXT_STEP, REFLECT, CITATION_PROMPT_TEMPLATE
|
||||
|
||||
return get_json_result(data={
|
||||
"task_analysis": ANALYZE_TASK_SYSTEM +"\n\n"+ ANALYZE_TASK_USER,
|
||||
"plan_generation": NEXT_STEP,
|
||||
"reflection": REFLECT,
|
||||
#"context_summary": SUMMARY4MEMORY,
|
||||
#"context_ranking": RANK_MEMORY,
|
||||
"citation_guidelines": CITATION_PROMPT_TEMPLATE
|
||||
})
|
||||
|
||||
|
||||
@manager.route('/download', methods=['GET']) # noqa: F821
|
||||
async def download():
|
||||
id = request.args.get("id")
|
||||
created_by = request.args.get("created_by")
|
||||
blob = FileService.get_blob(created_by, id)
|
||||
return await make_response(blob)
|
||||
File diff suppressed because it is too large.
Load diff
+1
-120
@@ -22,137 +22,18 @@ import ipaddress
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import Any, cast
|
||||
|
||||
import jwt
|
||||
|
||||
from agent.canvas import Canvas
|
||||
from api.apps.services.canvas_replica_service import CanvasReplicaService
|
||||
from api.db import CanvasCategory
|
||||
from api.db.services.canvas_service import UserCanvasService
|
||||
from api.db.services.file_service import FileService
|
||||
from api.db.services.user_service import UserService
|
||||
from api.db.services.user_canvas_version import UserCanvasVersionService
|
||||
from common.constants import RetCode
|
||||
from common.misc_utils import get_uuid
|
||||
from api.utils.api_utils import get_data_error_result, get_error_data_result, get_json_result, get_request_json, token_required
|
||||
from api.utils.api_utils import get_result
|
||||
from api.utils.api_utils import get_data_error_result, get_json_result
|
||||
from quart import request, Response
|
||||
from rag.utils.redis_conn import REDIS_CONN
|
||||
|
||||
|
||||
def _get_user_nickname(user_id: str) -> str:
|
||||
exists, user = UserService.get_by_id(user_id)
|
||||
if not exists:
|
||||
return user_id
|
||||
return str(getattr(user, "nickname", "") or user_id)
|
||||
|
||||
|
||||
@manager.route('/agents', methods=['GET']) # noqa: F821
|
||||
@token_required
|
||||
def list_agents(tenant_id):
|
||||
id = request.args.get("id")
|
||||
title = request.args.get("title")
|
||||
if id or title:
|
||||
canvas = UserCanvasService.query(id=id, title=title, user_id=tenant_id)
|
||||
if not canvas:
|
||||
return get_error_data_result("The agent doesn't exist.")
|
||||
page_number = int(request.args.get("page", 1))
|
||||
items_per_page = int(request.args.get("page_size", 30))
|
||||
order_by = request.args.get("orderby", "update_time")
|
||||
if str(request.args.get("desc","false")).lower() == "false":
|
||||
desc = False
|
||||
else:
|
||||
desc = True
|
||||
canvas = UserCanvasService.get_list(tenant_id, page_number, items_per_page, order_by, desc, id, title)
|
||||
return get_result(data=canvas)
|
||||
|
||||
|
||||
@manager.route("/agents", methods=["POST"]) # noqa: F821
|
||||
@token_required
|
||||
async def create_agent(tenant_id: str):
|
||||
req: dict[str, Any] = cast(dict[str, Any], await get_request_json())
|
||||
req["user_id"] = tenant_id
|
||||
|
||||
if req.get("dsl") is not None:
|
||||
try:
|
||||
req["dsl"] = CanvasReplicaService.normalize_dsl(req["dsl"])
|
||||
except ValueError as e:
|
||||
return get_json_result(data=False, message=str(e), code=RetCode.ARGUMENT_ERROR)
|
||||
else:
|
||||
return get_json_result(data=False, message="No DSL data in request.", code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
if req.get("title") is not None:
|
||||
req["title"] = req["title"].strip()
|
||||
else:
|
||||
return get_json_result(data=False, message="No title in request.", code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
if UserCanvasService.query(user_id=tenant_id, title=req["title"]):
|
||||
return get_data_error_result(message=f"Agent with title {req['title']} already exists.")
|
||||
|
||||
agent_id = get_uuid()
|
||||
req["id"] = agent_id
|
||||
|
||||
if not UserCanvasService.save(**req):
|
||||
return get_data_error_result(message="Fail to create agent.")
|
||||
|
||||
owner_nickname = _get_user_nickname(tenant_id)
|
||||
UserCanvasVersionService.save_or_replace_latest(
|
||||
user_canvas_id=agent_id,
|
||||
title=UserCanvasVersionService.build_version_title(owner_nickname, req.get("title")),
|
||||
dsl=req["dsl"]
|
||||
)
|
||||
|
||||
return get_json_result(data=True)
|
||||
|
||||
|
||||
@manager.route("/agents/<agent_id>", methods=["PUT"]) # noqa: F821
|
||||
@token_required
|
||||
async def update_agent(tenant_id: str, agent_id: str):
|
||||
req: dict[str, Any] = {k: v for k, v in cast(dict[str, Any], (await get_request_json())).items() if v is not None}
|
||||
req["user_id"] = tenant_id
|
||||
|
||||
if req.get("dsl") is not None:
|
||||
try:
|
||||
req["dsl"] = CanvasReplicaService.normalize_dsl(req["dsl"])
|
||||
except ValueError as e:
|
||||
return get_json_result(data=False, message=str(e), code=RetCode.ARGUMENT_ERROR)
|
||||
|
||||
if req.get("title") is not None:
|
||||
req["title"] = req["title"].strip()
|
||||
|
||||
if not UserCanvasService.query(user_id=tenant_id, id=agent_id):
|
||||
return get_json_result(
|
||||
data=False, message="Only owner of canvas authorized for this operation.",
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
|
||||
_, current_agent = UserCanvasService.get_by_id(agent_id)
|
||||
agent_title_for_version = req.get("title") or (current_agent.title if current_agent else "")
|
||||
owner_nickname = _get_user_nickname(tenant_id)
|
||||
|
||||
UserCanvasService.update_by_id(agent_id, req)
|
||||
|
||||
if req.get("dsl") is not None:
|
||||
UserCanvasVersionService.save_or_replace_latest(
|
||||
user_canvas_id=agent_id,
|
||||
title=UserCanvasVersionService.build_version_title(owner_nickname, agent_title_for_version),
|
||||
dsl=req["dsl"]
|
||||
)
|
||||
|
||||
return get_json_result(data=True)
|
||||
|
||||
|
||||
@manager.route("/agents/<agent_id>", methods=["DELETE"]) # noqa: F821
|
||||
@token_required
|
||||
def delete_agent(tenant_id: str, agent_id: str):
|
||||
if not UserCanvasService.query(user_id=tenant_id, id=agent_id):
|
||||
return get_json_result(
|
||||
data=False, message="Only owner of canvas authorized for this operation.",
|
||||
code=RetCode.OPERATING_ERROR)
|
||||
|
||||
UserCanvasService.delete_by_id(agent_id)
|
||||
return get_json_result(data=True)
|
||||
|
||||
@manager.route("/webhook/<agent_id>", methods=["POST", "GET", "PUT", "PATCH", "DELETE", "HEAD"]) # noqa: F821
|
||||
@manager.route("/webhook_test/<agent_id>",methods=["POST", "GET", "PUT", "PATCH", "DELETE", "HEAD"],) # noqa: F821
|
||||
async def webhook(agent_id: str):
|
||||
|
||||
+2
-213
@@ -14,7 +14,6 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
import json
|
||||
import copy
|
||||
import re
|
||||
import time
|
||||
|
||||
@@ -29,7 +28,7 @@ from common.token_utils import num_tokens_from_string
|
||||
from agent.canvas import Canvas
|
||||
from api.db.db_models import APIToken
|
||||
from api.db.services.api_service import API4ConversationService
|
||||
from api.db.services.canvas_service import UserCanvasService, completion_openai
|
||||
from api.db.services.canvas_service import UserCanvasService
|
||||
from api.db.services.canvas_service import completion as agent_completion
|
||||
from api.db.services.conversation_service import ConversationService
|
||||
from api.db.services.user_canvas_version import UserCanvasVersionService
|
||||
@@ -45,7 +44,7 @@ from api.db.services.user_service import UserTenantService
|
||||
from api.db.joint_services.tenant_model_service import get_tenant_default_model_by_type, get_model_config_by_id, \
|
||||
get_model_config_by_type_and_name
|
||||
from common.misc_utils import get_uuid
|
||||
from api.utils.api_utils import check_duplicate_ids, get_data_openai, get_error_data_result, get_json_result, \
|
||||
from api.utils.api_utils import check_duplicate_ids, get_error_data_result, get_json_result, \
|
||||
get_result, get_request_json, server_error_response, token_required, validate_request
|
||||
from rag.app.tag import label_question
|
||||
from rag.prompts.template import load_prompt
|
||||
@@ -54,7 +53,6 @@ from common.constants import RetCode, LLMType, StatusEnum
|
||||
from common import settings
|
||||
|
||||
|
||||
@manager.route("/agents/<agent_id>/sessions", methods=["POST"]) # noqa: F821
|
||||
@token_required
|
||||
async def create_agent_session(tenant_id, agent_id):
|
||||
req = await get_request_json()
|
||||
@@ -435,215 +433,6 @@ async def chat_completion_openai_like(tenant_id, chat_id):
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@manager.route("/agents_openai/<agent_id>/chat/completions", methods=["POST"]) # noqa: F821
|
||||
@validate_request("model", "messages") # noqa: F821
|
||||
@token_required
|
||||
async def agents_completion_openai_compatibility(tenant_id, agent_id):
|
||||
req = await get_request_json()
|
||||
messages = req.get("messages", [])
|
||||
if not messages:
|
||||
return get_error_data_result("You must provide at least one message.")
|
||||
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}")
|
||||
|
||||
filtered_messages = [m for m in messages if m["role"] in ["user", "assistant"]]
|
||||
prompt_tokens = sum(num_tokens_from_string(m["content"]) for m in filtered_messages)
|
||||
if not filtered_messages:
|
||||
return jsonify(
|
||||
get_data_openai(
|
||||
id=agent_id,
|
||||
content="No valid messages found (user or assistant).",
|
||||
finish_reason="stop",
|
||||
model=req.get("model", ""),
|
||||
completion_tokens=num_tokens_from_string("No valid messages found (user or assistant)."),
|
||||
prompt_tokens=prompt_tokens,
|
||||
)
|
||||
)
|
||||
|
||||
question = next((m["content"] for m in reversed(messages) if m["role"] == "user"), "")
|
||||
|
||||
stream = req.pop("stream", False)
|
||||
if stream:
|
||||
resp = Response(
|
||||
completion_openai(
|
||||
tenant_id,
|
||||
agent_id,
|
||||
question,
|
||||
session_id=req.pop("session_id", req.get("id", "")) or req.get("metadata", {}).get("id", ""),
|
||||
stream=True,
|
||||
**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
|
||||
else:
|
||||
# For non-streaming, just return the response directly
|
||||
async for response in completion_openai(
|
||||
tenant_id,
|
||||
agent_id,
|
||||
question,
|
||||
session_id=req.pop("session_id", req.get("id", "")) or req.get("metadata", {}).get("id", ""),
|
||||
stream=False,
|
||||
**req,
|
||||
):
|
||||
return jsonify(response)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@manager.route("/agents/<agent_id>/completions", methods=["POST"]) # noqa: F821
|
||||
@token_required
|
||||
async def agent_completions(tenant_id, agent_id):
|
||||
req = await get_request_json()
|
||||
return_trace = bool(req.get("return_trace", False))
|
||||
|
||||
if req.get("stream", True):
|
||||
|
||||
async def generate():
|
||||
trace_items = []
|
||||
async for answer in agent_completion(tenant_id=tenant_id, agent_id=agent_id, **req):
|
||||
if isinstance(answer, str):
|
||||
try:
|
||||
ans = json.loads(answer[5:]) # remove "data:"
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
event = ans.get("event")
|
||||
if event == "node_finished":
|
||||
if return_trace:
|
||||
data = ans.get("data", {})
|
||||
trace_items.append(
|
||||
{
|
||||
"component_id": data.get("component_id"),
|
||||
"trace": [copy.deepcopy(data)],
|
||||
}
|
||||
)
|
||||
ans.setdefault("data", {})["trace"] = trace_items
|
||||
answer = "data:" + json.dumps(ans, ensure_ascii=False) + "\n\n"
|
||||
yield answer
|
||||
|
||||
if event not in ["message", "message_end"]:
|
||||
continue
|
||||
|
||||
yield answer
|
||||
|
||||
yield "data:[DONE]\n\n"
|
||||
|
||||
resp = Response(generate(), 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
|
||||
|
||||
full_content = ""
|
||||
reference = {}
|
||||
final_ans = ""
|
||||
trace_items = []
|
||||
structured_output = {}
|
||||
async for answer in agent_completion(tenant_id=tenant_id, agent_id=agent_id, **req):
|
||||
try:
|
||||
ans = json.loads(answer[5:])
|
||||
|
||||
if ans["event"] == "message":
|
||||
full_content += ans["data"]["content"]
|
||||
|
||||
if ans.get("data", {}).get("reference", None):
|
||||
reference.update(ans["data"]["reference"])
|
||||
|
||||
if ans.get("event") == "node_finished":
|
||||
data = ans.get("data", {})
|
||||
node_out = data.get("outputs", {})
|
||||
component_id = data.get("component_id")
|
||||
if component_id is not None and "structured" in node_out:
|
||||
structured_output[component_id] = copy.deepcopy(node_out["structured"])
|
||||
if return_trace:
|
||||
trace_items.append(
|
||||
{
|
||||
"component_id": data.get("component_id"),
|
||||
"trace": [copy.deepcopy(data)],
|
||||
}
|
||||
)
|
||||
|
||||
final_ans = ans
|
||||
except Exception as e:
|
||||
return get_result(data=f"**ERROR**: {str(e)}")
|
||||
final_ans["data"]["content"] = full_content
|
||||
final_ans["data"]["reference"] = reference
|
||||
if structured_output:
|
||||
final_ans["data"]["structured"] = structured_output
|
||||
if return_trace and final_ans:
|
||||
final_ans["data"]["trace"] = trace_items
|
||||
return get_result(data=final_ans)
|
||||
|
||||
|
||||
@manager.route("/agents/<agent_id>/sessions", methods=["GET"]) # noqa: F821
|
||||
@token_required
|
||||
async def list_agent_session(tenant_id, agent_id):
|
||||
if not UserCanvasService.query(user_id=tenant_id, id=agent_id):
|
||||
return get_error_data_result(message=f"You don't own the agent {agent_id}.")
|
||||
id = request.args.get("id")
|
||||
user_id = request.args.get("user_id")
|
||||
page_number = int(request.args.get("page", 1))
|
||||
items_per_page = int(request.args.get("page_size", 30))
|
||||
orderby = request.args.get("orderby", "update_time")
|
||||
if request.args.get("desc") == "False" or request.args.get("desc") == "false":
|
||||
desc = False
|
||||
else:
|
||||
desc = True
|
||||
# dsl defaults to True in all cases except for False and false
|
||||
include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false"
|
||||
total, convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id,
|
||||
user_id, include_dsl)
|
||||
if not convs:
|
||||
return get_result(data=[])
|
||||
for conv in convs:
|
||||
conv["messages"] = conv.pop("message")
|
||||
infos = conv["messages"]
|
||||
for info in infos:
|
||||
if "prompt" in info:
|
||||
info.pop("prompt")
|
||||
conv["agent_id"] = conv.pop("dialog_id")
|
||||
# Fix for session listing endpoint
|
||||
if conv["reference"]:
|
||||
messages = conv["messages"]
|
||||
message_num = 0
|
||||
chunk_num = 0
|
||||
# Ensure reference is a list type to prevent KeyError
|
||||
if not isinstance(conv["reference"], list):
|
||||
conv["reference"] = []
|
||||
while message_num < len(messages):
|
||||
if message_num != 0 and messages[message_num]["role"] != "user":
|
||||
chunk_list = []
|
||||
# Add boundary and type checks to prevent KeyError
|
||||
if chunk_num < len(conv["reference"]) and conv["reference"][chunk_num] is not None and isinstance(
|
||||
conv["reference"][chunk_num], dict) and "chunks" in conv["reference"][chunk_num]:
|
||||
chunks = conv["reference"][chunk_num]["chunks"]
|
||||
for chunk in chunks:
|
||||
# Ensure chunk is a dictionary before calling get method
|
||||
if not isinstance(chunk, dict):
|
||||
continue
|
||||
new_chunk = {
|
||||
"id": chunk.get("chunk_id", chunk.get("id")),
|
||||
"content": chunk.get("content_with_weight", chunk.get("content")),
|
||||
"document_id": chunk.get("doc_id", chunk.get("document_id")),
|
||||
"document_name": chunk.get("docnm_kwd", chunk.get("document_name")),
|
||||
"dataset_id": chunk.get("kb_id", chunk.get("dataset_id")),
|
||||
"image_id": chunk.get("image_id", chunk.get("img_id")),
|
||||
"positions": chunk.get("positions", chunk.get("position_int")),
|
||||
}
|
||||
chunk_list.append(new_chunk)
|
||||
chunk_num += 1
|
||||
messages[message_num]["reference"] = chunk_list
|
||||
message_num += 1
|
||||
del conv["reference"]
|
||||
return get_result(data=convs)
|
||||
|
||||
|
||||
@manager.route("/agents/<agent_id>/sessions", methods=["DELETE"]) # noqa: F821
|
||||
@token_required
|
||||
async def delete_agent_session(tenant_id, agent_id):
|
||||
|
||||
@@ -44,6 +44,14 @@ class APITokenService(CommonService):
|
||||
class API4ConversationService(CommonService):
|
||||
model = API4Conversation
|
||||
|
||||
@staticmethod
|
||||
def _normalize_query_date(value, is_end=False):
|
||||
if "T" in value:
|
||||
value = datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone().replace(tzinfo=None).strftime("%Y-%m-%d %H:%M:%S")
|
||||
elif len(value) == 10:
|
||||
value = f"{value} 23:59:59" if is_end else f"{value} 00:00:00"
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
@DB.connection_context()
|
||||
def get_list(cls, dialog_id, tenant_id,
|
||||
@@ -62,10 +70,11 @@ class API4ConversationService(CommonService):
|
||||
sessions = sessions.where(cls.model.user_id == user_id)
|
||||
if keywords:
|
||||
sessions = sessions.where(peewee.fn.LOWER(cls.model.message).contains(keywords.lower()))
|
||||
date_field = cls.model.update_date if orderby.startswith("update_") else cls.model.create_date
|
||||
if from_date:
|
||||
sessions = sessions.where(cls.model.create_date >= from_date)
|
||||
sessions = sessions.where(date_field >= cls._normalize_query_date(from_date))
|
||||
if to_date:
|
||||
sessions = sessions.where(cls.model.create_date <= to_date)
|
||||
sessions = sessions.where(date_field <= cls._normalize_query_date(to_date, is_end=True))
|
||||
if exp_user_id:
|
||||
sessions = sessions.where(cls.model.exp_user_id == exp_user_id)
|
||||
if desc:
|
||||
|
||||
@@ -139,10 +139,17 @@ class UserCanvasService(CommonService):
|
||||
|
||||
@classmethod
|
||||
@DB.connection_context()
|
||||
def get_by_tenant_ids(cls, joined_tenant_ids, user_id,
|
||||
page_number, items_per_page,
|
||||
orderby, desc, keywords, canvas_category=None
|
||||
):
|
||||
def get_by_tenant_ids(
|
||||
cls,
|
||||
joined_tenant_ids,
|
||||
user_id,
|
||||
page_number,
|
||||
items_per_page,
|
||||
orderby,
|
||||
desc,
|
||||
keywords,
|
||||
canvas_category=None,
|
||||
):
|
||||
fields = [
|
||||
cls.model.id,
|
||||
cls.model.avatar,
|
||||
@@ -201,7 +208,11 @@ class UserCanvasService(CommonService):
|
||||
return False
|
||||
|
||||
tids = [t.tenant_id for t in UserTenantService.query(user_id=tenant_id)]
|
||||
if c["user_id"] != canvas_id and c["user_id"] not in tids:
|
||||
if c["user_id"] == tenant_id:
|
||||
return True
|
||||
if c["user_id"] not in tids:
|
||||
return False
|
||||
if c["permission"] != TenantPermission.TEAM.value:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -4424,62 +4424,71 @@ Failure:
|
||||
|
||||
Asks a specified agent a question to start an AI-powered conversation.
|
||||
|
||||
Uses a single completion endpoint for all agent conversations.
|
||||
|
||||
- Standard mode: send `agent_id` with `query`.
|
||||
- OpenAI-compatible mode: send the same endpoint with `"openai-compatible": true`.
|
||||
|
||||
:::tip NOTE
|
||||
|
||||
- In streaming mode, not all responses include a reference, as this depends on the system's judgement.
|
||||
- In streaming mode, the last message is an empty message:
|
||||
|
||||
```
|
||||
[DONE]
|
||||
```
|
||||
|
||||
- You can optionally return step-by-step trace logs (see `return_trace` below).
|
||||
- Older agent completion routes have been removed. Use only `/api/v1/agents/chat/completion`.
|
||||
- In standard streaming mode, not all responses include a reference, as this depends on the workflow result.
|
||||
- In streaming mode, the server terminates the stream with `[DONE]`.
|
||||
|
||||
:::
|
||||
|
||||
#### Request
|
||||
|
||||
- Method: POST
|
||||
- URL: `/api/v1/agents/{agent_id}/completions`
|
||||
- URL: `/api/v1/agents/chat/completion`
|
||||
- Headers:
|
||||
- `'content-Type: application/json'`
|
||||
- `'Authorization: Bearer <YOUR_API_KEY>'`
|
||||
- Body:
|
||||
- `"question"`: `string`
|
||||
- `"stream"`: `boolean`
|
||||
- `"session_id"`: `string` (optional)
|
||||
- `"inputs"`: `object` (optional)
|
||||
- `"user_id"`: `string` (optional)
|
||||
- `"return_trace"`: `boolean` (optional, default `false`) — whether to include execution trace logs. See the `node_finished` event.
|
||||
- `"release"`: `boolean` (optional, default `false`) - whether to visit the latest published canvas.
|
||||
|
||||
#### Standard mode
|
||||
|
||||
Use this mode for the native agent API.
|
||||
|
||||
##### Body
|
||||
|
||||
- `"agent_id"`: `string`
|
||||
- `"query"`: `string`
|
||||
- `"stream"`: `boolean`
|
||||
- `"session_id"`: `string` (optional)
|
||||
- `"inputs"`: `object` (optional)
|
||||
- `"files"`: `list[object]` (optional)
|
||||
- `"user_id"`: `string` (optional)
|
||||
- `"return_trace"`: `boolean` (optional, default `false`)
|
||||
- `"release"`: `boolean` (optional, default `false`)
|
||||
|
||||
#### Streaming events to handle
|
||||
|
||||
When `stream=true`, the server sends Server-Sent Events (SSE). A client should handle these events:
|
||||
|
||||
- `message`: Streaming content from the **Message** components.
|
||||
- `message_end`: End of a **Message** component, which may include `reference`/`attachment`.
|
||||
- `node_finished`: A component finishes; `data.inputs/outputs/error/elapsed_time` describes the node result. If a component produces structured output, read it from that component's `data.outputs.structured`. If `return_trace=true`, the trace is attached inside the same `node_finished` event (`data.trace`).
|
||||
- `message_end`: End of a **Message** component, which may include `reference` or `attachment`.
|
||||
- `node_finished`: A component finishes. `data.inputs`, `data.outputs`, `data.error`, and `data.elapsed_time` describe the node result. If `return_trace=true`, the same event also contains `data.trace`.
|
||||
|
||||
The stream terminates with `[DONE]`.
|
||||
|
||||
:::info IMPORTANT
|
||||
You can include custom parameters in the request body, but first ensure they are defined in the [Begin](../guides/agent/agent_component_reference/begin.mdx) component.
|
||||
You can include custom parameters in the request body, but they must be defined in the [Begin](../guides/agent/agent_component_reference/begin.mdx) component first.
|
||||
:::
|
||||
|
||||
##### Request example
|
||||
##### Request examples
|
||||
|
||||
- If the **Begin** component does not take parameters:
|
||||
If the **Begin** component does not take parameters:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url http://{address}/api/v1/agents/{agent_id}/completions \
|
||||
--url http://{address}/api/v1/agents/chat/completion \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer <YOUR_API_KEY>' \
|
||||
--data-binary '
|
||||
{
|
||||
"question": "Hello",
|
||||
"stream": false,
|
||||
"agent_id": "AGENT_ID",
|
||||
"query": "Hello",
|
||||
"stream": false
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -4487,12 +4496,13 @@ curl --request POST \
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url http://{address}/api/v1/agents/{agent_id}/completions \
|
||||
--url http://{address}/api/v1/agents/chat/completion \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer <YOUR_API_KEY>' \
|
||||
--data-binary '
|
||||
{
|
||||
"question": "Hello",
|
||||
{
|
||||
"agent_id": "AGENT_ID",
|
||||
"query": "",
|
||||
"stream": false,
|
||||
"inputs": {
|
||||
"line_var": {
|
||||
@@ -4516,25 +4526,26 @@ curl --request POST \
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
}'
|
||||
}'
|
||||
```
|
||||
|
||||
The following code will execute the completion process
|
||||
To continue an existing session:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url http://{address}/api/v1/agents/{agent_id}/completions \
|
||||
--url http://{address}/api/v1/agents/chat/completion \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer <YOUR_API_KEY>' \
|
||||
--data-binary '
|
||||
{
|
||||
"question": "Hello",
|
||||
"stream": true,
|
||||
"session_id": "cb2f385cb86211efa36e0242ac120005"
|
||||
"agent_id": "AGENT_ID",
|
||||
"query": "Hello again",
|
||||
"stream": true,
|
||||
"session_id": "cb2f385cb86211efa36e0242ac120005"
|
||||
}'
|
||||
```
|
||||
|
||||
##### Request Parameters
|
||||
##### Request parameters
|
||||
|
||||
- `agent_id`: (*Path parameter*), `string`
|
||||
The ID of the associated agent.
|
||||
@@ -4557,33 +4568,18 @@ For now, this method does *not* support a file type input/variable. As a workaro
|
||||
*You will get a corresponding file ID from its response body.*
|
||||
:::
|
||||
|
||||
#### Response
|
||||
##### Response
|
||||
|
||||
success without `session_id` provided and with no variables specified in the **Begin** component:
|
||||
|
||||
Stream:
|
||||
Standard mode stream:
|
||||
|
||||
```json
|
||||
...
|
||||
|
||||
data: {
|
||||
"event": "message",
|
||||
"message_id": "cecdcb0e83dc11f0858253708ecb6573",
|
||||
"created_at": 1756364483,
|
||||
"task_id": "d1f79142831f11f09cc51795b9eb07c0",
|
||||
"data": {
|
||||
"content": " themes"
|
||||
},
|
||||
"session_id": "cd097ca083dc11f0858253708ecb6573"
|
||||
}
|
||||
|
||||
data: {
|
||||
"event": "message",
|
||||
"message_id": "cecdcb0e83dc11f0858253708ecb6573",
|
||||
"created_at": 1756364483,
|
||||
"task_id": "d1f79142831f11f09cc51795b9eb07c0",
|
||||
"data": {
|
||||
"content": "."
|
||||
"content": "Hello"
|
||||
},
|
||||
"session_id": "cd097ca083dc11f0858253708ecb6573"
|
||||
}
|
||||
@@ -4594,140 +4590,7 @@ data: {
|
||||
"created_at": 1756364483,
|
||||
"task_id": "d1f79142831f11f09cc51795b9eb07c0",
|
||||
"data": {
|
||||
"reference": {
|
||||
"chunks": {
|
||||
"20": {
|
||||
"id": "4b8935ac0a22deb1",
|
||||
"content": "```cd /usr/ports/editors/neovim/ && make install```## Android[Termux](https://github.com/termux/termux-app) offers a Neovim package.",
|
||||
"document_id": "4bdd2ff65e1511f0907f09f583941b45",
|
||||
"document_name": "INSTALL22.md",
|
||||
"dataset_id": "456ce60c5e1511f0907f09f583941b45",
|
||||
"image_id": "",
|
||||
"positions": [
|
||||
[
|
||||
12,
|
||||
11,
|
||||
11,
|
||||
11,
|
||||
11
|
||||
]
|
||||
],
|
||||
"url": null,
|
||||
"similarity": 0.5705525104787287,
|
||||
"vector_similarity": 0.7351750337624289,
|
||||
"term_similarity": 0.5000000005,
|
||||
"doc_type": ""
|
||||
}
|
||||
},
|
||||
"doc_aggs": {
|
||||
"INSTALL22.md": {
|
||||
"doc_name": "INSTALL22.md",
|
||||
"doc_id": "4bdd2ff65e1511f0907f09f583941b45",
|
||||
"count": 3
|
||||
},
|
||||
"INSTALL.md": {
|
||||
"doc_name": "INSTALL.md",
|
||||
"doc_id": "4bd7fdd85e1511f0907f09f583941b45",
|
||||
"count": 2
|
||||
},
|
||||
"INSTALL(1).md": {
|
||||
"doc_name": "INSTALL(1).md",
|
||||
"doc_id": "4bdfb42e5e1511f0907f09f583941b45",
|
||||
"count": 2
|
||||
},
|
||||
"INSTALL3.md": {
|
||||
"doc_name": "INSTALL3.md",
|
||||
"doc_id": "4bdab5825e1511f0907f09f583941b45",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"session_id": "cd097ca083dc11f0858253708ecb6573"
|
||||
}
|
||||
|
||||
data: {
|
||||
"event": "node_finished",
|
||||
"message_id": "cecdcb0e83dc11f0858253708ecb6573",
|
||||
"created_at": 1756364483,
|
||||
"task_id": "d1f79142831f11f09cc51795b9eb07c0",
|
||||
"data": {
|
||||
"inputs": {
|
||||
"sys.query": "how to install neovim?"
|
||||
},
|
||||
"outputs": {
|
||||
"content": "xxxxxxx",
|
||||
"_created_time": 15294.0382,
|
||||
"_elapsed_time": 0.00017
|
||||
},
|
||||
"component_id": "Agent:EveryHairsChew",
|
||||
"component_name": "Agent_1",
|
||||
"component_type": "Agent",
|
||||
"error": null,
|
||||
"elapsed_time": 11.2091,
|
||||
"created_at": 15294.0382,
|
||||
"trace": [
|
||||
{
|
||||
"component_id": "begin",
|
||||
"trace": [
|
||||
{
|
||||
"inputs": {},
|
||||
"outputs": {
|
||||
"_created_time": 15257.7949,
|
||||
"_elapsed_time": 0.00070
|
||||
},
|
||||
"component_id": "begin",
|
||||
"component_name": "begin",
|
||||
"component_type": "Begin",
|
||||
"error": null,
|
||||
"elapsed_time": 0.00085,
|
||||
"created_at": 15257.7949
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"component_id": "Agent:WeakDragonsRead",
|
||||
"trace": [
|
||||
{
|
||||
"inputs": {
|
||||
"sys.query": "how to install neovim?"
|
||||
},
|
||||
"outputs": {
|
||||
"content": "xxxxxxx",
|
||||
"_created_time": 15257.7982,
|
||||
"_elapsed_time": 36.2382
|
||||
},
|
||||
"component_id": "Agent:WeakDragonsRead",
|
||||
"component_name": "Agent_0",
|
||||
"component_type": "Agent",
|
||||
"error": null,
|
||||
"elapsed_time": 36.2385,
|
||||
"created_at": 15257.7982
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"component_id": "Agent:EveryHairsChew",
|
||||
"trace": [
|
||||
{
|
||||
"inputs": {
|
||||
"sys.query": "how to install neovim?"
|
||||
},
|
||||
"outputs": {
|
||||
"content": "xxxxxxxxxxxxxxxxx",
|
||||
"_created_time": 15294.0382,
|
||||
"_elapsed_time": 0.00017
|
||||
},
|
||||
"component_id": "Agent:EveryHairsChew",
|
||||
"component_name": "Agent_1",
|
||||
"component_type": "Agent",
|
||||
"error": null,
|
||||
"elapsed_time": 11.2091,
|
||||
"created_at": 15294.0382
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"reference": {}
|
||||
},
|
||||
"session_id": "cd097ca083dc11f0858253708ecb6573"
|
||||
}
|
||||
@@ -4737,175 +4600,17 @@ data:[DONE]
|
||||
|
||||
When `extra_body.reference_metadata.include` is `true`, each reference chunk may include a `document_metadata` object.
|
||||
|
||||
Non-stream:
|
||||
|
||||
If one or more components produce structured output, ensure you set `return_trace=true` and check each component's structured output via `trace`. The top-level `data.structured` field is a shortcut aggregated by `component_id`.
|
||||
Standard mode non-stream:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"created_at": 1756363177,
|
||||
"data": {
|
||||
"content": "\nTo install Neovim, the process varies depending on your operating system:\n\n### For macOS:\nUsing Homebrew:\n```bash\nbrew install neovim\n```\n\n### For Linux (Debian/Ubuntu):\n```bash\nsudo apt update\nsudo apt install neovim\n```\n\nFor other Linux distributions, you can use their respective package managers or build from source.\n\n### For Windows:\n1. Download the latest Windows installer from the official Neovim GitHub releases page\n2. Run the installer and follow the prompts\n3. Add Neovim to your PATH if not done automatically\n\n### From source (Unix-like systems):\n```bash\ngit clone https://github.com/neovim/neovim.git\ncd neovim\nmake CMAKE_BUILD_TYPE=Release\nsudo make install\n```\n\nAfter installation, you can verify it by running `nvim --version` in your terminal.",
|
||||
"created_at": 18129.044975627,
|
||||
"elapsed_time": 10.0157331670016,
|
||||
"inputs": {
|
||||
"var1": {
|
||||
"value": "I am var1"
|
||||
},
|
||||
"var2": {
|
||||
"value": "I am var2"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"_created_time": 18129.502422278,
|
||||
"_elapsed_time": 0.00013378599760471843,
|
||||
"content": "\nTo install Neovim, the process varies depending on your operating system:\n\n### For macOS:\nUsing Homebrew:\n```bash\nbrew install neovim\n```\n\n### For Linux (Debian/Ubuntu):\n```bash\nsudo apt update\nsudo apt install neovim\n```\n\nFor other Linux distributions, you can use their respective package managers or build from source.\n\n### For Windows:\n1. Download the latest Windows installer from the official Neovim GitHub releases page\n2. Run the installer and follow the prompts\n3. Add Neovim to your PATH if not done automatically\n\n### From source (Unix-like systems):\n```bash\ngit clone https://github.com/neovim/neovim.git\ncd neovim\nmake CMAKE_BUILD_TYPE=Release\nsudo make install\n```\n\nAfter installation, you can verify it by running `nvim --version` in your terminal."
|
||||
},
|
||||
"reference": {
|
||||
"chunks": {
|
||||
"20": {
|
||||
"content": "```cd /usr/ports/editors/neovim/ && make install```## Android[Termux](https://github.com/termux/termux-app) offers a Neovim package.",
|
||||
"dataset_id": "456ce60c5e1511f0907f09f583941b45",
|
||||
"doc_type": "",
|
||||
"document_id": "4bdd2ff65e1511f0907f09f583941b45",
|
||||
"document_name": "INSTALL22.md",
|
||||
"id": "4b8935ac0a22deb1",
|
||||
"image_id": "",
|
||||
"positions": [
|
||||
[
|
||||
12,
|
||||
11,
|
||||
11,
|
||||
11,
|
||||
11
|
||||
]
|
||||
],
|
||||
"similarity": 0.5705525104787287,
|
||||
"term_similarity": 0.5000000005,
|
||||
"url": null,
|
||||
"vector_similarity": 0.7351750337624289
|
||||
}
|
||||
},
|
||||
"doc_aggs": {
|
||||
"INSTALL(1).md": {
|
||||
"count": 2,
|
||||
"doc_id": "4bdfb42e5e1511f0907f09f583941b45",
|
||||
"doc_name": "INSTALL(1).md"
|
||||
},
|
||||
"INSTALL.md": {
|
||||
"count": 2,
|
||||
"doc_id": "4bd7fdd85e1511f0907f09f583941b45",
|
||||
"doc_name": "INSTALL.md"
|
||||
},
|
||||
"INSTALL22.md": {
|
||||
"count": 3,
|
||||
"doc_id": "4bdd2ff65e1511f0907f09f583941b45",
|
||||
"doc_name": "INSTALL22.md"
|
||||
},
|
||||
"INSTALL3.md": {
|
||||
"count": 1,
|
||||
"doc_id": "4bdab5825e1511f0907f09f583941b45",
|
||||
"doc_name": "INSTALL3.md"
|
||||
}
|
||||
}
|
||||
},
|
||||
"trace": [
|
||||
{
|
||||
"component_id": "begin",
|
||||
"trace": [
|
||||
{
|
||||
"component_id": "begin",
|
||||
"component_name": "begin",
|
||||
"component_type": "Begin",
|
||||
"created_at": 15926.567517862,
|
||||
"elapsed_time": 0.0008189299987861887,
|
||||
"error": null,
|
||||
"inputs": {},
|
||||
"outputs": {
|
||||
"_created_time": 15926.567517862,
|
||||
"_elapsed_time": 0.0006958619997021742
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"component_id": "Agent:WeakDragonsRead",
|
||||
"trace": [
|
||||
{
|
||||
"component_id": "Agent:WeakDragonsRead",
|
||||
"component_name": "Agent_0",
|
||||
"component_type": "Agent",
|
||||
"created_at": 15926.569121755,
|
||||
"elapsed_time": 53.49016142000073,
|
||||
"error": null,
|
||||
"inputs": {
|
||||
"sys.query": "how to install neovim?"
|
||||
},
|
||||
"outputs": {
|
||||
"_created_time": 15926.569121755,
|
||||
"_elapsed_time": 53.489981256001556,
|
||||
"content": "xxxxxxxxxxxxxx",
|
||||
"use_tools": [
|
||||
{
|
||||
"arguments": {
|
||||
"query": "xxxx"
|
||||
},
|
||||
"name": "search_my_dateset",
|
||||
"results": "xxxxxxxxxxx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"component_id": "Agent:EveryHairsChew",
|
||||
"trace": [
|
||||
{
|
||||
"component_id": "Agent:EveryHairsChew",
|
||||
"component_name": "Agent_1",
|
||||
"component_type": "Agent",
|
||||
"created_at": 15980.060569101,
|
||||
"elapsed_time": 23.61718057500002,
|
||||
"error": null,
|
||||
"inputs": {
|
||||
"sys.query": "how to install neovim?"
|
||||
},
|
||||
"outputs": {
|
||||
"_created_time": 15980.060569101,
|
||||
"_elapsed_time": 0.0003451630000199657,
|
||||
"content": "xxxxxxxxxxxx"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"component_id": "Message:SlickDingosHappen",
|
||||
"trace": [
|
||||
{
|
||||
"component_id": "Message:SlickDingosHappen",
|
||||
"component_name": "Message_0",
|
||||
"component_type": "Message",
|
||||
"created_at": 15980.061302513,
|
||||
"elapsed_time": 23.61655923699982,
|
||||
"error": null,
|
||||
"inputs": {
|
||||
"Agent:EveryHairsChew@content": "xxxxxxxxx",
|
||||
"Agent:WeakDragonsRead@content": "xxxxxxxxxxx"
|
||||
},
|
||||
"outputs": {
|
||||
"_created_time": 15980.061302513,
|
||||
"_elapsed_time": 0.0006695749998471001,
|
||||
"content": "xxxxxxxxxxx"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"content": "Hello",
|
||||
"reference": {},
|
||||
"trace": []
|
||||
},
|
||||
"event": "workflow_finished",
|
||||
"message_id": "c4692a2683d911f0858253708ecb6573",
|
||||
"session_id": "c39f6f9c83d911f0858253708ecb6573",
|
||||
"task_id": "d1f79142831f11f09cc51795b9eb07c0"
|
||||
@@ -4913,159 +4618,126 @@ If one or more components produce structured output, ensure you set `return_trac
|
||||
}
|
||||
```
|
||||
|
||||
Success without `session_id` provided and with variables specified in the **Begin** component:
|
||||
If one or more components produce structured output, set `return_trace=true` and inspect that component output from `trace`.
|
||||
|
||||
Stream:
|
||||
#### OpenAI-compatible mode
|
||||
|
||||
```json
|
||||
data:{
|
||||
"event": "message",
|
||||
"message_id": "0e273472783711f0806e1a6272e682d8",
|
||||
"created_at": 1755083830,
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8",
|
||||
"data": {
|
||||
"content": "Hello"
|
||||
},
|
||||
"session_id": "0e0d1542783711f0806e1a6272e682d8"
|
||||
}
|
||||
Use the same endpoint and add `"openai-compatible": true`.
|
||||
|
||||
data:{
|
||||
"event": "message",
|
||||
"message_id": "0e273472783711f0806e1a6272e682d8",
|
||||
"created_at": 1755083830,
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8",
|
||||
"data": {
|
||||
"content": "!"
|
||||
},
|
||||
"session_id": "0e0d1542783711f0806e1a6272e682d8"
|
||||
}
|
||||
##### Body
|
||||
|
||||
data:{
|
||||
"event": "message",
|
||||
"message_id": "0e273472783711f0806e1a6272e682d8",
|
||||
"created_at": 1755083830,
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8",
|
||||
"data": {
|
||||
"content": " How"
|
||||
},
|
||||
"session_id": "0e0d1542783711f0806e1a6272e682d8"
|
||||
}
|
||||
- `"agent_id"`: `string`
|
||||
- `"messages"`: `list[object]`
|
||||
- `"openai-compatible"`: `boolean`, must be `true`
|
||||
- `"stream"`: `boolean`
|
||||
- `"session_id"`: `string` (optional)
|
||||
- `"model"`: `string` (optional, accepted for compatibility)
|
||||
|
||||
...
|
||||
##### Request examples
|
||||
|
||||
data:[DONE]
|
||||
Streaming request:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url http://{address}/api/v1/agents/chat/completion \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer <YOUR_API_KEY>' \
|
||||
--data-binary '
|
||||
{
|
||||
"agent_id": "AGENT_ID",
|
||||
"openai-compatible": true,
|
||||
"stream": true,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Non-stream:
|
||||
Non-stream request with existing session:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url http://{address}/api/v1/agents/chat/completion \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer <YOUR_API_KEY>' \
|
||||
--data-binary '
|
||||
{
|
||||
"agent_id": "AGENT_ID",
|
||||
"openai-compatible": true,
|
||||
"stream": false,
|
||||
"session_id": "cb2f385cb86211efa36e0242ac120005",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
##### Request parameters
|
||||
|
||||
- `"agent_id"`: (*Body parameter*), `string`, *Required*
|
||||
The ID of the associated agent.
|
||||
- `"messages"`: (*Body parameter*), `list[object]`, *Required*
|
||||
OpenAI-style chat messages.
|
||||
- `"openai-compatible"`: (*Body parameter*), `boolean`, *Required*
|
||||
Must be `true` to enable OpenAI-compatible responses.
|
||||
- `"stream"`: (*Body parameter*), `boolean`
|
||||
Whether to return streaming chunks.
|
||||
- `"session_id"`: (*Body parameter*), `string`
|
||||
Optional existing session ID.
|
||||
- `"model"`: (*Body parameter*), `string`
|
||||
Optional compatibility field. The server still routes by `agent_id`.
|
||||
|
||||
##### Response
|
||||
|
||||
OpenAI-compatible stream:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"created_at": 1755083779,
|
||||
"data": {
|
||||
"created_at": 547400.868004651,
|
||||
"elapsed_time": 3.5037803899031132,
|
||||
"inputs": {
|
||||
"boolean_var": {
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
},
|
||||
"int_var": {
|
||||
"type": "integer",
|
||||
"value": 1
|
||||
},
|
||||
"line_var": {
|
||||
"type": "line",
|
||||
"value": "I am line_var"
|
||||
},
|
||||
"option_var": {
|
||||
"type": "options",
|
||||
"value": "option 2"
|
||||
},
|
||||
"paragraph_var": {
|
||||
"type": "paragraph",
|
||||
"value": "a\nb\nc"
|
||||
}
|
||||
data: {
|
||||
"id": "chatcmpl-xxx",
|
||||
"object": "chat.completion.chunk",
|
||||
"model": "AGENT_ID",
|
||||
"choices": [
|
||||
{
|
||||
"delta": {
|
||||
"content": "Hello"
|
||||
},
|
||||
"outputs": {
|
||||
"_created_time": 547400.869271305,
|
||||
"_elapsed_time": 0.0001251999055966735,
|
||||
"content": "Hello there! How can I assist you today?"
|
||||
}
|
||||
},
|
||||
"event": "workflow_finished",
|
||||
"message_id": "effdad8c783611f089261a6272e682d8",
|
||||
"session_id": "efe523b6783611f089261a6272e682d8",
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8"
|
||||
}
|
||||
"finish_reason": null,
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
Success with variables specified in the **Begin** component:
|
||||
|
||||
Stream:
|
||||
|
||||
```json
|
||||
data:{
|
||||
"event": "message",
|
||||
"message_id": "5b62e790783711f0bc531a6272e682d8",
|
||||
"created_at": 1755083960,
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8",
|
||||
"data": {
|
||||
"content": "Hello"
|
||||
},
|
||||
"session_id": "979e450c781d11f095cb729e3aa55728"
|
||||
}
|
||||
|
||||
data:{
|
||||
"event": "message",
|
||||
"message_id": "5b62e790783711f0bc531a6272e682d8",
|
||||
"created_at": 1755083960,
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8",
|
||||
"data": {
|
||||
"content": "!"
|
||||
},
|
||||
"session_id": "979e450c781d11f095cb729e3aa55728"
|
||||
}
|
||||
|
||||
data:{
|
||||
"event": "message",
|
||||
"message_id": "5b62e790783711f0bc531a6272e682d8",
|
||||
"created_at": 1755083960,
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8",
|
||||
"data": {
|
||||
"content": " You"
|
||||
},
|
||||
"session_id": "979e450c781d11f095cb729e3aa55728"
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
data:[DONE]
|
||||
```
|
||||
|
||||
Non-stream:
|
||||
OpenAI-compatible non-stream:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"created_at": 1755084029,
|
||||
"data": {
|
||||
"created_at": 547650.750818867,
|
||||
"elapsed_time": 1.6227330720284954,
|
||||
"inputs": {},
|
||||
"outputs": {
|
||||
"_created_time": 547650.752800839,
|
||||
"_elapsed_time": 9.628792759031057e-05,
|
||||
"content": "Hello! It appears you've sent another \"Hello\" without additional context. I'm here and ready to respond to any requests or questions you may have. Is there something specific you'd like to discuss or learn about?"
|
||||
"id": "chatcmpl-xxx",
|
||||
"object": "chat.completion",
|
||||
"model": "AGENT_ID",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello",
|
||||
"reference": {}
|
||||
}
|
||||
},
|
||||
"event": "workflow_finished",
|
||||
"message_id": "84eec534783711f08db41a6272e682d8",
|
||||
"session_id": "979e450c781d11f095cb729e3aa55728",
|
||||
"task_id": "99ee29d6783511f09c921a6272e682d8"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 6,
|
||||
"completion_tokens": 1,
|
||||
"total_tokens": 7
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -5075,7 +4747,7 @@ Failure:
|
||||
```json
|
||||
{
|
||||
"code": 102,
|
||||
"message": "`question` is required."
|
||||
"message": "Agent not found."
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1710,7 +1710,7 @@ from ragflow_sdk import RAGFlow, Agent
|
||||
|
||||
rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
|
||||
agent_id = "AGENT_ID"
|
||||
agent = rag_object.list_agents(id = agent_id)[0]
|
||||
agent = rag_object.get_agent(agent_id)
|
||||
session = agent.create_session()
|
||||
# Or create in release mode:
|
||||
# session = agent.create_session(release=True)
|
||||
@@ -1721,10 +1721,10 @@ session = agent.create_session()
|
||||
### Converse with agent
|
||||
|
||||
```python
|
||||
Session.ask(question: str="", stream: bool = False) -> Optional[Message, iter[Message]]
|
||||
Session.ask(question: str = "", stream: bool = False, **kwargs) -> Optional[Message | iter[Message]]
|
||||
```
|
||||
|
||||
Asks a specified agent a question to start an AI-powered conversation.
|
||||
Asks a specified agent through the unified completion endpoint.
|
||||
|
||||
:::tip NOTE
|
||||
In streaming mode, not all responses include a reference, as this depends on the system's judgement.
|
||||
@@ -1734,15 +1734,25 @@ In streaming mode, not all responses include a reference, as this depends on the
|
||||
|
||||
##### question: `string`
|
||||
|
||||
The question to start an AI-powered conversation. If the **Begin** component takes parameters, a question is not required.
|
||||
The user message sent to the agent. If the **Begin** component takes parameters, `question` can be an empty string.
|
||||
|
||||
##### stream: `bool`
|
||||
|
||||
Indicates whether to output responses in a streaming way:
|
||||
|
||||
- `True`: Enable streaming (default).
|
||||
- `True`: Enable streaming.
|
||||
- `False`: Disable streaming.
|
||||
|
||||
##### kwargs: `dict`
|
||||
|
||||
Additional request parameters forwarded to the completion API. Common options:
|
||||
|
||||
- `inputs`: Variables defined in the **Begin** component.
|
||||
- `session_id`: Continue an existing session instead of creating a new one.
|
||||
- `release`: Use the latest published version of the agent.
|
||||
- `return_trace`: Include execution trace information in the response.
|
||||
- Other custom Begin component parameters supported by the current workflow.
|
||||
|
||||
#### Returns
|
||||
|
||||
- A `Message` object containing the response to the question if `stream` is set to `False`
|
||||
@@ -1792,8 +1802,8 @@ from ragflow_sdk import RAGFlow, Agent
|
||||
|
||||
rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
|
||||
AGENT_id = "AGENT_ID"
|
||||
agent = rag_object.list_agents(id = AGENT_id)[0]
|
||||
session = agent.create_session()
|
||||
agent = rag_object.get_agent(AGENT_id)
|
||||
session = agent.create_session()
|
||||
|
||||
print("\n===== Miss R ====\n")
|
||||
print("Hello. What can I do for you?")
|
||||
@@ -1808,6 +1818,31 @@ while True:
|
||||
cont = ans.content
|
||||
```
|
||||
|
||||
Use Begin inputs and request trace output:
|
||||
|
||||
```python
|
||||
from ragflow_sdk import RAGFlow, Agent
|
||||
|
||||
rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
|
||||
agent = rag_object.get_agent("AGENT_ID")
|
||||
session = agent.create_session()
|
||||
|
||||
message = session.ask(
|
||||
"",
|
||||
stream=False,
|
||||
inputs={
|
||||
"line_var": {
|
||||
"type": "line",
|
||||
"value": "I am line_var",
|
||||
}
|
||||
},
|
||||
return_trace=True,
|
||||
)
|
||||
|
||||
print(message.content)
|
||||
print(message.reference)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### List agent sessions
|
||||
@@ -1861,7 +1896,7 @@ from ragflow_sdk import RAGFlow
|
||||
|
||||
rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
|
||||
AGENT_id = "AGENT_ID"
|
||||
agent = rag_object.list_agents(id = AGENT_id)[0]
|
||||
agent = rag_object.get_agent(AGENT_id)
|
||||
sessons = agent.list_sessions()
|
||||
for session in sessions:
|
||||
print(session)
|
||||
@@ -1900,7 +1935,7 @@ from ragflow_sdk import RAGFlow
|
||||
|
||||
rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
|
||||
AGENT_id = "AGENT_ID"
|
||||
agent = rag_object.list_agents(id = AGENT_id)[0]
|
||||
agent = rag_object.get_agent(AGENT_id)
|
||||
agent.delete_sessions(ids=["id_1","id_2"])
|
||||
agent.delete_sessions(delete_all=True)
|
||||
```
|
||||
@@ -1917,14 +1952,12 @@ agent.delete_sessions(delete_all=True)
|
||||
RAGFlow.list_agents(
|
||||
page: int = 1,
|
||||
page_size: int = 30,
|
||||
orderby: str = "create_time",
|
||||
desc: bool = True,
|
||||
id: str = None,
|
||||
title: str = None
|
||||
orderby: str = "update_time",
|
||||
desc: bool = True
|
||||
) -> List[Agent]
|
||||
```
|
||||
|
||||
Lists agents.
|
||||
Lists agents. This is a collection API and always returns a list.
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -1940,21 +1973,13 @@ The number of agents on each page. Defaults to `30`.
|
||||
|
||||
The attribute by which the results are sorted. Available options:
|
||||
|
||||
- `"create_time"` (default)
|
||||
- `"update_time"`
|
||||
- `"create_time"`
|
||||
- `"update_time"` (default)
|
||||
|
||||
##### desc: `bool`
|
||||
|
||||
Indicates whether the retrieved agents should be sorted in descending order. Defaults to `True`.
|
||||
|
||||
##### id: `string`
|
||||
|
||||
The ID of the agent to retrieve. Defaults to `None`.
|
||||
|
||||
##### name: `string`
|
||||
|
||||
The name of the agent to retrieve. Defaults to `None`.
|
||||
|
||||
#### Returns
|
||||
|
||||
- Success: A list of `Agent` objects.
|
||||
@@ -1971,6 +1996,37 @@ for agent in rag_object.list_agents():
|
||||
|
||||
---
|
||||
|
||||
### Get agent
|
||||
|
||||
```python
|
||||
RAGFlow.get_agent(agent_id: str) -> Agent
|
||||
```
|
||||
|
||||
Gets a single agent by ID and returns the detailed agent payload.
|
||||
|
||||
#### Parameters
|
||||
|
||||
##### agent_id: `string`
|
||||
|
||||
The ID of the agent to retrieve.
|
||||
|
||||
#### Returns
|
||||
|
||||
- Success: An `Agent` object.
|
||||
- Failure: `Exception`.
|
||||
|
||||
#### Examples
|
||||
|
||||
```python
|
||||
from ragflow_sdk import RAGFlow
|
||||
|
||||
rag_object = RAGFlow(api_key="<YOUR_API_KEY>", base_url="http://<YOUR_BASE_URL>:9380")
|
||||
agent = rag_object.get_agent("AGENT_ID")
|
||||
print(agent)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Create agent
|
||||
|
||||
```python
|
||||
|
||||
@@ -108,10 +108,15 @@ class Session(Base):
|
||||
return res
|
||||
|
||||
def _ask_agent(self, question: str, stream: bool, **kwargs):
|
||||
json_data = {"question": question, "stream": stream, "session_id": self.id}
|
||||
json_data = {
|
||||
"agent_id": self.agent_id,
|
||||
"query": question,
|
||||
"stream": stream,
|
||||
"session_id": self.id,
|
||||
"openai-compatible": False,
|
||||
}
|
||||
json_data.update(kwargs)
|
||||
res = self.post(f"/agents/{self.agent_id}/completions",
|
||||
json_data, stream=stream)
|
||||
res = self.post("/agents/chat/completion", json_data, stream=stream)
|
||||
return res
|
||||
|
||||
def update(self, update_message):
|
||||
|
||||
@@ -230,7 +230,7 @@ class RAGFlow:
|
||||
return chunks
|
||||
raise Exception(res.get("message"))
|
||||
|
||||
def list_agents(self, page: int = 1, page_size: int = 30, orderby: str = "update_time", desc: bool = True, id: str | None = None, title: str | None = None) -> list[Agent]:
|
||||
def list_agents(self, page: int = 1, page_size: int = 30, orderby: str = "update_time", desc: bool = True) -> list[Agent]:
|
||||
res = self.get(
|
||||
"/agents",
|
||||
{
|
||||
@@ -238,18 +238,25 @@ class RAGFlow:
|
||||
"page_size": page_size,
|
||||
"orderby": orderby,
|
||||
"desc": desc,
|
||||
"id": id,
|
||||
"title": title,
|
||||
},
|
||||
)
|
||||
res = res.json()
|
||||
result_list = []
|
||||
if res.get("code") == 0:
|
||||
for data in res["data"]:
|
||||
data = res.get("data") or {}
|
||||
data_list = data.get("canvas", [])
|
||||
for data in data_list:
|
||||
result_list.append(Agent(self, data))
|
||||
return result_list
|
||||
raise Exception(res["message"])
|
||||
|
||||
def get_agent(self, agent_id: str) -> Agent:
|
||||
res = self.get(f"/agents/{agent_id}")
|
||||
res = res.json()
|
||||
if res.get("code") == 0:
|
||||
return Agent(self, res["data"])
|
||||
raise Exception(res["message"])
|
||||
|
||||
def create_agent(self, title: str, dsl: dict, description: str | None = None) -> None:
|
||||
req = {"title": title, "dsl": dsl}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI, Request
|
||||
app = FastAPI()
|
||||
@app.post("/")
|
||||
async def echo(request: Request):
|
||||
body = await request.body()
|
||||
return body
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
@@ -406,8 +406,11 @@ def delete_all_agent_sessions(auth, agent_id, *, page_size=1000):
|
||||
|
||||
|
||||
def agent_completions(auth, agent_id, payload=None):
|
||||
url = f"{HOST_ADDRESS}{AGENT_API_URL}/{agent_id}/completions"
|
||||
res = requests.post(url=url, headers=HEADERS, auth=auth, json=payload)
|
||||
url = f"{HOST_ADDRESS}{AGENT_API_URL}/chat/completion"
|
||||
body = {"agent_id": agent_id}
|
||||
if payload:
|
||||
body.update(payload)
|
||||
res = requests.post(url=url, headers=HEADERS, auth=auth, json=body)
|
||||
return res.json()
|
||||
|
||||
|
||||
|
||||
@@ -49,11 +49,18 @@ MINIMAL_DSL = {
|
||||
"variables": {},
|
||||
}
|
||||
|
||||
|
||||
def _agent_items(res):
|
||||
data = res.get("data", [])
|
||||
if isinstance(data, dict):
|
||||
return data.get("canvas", [])
|
||||
return data
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def agent_id(HttpApiAuth, request):
|
||||
res = list_agents(HttpApiAuth, {"page_size": 1000})
|
||||
assert res["code"] == 0, res
|
||||
for agent in res.get("data", []):
|
||||
for agent in _agent_items(res):
|
||||
if agent.get("title") == AGENT_TITLE:
|
||||
delete_agent(HttpApiAuth, agent["id"])
|
||||
|
||||
@@ -61,8 +68,9 @@ def agent_id(HttpApiAuth, request):
|
||||
assert res["code"] == 0, res
|
||||
res = list_agents(HttpApiAuth, {"title": AGENT_TITLE})
|
||||
assert res["code"] == 0, res
|
||||
assert res.get("data"), res
|
||||
agent_id = res["data"][0]["id"]
|
||||
agents = _agent_items(res)
|
||||
assert agents, res
|
||||
agent_id = agents[0]["id"]
|
||||
|
||||
def cleanup():
|
||||
delete_all_agent_sessions(HttpApiAuth, agent_id)
|
||||
@@ -82,7 +90,7 @@ class TestAgentCompletions:
|
||||
res = agent_completions(
|
||||
HttpApiAuth,
|
||||
agent_id,
|
||||
{"question": "hello", "stream": False, "session_id": session_id},
|
||||
{"query": "hello", "stream": False, "session_id": session_id},
|
||||
)
|
||||
assert res["code"] == 0, res
|
||||
if isinstance(res["data"], dict):
|
||||
|
||||
@@ -17,11 +17,8 @@ import pytest
|
||||
import requests
|
||||
from common import (
|
||||
create_agent,
|
||||
create_agent_session,
|
||||
delete_agent,
|
||||
delete_all_agent_sessions,
|
||||
delete_agent_sessions,
|
||||
list_agent_sessions,
|
||||
list_agents,
|
||||
)
|
||||
from configs import HOST_ADDRESS, VERSION
|
||||
@@ -52,11 +49,18 @@ MINIMAL_DSL = {
|
||||
"variables": {},
|
||||
}
|
||||
|
||||
|
||||
def _agent_items(res):
|
||||
data = res.get("data", [])
|
||||
if isinstance(data, dict):
|
||||
return data.get("canvas", [])
|
||||
return data
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def agent_id(HttpApiAuth, request):
|
||||
res = list_agents(HttpApiAuth, {"page_size": 1000})
|
||||
assert res["code"] == 0, res
|
||||
for agent in res.get("data", []):
|
||||
for agent in _agent_items(res):
|
||||
if agent.get("title") == AGENT_TITLE:
|
||||
delete_agent(HttpApiAuth, agent["id"])
|
||||
|
||||
@@ -64,8 +68,9 @@ def agent_id(HttpApiAuth, request):
|
||||
assert res["code"] == 0, res
|
||||
res = list_agents(HttpApiAuth, {"title": AGENT_TITLE})
|
||||
assert res["code"] == 0, res
|
||||
assert res.get("data"), res
|
||||
agent_id = res["data"][0]["id"]
|
||||
agents = _agent_items(res)
|
||||
assert agents, res
|
||||
agent_id = agents[0]["id"]
|
||||
|
||||
def cleanup():
|
||||
delete_all_agent_sessions(HttpApiAuth, agent_id)
|
||||
@@ -76,39 +81,14 @@ def agent_id(HttpApiAuth, request):
|
||||
|
||||
|
||||
class TestAgentSessions:
|
||||
@pytest.mark.p2
|
||||
def test_delete_agent_sessions_empty_ids_noop(self, HttpApiAuth, agent_id):
|
||||
res = create_agent_session(HttpApiAuth, agent_id, payload={})
|
||||
assert res["code"] == 0, res
|
||||
session_id = res["data"]["id"]
|
||||
|
||||
res = delete_agent_sessions(HttpApiAuth, agent_id, {"ids": []})
|
||||
assert res["code"] == 0, res
|
||||
|
||||
res = list_agent_sessions(HttpApiAuth, agent_id, params={"id": session_id})
|
||||
assert res["code"] == 0, res
|
||||
assert len(res["data"]) == 1, res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_create_list_delete_agent_sessions(self, HttpApiAuth, agent_id):
|
||||
res = create_agent_session(HttpApiAuth, agent_id, payload={})
|
||||
assert res["code"] == 0, res
|
||||
session_id = res["data"]["id"]
|
||||
assert res["data"]["agent_id"] == agent_id, res
|
||||
|
||||
res = list_agent_sessions(HttpApiAuth, agent_id, params={"id": session_id})
|
||||
assert res["code"] == 0, res
|
||||
assert len(res["data"]) == 1, res
|
||||
assert res["data"][0]["id"] == session_id, res
|
||||
|
||||
res = delete_agent_sessions(HttpApiAuth, agent_id, {"ids": [session_id]})
|
||||
assert res["code"] == 0, res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_agent_crud_validation_contract(self, HttpApiAuth, agent_id):
|
||||
res = list_agents(HttpApiAuth, {"id": "missing-agent-id", "title": "missing-agent-title"})
|
||||
assert res["code"] == 102, res
|
||||
assert "doesn't exist" in res["message"], res
|
||||
assert res["code"] == 0, res
|
||||
assert isinstance(res.get("data"), dict), res
|
||||
assert "canvas" in res["data"], res
|
||||
assert "total" in res["data"], res
|
||||
|
||||
res = list_agents(HttpApiAuth, {"title": AGENT_TITLE, "desc": "true", "page_size": 1})
|
||||
assert res["code"] == 0, res
|
||||
|
||||
+184
-87
@@ -498,6 +498,14 @@ def _load_session_module(monkeypatch):
|
||||
monkeypatch.setitem(sys.modules, "agent.canvas", agent_canvas_mod)
|
||||
monkeypatch.setitem(sys.modules, "agent.dsl_migration", agent_dsl_migration_mod)
|
||||
|
||||
quart_mod = ModuleType("quart")
|
||||
quart_mod.request = SimpleNamespace(args=_Args(), headers={}, files=_AwaitableValue({}), method="POST")
|
||||
quart_mod.Response = _StubResponse
|
||||
quart_mod.jsonify = lambda payload: payload
|
||||
quart_mod.current_app = SimpleNamespace()
|
||||
quart_mod.has_app_context = lambda: False
|
||||
monkeypatch.setitem(sys.modules, "quart", quart_mod)
|
||||
|
||||
module_path = repo_root / "api" / "apps" / "sdk" / "session.py"
|
||||
spec = importlib.util.spec_from_file_location("test_session_sdk_routes_unit_module", module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
@@ -530,6 +538,134 @@ def _load_session_module(monkeypatch):
|
||||
return module
|
||||
|
||||
|
||||
def _load_agent_api_module(monkeypatch):
|
||||
_load_session_module(monkeypatch)
|
||||
repo_root = Path(__file__).resolve().parents[4]
|
||||
|
||||
agent_component_mod = ModuleType("agent.component")
|
||||
|
||||
class _StubAgentLLM:
|
||||
pass
|
||||
|
||||
agent_component_mod.LLM = _StubAgentLLM
|
||||
monkeypatch.setitem(sys.modules, "agent.component", agent_component_mod)
|
||||
|
||||
api_apps_mod = ModuleType("api.apps")
|
||||
api_apps_mod.__path__ = [str(repo_root / "api" / "apps")]
|
||||
api_apps_mod.login_required = lambda func: func
|
||||
monkeypatch.setitem(sys.modules, "api.apps", api_apps_mod)
|
||||
|
||||
api_apps_services_mod = ModuleType("api.apps.services")
|
||||
api_apps_services_mod.__path__ = [str(repo_root / "api" / "apps" / "services")]
|
||||
monkeypatch.setitem(sys.modules, "api.apps.services", api_apps_services_mod)
|
||||
|
||||
canvas_replica_mod = ModuleType("api.apps.services.canvas_replica_service")
|
||||
|
||||
class _StubCanvasReplicaService:
|
||||
@staticmethod
|
||||
def normalize_dsl(dsl):
|
||||
return dsl
|
||||
|
||||
@staticmethod
|
||||
def replace_for_set(**_kwargs):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def bootstrap(**_kwargs):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def load_for_run(**_kwargs):
|
||||
return {"dsl": {}, "title": "agent", "canvas_category": "agent"}
|
||||
|
||||
@staticmethod
|
||||
def commit_after_run(**_kwargs):
|
||||
return True
|
||||
|
||||
canvas_replica_mod.CanvasReplicaService = _StubCanvasReplicaService
|
||||
monkeypatch.setitem(sys.modules, "api.apps.services.canvas_replica_service", canvas_replica_mod)
|
||||
|
||||
file_service_mod = ModuleType("api.db.services.file_service")
|
||||
file_service_mod.FileService = SimpleNamespace(upload_info=lambda *_args, **_kwargs: {})
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.file_service", file_service_mod)
|
||||
|
||||
api_service_mod = ModuleType("api.db.services.api_service")
|
||||
api_service_mod.API4ConversationService = SimpleNamespace(
|
||||
get_names=lambda *_args, **_kwargs: [],
|
||||
get_list=lambda *_args, **_kwargs: (0, []),
|
||||
save=lambda **_kwargs: True,
|
||||
get_by_id=lambda _session_id: (True, SimpleNamespace(to_dict=lambda: {"id": _session_id})),
|
||||
delete_by_id=lambda *_args, **_kwargs: True,
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.api_service", api_service_mod)
|
||||
|
||||
document_service_mod = ModuleType("api.db.services.document_service")
|
||||
document_service_mod.DocumentService = SimpleNamespace(
|
||||
clear_chunk_num_when_rerun=lambda *_args, **_kwargs: True,
|
||||
update_by_id=lambda *_args, **_kwargs: True,
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.document_service", document_service_mod)
|
||||
|
||||
knowledgebase_service_mod = ModuleType("api.db.services.knowledgebase_service")
|
||||
knowledgebase_service_mod.KnowledgebaseService = SimpleNamespace(query=lambda **_kwargs: [])
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", knowledgebase_service_mod)
|
||||
|
||||
task_service_mod = ModuleType("api.db.services.task_service")
|
||||
task_service_mod.CANVAS_DEBUG_DOC_ID = "debug-doc"
|
||||
task_service_mod.GRAPH_RAPTOR_FAKE_DOC_ID = "graph-raptor-fake-doc"
|
||||
task_service_mod.TaskService = SimpleNamespace(filter_delete=lambda *_args, **_kwargs: True)
|
||||
task_service_mod.queue_dataflow = lambda *_args, **_kwargs: (True, "")
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.task_service", task_service_mod)
|
||||
|
||||
pipeline_operation_log_service_mod = ModuleType("api.db.services.pipeline_operation_log_service")
|
||||
pipeline_operation_log_service_mod.PipelineOperationLogService = SimpleNamespace(
|
||||
get_documents_info=lambda *_args, **_kwargs: [],
|
||||
update_by_id=lambda *_args, **_kwargs: True,
|
||||
)
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"api.db.services.pipeline_operation_log_service",
|
||||
pipeline_operation_log_service_mod,
|
||||
)
|
||||
|
||||
user_service_mod = ModuleType("api.db.services.user_service")
|
||||
user_service_mod.TenantService = SimpleNamespace(get_joined_tenants_by_user_id=lambda *_args, **_kwargs: [])
|
||||
user_service_mod.UserService = SimpleNamespace(get_by_id=lambda *_args, **_kwargs: (False, None))
|
||||
user_service_mod.UserTenantService = SimpleNamespace(query=lambda **_kwargs: [])
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.user_service", user_service_mod)
|
||||
|
||||
user_canvas_version_mod = ModuleType("api.db.services.user_canvas_version")
|
||||
user_canvas_version_mod.UserCanvasVersionService = SimpleNamespace(
|
||||
list_by_canvas_id=lambda *_args, **_kwargs: [],
|
||||
get_by_id=lambda *_args, **_kwargs: (False, None),
|
||||
get_latest_version_title=lambda *_args, **_kwargs: "",
|
||||
save_or_replace_latest=lambda **_kwargs: True,
|
||||
build_version_title=lambda *_args, **_kwargs: "v1",
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.user_canvas_version", user_canvas_version_mod)
|
||||
|
||||
rag_flow_pipeline_mod = ModuleType("rag.flow.pipeline")
|
||||
|
||||
class _StubPipeline:
|
||||
def __init__(self, *_args, **_kwargs):
|
||||
pass
|
||||
|
||||
rag_flow_pipeline_mod.Pipeline = _StubPipeline
|
||||
monkeypatch.setitem(sys.modules, "rag.flow.pipeline", rag_flow_pipeline_mod)
|
||||
|
||||
rag_redis_mod = ModuleType("rag.utils.redis_conn")
|
||||
rag_redis_mod.REDIS_CONN = SimpleNamespace(get=lambda *_args, **_kwargs: None)
|
||||
monkeypatch.setitem(sys.modules, "rag.utils.redis_conn", rag_redis_mod)
|
||||
|
||||
module_path = repo_root / "api" / "apps" / "restful_apis" / "agent_api.py"
|
||||
spec = importlib.util.spec_from_file_location("test_agent_api_unit_module", module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
module.manager = _DummyManager()
|
||||
monkeypatch.setitem(sys.modules, "test_agent_api_unit_module", module)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_create_and_update_guard_matrix(monkeypatch):
|
||||
module = _load_session_module(monkeypatch)
|
||||
@@ -734,34 +870,22 @@ def test_openai_nonstream_branch_unit(monkeypatch):
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_agents_openai_compatibility_unit(monkeypatch):
|
||||
module = _load_session_module(monkeypatch)
|
||||
module = _load_agent_api_module(monkeypatch)
|
||||
|
||||
monkeypatch.setattr(module, "Response", _StubResponse)
|
||||
monkeypatch.setattr(module, "jsonify", lambda payload: payload)
|
||||
monkeypatch.setattr(module, "num_tokens_from_string", lambda text: len(text or ""))
|
||||
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"openai-compatible": True}))
|
||||
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
|
||||
assert "`agent_id` is required." in res["message"]
|
||||
|
||||
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"model": "model", "messages": []}))
|
||||
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({"agent_id": "agent-1", "openai-compatible": True, "model": "model", "messages": []}),
|
||||
)
|
||||
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
|
||||
assert "at least one message" in res["message"]
|
||||
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({"model": "model", "messages": [{"role": "user", "content": "hello"}]}),
|
||||
)
|
||||
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [])
|
||||
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
|
||||
assert "don't own the agent" in res["message"]
|
||||
|
||||
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [SimpleNamespace(id="agent-1")])
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({"model": "model", "messages": [{"role": "system", "content": "system only"}]}),
|
||||
)
|
||||
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
|
||||
assert "No valid messages found" in json.dumps(res)
|
||||
|
||||
captured_calls = []
|
||||
|
||||
async def _completion_openai_stream(*args, **kwargs):
|
||||
@@ -774,6 +898,8 @@ def test_agents_openai_compatibility_unit(monkeypatch):
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"agent_id": "agent-1",
|
||||
"openai-compatible": True,
|
||||
"model": "model",
|
||||
"messages": [
|
||||
{"role": "assistant", "content": "preface"},
|
||||
@@ -784,7 +910,7 @@ def test_agents_openai_compatibility_unit(monkeypatch):
|
||||
}
|
||||
),
|
||||
)
|
||||
resp = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
|
||||
resp = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
|
||||
assert isinstance(resp, _StubResponse)
|
||||
assert resp.headers.get("Content-Type") == "text/event-stream; charset=utf-8"
|
||||
_run(_collect_stream(resp.body))
|
||||
@@ -795,11 +921,15 @@ def test_agents_openai_compatibility_unit(monkeypatch):
|
||||
yield {"id": "non-stream"}
|
||||
|
||||
monkeypatch.setattr(module, "completion_openai", _completion_openai_nonstream)
|
||||
monkeypatch.setattr(module.API4ConversationService, "get_by_id", lambda _session_id: (True, SimpleNamespace(dialog_id="agent-1")))
|
||||
monkeypatch.setattr(module.UserCanvasService, "accessible", lambda *_args, **_kwargs: True)
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"agent_id": "agent-1",
|
||||
"openai-compatible": True,
|
||||
"model": "model",
|
||||
"messages": [
|
||||
{"role": "user", "content": "first"},
|
||||
@@ -812,7 +942,7 @@ def test_agents_openai_compatibility_unit(monkeypatch):
|
||||
}
|
||||
),
|
||||
)
|
||||
res = _run(inspect.unwrap(module.agents_completion_openai_compatibility)("tenant-1", "agent-1"))
|
||||
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
|
||||
assert res["id"] == "non-stream"
|
||||
assert captured_calls[-1][0][2] == "final user"
|
||||
assert captured_calls[-1][1]["stream"] is False
|
||||
@@ -821,9 +951,11 @@ def test_agents_openai_compatibility_unit(monkeypatch):
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
|
||||
module = _load_session_module(monkeypatch)
|
||||
module = _load_agent_api_module(monkeypatch)
|
||||
|
||||
monkeypatch.setattr(module, "Response", _StubResponse)
|
||||
monkeypatch.setattr(module.API4ConversationService, "get_by_id", lambda _session_id: (True, SimpleNamespace(dialog_id="agent-1")))
|
||||
monkeypatch.setattr(module.UserCanvasService, "accessible", lambda *_args, **_kwargs: True)
|
||||
|
||||
async def _agent_stream(*_args, **_kwargs):
|
||||
yield "data:not-json"
|
||||
@@ -843,9 +975,20 @@ def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
|
||||
yield "data:" + json.dumps({"event": "message", "data": {"content": "hello"}})
|
||||
|
||||
monkeypatch.setattr(module, "agent_completion", _agent_stream)
|
||||
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"stream": True, "return_trace": True}))
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"agent_id": "agent-1",
|
||||
"session_id": "session-1",
|
||||
"stream": True,
|
||||
"return_trace": True,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
resp = _run(inspect.unwrap(module.agent_completions)("tenant-1", "agent-1"))
|
||||
resp = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
|
||||
chunks = _run(_collect_stream(resp.body))
|
||||
assert resp.headers.get("Content-Type") == "text/event-stream; charset=utf-8"
|
||||
assert any('"trace"' in chunk for chunk in chunks)
|
||||
@@ -874,8 +1017,19 @@ def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
|
||||
)
|
||||
|
||||
monkeypatch.setattr(module, "agent_completion", _agent_nonstream)
|
||||
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"stream": False, "return_trace": True}))
|
||||
res = _run(inspect.unwrap(module.agent_completions)("tenant-1", "agent-1"))
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"agent_id": "agent-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"return_trace": True,
|
||||
}
|
||||
),
|
||||
)
|
||||
res = _run(inspect.unwrap(module.agent_chat_completion)("tenant-1"))
|
||||
assert res["data"]["data"]["content"] == "A"
|
||||
assert res["data"]["data"]["reference"] == {"doc": "r"}
|
||||
assert res["data"]["data"]["structured"] == {
|
||||
@@ -884,64 +1038,7 @@ def test_agent_completions_stream_and_nonstream_unit(monkeypatch):
|
||||
"c4": {},
|
||||
}
|
||||
assert [item["component_id"] for item in res["data"]["data"]["trace"]] == ["c2", "c3", "c4"]
|
||||
|
||||
async def _agent_nonstream_broken(*_args, **_kwargs):
|
||||
yield "data:{"
|
||||
|
||||
monkeypatch.setattr(module, "agent_completion", _agent_nonstream_broken)
|
||||
monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({"stream": False, "return_trace": False}))
|
||||
res = _run(inspect.unwrap(module.agent_completions)("tenant-1", "agent-1"))
|
||||
assert res["data"].startswith("**ERROR**")
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_list_agent_session_projection_unit(monkeypatch):
|
||||
module = _load_session_module(monkeypatch)
|
||||
|
||||
monkeypatch.setattr(module, "request", SimpleNamespace(args=_Args({})))
|
||||
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [SimpleNamespace(id="agent-1")])
|
||||
|
||||
conv_non_list_reference = {
|
||||
"id": "session-1",
|
||||
"dialog_id": "agent-1",
|
||||
"message": [{"role": "assistant", "content": "hello", "prompt": "internal"}],
|
||||
"reference": {"unexpected": "shape"},
|
||||
}
|
||||
monkeypatch.setattr(module.API4ConversationService, "get_list", lambda *_args, **_kwargs: (1, [conv_non_list_reference]))
|
||||
res = _run(inspect.unwrap(module.list_agent_session)("tenant-1", "agent-1"))
|
||||
assert res["data"][0]["agent_id"] == "agent-1"
|
||||
assert "prompt" not in res["data"][0]["messages"][0]
|
||||
|
||||
conv_with_chunks = {
|
||||
"id": "session-2",
|
||||
"dialog_id": "agent-1",
|
||||
"message": [
|
||||
{"role": "user", "content": "question"},
|
||||
{"role": "assistant", "content": "answer", "prompt": "internal"},
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"chunks": [
|
||||
"not-a-dict",
|
||||
{
|
||||
"chunk_id": "chunk-2",
|
||||
"content_with_weight": "weighted",
|
||||
"doc_id": "doc-2",
|
||||
"docnm_kwd": "doc-name-2",
|
||||
"kb_id": "kb-2",
|
||||
"image_id": "img-2",
|
||||
"positions": [9],
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
monkeypatch.setattr(module.API4ConversationService, "get_list", lambda *_args, **_kwargs: (1, [conv_with_chunks]))
|
||||
res = _run(inspect.unwrap(module.list_agent_session)("tenant-1", "agent-1"))
|
||||
projected_chunk = res["data"][0]["messages"][1]["reference"][0]
|
||||
assert projected_chunk["image_id"] == "img-2"
|
||||
assert projected_chunk["positions"] == [9]
|
||||
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_delete_routes_partial_duplicate_unit(monkeypatch):
|
||||
|
||||
@@ -47,12 +47,12 @@ def test_list_agents_success_and_error(monkeypatch):
|
||||
captured["path"] = path
|
||||
captured["params"] = params
|
||||
captured["json"] = json
|
||||
return _DummyResponse({"code": 0, "data": [{"id": "agent-1", "title": "Agent One"}]})
|
||||
return _DummyResponse({"code": 0, "data": {"canvas": [{"id": "agent-1", "title": "Agent One"}], "total": 1}})
|
||||
|
||||
monkeypatch.setattr(client, "get", _ok_get)
|
||||
agents = client.list_agents(title="Agent One")
|
||||
agents = client.list_agents()
|
||||
assert captured["path"] == "/agents"
|
||||
assert captured["params"]["title"] == "Agent One"
|
||||
assert captured["params"] == {"page": 1, "page_size": 30, "orderby": "update_time", "desc": True}
|
||||
assert isinstance(agents[0], Agent), str(agents)
|
||||
assert agents[0].id == "agent-1", str(agents[0])
|
||||
assert agents[0].title == "Agent One", str(agents[0])
|
||||
|
||||
+4
-2
@@ -160,8 +160,10 @@ def test_session_module_streaming_and_helper_paths_unit(monkeypatch):
|
||||
assert calls[0][2]["session_id"] == "session-chat"
|
||||
assert calls[0][2]["temperature"] == 0.2
|
||||
assert calls[0][3] is True
|
||||
assert calls[1][1] == "/agents/agent-1/completions"
|
||||
assert calls[1][2]["question"] == "hello agent"
|
||||
assert calls[1][1] == "/agents/chat/completion"
|
||||
assert calls[1][2]["agent_id"] == "agent-1"
|
||||
assert calls[1][2]["query"] == "hello agent"
|
||||
assert calls[1][2]["session_id"] == "session-agent"
|
||||
assert calls[1][2]["openai-compatible"] is False
|
||||
assert calls[1][2]["top_p"] == 0.8
|
||||
assert calls[1][3] is True
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+119
-118
@@ -28,8 +28,9 @@ import agentService, {
|
||||
fetchPipeLineList,
|
||||
fetchTrace,
|
||||
fetchWebhookTrace,
|
||||
updateAgent,
|
||||
uploadAgentFile,
|
||||
} from '@/services/agent-service';
|
||||
import api from '@/utils/api';
|
||||
import { buildMessageListWithUuid } from '@/utils/chat';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useDebounce } from 'ahooks';
|
||||
@@ -51,15 +52,14 @@ export const enum AgentApiAction {
|
||||
ResetAgent = 'resetAgent',
|
||||
SetAgent = 'setAgent',
|
||||
FetchAgentTemplates = 'fetchAgentTemplates',
|
||||
UploadCanvasFile = 'uploadCanvasFile',
|
||||
UploadCanvasFileWithProgress = 'uploadCanvasFileWithProgress',
|
||||
UploadAgentFile = 'uploadAgentFile',
|
||||
UploadAgentFileWithProgress = 'uploadAgentFileWithProgress',
|
||||
Trace = 'trace',
|
||||
TestDbConnect = 'testDbConnect',
|
||||
DebugSingle = 'debugSingle',
|
||||
FetchInputForm = 'fetchInputForm',
|
||||
FetchVersionList = 'fetchVersionList',
|
||||
FetchVersion = 'fetchVersion',
|
||||
FetchAgentAvatar = 'fetchAgentAvatar',
|
||||
FetchExternalAgentInputs = 'fetchExternalAgentInputs',
|
||||
SetAgentSetting = 'setAgentSetting',
|
||||
FetchPrompt = 'fetchPrompt',
|
||||
@@ -72,7 +72,7 @@ export const enum AgentApiAction {
|
||||
DeleteAgentSession = 'deleteAgentSession',
|
||||
FetchSessionByIdManually = 'fetchSessionByIdManually',
|
||||
FetchAgentLog = 'fetchAgentLog',
|
||||
FetchFlowDetailSSE = 'flowDetailSSE',
|
||||
FetchSharedAgent = 'fetchSharedAgent',
|
||||
}
|
||||
|
||||
export const useFetchAgentTemplates = () => {
|
||||
@@ -80,7 +80,7 @@ export const useFetchAgentTemplates = () => {
|
||||
queryKey: [AgentApiAction.FetchAgentTemplates],
|
||||
initialData: [],
|
||||
queryFn: async () => {
|
||||
const { data } = await agentService.listTemplates();
|
||||
const { data } = await agentService.listAgentTemplate();
|
||||
|
||||
return data.data;
|
||||
},
|
||||
@@ -89,6 +89,37 @@ export const useFetchAgentTemplates = () => {
|
||||
return data;
|
||||
};
|
||||
|
||||
const buildAgentListParams = ({
|
||||
page,
|
||||
pageSize,
|
||||
keywords,
|
||||
canvasCategory,
|
||||
ownerIds,
|
||||
}: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
keywords?: string;
|
||||
canvasCategory?: string;
|
||||
ownerIds?: string[];
|
||||
}) => {
|
||||
const params: Record<string, unknown> = {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
};
|
||||
|
||||
if (keywords) {
|
||||
params.keywords = keywords;
|
||||
}
|
||||
if (canvasCategory) {
|
||||
params.canvas_category = canvasCategory;
|
||||
}
|
||||
if (Array.isArray(ownerIds) && ownerIds.length > 0) {
|
||||
params.owner_ids = ownerIds.join(',');
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
export const useFetchAgentListByPage = () => {
|
||||
const { searchString, handleInputChange } = useHandleSearchChange();
|
||||
const { pagination, setPagination } = useGetPaginationWithRouter();
|
||||
@@ -99,17 +130,13 @@ export const useFetchAgentListByPage = () => {
|
||||
: [];
|
||||
const owner = filterValue.owner;
|
||||
|
||||
const requestParams: Record<string, any> = {
|
||||
keywords: debouncedSearchString,
|
||||
page_size: pagination.pageSize,
|
||||
const requestParams = buildAgentListParams({
|
||||
page: pagination.current,
|
||||
canvas_category:
|
||||
canvasCategory.length === 1 ? canvasCategory[0] : undefined,
|
||||
};
|
||||
|
||||
if (Array.isArray(owner) && owner.length > 0) {
|
||||
requestParams.owner_ids = owner.join(',');
|
||||
}
|
||||
pageSize: pagination.pageSize,
|
||||
keywords: debouncedSearchString,
|
||||
canvasCategory: canvasCategory.length === 1 ? canvasCategory[0] : undefined,
|
||||
ownerIds: Array.isArray(owner) ? owner : undefined,
|
||||
});
|
||||
|
||||
const { data, isFetching: loading } = useQuery<{
|
||||
canvas: IFlow[];
|
||||
@@ -131,7 +158,7 @@ export const useFetchAgentListByPage = () => {
|
||||
},
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const { data } = await agentService.listCanvas(
|
||||
const { data } = await agentService.listAgents(
|
||||
{
|
||||
params: requestParams,
|
||||
},
|
||||
@@ -166,13 +193,13 @@ export function useFetchAllAgentList() {
|
||||
const { data, isFetching: loading } = useQuery<IFlow[]>({
|
||||
queryKey: [AgentApiAction.FetchAllAgentList],
|
||||
queryFn: async () => {
|
||||
const { data } = await agentService.listCanvas(
|
||||
const { data } = await agentService.listAgents(
|
||||
{
|
||||
params: {
|
||||
params: buildAgentListParams({
|
||||
page: 1,
|
||||
page_size: 100000,
|
||||
canvas_category: AgentCategory.AgentCanvas,
|
||||
},
|
||||
pageSize: 100000,
|
||||
canvasCategory: AgentCategory.AgentCanvas,
|
||||
}),
|
||||
},
|
||||
true,
|
||||
);
|
||||
@@ -194,7 +221,12 @@ export const useUpdateAgentSetting = () => {
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.UpdateAgentSetting],
|
||||
mutationFn: async (params: any) => {
|
||||
const ret = await agentService.settingCanvas(params);
|
||||
const ret = await updateAgent(params.id, {
|
||||
title: params.title,
|
||||
description: params.description,
|
||||
permission: params.permission,
|
||||
avatar: params.avatar,
|
||||
});
|
||||
if (ret?.data?.code === 0) {
|
||||
message.success('success');
|
||||
queryClient.invalidateQueries({
|
||||
@@ -218,14 +250,14 @@ export const useDeleteAgent = () => {
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.DeleteAgent],
|
||||
mutationFn: async (canvasIds: string[]) => {
|
||||
const { data } = await agentService.removeCanvas({ canvasIds });
|
||||
mutationFn: async (agentId: string) => {
|
||||
const { data } = await agentService.deleteAgent(agentId);
|
||||
if (data.code === 0) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [AgentApiAction.FetchAgentListByPage],
|
||||
});
|
||||
}
|
||||
return data?.data ?? [];
|
||||
return data?.data ?? false;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -252,7 +284,7 @@ export const useFetchAgent = (): {
|
||||
refetchOnWindowFocus: false,
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const { data } = await agentService.fetchCanvas(sharedId || id);
|
||||
const { data } = await agentService.getAgent(sharedId || id);
|
||||
|
||||
const messageList = buildMessageListWithUuid(
|
||||
get(data, 'data.dsl.messages', []),
|
||||
@@ -286,7 +318,7 @@ export const useResetAgent = () => {
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.ResetAgent],
|
||||
mutationFn: async () => {
|
||||
const { data } = await agentService.resetCanvas({ id });
|
||||
const { data } = await agentService.resetAgent(id);
|
||||
return data;
|
||||
},
|
||||
});
|
||||
@@ -295,6 +327,7 @@ export const useResetAgent = () => {
|
||||
};
|
||||
|
||||
export const useSetAgent = (showMessage: boolean = true) => {
|
||||
const { id } = useParams();
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
@@ -309,17 +342,34 @@ export const useSetAgent = (showMessage: boolean = true) => {
|
||||
avatar?: string;
|
||||
canvas_category?: string;
|
||||
release?: string;
|
||||
description?: string | null;
|
||||
permission?: string;
|
||||
}) => {
|
||||
const { data = {} } = await agentService.setCanvas(params);
|
||||
const agentId = params.id ?? id;
|
||||
const { data = {} } = agentId
|
||||
? await updateAgent(agentId, {
|
||||
title: params.title,
|
||||
dsl: params.dsl,
|
||||
avatar: params.avatar,
|
||||
description: params.description,
|
||||
permission: params.permission,
|
||||
release: params.release,
|
||||
})
|
||||
: await agentService.createAgent(params);
|
||||
if (data.code === 0) {
|
||||
if (showMessage) {
|
||||
message.success(
|
||||
i18n.t(`message.${params?.id ? 'modified' : 'created'}`),
|
||||
i18n.t(`message.${agentId ? 'modified' : 'created'}`),
|
||||
);
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [AgentApiAction.FetchAgentListByPage],
|
||||
});
|
||||
if (agentId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [AgentApiAction.FetchAgentDetail],
|
||||
});
|
||||
}
|
||||
}
|
||||
return data;
|
||||
},
|
||||
@@ -329,17 +379,17 @@ export const useSetAgent = (showMessage: boolean = true) => {
|
||||
};
|
||||
|
||||
// Only one file can be uploaded at a time
|
||||
export const useUploadCanvasFile = () => {
|
||||
export const useUploadAgentFile = () => {
|
||||
const { id } = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const shared_id = searchParams.get('shared_id');
|
||||
const canvasId = id || shared_id;
|
||||
const agentId = id || shared_id;
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.UploadCanvasFile],
|
||||
mutationKey: [AgentApiAction.UploadAgentFile],
|
||||
mutationFn: async (body: any) => {
|
||||
let nextBody = body;
|
||||
try {
|
||||
@@ -350,10 +400,7 @@ export const useUploadCanvasFile = () => {
|
||||
});
|
||||
}
|
||||
|
||||
const { data } = await agentService.uploadCanvasFile(
|
||||
{ url: api.uploadAgentFile(canvasId as string), data: nextBody },
|
||||
true,
|
||||
);
|
||||
const { data } = await uploadAgentFile(agentId as string, nextBody);
|
||||
if (data?.code === 0) {
|
||||
message.success(i18n.t('message.uploaded'));
|
||||
}
|
||||
@@ -364,10 +411,10 @@ export const useUploadCanvasFile = () => {
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, uploadCanvasFile: mutateAsync };
|
||||
return { data, loading, uploadAgentFile: mutateAsync };
|
||||
};
|
||||
|
||||
export const useUploadCanvasFileWithProgress = (identifier?: string | null) => {
|
||||
export const useUploadAgentFileWithProgress = (identifier?: string | null) => {
|
||||
const { id } = useParams();
|
||||
|
||||
type UploadParameters = Parameters<NonNullable<FileUploadProps['onUpload']>>;
|
||||
@@ -379,7 +426,7 @@ export const useUploadCanvasFileWithProgress = (identifier?: string | null) => {
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.UploadCanvasFileWithProgress],
|
||||
mutationKey: [AgentApiAction.UploadAgentFileWithProgress],
|
||||
mutationFn: async ({
|
||||
files,
|
||||
options: { onError, onSuccess, onProgress },
|
||||
@@ -392,9 +439,9 @@ export const useUploadCanvasFileWithProgress = (identifier?: string | null) => {
|
||||
});
|
||||
}
|
||||
|
||||
const { data } = await agentService.uploadCanvasFile(
|
||||
const { data } = await agentService.uploadAgentFile(
|
||||
{
|
||||
url: api.uploadAgentFile(identifier || id),
|
||||
agentId: identifier || id,
|
||||
data: formData,
|
||||
onUploadProgress: ({ progress }) => {
|
||||
files.forEach((file) => {
|
||||
@@ -420,7 +467,7 @@ export const useUploadCanvasFileWithProgress = (identifier?: string | null) => {
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, uploadCanvasFile: mutateAsync };
|
||||
return { data, loading, uploadAgentFile: mutateAsync };
|
||||
};
|
||||
|
||||
export const useFetchMessageTrace = (canvasId?: string) => {
|
||||
@@ -490,9 +537,18 @@ export const useDebugSingle = () => {
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.FetchInputForm],
|
||||
mutationKey: [AgentApiAction.DebugSingle],
|
||||
mutationFn: async (params: IDebugSingleRequestBody) => {
|
||||
const ret = await agentService.debugSingle({ id, ...params });
|
||||
const ret = await agentService.debugSingle(
|
||||
{
|
||||
agentId: id as string,
|
||||
componentId: params.component_id,
|
||||
data: {
|
||||
params: params.params,
|
||||
},
|
||||
},
|
||||
true,
|
||||
);
|
||||
if (ret?.data?.code !== 0) {
|
||||
message.error(ret?.data?.message);
|
||||
}
|
||||
@@ -512,12 +568,7 @@ export const useFetchInputForm = (componentId?: string) => {
|
||||
enabled: !!id && !!componentId,
|
||||
queryFn: async () => {
|
||||
const { data } = await agentService.inputForm(
|
||||
{
|
||||
params: {
|
||||
id,
|
||||
component_id: componentId,
|
||||
},
|
||||
},
|
||||
{ agentId: id as string, componentId: componentId as string },
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -552,15 +603,19 @@ export const useFetchVersion = (
|
||||
data?: IFlow;
|
||||
loading: boolean;
|
||||
} => {
|
||||
const { id } = useParams();
|
||||
const { data, isFetching: loading } = useQuery({
|
||||
queryKey: [AgentApiAction.FetchVersion, version_id],
|
||||
queryKey: [AgentApiAction.FetchVersion, id, version_id],
|
||||
initialData: undefined,
|
||||
gcTime: 0,
|
||||
enabled: !!version_id, // Only call API when both values are provided
|
||||
enabled: !!id && !!version_id,
|
||||
queryFn: async () => {
|
||||
if (!version_id) return undefined;
|
||||
if (!id || !version_id) return undefined;
|
||||
|
||||
const { data } = await agentService.fetchVersion(version_id);
|
||||
const { data } = await agentService.fetchVersion({
|
||||
agentId: id,
|
||||
versionId: version_id,
|
||||
});
|
||||
|
||||
return data?.data ?? undefined;
|
||||
},
|
||||
@@ -569,35 +624,6 @@ export const useFetchVersion = (
|
||||
return { data, loading };
|
||||
};
|
||||
|
||||
export const useFetchAgentAvatar = (): {
|
||||
data: IFlow;
|
||||
loading: boolean;
|
||||
refetch: () => void;
|
||||
} => {
|
||||
const { sharedId } = useGetSharedChatSearchParams();
|
||||
|
||||
const {
|
||||
data,
|
||||
isFetching: loading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: [AgentApiAction.FetchAgentAvatar],
|
||||
initialData: {} as IFlow,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
if (!sharedId) return {};
|
||||
const { data } = await agentService.fetchAgentAvatar(sharedId);
|
||||
|
||||
return data?.data ?? {};
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, refetch };
|
||||
};
|
||||
|
||||
export const useFetchAgentLog = (searchParams: IAgentLogsRequest) => {
|
||||
const { id } = useParams();
|
||||
const { data, isFetching: loading } = useQuery<IAgentLogsResponse>({
|
||||
@@ -609,7 +635,7 @@ export const useFetchAgentLog = (searchParams: IAgentLogsRequest) => {
|
||||
...searchParams,
|
||||
});
|
||||
|
||||
return data?.data ?? [];
|
||||
return { total: data?.total ?? 0, sessions: data?.data ?? [] };
|
||||
},
|
||||
});
|
||||
|
||||
@@ -636,7 +662,7 @@ export const useFetchSessionsByCanvasId = () => {
|
||||
exp_user_id: tenantInfo.tenant_id,
|
||||
});
|
||||
|
||||
return data?.data ?? { total: 0, sessions: [] };
|
||||
return { total: data?.total ?? 0, sessions: data?.data ?? [] };
|
||||
},
|
||||
});
|
||||
|
||||
@@ -672,33 +698,6 @@ export const useFetchExternalAgentInputs = () => {
|
||||
return { data, loading, refetch };
|
||||
};
|
||||
|
||||
export const useSetAgentSetting = () => {
|
||||
const { id } = useParams();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: [AgentApiAction.SetAgentSetting],
|
||||
mutationFn: async (params: any) => {
|
||||
const ret = await agentService.settingCanvas({ id, ...params });
|
||||
if (ret?.data?.code === 0) {
|
||||
message.success('success');
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [AgentApiAction.FetchAgentDetail],
|
||||
});
|
||||
} else {
|
||||
message.error(ret?.data?.data);
|
||||
}
|
||||
return ret?.data?.code;
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, setAgentSetting: mutateAsync };
|
||||
};
|
||||
|
||||
export const useFetchPrompt = () => {
|
||||
const {
|
||||
data,
|
||||
@@ -731,7 +730,9 @@ export const useFetchAgentList = ({
|
||||
initialData: { canvas: [], total: 0 },
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const { data } = await fetchPipeLineList({ canvas_category });
|
||||
const { data } = await fetchPipeLineList({
|
||||
canvas_category,
|
||||
});
|
||||
|
||||
return data?.data ?? [];
|
||||
},
|
||||
@@ -767,7 +768,7 @@ export const useCancelDataflow = () => {
|
||||
// initialData: [],
|
||||
// gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3
|
||||
// queryFn: async () => {
|
||||
// const { data } = await agentService.listCanvas();
|
||||
// const { data } = await agentService.listAgents();
|
||||
|
||||
// return data?.data ?? [];
|
||||
// },
|
||||
@@ -793,7 +794,7 @@ export function useCancelConversation() {
|
||||
return { data, loading, cancelConversation: mutateAsync };
|
||||
}
|
||||
|
||||
export const useFetchFlowSSE = (): {
|
||||
export const useFetchSharedAgent = (): {
|
||||
data: IFlow;
|
||||
loading: boolean;
|
||||
refetch: () => void;
|
||||
@@ -805,7 +806,7 @@ export const useFetchFlowSSE = (): {
|
||||
isFetching: loading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: [AgentApiAction.FetchFlowDetailSSE],
|
||||
queryKey: [AgentApiAction.FetchSharedAgent, sharedId],
|
||||
initialData: {} as IFlow,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
@@ -813,7 +814,7 @@ export const useFetchFlowSSE = (): {
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
if (!sharedId) return {};
|
||||
const { data } = await agentService.getCanvasSSE(sharedId);
|
||||
const { data } = await agentService.getAgent(sharedId);
|
||||
|
||||
const messageList = buildMessageListWithUuid(
|
||||
get(data, 'data.dsl.messages', []),
|
||||
|
||||
@@ -297,6 +297,7 @@ export interface IPipeLineListRequest {
|
||||
orderby?: string;
|
||||
desc?: boolean;
|
||||
canvas_category?: AgentCategory;
|
||||
ext?: string;
|
||||
}
|
||||
|
||||
export interface GlobalVariableType {
|
||||
|
||||
@@ -10,7 +10,7 @@ import PdfSheet from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import {
|
||||
useFetchAgent,
|
||||
useUploadCanvasFileWithProgress,
|
||||
useUploadAgentFileWithProgress,
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
@@ -44,7 +44,7 @@ function AgentChatBox() {
|
||||
useGetFileIcon();
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { id: canvasId } = useParams();
|
||||
const { uploadCanvasFile, loading } = useUploadCanvasFileWithProgress();
|
||||
const { uploadAgentFile, loading } = useUploadAgentFileWithProgress();
|
||||
|
||||
const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
|
||||
derivedMessages,
|
||||
@@ -60,10 +60,10 @@ function AgentChatBox() {
|
||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||
useCallback(
|
||||
async (files, options) => {
|
||||
const ret = await uploadCanvasFile({ files, options });
|
||||
const ret = await uploadAgentFile({ files, options });
|
||||
appendUploadResponseList(ret.data, files);
|
||||
},
|
||||
[appendUploadResponseList, uploadCanvasFile],
|
||||
[appendUploadResponseList, uploadAgentFile],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -240,7 +240,7 @@ export const useSendAgentMessage = ({
|
||||
const inputs = useSelectBeginNodeDataInputs();
|
||||
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||
const { send, answerList, done, stopOutputMessage, resetAnswerList } =
|
||||
useSendMessageBySSE(url || api.runCanvas);
|
||||
useSendMessageBySSE(url || api.agentChatCompletion);
|
||||
const firstAnswer = answerList[0];
|
||||
const messageId = useMemo(() => {
|
||||
return firstAnswer?.message_id;
|
||||
@@ -298,13 +298,12 @@ export const useSendAgentMessage = ({
|
||||
beginInputs?: BeginQuery[];
|
||||
exploreSessionId?: string;
|
||||
}) => {
|
||||
const params: Record<string, unknown> = {
|
||||
id: agentId,
|
||||
};
|
||||
const params: Record<string, unknown> = { agent_id: agentId };
|
||||
|
||||
params.running_hint_text = i18n.t('flow.runningHintText', {
|
||||
defaultValue: 'is running...🕞',
|
||||
});
|
||||
params['openai-compatible'] = false;
|
||||
if (typeof message.content === 'string') {
|
||||
const query = inputs;
|
||||
|
||||
@@ -361,7 +360,7 @@ export const useSendAgentMessage = ({
|
||||
);
|
||||
|
||||
const sendFormMessage = useCallback(
|
||||
async (body: { id?: string; inputs: Record<string, BeginQuery> }) => {
|
||||
async (body: { agent_id?: string; inputs: Record<string, BeginQuery> }) => {
|
||||
addNewestOneQuestion({
|
||||
content: Object.entries(body.inputs)
|
||||
.map(([, val]) => `${val.name}: ${val.value}`)
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type FileUploadProps,
|
||||
} from '@/components/file-upload';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useUploadCanvasFile } from '@/hooks/use-agent-request';
|
||||
import { useUploadAgentFile } from '@/hooks/use-agent-request';
|
||||
import { Upload, X } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { toast } from 'sonner';
|
||||
@@ -34,7 +34,7 @@ export function FileUploadDirectUpload({
|
||||
Array.isArray(value) ? value : value ? [value] : [],
|
||||
);
|
||||
|
||||
const { uploadCanvasFile } = useUploadCanvasFile();
|
||||
const { uploadAgentFile } = useUploadAgentFile();
|
||||
|
||||
const onUpload: NonNullable<FileUploadProps['onUpload']> = React.useCallback(
|
||||
async (files, { onSuccess, onError }) => {
|
||||
@@ -47,7 +47,7 @@ export function FileUploadDirectUpload({
|
||||
);
|
||||
};
|
||||
try {
|
||||
const ret = await uploadCanvasFile([file]);
|
||||
const ret = await uploadAgentFile([file]);
|
||||
if (ret.code === 0) {
|
||||
onSuccess(file);
|
||||
uploadedFilesRef.current = [
|
||||
@@ -70,7 +70,7 @@ export function FileUploadDirectUpload({
|
||||
console.error('Unexpected error during upload:', error);
|
||||
}
|
||||
},
|
||||
[onChange, uploadCanvasFile],
|
||||
[onChange, uploadAgentFile],
|
||||
);
|
||||
|
||||
const onFileReject = React.useCallback((file: File, message: string) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import MessageItem from '@/components/next-message-item';
|
||||
import PdfSheet from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useUploadCanvasFileWithProgress } from '@/hooks/use-agent-request';
|
||||
import { useUploadAgentFileWithProgress } from '@/hooks/use-agent-request';
|
||||
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
|
||||
import { IAgentLogResponse } from '@/interfaces/database/agent';
|
||||
import { IMessage } from '@/interfaces/database/chat';
|
||||
@@ -55,16 +55,16 @@ export function SessionChat({ session }: SessionChatProps) {
|
||||
useClickDrawer();
|
||||
|
||||
// File upload
|
||||
const { uploadCanvasFile, loading: isUploading } =
|
||||
useUploadCanvasFileWithProgress();
|
||||
const { uploadAgentFile, loading: isUploading } =
|
||||
useUploadAgentFileWithProgress();
|
||||
|
||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||
useCallback(
|
||||
async (files, options) => {
|
||||
const ret = await uploadCanvasFile({ files, options });
|
||||
const ret = await uploadAgentFile({ files, options });
|
||||
appendUploadResponseList(ret.data, files);
|
||||
},
|
||||
[appendUploadResponseList, uploadCanvasFile],
|
||||
[appendUploadResponseList, uploadAgentFile],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { useSendAgentMessage } from '@/pages/agent/chat/use-send-agent-message';
|
||||
import { buildBeginInputListFromObject } from '@/pages/agent/form/begin-form/utils';
|
||||
import api from '@/utils/api';
|
||||
import { get, isEmpty } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
@@ -63,7 +62,6 @@ export const useSendSessionMessage = () => {
|
||||
value,
|
||||
...chatLogic
|
||||
} = useSendAgentMessage({
|
||||
url: api.runCanvasExplore(canvasId!),
|
||||
beginParams,
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ type IAwaitCompentData = {
|
||||
derivedMessages: IMessage[];
|
||||
sendFormMessage: (params: {
|
||||
inputs: Record<string, BeginQuery>;
|
||||
id: string;
|
||||
agent_id: string;
|
||||
}) => void;
|
||||
canvasId: string;
|
||||
};
|
||||
@@ -37,7 +37,7 @@ const useAwaitCompentData = (props: IAwaitCompentData) => {
|
||||
const nextInputs = buildBeginQueryWithObject(inputs, values);
|
||||
sendFormMessage({
|
||||
inputs: nextInputs,
|
||||
id: canvasId,
|
||||
agent_id: canvasId,
|
||||
});
|
||||
},
|
||||
[getInputs, sendFormMessage, canvasId],
|
||||
|
||||
@@ -13,7 +13,7 @@ export function useRunDataflow({
|
||||
}: {
|
||||
showLogSheet: () => void;
|
||||
} & Pick<UseFetchLogReturnType, 'setMessageId'>) {
|
||||
const { send } = useSendMessageBySSE(api.runCanvas);
|
||||
const { send } = useSendMessageBySSE(api.agentChatCompletion);
|
||||
const { id } = useParams();
|
||||
const { saveGraph, loading } = useSaveGraph();
|
||||
const [uploadedFileData, setUploadedFileData] =
|
||||
@@ -27,8 +27,9 @@ export function useRunDataflow({
|
||||
|
||||
showLogSheet();
|
||||
const res = await send({
|
||||
id,
|
||||
agent_id: id,
|
||||
query: '',
|
||||
'openai-compatible': false,
|
||||
session_id: null,
|
||||
files: [fileResponseData.file],
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { useSetAgentSetting } from '@/hooks/use-agent-request';
|
||||
import { useSetAgent } from '@/hooks/use-agent-request';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -18,16 +18,16 @@ import {
|
||||
|
||||
export function SettingDialog({ hideModal }: IModalProps<any>) {
|
||||
const { t } = useTranslation();
|
||||
const { setAgentSetting } = useSetAgentSetting();
|
||||
const { setAgent } = useSetAgent();
|
||||
|
||||
const submit = useCallback(
|
||||
async (values: SettingFormSchemaType) => {
|
||||
const code = await setAgentSetting(values);
|
||||
if (code === 0) {
|
||||
const ret = await setAgent(values);
|
||||
if (ret?.code === 0) {
|
||||
hideModal?.();
|
||||
}
|
||||
},
|
||||
[hideModal, setAgentSetting],
|
||||
[hideModal, setAgent],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,7 @@ import PdfSheet from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { useSyncThemeFromParams } from '@/components/theme-provider';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useUploadCanvasFileWithProgress } from '@/hooks/use-agent-request';
|
||||
import { useUploadAgentFileWithProgress } from '@/hooks/use-agent-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import i18n, { changeLanguageAsync } from '@/locales/config';
|
||||
import DebugContent from '@/pages/agent/debug-content';
|
||||
@@ -33,8 +33,8 @@ const ChatContainer = () => {
|
||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||
useClickDrawer();
|
||||
|
||||
const { uploadCanvasFile, loading } =
|
||||
useUploadCanvasFileWithProgress(conversationId);
|
||||
const { uploadAgentFile, loading } =
|
||||
useUploadAgentFileWithProgress(conversationId);
|
||||
const {
|
||||
addEventList,
|
||||
setCurrentMessageId,
|
||||
@@ -80,10 +80,10 @@ const ChatContainer = () => {
|
||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||
useCallback(
|
||||
async (files, options) => {
|
||||
const ret = await uploadCanvasFile({ files, options });
|
||||
const ret = await uploadAgentFile({ files, options });
|
||||
appendUploadResponseList(ret.data, files);
|
||||
},
|
||||
[appendUploadResponseList, uploadCanvasFile],
|
||||
[appendUploadResponseList, uploadAgentFile],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -37,7 +37,7 @@ export function AgentDropdown({
|
||||
);
|
||||
|
||||
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
|
||||
deleteAgent([agent.id]);
|
||||
deleteAgent(agent.id);
|
||||
}, [agent.id, deleteAgent]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@ import PdfSheet from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { useSyncThemeFromParams } from '@/components/theme-provider';
|
||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
||||
import { useFetchFlowSSE } from '@/hooks/use-agent-request';
|
||||
import { useFetchSharedAgent } from '@/hooks/use-agent-request';
|
||||
import { useFetchExternalChatInfo } from '@/hooks/use-chat-request';
|
||||
import i18n, { changeLanguageAsync } from '@/locales/config';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
@@ -44,7 +44,7 @@ const ChatContainer = () => {
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
const { data: chatInfo } = useFetchExternalChatInfo();
|
||||
|
||||
const { data: flowData } = useFetchFlowSSE();
|
||||
const { data: flowData } = useFetchSharedAgent();
|
||||
React.useEffect(() => {
|
||||
if (locale && i18n.language !== locale) {
|
||||
changeLanguageAsync(locale);
|
||||
|
||||
@@ -8,25 +8,20 @@ import { registerNextServer } from '@/utils/register-server';
|
||||
import request from '@/utils/request';
|
||||
|
||||
const {
|
||||
getCanvasSSE,
|
||||
setCanvas,
|
||||
listCanvas,
|
||||
resetCanvas,
|
||||
removeCanvas,
|
||||
runCanvas,
|
||||
listTemplates,
|
||||
createAgent,
|
||||
updateAgent: updateAgentApi,
|
||||
listAgents,
|
||||
deleteAgent,
|
||||
agentChatCompletion,
|
||||
resetAgent,
|
||||
listAgentTemplate,
|
||||
testDbConnect,
|
||||
getInputElements,
|
||||
debug,
|
||||
settingCanvas,
|
||||
uploadCanvasFile,
|
||||
trace,
|
||||
inputForm,
|
||||
fetchVersionList,
|
||||
fetchVersion,
|
||||
fetchCanvas,
|
||||
fetchAgentAvatar,
|
||||
fetchAgentLogs,
|
||||
getAgent,
|
||||
fetchAgentSessions,
|
||||
fetchExternalAgentInputs,
|
||||
prompt,
|
||||
cancelDataflow,
|
||||
@@ -34,16 +29,12 @@ const {
|
||||
} = api;
|
||||
|
||||
const methods = {
|
||||
fetchCanvas: {
|
||||
url: fetchCanvas,
|
||||
getAgent: {
|
||||
url: getAgent,
|
||||
method: 'get',
|
||||
},
|
||||
getCanvasSSE: {
|
||||
url: getCanvasSSE,
|
||||
method: 'get',
|
||||
},
|
||||
setCanvas: {
|
||||
url: setCanvas,
|
||||
createAgent: {
|
||||
url: createAgent,
|
||||
method: 'post',
|
||||
},
|
||||
fetchVersionList: {
|
||||
@@ -51,27 +42,28 @@ const methods = {
|
||||
method: 'get',
|
||||
},
|
||||
fetchVersion: {
|
||||
url: fetchVersion,
|
||||
url: (config: { agentId: string; versionId: string }) =>
|
||||
fetchVersion(config.agentId, config.versionId),
|
||||
method: 'get',
|
||||
},
|
||||
listCanvas: {
|
||||
url: listCanvas,
|
||||
listAgents: {
|
||||
url: listAgents,
|
||||
method: 'get',
|
||||
},
|
||||
resetCanvas: {
|
||||
url: resetCanvas,
|
||||
resetAgent: {
|
||||
url: resetAgent,
|
||||
method: 'post',
|
||||
},
|
||||
removeCanvas: {
|
||||
url: removeCanvas,
|
||||
deleteAgent: {
|
||||
url: deleteAgent,
|
||||
method: 'delete',
|
||||
},
|
||||
agentChatCompletion: {
|
||||
url: agentChatCompletion,
|
||||
method: 'post',
|
||||
},
|
||||
runCanvas: {
|
||||
url: runCanvas,
|
||||
method: 'post',
|
||||
},
|
||||
listTemplates: {
|
||||
url: listTemplates,
|
||||
listAgentTemplate: {
|
||||
url: listAgentTemplate,
|
||||
method: 'get',
|
||||
},
|
||||
testDbConnect: {
|
||||
@@ -83,31 +75,26 @@ const methods = {
|
||||
method: 'get',
|
||||
},
|
||||
debugSingle: {
|
||||
url: debug,
|
||||
url: (config: { agentId: string; componentId: string }) =>
|
||||
api.debug(config.agentId, config.componentId),
|
||||
method: 'post',
|
||||
},
|
||||
settingCanvas: {
|
||||
url: settingCanvas,
|
||||
method: 'post',
|
||||
},
|
||||
uploadCanvasFile: {
|
||||
url: uploadCanvasFile,
|
||||
uploadAgentFile: {
|
||||
url: (config: { agentId: string }) => api.uploadAgentFile(config.agentId),
|
||||
method: 'post',
|
||||
},
|
||||
trace: {
|
||||
url: trace,
|
||||
url: (config: { agentId: string; messageId: string }) =>
|
||||
trace(config.agentId, config.messageId),
|
||||
method: 'get',
|
||||
},
|
||||
inputForm: {
|
||||
url: inputForm,
|
||||
method: 'get',
|
||||
},
|
||||
fetchAgentAvatar: {
|
||||
url: fetchAgentAvatar,
|
||||
url: (config: { agentId: string; componentId: string }) =>
|
||||
api.inputForm(config.agentId, config.componentId),
|
||||
method: 'get',
|
||||
},
|
||||
fetchAgentLogs: {
|
||||
url: fetchAgentLogs,
|
||||
url: fetchAgentSessions,
|
||||
method: 'get',
|
||||
},
|
||||
fetchExternalAgentInputs: {
|
||||
@@ -127,15 +114,34 @@ const methods = {
|
||||
method: 'put',
|
||||
},
|
||||
createAgentSession: {
|
||||
url: fetchAgentLogs,
|
||||
method: 'put',
|
||||
url: api.createAgentSession,
|
||||
method: 'post',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const agentService = registerNextServer<keyof typeof methods>(methods);
|
||||
|
||||
export const updateAgent = (
|
||||
agentId: string,
|
||||
params: {
|
||||
title?: string;
|
||||
dsl?: Record<string, any>;
|
||||
avatar?: string;
|
||||
description?: string | null;
|
||||
permission?: string;
|
||||
release?: string;
|
||||
},
|
||||
) => {
|
||||
return request(updateAgentApi(agentId), { method: 'put', data: params });
|
||||
};
|
||||
|
||||
export const fetchTrace = (data: { canvas_id: string; message_id: string }) => {
|
||||
return request.get(methods.trace.url, { params: data });
|
||||
return request.get(
|
||||
methods.trace.url({
|
||||
agentId: data.canvas_id,
|
||||
messageId: data.message_id,
|
||||
}),
|
||||
);
|
||||
};
|
||||
export const fetchAgentLogsByCanvasId = (
|
||||
canvasId: string,
|
||||
@@ -145,11 +151,11 @@ export const fetchAgentLogsByCanvasId = (
|
||||
};
|
||||
|
||||
export const fetchAgentLogsById = (canvasId: string, sessionId: string) => {
|
||||
return request.get(api.fetchAgentLogsById(canvasId, sessionId));
|
||||
return request.get(api.fetchAgentSessionById(canvasId, sessionId));
|
||||
};
|
||||
|
||||
export const fetchPipeLineList = (params: IPipeLineListRequest) => {
|
||||
return request.get(api.listCanvas, { params: params });
|
||||
return request.get(api.listAgents, { params: params });
|
||||
};
|
||||
|
||||
export const fetchWebhookTrace = (
|
||||
@@ -160,11 +166,18 @@ export const fetchWebhookTrace = (
|
||||
};
|
||||
|
||||
export function createAgentSession({ id, name }: { id: string; name: string }) {
|
||||
return request.put(api.fetchAgentLogs(id), { data: { name } });
|
||||
return request.post(api.createAgentSession(id), { data: { name } });
|
||||
}
|
||||
|
||||
export const deleteAgentSession = (canvasId: string, sessionId: string) => {
|
||||
return request.delete(api.fetchAgentLogsById(canvasId, sessionId));
|
||||
return request.delete(api.fetchAgentSessionById(canvasId, sessionId));
|
||||
};
|
||||
|
||||
export const uploadAgentFile = (agentId: string, data: FormData) => {
|
||||
return request(api.uploadAgentFile(agentId), {
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export default agentService;
|
||||
+28
-29
@@ -83,7 +83,7 @@ export default {
|
||||
`${restAPIv1}/datasets/${datasetId}/trace_raptor`,
|
||||
unbindPipelineTask: ({ kb_id, type }: { kb_id: string; type: string }) =>
|
||||
`${webAPI}/kb/unbind_task?kb_id=${kb_id}&pipeline_task_type=${type}`,
|
||||
pipelineRerun: `${webAPI}/canvas/rerun`,
|
||||
pipelineRerun: `${restAPIv1}/agents/rerun`,
|
||||
getMetaData: (datasetId: string) =>
|
||||
`${restAPIv1}/datasets/${datasetId}/metadata/summary`,
|
||||
updateDocumentsMetadata: (datasetId: string) =>
|
||||
@@ -177,46 +177,45 @@ export default {
|
||||
setLangfuseConfig: `${restAPIv1}/langfuse/api-key`,
|
||||
|
||||
// flow
|
||||
listTemplates: `${webAPI}/canvas/templates`,
|
||||
listCanvas: `${webAPI}/canvas/list`,
|
||||
getCanvas: `${webAPI}/canvas/get`,
|
||||
getCanvasSSE: (canvasId: string) => `${webAPI}/canvas/getsse/${canvasId}`,
|
||||
removeCanvas: `${webAPI}/canvas/rm`,
|
||||
setCanvas: `${webAPI}/canvas/set`,
|
||||
settingCanvas: `${webAPI}/canvas/setting`,
|
||||
getListVersion: `${webAPI}/canvas/getlistversion`,
|
||||
getVersion: `${webAPI}/canvas/getversion`,
|
||||
resetCanvas: `${webAPI}/canvas/reset`,
|
||||
runCanvas: `${webAPI}/canvas/completion`,
|
||||
testDbConnect: `${webAPI}/canvas/test_db_connect`,
|
||||
listAgentTemplate: `${restAPIv1}/agents/templates`,
|
||||
listAgents: `${restAPIv1}/agents`,
|
||||
createAgent: `${restAPIv1}/agents`,
|
||||
updateAgent: (agentId: string) => `${restAPIv1}/agents/${agentId}`,
|
||||
deleteAgent: (agentId: string) => `${restAPIv1}/agents/${agentId}`,
|
||||
agentChatCompletion: `${restAPIv1}/agents/chat/completion`,
|
||||
resetAgent: (agentId: string) => `${restAPIv1}/agents/${agentId}/reset`,
|
||||
testDbConnect: `${restAPIv1}/agents/test_db_connection`,
|
||||
getInputElements: `${webAPI}/canvas/input_elements`,
|
||||
debug: `${webAPI}/canvas/debug`,
|
||||
uploadCanvasFile: `${webAPI}/canvas/upload`,
|
||||
trace: `${webAPI}/canvas/trace`,
|
||||
debug: (agentId: string, componentId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/components/${componentId}/debug`,
|
||||
trace: (agentId: string, messageId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/logs/${messageId}`,
|
||||
cancelCanvas: (taskId: string) => `${webAPI}/canvas/cancel/${taskId}`, // cancel conversation
|
||||
// agent
|
||||
inputForm: `${webAPI}/canvas/input_form`,
|
||||
fetchVersionList: (id: string) => `${webAPI}/canvas/getlistversion/${id}`,
|
||||
fetchVersion: (id: string) => `${webAPI}/canvas/getversion/${id}`,
|
||||
fetchCanvas: (id: string) => `${webAPI}/canvas/get/${id}`,
|
||||
fetchAgentAvatar: (id: string) => `${webAPI}/canvas/getsse/${id}`,
|
||||
uploadAgentFile: (id?: string) => `${webAPI}/canvas/upload/${id}`,
|
||||
inputForm: (agentId: string, componentId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/components/${componentId}/input-form`,
|
||||
fetchVersionList: (id: string) => `${restAPIv1}/agents/${id}/versions`,
|
||||
fetchVersion: (agentId: string, versionId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/versions/${versionId}`,
|
||||
getAgent: (id: string) => `${restAPIv1}/agents/${id}`,
|
||||
uploadAgentFile: (id?: string) => `${restAPIv1}/agents/${id}/upload`,
|
||||
createAgentSession: (agentId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/sessions`,
|
||||
fetchAgentLogs: (canvasId: string) => `${webAPI}/canvas/${canvasId}/sessions`,
|
||||
fetchAgentLogsById: (canvasId: string, sessionId: string) =>
|
||||
`${webAPI}/canvas/${canvasId}/sessions/${sessionId}`,
|
||||
fetchAgentSessions: (agentId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/sessions`,
|
||||
fetchAgentSessionById: (agentId: string, sessionId: string) =>
|
||||
`${restAPIv1}/agents/${agentId}/sessions/${sessionId}`,
|
||||
fetchExternalAgentInputs: (canvasId: string) =>
|
||||
`${restAPIv1}/agentbots/${canvasId}/inputs`,
|
||||
prompt: `${webAPI}/canvas/prompts`,
|
||||
prompt: `${restAPIv1}/agents/prompts`,
|
||||
cancelDataflow: (id: string) => `${webAPI}/canvas/cancel/${id}`,
|
||||
downloadFile: `${webAPI}/canvas/download`,
|
||||
downloadFile: `${restAPIv1}/agents/download`,
|
||||
testWebhook: (id: string) => `${restAPIv1}/webhook_test/${id}`,
|
||||
fetchWebhookTrace: (id: string) => `${restAPIv1}/webhook_trace/${id}`,
|
||||
|
||||
// explore
|
||||
|
||||
runCanvasExplore: (canvasId: string) =>
|
||||
`${webAPI}/canvas/${canvasId}/completion`,
|
||||
|
||||
// mcp server
|
||||
listMcpServer: `${restAPIv1}/mcp/servers`,
|
||||
getMcpServer: (id: string) => `${restAPIv1}/mcp/servers/${id}`,
|
||||
|
||||
Reference in new issue
Block a user