mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-14 20:54:30 +08:00
Refa: align chat and search restful APIs (#14229)
### What problem does this PR solve? Refactor /api/v1/chats to be more RESTful. ### Type of change - [x] Refactoring --------- Co-authored-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
@@ -267,7 +267,7 @@ def list_session_with_chat_assistants(auth, chat_assistant_id, params=None):
|
||||
|
||||
def update_session_with_chat_assistant(auth, chat_assistant_id, session_id, payload=None):
|
||||
url = f"{HOST_ADDRESS}{SESSION_WITH_CHAT_ASSISTANT_API_URL}/{session_id}".format(chat_id=chat_assistant_id)
|
||||
res = requests.put(url=url, headers=HEADERS, auth=auth, json=payload)
|
||||
res = requests.patch(url=url, headers=HEADERS, auth=auth, json=payload)
|
||||
return res.json()
|
||||
|
||||
|
||||
@@ -395,7 +395,7 @@ def agent_completions(auth, agent_id, payload=None):
|
||||
return res.json()
|
||||
|
||||
|
||||
def chat_completions(auth, chat_id, payload=None):
|
||||
def chat_completions(auth, chat_id=None, payload=None):
|
||||
"""
|
||||
Send a question/message to a chat assistant and get completion.
|
||||
|
||||
@@ -403,14 +403,19 @@ def chat_completions(auth, chat_id, payload=None):
|
||||
auth: Authentication object
|
||||
chat_id: Chat assistant ID
|
||||
payload: Dictionary containing:
|
||||
- question: str (required) - The question to ask
|
||||
- messages: list (required) - Conversation messages
|
||||
- stream: bool (optional) - Whether to stream responses, default False
|
||||
- session_id: str (optional) - Session ID for conversation context
|
||||
|
||||
Returns:
|
||||
Response JSON with answer data
|
||||
"""
|
||||
url = f"{HOST_ADDRESS}/api/{VERSION}/chats/{chat_id}/completions"
|
||||
url = f"{HOST_ADDRESS}/api/{VERSION}/chat/completions"
|
||||
payload = dict(payload or {})
|
||||
if chat_id:
|
||||
payload.setdefault("chat_id", chat_id)
|
||||
if "question" in payload and "messages" not in payload:
|
||||
payload["messages"] = [{"role": "user", "content": payload.pop("question")}]
|
||||
res = requests.post(url=url, headers=HEADERS, auth=auth, json=payload)
|
||||
return res.json()
|
||||
|
||||
|
||||
@@ -62,7 +62,11 @@ class TestChatCompletions:
|
||||
res = chat_completions(
|
||||
HttpApiAuth,
|
||||
chat_id,
|
||||
{"question": "hello", "stream": False, "session_id": session_id},
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"stream": False,
|
||||
"session_id": session_id,
|
||||
},
|
||||
)
|
||||
assert res["code"] == 0, res
|
||||
assert isinstance(res["data"], dict), res
|
||||
@@ -75,10 +79,14 @@ class TestChatCompletions:
|
||||
res = chat_completions(
|
||||
HttpApiAuth,
|
||||
"invalid_chat_id",
|
||||
{"question": "hello", "stream": False, "session_id": "invalid_session"},
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"stream": False,
|
||||
"session_id": "invalid_session",
|
||||
},
|
||||
)
|
||||
assert res["code"] == 102, res
|
||||
assert "You don't own the chat" in res.get("message", ""), res
|
||||
assert res["code"] == 109, res
|
||||
assert "No authorization." in res.get("message", ""), res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_chat_completion_invalid_session(self, HttpApiAuth, request):
|
||||
@@ -91,32 +99,44 @@ class TestChatCompletions:
|
||||
res = chat_completions(
|
||||
HttpApiAuth,
|
||||
chat_id,
|
||||
{"question": "hello", "stream": False, "session_id": "invalid_session"},
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"stream": False,
|
||||
"session_id": "invalid_session",
|
||||
},
|
||||
)
|
||||
assert res["code"] == 102, res
|
||||
assert "You don't own the session" in res.get("message", ""), res
|
||||
assert "Session not found!" in res.get("message", ""), res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_chat_completion_invalid_metadata_condition(self, HttpApiAuth, request):
|
||||
def test_chat_completion_stream_false_with_chat_without_session(self, HttpApiAuth, request):
|
||||
res = create_chat_assistant(HttpApiAuth, {"name": "chat_completion_invalid_meta", "dataset_ids": []})
|
||||
assert res["code"] == 0, res
|
||||
chat_id = res["data"]["id"]
|
||||
request.addfinalizer(lambda: delete_all_chat_assistants(HttpApiAuth))
|
||||
request.addfinalizer(lambda: delete_all_sessions_with_chat_assistant(HttpApiAuth, chat_id))
|
||||
|
||||
res = create_session_with_chat_assistant(HttpApiAuth, chat_id, {"name": "session_for_meta"})
|
||||
assert res["code"] == 0, res
|
||||
session_id = res["data"]["id"]
|
||||
|
||||
res = chat_completions(
|
||||
HttpApiAuth,
|
||||
chat_id,
|
||||
{
|
||||
"question": "hello",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"stream": False,
|
||||
"session_id": session_id,
|
||||
"metadata_condition": "invalid",
|
||||
},
|
||||
)
|
||||
assert res["code"] == 102, res
|
||||
assert "metadata_condition" in res.get("message", ""), res
|
||||
assert res["code"] == 0, res
|
||||
assert res["data"]["session_id"], res
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_chat_completion_stream_false_without_chat(self, HttpApiAuth):
|
||||
res = chat_completions(
|
||||
HttpApiAuth,
|
||||
None,
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"stream": False,
|
||||
},
|
||||
)
|
||||
assert res["code"] == 0, res
|
||||
assert isinstance(res["data"], dict), res
|
||||
assert "answer" in res["data"], res
|
||||
|
||||
@@ -40,6 +40,13 @@ class _DummyAtomic:
|
||||
return False
|
||||
|
||||
|
||||
class _StubResponse:
|
||||
def __init__(self, data=None, mimetype=None):
|
||||
self.data = data
|
||||
self.mimetype = mimetype
|
||||
self.headers = {}
|
||||
|
||||
|
||||
class _Args(dict):
|
||||
def get(self, key, default=None):
|
||||
return super().get(key, default)
|
||||
@@ -111,6 +118,7 @@ def _load_search_api(monkeypatch):
|
||||
|
||||
quart_mod = ModuleType("quart")
|
||||
quart_mod.request = SimpleNamespace(args=_Args())
|
||||
quart_mod.Response = _StubResponse
|
||||
monkeypatch.setitem(sys.modules, "quart", quart_mod)
|
||||
|
||||
common_pkg = ModuleType("common")
|
||||
@@ -201,6 +209,15 @@ def _load_search_api(monkeypatch):
|
||||
search_service_mod.SearchService = _SearchService
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.search_service", search_service_mod)
|
||||
|
||||
dialog_service_mod = ModuleType("api.db.services.dialog_service")
|
||||
|
||||
async def _async_ask(*_args, **_kwargs):
|
||||
if False:
|
||||
yield None
|
||||
|
||||
dialog_service_mod.async_ask = _async_ask
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.dialog_service", dialog_service_mod)
|
||||
|
||||
user_service_mod = ModuleType("api.db.services.user_service")
|
||||
|
||||
class _TenantService:
|
||||
|
||||
Reference in New Issue
Block a user