mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-29 04:08:12 +08:00
Refactor: reformat all code for lefthook using ruff and gofmt (#16585)
This commit is contained in:
@@ -523,5 +523,3 @@ def search_dataset(auth, dataset_id, payload=None, *, headers=HEADERS):
|
||||
url = f"{HOST_ADDRESS}{DATASETS_API_URL}/{dataset_id}/search"
|
||||
res = requests.post(url=url, headers=headers, auth=auth, json=payload)
|
||||
return res.json()
|
||||
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ def _load_chat_module(monkeypatch):
|
||||
common_constants_mod.StatusEnum = _StubStatusEnum
|
||||
# Import pure-Python constants from the real module (no heavy deps)
|
||||
from common.constants import MAXIMUM_PAGE_NUMBER as _MPN, MAXIMUM_TASK_PAGE_NUMBER as _MTPN
|
||||
|
||||
common_constants_mod.MAXIMUM_PAGE_NUMBER = _MPN
|
||||
common_constants_mod.MAXIMUM_TASK_PAGE_NUMBER = _MTPN
|
||||
monkeypatch.setitem(sys.modules, "common.constants", common_constants_mod)
|
||||
@@ -410,7 +411,7 @@ def _load_chat_module(monkeypatch):
|
||||
api_utils_mod.get_json_result = lambda data=None, message="", code=0: {"code": code, "data": data, "message": message}
|
||||
api_utils_mod.get_request_json = lambda: _AwaitableValue({})
|
||||
api_utils_mod.server_error_response = lambda ex: {"code": 500, "data": None, "message": str(ex)}
|
||||
api_utils_mod.validate_request = lambda *_args, **_kwargs: (lambda func: func)
|
||||
api_utils_mod.validate_request = lambda *_args, **_kwargs: lambda func: func
|
||||
monkeypatch.setitem(sys.modules, "api.utils.api_utils", api_utils_mod)
|
||||
|
||||
rag_pkg = ModuleType("rag")
|
||||
|
||||
@@ -90,11 +90,7 @@ TEST_EXCEL_DATA_2 = [
|
||||
["Keyboard", "79", "Electronics"],
|
||||
]
|
||||
|
||||
DEFAULT_CHAT_PROMPT = (
|
||||
"You are a helpful assistant that answers questions about table data using SQL queries.\n\n"
|
||||
"Here is the knowledge base:\n{knowledge}\n\n"
|
||||
"Use this information to answer questions."
|
||||
)
|
||||
DEFAULT_CHAT_PROMPT = "You are a helpful assistant that answers questions about table data using SQL queries.\n\nHere is the knowledge base:\n{knowledge}\n\nUse this information to answer questions."
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("add_table_parser_dataset")
|
||||
@@ -171,12 +167,7 @@ class TestTableParserDatasetChat:
|
||||
Test that table parser dataset chat works correctly.
|
||||
"""
|
||||
# Use class-level attributes (set by setup fixture)
|
||||
answer = self._ask_question(
|
||||
self.__class__.auth,
|
||||
self.__class__.chat_id,
|
||||
self.__class__.session_id,
|
||||
question
|
||||
)
|
||||
answer = self._ask_question(self.__class__.auth, self.__class__.chat_id, self.__class__.session_id, question)
|
||||
|
||||
# Verify answer matches expected pattern if provided
|
||||
if expected_answer_pattern:
|
||||
@@ -315,7 +306,4 @@ class TestTableParserDatasetChat:
|
||||
answer: The actual answer from the chat assistant
|
||||
pattern: Regular expression pattern to match
|
||||
"""
|
||||
assert re.search(pattern, answer, re.IGNORECASE), (
|
||||
f"Answer does not match expected pattern '{pattern}'.\n"
|
||||
f"Answer: {answer}"
|
||||
)
|
||||
assert re.search(pattern, answer, re.IGNORECASE), f"Answer does not match expected pattern '{pattern}'.\nAnswer: {answer}"
|
||||
|
||||
@@ -107,9 +107,7 @@ class TestAddChunk:
|
||||
assert False, res
|
||||
chunks_count = res["data"]["doc"]["chunk_count"]
|
||||
res = add_chunk(HttpApiAuth, dataset_id, document_id, payload)
|
||||
assert res["code"] == expected_code, (
|
||||
f"Expected code: {expected_code}, got: {res['code']}, message: {res.get('message')}"
|
||||
)
|
||||
assert res["code"] == expected_code, f"Expected code: {expected_code}, got: {res['code']}, message: {res.get('message')}"
|
||||
if expected_code == 0:
|
||||
validate_chunk_details(dataset_id, document_id, payload, res)
|
||||
res = list_chunks(HttpApiAuth, dataset_id, document_id)
|
||||
|
||||
@@ -53,11 +53,7 @@ class _DummyKB:
|
||||
|
||||
class _DummyRetriever:
|
||||
async def retrieval(self, *_args, **_kwargs):
|
||||
return {
|
||||
"chunks": [
|
||||
{"doc_id": "doc-1", "content_with_weight": "chunk-content", "similarity": 0.8, "docnm_kwd": "doc-title", "vector": [0.1]}
|
||||
]
|
||||
}
|
||||
return {"chunks": [{"doc_id": "doc-1", "content_with_weight": "chunk-content", "similarity": 0.8, "docnm_kwd": "doc-title", "vector": [0.1]}]}
|
||||
|
||||
def retrieval_by_children(self, chunks, _tenant_ids):
|
||||
return chunks
|
||||
@@ -115,7 +111,7 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
|
||||
# Mock tenant_llm_service for TenantLLMService and TenantService
|
||||
tenant_llm_service_mod = ModuleType("api.db.services.tenant_llm_service")
|
||||
|
||||
|
||||
class _MockModelConfig:
|
||||
def __init__(self, tenant_id, model_name):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -128,7 +124,7 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
self.used_tokens = 0
|
||||
self.status = 1
|
||||
self.id = 1
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"tenant_id": self.tenant_id,
|
||||
@@ -140,35 +136,27 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
"max_tokens": self.max_tokens,
|
||||
"used_tokens": self.used_tokens,
|
||||
"status": self.status,
|
||||
"id": self.id
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
|
||||
class _StubTenantService:
|
||||
@staticmethod
|
||||
def get_by_id(tenant_id):
|
||||
# Return a mock tenant with default model configurations
|
||||
return True, SimpleNamespace(
|
||||
id=tenant_id,
|
||||
llm_id="chat-model",
|
||||
embd_id="embd-model",
|
||||
asr_id="asr-model",
|
||||
img2txt_id="img2txt-model",
|
||||
rerank_id="rerank-model",
|
||||
tts_id="tts-model"
|
||||
)
|
||||
|
||||
return True, SimpleNamespace(id=tenant_id, llm_id="chat-model", embd_id="embd-model", asr_id="asr-model", img2txt_id="img2txt-model", rerank_id="rerank-model", tts_id="tts-model")
|
||||
|
||||
class _StubTenantLLMService:
|
||||
@staticmethod
|
||||
def get_api_key(tenant_id, model_name):
|
||||
return _MockModelConfig(tenant_id, model_name)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def split_model_name_and_factory(model_name):
|
||||
if "@" in model_name:
|
||||
parts = model_name.split("@")
|
||||
return parts[0], parts[1]
|
||||
return model_name, None
|
||||
|
||||
|
||||
tenant_llm_service_mod.TenantService = _StubTenantService
|
||||
tenant_llm_service_mod.TenantLLMService = _StubTenantLLMService
|
||||
|
||||
@@ -180,32 +168,31 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
|
||||
# Mock llm_service for LLMService
|
||||
llm_service_mod = ModuleType("api.db.services.llm_service")
|
||||
|
||||
|
||||
class _StubLLM:
|
||||
def __init__(self, llm_name):
|
||||
self.llm_name = llm_name
|
||||
self.is_tools = False
|
||||
|
||||
|
||||
class _StubLLMBundle:
|
||||
def __init__(self, tenant_id: str, model_config: dict, lang="Chinese", **kwargs):
|
||||
self.tenant_id = tenant_id
|
||||
self.model_config = model_config
|
||||
self.lang = lang
|
||||
|
||||
|
||||
def encode(self, texts: list):
|
||||
import numpy as np
|
||||
|
||||
# Return mock embeddings and token usage
|
||||
return [np.array([0.1, 0.2, 0.3]) for _ in texts], len(texts) * 10
|
||||
|
||||
llm_service_mod.LLMService = SimpleNamespace(
|
||||
query=lambda llm_name: [_StubLLM(llm_name)] if llm_name else []
|
||||
)
|
||||
|
||||
llm_service_mod.LLMService = SimpleNamespace(query=lambda llm_name: [_StubLLM(llm_name)] if llm_name else [])
|
||||
llm_service_mod.LLMBundle = _StubLLMBundle
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.llm_service", llm_service_mod)
|
||||
|
||||
# Mock tenant_model_service to ensure it uses mocked services
|
||||
tenant_model_service_mod = ModuleType("api.db.joint_services.tenant_model_service")
|
||||
|
||||
|
||||
class _MockModelConfig2:
|
||||
def __init__(self, tenant_id, model_name):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -218,7 +205,7 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
self.used_tokens = 0
|
||||
self.status = 1
|
||||
self.id = 1
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"tenant_id": self.tenant_id,
|
||||
@@ -230,9 +217,9 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
"max_tokens": self.max_tokens,
|
||||
"used_tokens": self.used_tokens,
|
||||
"status": self.status,
|
||||
"id": self.id
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
|
||||
def _get_model_config_by_id(
|
||||
tenant_model_id: int,
|
||||
allowed_tenant_ids=None,
|
||||
@@ -247,16 +234,16 @@ def _load_dify_retrieval_module(monkeypatch):
|
||||
if mock_tenant_id not in allowed_tenant_ids and str(requester_tenant_id) != mock_tenant_id:
|
||||
raise LookupError(f"Tenant Model with id {tenant_model_id} not authorized")
|
||||
return _MockModelConfig2(mock_tenant_id, "model-1").to_dict()
|
||||
|
||||
|
||||
def _get_model_config_from_provider_instance(tenant_id: str, model_type: str, model_name: str):
|
||||
if not model_name:
|
||||
raise Exception("Model Name is required")
|
||||
return _MockModelConfig2(tenant_id, model_name).to_dict()
|
||||
|
||||
|
||||
def _get_tenant_default_model_by_type(tenant_id: str, model_type):
|
||||
# Return mock tenant with default model configurations
|
||||
return _MockModelConfig2(tenant_id, "chat-model").to_dict()
|
||||
|
||||
|
||||
tenant_model_service_mod.get_model_config_by_id = _get_model_config_by_id
|
||||
tenant_model_service_mod.get_model_config_from_provider_instance = _get_model_config_from_provider_instance
|
||||
tenant_model_service_mod.get_tenant_default_model_by_type = _get_tenant_default_model_by_type
|
||||
|
||||
@@ -146,8 +146,8 @@ class TestDatasetsList:
|
||||
@pytest.mark.parametrize(
|
||||
"params, assertions",
|
||||
[
|
||||
({"orderby": "create_time"}, lambda r: (is_sorted(r["data"], "create_time", True))),
|
||||
({"orderby": "update_time"}, lambda r: (is_sorted(r["data"], "update_time", True))),
|
||||
({"orderby": "create_time"}, lambda r: is_sorted(r["data"], "create_time", True)),
|
||||
({"orderby": "update_time"}, lambda r: is_sorted(r["data"], "update_time", True)),
|
||||
],
|
||||
ids=["orderby_create_time", "orderby_update_time"],
|
||||
)
|
||||
@@ -185,16 +185,16 @@ class TestDatasetsList:
|
||||
@pytest.mark.parametrize(
|
||||
"params, assertions",
|
||||
[
|
||||
({"desc": True}, lambda r: (is_sorted(r["data"], "create_time", True))),
|
||||
({"desc": False}, lambda r: (is_sorted(r["data"], "create_time", False))),
|
||||
({"desc": "true"}, lambda r: (is_sorted(r["data"], "create_time", True))),
|
||||
({"desc": "false"}, lambda r: (is_sorted(r["data"], "create_time", False))),
|
||||
({"desc": 1}, lambda r: (is_sorted(r["data"], "create_time", True))),
|
||||
({"desc": 0}, lambda r: (is_sorted(r["data"], "create_time", False))),
|
||||
({"desc": "yes"}, lambda r: (is_sorted(r["data"], "create_time", True))),
|
||||
({"desc": "no"}, lambda r: (is_sorted(r["data"], "create_time", False))),
|
||||
({"desc": "y"}, lambda r: (is_sorted(r["data"], "create_time", True))),
|
||||
({"desc": "n"}, lambda r: (is_sorted(r["data"], "create_time", False))),
|
||||
({"desc": True}, lambda r: is_sorted(r["data"], "create_time", True)),
|
||||
({"desc": False}, lambda r: is_sorted(r["data"], "create_time", False)),
|
||||
({"desc": "true"}, lambda r: is_sorted(r["data"], "create_time", True)),
|
||||
({"desc": "false"}, lambda r: is_sorted(r["data"], "create_time", False)),
|
||||
({"desc": 1}, lambda r: is_sorted(r["data"], "create_time", True)),
|
||||
({"desc": 0}, lambda r: is_sorted(r["data"], "create_time", False)),
|
||||
({"desc": "yes"}, lambda r: is_sorted(r["data"], "create_time", True)),
|
||||
({"desc": "no"}, lambda r: is_sorted(r["data"], "create_time", False)),
|
||||
({"desc": "y"}, lambda r: is_sorted(r["data"], "create_time", True)),
|
||||
({"desc": "n"}, lambda r: is_sorted(r["data"], "create_time", False)),
|
||||
],
|
||||
ids=["desc=True", "desc=False", "desc=true", "desc=false", "desc=1", "desc=0", "desc=yes", "desc=no", "desc=y", "desc=n"],
|
||||
)
|
||||
|
||||
@@ -223,12 +223,16 @@ def test_upload_file_success_uses_new_service_layer(monkeypatch):
|
||||
"create_folder",
|
||||
lambda _file, parent_id, _names, _len_id, *_args: SimpleNamespace(id=parent_id),
|
||||
)
|
||||
monkeypatch.setattr(module.settings, "STORAGE_IMPL", SimpleNamespace(
|
||||
obj_exist=lambda *_args, **_kwargs: False,
|
||||
put=lambda bucket, location, blob: storage_puts.append((bucket, location, blob)),
|
||||
rm=lambda *_args, **_kwargs: None,
|
||||
move=lambda *_args, **_kwargs: None,
|
||||
))
|
||||
monkeypatch.setattr(
|
||||
module.settings,
|
||||
"STORAGE_IMPL",
|
||||
SimpleNamespace(
|
||||
obj_exist=lambda *_args, **_kwargs: False,
|
||||
put=lambda bucket, location, blob: storage_puts.append((bucket, location, blob)),
|
||||
rm=lambda *_args, **_kwargs: None,
|
||||
move=lambda *_args, **_kwargs: None,
|
||||
),
|
||||
)
|
||||
|
||||
ok, data = _run(module.upload_file("tenant1", "pf1", [_DummyUploadFile("a.txt", b"hello")]))
|
||||
assert ok is True
|
||||
@@ -291,12 +295,16 @@ def test_move_files_handles_dest_and_storage_move(monkeypatch):
|
||||
"get_by_ids",
|
||||
lambda _ids: [_DummyFile("file1", module.FileType.DOC.value, parent_id="src", location="old", name="a.txt")],
|
||||
)
|
||||
monkeypatch.setattr(module.settings, "STORAGE_IMPL", SimpleNamespace(
|
||||
obj_exist=lambda *_args, **_kwargs: False,
|
||||
put=lambda *_args, **_kwargs: None,
|
||||
rm=lambda *_args, **_kwargs: None,
|
||||
move=lambda old_bucket, old_loc, new_bucket, new_loc: moved.append((old_bucket, old_loc, new_bucket, new_loc)),
|
||||
))
|
||||
monkeypatch.setattr(
|
||||
module.settings,
|
||||
"STORAGE_IMPL",
|
||||
SimpleNamespace(
|
||||
obj_exist=lambda *_args, **_kwargs: False,
|
||||
put=lambda *_args, **_kwargs: None,
|
||||
rm=lambda *_args, **_kwargs: None,
|
||||
move=lambda old_bucket, old_loc, new_bucket, new_loc: moved.append((old_bucket, old_loc, new_bucket, new_loc)),
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(module.FileService, "update_by_id", lambda file_id, data: updated.append((file_id, data)) or True)
|
||||
|
||||
ok, message = _run(module.move_files("tenant1", ["file1"], "missing"))
|
||||
|
||||
@@ -147,8 +147,10 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
monkeypatch.setitem(sys.modules, "common.settings", common_settings_mod)
|
||||
|
||||
common_misc_utils_mod = ModuleType("common.misc_utils")
|
||||
|
||||
async def _thread_pool_exec(func, *args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
common_misc_utils_mod.thread_pool_exec = _thread_pool_exec
|
||||
monkeypatch.setitem(sys.modules, "common.misc_utils", common_misc_utils_mod)
|
||||
|
||||
@@ -243,9 +245,7 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
api_utils_mod.get_error_data_result = lambda message="Sorry! Data missing!", code=102: {"code": code, "message": message}
|
||||
api_utils_mod.get_request_json = lambda: _AwaitableValue({})
|
||||
api_utils_mod.get_result = lambda code=0, message="", data=None, total=None: {
|
||||
key: value
|
||||
for key, value in {"code": code, "message": message, "data": data, "total": total}.items()
|
||||
if value is not None
|
||||
key: value for key, value in {"code": code, "message": message, "data": data, "total": total}.items() if value is not None
|
||||
}
|
||||
api_utils_mod.server_error_response = lambda e: {"code": 500, "message": str(e)}
|
||||
monkeypatch.setitem(sys.modules, "api.utils.api_utils", api_utils_mod)
|
||||
@@ -255,12 +255,11 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
monkeypatch.setitem(sys.modules, "api.utils.image_utils", image_utils_mod)
|
||||
|
||||
reference_metadata_utils_mod = ModuleType("api.utils.reference_metadata_utils")
|
||||
reference_metadata_utils_mod.resolve_reference_metadata_preferences = (
|
||||
lambda req, *_args, **_kwargs: (
|
||||
bool((req.get("reference_metadata") or {}).get("include")),
|
||||
set((req.get("reference_metadata") or {}).get("fields") or []),
|
||||
)
|
||||
reference_metadata_utils_mod.resolve_reference_metadata_preferences = lambda req, *_args, **_kwargs: (
|
||||
bool((req.get("reference_metadata") or {}).get("include")),
|
||||
set((req.get("reference_metadata") or {}).get("fields") or []),
|
||||
)
|
||||
|
||||
def _enrich_chunks_with_document_metadata(chunks, metadata_fields=None):
|
||||
for chunk in chunks:
|
||||
doc_id = chunk.get("doc_id") or chunk.get("document_id")
|
||||
@@ -325,7 +324,7 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
|
||||
# Mock tenant_llm_service for TenantLLMService and TenantService
|
||||
tenant_llm_service_mod = ModuleType("api.db.services.tenant_llm_service")
|
||||
|
||||
|
||||
class _MockModelConfig:
|
||||
def __init__(self, tenant_id, model_name):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -338,7 +337,7 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
self.used_tokens = 0
|
||||
self.status = 1
|
||||
self.id = 1
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"tenant_id": self.tenant_id,
|
||||
@@ -350,46 +349,40 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
"max_tokens": self.max_tokens,
|
||||
"used_tokens": self.used_tokens,
|
||||
"status": self.status,
|
||||
"id": self.id
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
|
||||
class _StubTenantService:
|
||||
@staticmethod
|
||||
def get_by_id(tenant_id):
|
||||
return True, SimpleNamespace(
|
||||
id=tenant_id,
|
||||
llm_id="chat-model",
|
||||
embd_id="embd-model",
|
||||
asr_id="asr-model",
|
||||
img2txt_id="img2txt-model",
|
||||
rerank_id="rerank-model",
|
||||
tts_id="tts-model"
|
||||
)
|
||||
|
||||
return True, SimpleNamespace(id=tenant_id, llm_id="chat-model", embd_id="embd-model", asr_id="asr-model", img2txt_id="img2txt-model", rerank_id="rerank-model", tts_id="tts-model")
|
||||
|
||||
class _StubTenantLLMService:
|
||||
@staticmethod
|
||||
def get_api_key(tenant_id, model_name):
|
||||
return _MockModelConfig(tenant_id, model_name)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def split_model_name_and_factory(model_name):
|
||||
if "@" in model_name:
|
||||
parts = model_name.split("@")
|
||||
return parts[0], parts[1]
|
||||
return model_name, None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(tenant_model_id):
|
||||
return True, _MockModelConfig("tenant-1", "model-1")
|
||||
|
||||
|
||||
@staticmethod
|
||||
def model_instance(model_config):
|
||||
class _EmbedModel:
|
||||
def encode(self, texts):
|
||||
import numpy as np
|
||||
|
||||
return [np.array([0.2, 0.8]), np.array([0.3, 0.7])], 1
|
||||
|
||||
return _EmbedModel()
|
||||
|
||||
|
||||
tenant_llm_service_mod.TenantService = _StubTenantService
|
||||
tenant_llm_service_mod.TenantLLMService = _StubTenantLLMService
|
||||
|
||||
@@ -401,32 +394,31 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
|
||||
# Mock LLMService
|
||||
llm_service_mod = ModuleType("api.db.services.llm_service")
|
||||
|
||||
|
||||
class _StubLLM:
|
||||
def __init__(self, llm_name):
|
||||
self.llm_name = llm_name
|
||||
self.is_tools = False
|
||||
|
||||
|
||||
class _StubLLMBundle:
|
||||
def __init__(self, tenant_id: str, model_config: dict, lang="Chinese", **kwargs):
|
||||
self.tenant_id = tenant_id
|
||||
self.model_config = model_config
|
||||
self.lang = lang
|
||||
|
||||
|
||||
def encode(self, texts: list):
|
||||
import numpy as np
|
||||
|
||||
# Return mock embeddings and token usage
|
||||
return [np.array([0.2, 0.8]), np.array([0.3, 0.7])], len(texts) * 10
|
||||
|
||||
llm_service_mod.LLMService = SimpleNamespace(
|
||||
query=lambda llm_name: [_StubLLM(llm_name)] if llm_name else []
|
||||
)
|
||||
|
||||
llm_service_mod.LLMService = SimpleNamespace(query=lambda llm_name: [_StubLLM(llm_name)] if llm_name else [])
|
||||
llm_service_mod.LLMBundle = _StubLLMBundle
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.llm_service", llm_service_mod)
|
||||
|
||||
# Mock tenant_model_service to ensure it uses mocked services
|
||||
tenant_model_service_mod = ModuleType("api.db.joint_services.tenant_model_service")
|
||||
|
||||
|
||||
class _MockModelConfig2:
|
||||
def __init__(self, tenant_id, model_name):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -439,7 +431,7 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
self.used_tokens = 0
|
||||
self.status = 1
|
||||
self.id = 1
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"tenant_id": self.tenant_id,
|
||||
@@ -451,9 +443,9 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
"max_tokens": self.max_tokens,
|
||||
"used_tokens": self.used_tokens,
|
||||
"status": self.status,
|
||||
"id": self.id
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
|
||||
def _get_model_config_by_id(
|
||||
tenant_model_id: int,
|
||||
allowed_tenant_ids=None,
|
||||
@@ -468,16 +460,16 @@ def _load_doc_module(monkeypatch, module_basename="chunk_api"):
|
||||
if mock_tenant_id not in allowed_tenant_ids and str(requester_tenant_id) != mock_tenant_id:
|
||||
raise LookupError(f"Tenant Model with id {tenant_model_id} not authorized")
|
||||
return _MockModelConfig2(mock_tenant_id, "model-1").to_dict()
|
||||
|
||||
|
||||
def _get_model_config_from_provider_instance(tenant_id: str, model_type: str, model_name: str):
|
||||
if not model_name:
|
||||
raise Exception("Model Name is required")
|
||||
return _MockModelConfig2(tenant_id, model_name).to_dict()
|
||||
|
||||
|
||||
def _get_tenant_default_model_by_type(tenant_id: str, model_type):
|
||||
# Return mock tenant with default model configurations
|
||||
return _MockModelConfig2(tenant_id, "chat-model").to_dict()
|
||||
|
||||
|
||||
tenant_model_service_mod.get_model_config_by_id = _get_model_config_by_id
|
||||
tenant_model_service_mod.get_model_config_from_provider_instance = _get_model_config_from_provider_instance
|
||||
tenant_model_service_mod.get_tenant_default_model_by_type = _get_tenant_default_model_by_type
|
||||
@@ -636,7 +628,6 @@ class TestDocRoutesUnit:
|
||||
assert res["filename"] == "report.pdf"
|
||||
assert res["mimetype"] == "application/pdf"
|
||||
|
||||
|
||||
def test_parse_branches(self, monkeypatch):
|
||||
module = _load_doc_module(monkeypatch)
|
||||
monkeypatch.setattr(module.KnowledgebaseService, "accessible", lambda **_kwargs: False)
|
||||
|
||||
@@ -155,10 +155,10 @@ class TestDocumentsList:
|
||||
@pytest.mark.parametrize(
|
||||
"params, expected_code, assertions, expected_message",
|
||||
[
|
||||
({"orderby": None}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""),
|
||||
({"orderby": "create_time"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""),
|
||||
({"orderby": "update_time"}, 0, lambda r: (is_sorted(r["data"]["docs"], "update_time", True)), ""),
|
||||
pytest.param({"orderby": "name", "desc": "False"}, 0, lambda r: (is_sorted(r["data"]["docs"], "name", False)), "", marks=pytest.mark.skip(reason="issues/5851")),
|
||||
({"orderby": None}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", True), ""),
|
||||
({"orderby": "create_time"}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", True), ""),
|
||||
({"orderby": "update_time"}, 0, lambda r: is_sorted(r["data"]["docs"], "update_time", True), ""),
|
||||
pytest.param({"orderby": "name", "desc": "False"}, 0, lambda r: is_sorted(r["data"]["docs"], "name", False), "", marks=pytest.mark.skip(reason="issues/5851")),
|
||||
pytest.param({"orderby": "unknown"}, 102, 0, "orderby should be create_time or update_time", marks=pytest.mark.skip(reason="issues/5851")),
|
||||
],
|
||||
)
|
||||
@@ -184,14 +184,14 @@ class TestDocumentsList:
|
||||
@pytest.mark.parametrize(
|
||||
"params, expected_code, assertions, expected_message",
|
||||
[
|
||||
({"desc": None}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""),
|
||||
({"desc": "true"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""),
|
||||
({"desc": "True"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""),
|
||||
({"desc": True}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", True)), ""),
|
||||
pytest.param({"desc": "false"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", False)), "", marks=pytest.mark.skip(reason="issues/5851")),
|
||||
({"desc": "False"}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", False)), ""),
|
||||
({"desc": False}, 0, lambda r: (is_sorted(r["data"]["docs"], "create_time", False)), ""),
|
||||
({"desc": "False", "orderby": "update_time"}, 0, lambda r: (is_sorted(r["data"]["docs"], "update_time", False)), ""),
|
||||
({"desc": None}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", True), ""),
|
||||
({"desc": "true"}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", True), ""),
|
||||
({"desc": "True"}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", True), ""),
|
||||
({"desc": True}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", True), ""),
|
||||
pytest.param({"desc": "false"}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", False), "", marks=pytest.mark.skip(reason="issues/5851")),
|
||||
({"desc": "False"}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", False), ""),
|
||||
({"desc": False}, 0, lambda r: is_sorted(r["data"]["docs"], "create_time", False), ""),
|
||||
({"desc": "False", "orderby": "update_time"}, 0, lambda r: is_sorted(r["data"]["docs"], "update_time", False), ""),
|
||||
pytest.param({"desc": "unknown"}, 102, 0, "desc should be true or false", marks=pytest.mark.skip(reason="issues/5851")),
|
||||
],
|
||||
)
|
||||
@@ -231,7 +231,6 @@ class TestDocumentsList:
|
||||
assert len(res["data"]["docs"]) == expected_num
|
||||
assert res["data"]["total"] == expected_num
|
||||
|
||||
|
||||
@pytest.mark.p1
|
||||
@pytest.mark.parametrize(
|
||||
"params, expected_code, expected_num, expected_message",
|
||||
@@ -240,21 +239,21 @@ class TestDocumentsList:
|
||||
({"name": ""}, 0, 5, ""),
|
||||
({"name": "ragflow_test_upload_0.txt"}, 0, 1, ""),
|
||||
(
|
||||
{"name": "unknown.txt"},
|
||||
102,
|
||||
0,
|
||||
"You don't own the document unknown.txt.",
|
||||
{"name": "unknown.txt"},
|
||||
102,
|
||||
0,
|
||||
"You don't own the document unknown.txt.",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_name(
|
||||
self,
|
||||
HttpApiAuth,
|
||||
add_documents,
|
||||
params,
|
||||
expected_code,
|
||||
expected_num,
|
||||
expected_message,
|
||||
self,
|
||||
HttpApiAuth,
|
||||
add_documents,
|
||||
params,
|
||||
expected_code,
|
||||
expected_num,
|
||||
expected_message,
|
||||
):
|
||||
dataset_id, _ = add_documents
|
||||
res = list_documents(HttpApiAuth, dataset_id, params=params)
|
||||
@@ -267,7 +266,6 @@ class TestDocumentsList:
|
||||
else:
|
||||
assert res["message"] == expected_message
|
||||
|
||||
|
||||
@pytest.mark.p1
|
||||
@pytest.mark.parametrize(
|
||||
"document_id, expected_code, expected_num, expected_message",
|
||||
@@ -279,13 +277,13 @@ class TestDocumentsList:
|
||||
],
|
||||
)
|
||||
def test_id(
|
||||
self,
|
||||
HttpApiAuth,
|
||||
add_documents,
|
||||
document_id,
|
||||
expected_code,
|
||||
expected_num,
|
||||
expected_message,
|
||||
self,
|
||||
HttpApiAuth,
|
||||
add_documents,
|
||||
document_id,
|
||||
expected_code,
|
||||
expected_num,
|
||||
expected_message,
|
||||
):
|
||||
dataset_id, document_ids = add_documents
|
||||
if callable(document_id):
|
||||
@@ -304,7 +302,6 @@ class TestDocumentsList:
|
||||
else:
|
||||
assert res["message"] == expected_message
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
@pytest.mark.parametrize(
|
||||
"document_id, name, expected_code, expected_num, expected_message",
|
||||
@@ -313,23 +310,23 @@ class TestDocumentsList:
|
||||
(lambda r: r[0], "ragflow_test_upload_1.txt", 0, 0, ""),
|
||||
(lambda r: r[0], "unknown", 102, 0, "You don't own the document unknown."),
|
||||
(
|
||||
"id",
|
||||
"ragflow_test_upload_0.txt",
|
||||
102,
|
||||
0,
|
||||
"You don't own the document id.",
|
||||
"id",
|
||||
"ragflow_test_upload_0.txt",
|
||||
102,
|
||||
0,
|
||||
"You don't own the document id.",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_name_and_id(
|
||||
self,
|
||||
HttpApiAuth,
|
||||
add_documents,
|
||||
document_id,
|
||||
name,
|
||||
expected_code,
|
||||
expected_num,
|
||||
expected_message,
|
||||
self,
|
||||
HttpApiAuth,
|
||||
add_documents,
|
||||
document_id,
|
||||
name,
|
||||
expected_code,
|
||||
expected_num,
|
||||
expected_message,
|
||||
):
|
||||
dataset_id, document_ids = add_documents
|
||||
if callable(document_id):
|
||||
@@ -343,7 +340,6 @@ class TestDocumentsList:
|
||||
else:
|
||||
assert res["message"] == expected_message
|
||||
|
||||
|
||||
@pytest.mark.p3
|
||||
def test_concurrent_list(self, HttpApiAuth, add_documents):
|
||||
dataset_id, _ = add_documents
|
||||
@@ -379,9 +375,7 @@ class TestDocumentsList:
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_metadata_condition_validation(
|
||||
self, HttpApiAuth, add_documents, params, expected_code, expected_message
|
||||
):
|
||||
def test_metadata_condition_validation(self, HttpApiAuth, add_documents, params, expected_code, expected_message):
|
||||
dataset_id, _ = add_documents
|
||||
res = list_documents(HttpApiAuth, dataset_id, params=params)
|
||||
assert res["code"] == expected_code
|
||||
@@ -399,9 +393,7 @@ class TestDocumentsList:
|
||||
({"create_time_from": "0", "create_time_to": "9999999999000"}, 0, 5),
|
||||
],
|
||||
)
|
||||
def test_create_time_filter(
|
||||
self, HttpApiAuth, add_documents, params, expected_code, expected_total
|
||||
):
|
||||
def test_create_time_filter(self, HttpApiAuth, add_documents, params, expected_code, expected_total):
|
||||
dataset_id, _ = add_documents
|
||||
res = list_documents(HttpApiAuth, dataset_id, params=params)
|
||||
|
||||
@@ -417,9 +409,7 @@ class TestDocumentsList:
|
||||
({"run": ["INVALID_STATUS"]}, 102, "Invalid filter run status conditions: INVALID_STATUS"),
|
||||
],
|
||||
)
|
||||
def test_run_status_filter_invalid(
|
||||
self, HttpApiAuth, add_documents, params, expected_code, expected_message
|
||||
):
|
||||
def test_run_status_filter_invalid(self, HttpApiAuth, add_documents, params, expected_code, expected_message):
|
||||
dataset_id, _ = add_documents
|
||||
res = list_documents(HttpApiAuth, dataset_id, params=params)
|
||||
|
||||
@@ -434,9 +424,7 @@ class TestDocumentsList:
|
||||
({"run": ["UNSTART"]}, 5),
|
||||
],
|
||||
)
|
||||
def test_run_status_filter_unstart(
|
||||
self, HttpApiAuth, add_documents, params, expected_size
|
||||
):
|
||||
def test_run_status_filter_unstart(self, HttpApiAuth, add_documents, params, expected_size):
|
||||
dataset_id, _ = add_documents
|
||||
res = list_documents(HttpApiAuth, dataset_id, params=params)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ End-to-end tests for metadata batch update API.
|
||||
This test file converts the unit test test_metadata_batch_update from test_doc_sdk_routes_unit.py
|
||||
to end-to-end tests that call the actual HTTP API.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from common import (
|
||||
update_documents_metadata,
|
||||
@@ -376,7 +377,7 @@ class TestMetadataBatchUpdateSuccess:
|
||||
{
|
||||
"selector": {
|
||||
"document_ids": document_ids,
|
||||
"metadata_condition": {"conditions": [{"comparison_operator":"is", "name": "nonexistent_key", "value": "nonexistent_value"}]},
|
||||
"metadata_condition": {"conditions": [{"comparison_operator": "is", "name": "nonexistent_key", "value": "nonexistent_value"}]},
|
||||
},
|
||||
"updates": [{"key": "author", "value": "test"}],
|
||||
"deletes": [],
|
||||
|
||||
@@ -53,10 +53,7 @@ def _condition_parsing_complete(_auth, dataset_id):
|
||||
@pytest.fixture(scope="function")
|
||||
def add_dataset_with_metadata(HttpApiAuth):
|
||||
# First create the dataset
|
||||
res = create_dataset(HttpApiAuth, {
|
||||
"name": f"test_metadata_{int(__import__('time').time())}",
|
||||
"chunk_method": "naive"
|
||||
})
|
||||
res = create_dataset(HttpApiAuth, {"name": f"test_metadata_{int(__import__('time').time())}", "chunk_method": "naive"})
|
||||
|
||||
assert res["code"] == 0, f"Failed to create dataset: {res}"
|
||||
dataset_id = res["data"]["id"]
|
||||
@@ -75,7 +72,7 @@ def add_dataset_with_metadata(HttpApiAuth):
|
||||
{"key": "era", "type": "string", "description": "Historical era"},
|
||||
{"key": "achievements", "type": "list", "description": "Major achievements"},
|
||||
]
|
||||
}
|
||||
},
|
||||
).json()
|
||||
|
||||
assert res["code"] == 0, f"Failed to configure metadata: {res}"
|
||||
@@ -118,14 +115,10 @@ class TestMetadataWithRetrieval:
|
||||
doc2_id = res["data"][1]["id"]
|
||||
|
||||
# Add different metadata to each document
|
||||
res = update_document(HttpApiAuth, dataset_id, doc1_id, {
|
||||
"meta_fields": {"character": "Zhuge Liang", "era": "Three Kingdoms"}
|
||||
})
|
||||
res = update_document(HttpApiAuth, dataset_id, doc1_id, {"meta_fields": {"character": "Zhuge Liang", "era": "Three Kingdoms"}})
|
||||
assert res["code"] == 0, f"Failed to update doc1 metadata: {res}"
|
||||
|
||||
res = update_document(HttpApiAuth, dataset_id, doc2_id, {
|
||||
"meta_fields": {"character": "Cao Cao", "era": "Late Eastern Han"}
|
||||
})
|
||||
res = update_document(HttpApiAuth, dataset_id, doc2_id, {"meta_fields": {"character": "Cao Cao", "era": "Late Eastern Han"}})
|
||||
assert res["code"] == 0, f"Failed to update doc2 metadata: {res}"
|
||||
|
||||
# Parse both documents
|
||||
@@ -136,20 +129,14 @@ class TestMetadataWithRetrieval:
|
||||
assert _condition_parsing_complete(HttpApiAuth, dataset_id), "Parsing timeout"
|
||||
|
||||
# Test retrieval WITH metadata filter for "Zhuge Liang"
|
||||
res = retrieval_chunks(HttpApiAuth, {
|
||||
"question": "Zhuge Liang",
|
||||
"dataset_ids": [dataset_id],
|
||||
"metadata_condition": {
|
||||
"logic": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"name": "character",
|
||||
"comparison_operator": "is",
|
||||
"value": "Zhuge Liang"
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
res = retrieval_chunks(
|
||||
HttpApiAuth,
|
||||
{
|
||||
"question": "Zhuge Liang",
|
||||
"dataset_ids": [dataset_id],
|
||||
"metadata_condition": {"logic": "and", "conditions": [{"name": "character", "comparison_operator": "is", "value": "Zhuge Liang"}]},
|
||||
},
|
||||
)
|
||||
assert res["code"] == 0, f"Retrieval with metadata filter failed: {res}"
|
||||
|
||||
chunks_with_filter = res["data"]["chunks"]
|
||||
|
||||
@@ -140,7 +140,7 @@ class TestDocumentsUpload:
|
||||
fp = create_txt_file(tmp_path / "ragflow_test.txt")
|
||||
res = upload_documents(HttpApiAuth, "invalid_dataset_id", [fp])
|
||||
assert res["code"] == 102
|
||||
assert res["message"] == "Can\'t find the dataset with ID invalid_dataset_id!"
|
||||
assert res["message"] == "Can't find the dataset with ID invalid_dataset_id!"
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_duplicate_files(self, HttpApiAuth, add_dataset_func, tmp_path):
|
||||
|
||||
@@ -56,6 +56,7 @@ def _agent_items(res):
|
||||
return data.get("canvas", [])
|
||||
return data
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def agent_id(HttpApiAuth, request):
|
||||
res = list_agents(HttpApiAuth, {"page_size": 100})
|
||||
|
||||
@@ -56,6 +56,7 @@ def _agent_items(res):
|
||||
return data.get("canvas", [])
|
||||
return data
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def agent_id(HttpApiAuth, request):
|
||||
res = list_agents(HttpApiAuth, {"page_size": 100})
|
||||
@@ -81,7 +82,6 @@ def agent_id(HttpApiAuth, request):
|
||||
|
||||
|
||||
class TestAgentSessions:
|
||||
|
||||
@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"})
|
||||
|
||||
@@ -77,8 +77,7 @@ class TestChatCompletionsOpenAI:
|
||||
assert "prompt_tokens" in usage, f"'usage' should contain 'prompt_tokens': {usage}"
|
||||
assert "completion_tokens" in usage, f"'usage' should contain 'completion_tokens': {usage}"
|
||||
assert "total_tokens" in usage, f"'usage' should contain 'total_tokens': {usage}"
|
||||
assert usage["total_tokens"] == usage["prompt_tokens"] + usage["completion_tokens"], \
|
||||
f"total_tokens should equal prompt_tokens + completion_tokens: {usage}"
|
||||
assert usage["total_tokens"] == usage["prompt_tokens"] + usage["completion_tokens"], f"total_tokens should equal prompt_tokens + completion_tokens: {usage}"
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_openai_chat_completion_token_count_reasonable(self, HttpApiAuth, add_dataset_func, tmp_path, request):
|
||||
|
||||
@@ -86,10 +86,10 @@ class TestSessionsWithChatAssistantList:
|
||||
@pytest.mark.parametrize(
|
||||
"params, expected_code, assertions, expected_message",
|
||||
[
|
||||
({"orderby": None}, 0, lambda r: (is_sorted(r["data"], "create_time", True)), ""),
|
||||
({"orderby": "create_time"}, 0, lambda r: (is_sorted(r["data"], "create_time", True)), ""),
|
||||
({"orderby": "update_time"}, 0, lambda r: (is_sorted(r["data"], "update_time", True)), ""),
|
||||
({"orderby": "name", "desc": "False"}, 0, lambda r: (is_sorted(r["data"], "name", False)), ""),
|
||||
({"orderby": None}, 0, lambda r: is_sorted(r["data"], "create_time", True), ""),
|
||||
({"orderby": "create_time"}, 0, lambda r: is_sorted(r["data"], "create_time", True), ""),
|
||||
({"orderby": "update_time"}, 0, lambda r: is_sorted(r["data"], "update_time", True), ""),
|
||||
({"orderby": "name", "desc": "False"}, 0, lambda r: is_sorted(r["data"], "name", False), ""),
|
||||
pytest.param({"orderby": "unknown"}, 102, 0, "orderby should be create_time or update_time", marks=pytest.mark.skip(reason="issues/")),
|
||||
],
|
||||
)
|
||||
@@ -115,14 +115,14 @@ class TestSessionsWithChatAssistantList:
|
||||
@pytest.mark.parametrize(
|
||||
"params, expected_code, assertions, expected_message",
|
||||
[
|
||||
({"desc": None}, 0, lambda r: (is_sorted(r["data"], "create_time", True)), ""),
|
||||
({"desc": "true"}, 0, lambda r: (is_sorted(r["data"], "create_time", True)), ""),
|
||||
({"desc": "True"}, 0, lambda r: (is_sorted(r["data"], "create_time", True)), ""),
|
||||
({"desc": True}, 0, lambda r: (is_sorted(r["data"], "create_time", True)), ""),
|
||||
({"desc": "false"}, 0, lambda r: (is_sorted(r["data"], "create_time", False)), ""),
|
||||
({"desc": "False"}, 0, lambda r: (is_sorted(r["data"], "create_time", False)), ""),
|
||||
({"desc": False}, 0, lambda r: (is_sorted(r["data"], "create_time", False)), ""),
|
||||
({"desc": "False", "orderby": "update_time"}, 0, lambda r: (is_sorted(r["data"], "update_time", False)), ""),
|
||||
({"desc": None}, 0, lambda r: is_sorted(r["data"], "create_time", True), ""),
|
||||
({"desc": "true"}, 0, lambda r: is_sorted(r["data"], "create_time", True), ""),
|
||||
({"desc": "True"}, 0, lambda r: is_sorted(r["data"], "create_time", True), ""),
|
||||
({"desc": True}, 0, lambda r: is_sorted(r["data"], "create_time", True), ""),
|
||||
({"desc": "false"}, 0, lambda r: is_sorted(r["data"], "create_time", False), ""),
|
||||
({"desc": "False"}, 0, lambda r: is_sorted(r["data"], "create_time", False), ""),
|
||||
({"desc": False}, 0, lambda r: is_sorted(r["data"], "create_time", False), ""),
|
||||
({"desc": "False", "orderby": "update_time"}, 0, lambda r: is_sorted(r["data"], "update_time", False), ""),
|
||||
pytest.param({"desc": "unknown"}, 102, 0, "desc should be true or false", marks=pytest.mark.skip(reason="issues/")),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -247,6 +247,7 @@ def _load_session_module(monkeypatch):
|
||||
common_constants_mod.TAG_FLD = "tag_feas"
|
||||
# Import pure-Python constants from the real module (no heavy deps)
|
||||
from common.constants import MAXIMUM_PAGE_NUMBER as _MPN, MAXIMUM_TASK_PAGE_NUMBER as _MTPN
|
||||
|
||||
common_constants_mod.MAXIMUM_PAGE_NUMBER = _MPN
|
||||
common_constants_mod.MAXIMUM_TASK_PAGE_NUMBER = _MTPN
|
||||
monkeypatch.setitem(sys.modules, "common.constants", common_constants_mod)
|
||||
@@ -269,13 +270,11 @@ def _load_session_module(monkeypatch):
|
||||
api_utils_mod.get_error_data_result = lambda message="Sorry! Data missing!", code=_StubRetCode.DATA_ERROR: {"code": code, "message": message}
|
||||
api_utils_mod.get_json_result = lambda code=_StubRetCode.SUCCESS, message="success", data=None: {"code": code, "message": message, "data": data}
|
||||
api_utils_mod.get_result = lambda code=_StubRetCode.SUCCESS, message="", data=None, total=None: {
|
||||
key: value
|
||||
for key, value in {"code": code, "message": message, "data": data, "total": total}.items()
|
||||
if value is not None
|
||||
key: value for key, value in {"code": code, "message": message, "data": data, "total": total}.items() if value is not None
|
||||
}
|
||||
api_utils_mod.get_request_json = lambda: _AwaitableValue({})
|
||||
api_utils_mod.server_error_response = lambda e: {"code": _StubRetCode.SERVER_ERROR, "message": str(e)}
|
||||
api_utils_mod.validate_request = lambda *_args, **_kwargs: (lambda func: func)
|
||||
api_utils_mod.validate_request = lambda *_args, **_kwargs: lambda func: func
|
||||
monkeypatch.setitem(sys.modules, "api.utils.api_utils", api_utils_mod)
|
||||
|
||||
rag_app_tag_mod = ModuleType("rag.app.tag")
|
||||
@@ -344,7 +343,7 @@ def _load_session_module(monkeypatch):
|
||||
|
||||
# Mock tenant_llm_service for TenantLLMService and TenantService
|
||||
tenant_llm_service_mod = ModuleType("api.db.services.tenant_llm_service")
|
||||
|
||||
|
||||
class _MockModelConfig:
|
||||
def __init__(self, tenant_id, model_name):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -357,7 +356,7 @@ def _load_session_module(monkeypatch):
|
||||
self.used_tokens = 0
|
||||
self.status = 1
|
||||
self.id = 1
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"tenant_id": self.tenant_id,
|
||||
@@ -369,23 +368,15 @@ def _load_session_module(monkeypatch):
|
||||
"max_tokens": self.max_tokens,
|
||||
"used_tokens": self.used_tokens,
|
||||
"status": self.status,
|
||||
"id": self.id
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
|
||||
class _StubTenantService:
|
||||
@staticmethod
|
||||
def get_by_id(tenant_id):
|
||||
# Return a mock tenant with default model configurations
|
||||
return True, SimpleNamespace(
|
||||
id=tenant_id,
|
||||
llm_id="chat-model",
|
||||
embd_id="embd-model",
|
||||
asr_id="asr-model",
|
||||
img2txt_id="img2txt-model",
|
||||
rerank_id="rerank-model",
|
||||
tts_id="tts-model"
|
||||
)
|
||||
|
||||
return True, SimpleNamespace(id=tenant_id, llm_id="chat-model", embd_id="embd-model", asr_id="asr-model", img2txt_id="img2txt-model", rerank_id="rerank-model", tts_id="tts-model")
|
||||
|
||||
class _StubTenantLLMService:
|
||||
@staticmethod
|
||||
def get_api_key(tenant_id, model_name):
|
||||
@@ -410,16 +401,14 @@ def _load_session_module(monkeypatch):
|
||||
|
||||
# Mock LLMService
|
||||
llm_service_mod = ModuleType("api.db.services.llm_service")
|
||||
|
||||
|
||||
class _StubLLM:
|
||||
def __init__(self, llm_name):
|
||||
self.llm_name = llm_name
|
||||
self.is_tools = False
|
||||
|
||||
llm_service_mod.LLMService = SimpleNamespace(
|
||||
query=lambda llm_name: [_StubLLM(llm_name)] if llm_name else []
|
||||
)
|
||||
|
||||
|
||||
llm_service_mod.LLMService = SimpleNamespace(query=lambda llm_name: [_StubLLM(llm_name)] if llm_name else [])
|
||||
|
||||
class _StubLLMBundle:
|
||||
def __init__(self, tenant_id: str, model_config: dict, lang="Chinese", **kwargs):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -431,13 +420,13 @@ def _load_session_module(monkeypatch):
|
||||
|
||||
def transcription(self, audio_path):
|
||||
return "mock transcription"
|
||||
|
||||
|
||||
llm_service_mod.LLMBundle = _StubLLMBundle
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.llm_service", llm_service_mod)
|
||||
|
||||
# Mock tenant_model_service to ensure it uses mocked services
|
||||
tenant_model_service_mod = ModuleType("api.db.joint_services.tenant_model_service")
|
||||
|
||||
|
||||
class _MockModelConfig2:
|
||||
def __init__(self, tenant_id, model_name, model_type="chat"):
|
||||
self.tenant_id = tenant_id
|
||||
@@ -462,7 +451,7 @@ def _load_session_module(monkeypatch):
|
||||
"max_tokens": self.max_tokens,
|
||||
"used_tokens": self.used_tokens,
|
||||
"status": self.status,
|
||||
"id": self.id
|
||||
"id": self.id,
|
||||
}
|
||||
|
||||
def _get_model_config_by_id(
|
||||
@@ -501,6 +490,7 @@ def _load_session_module(monkeypatch):
|
||||
def _get_tenant_default_model_by_type(tenant_id: str, model_type):
|
||||
# Check if tenant exists
|
||||
from api.db.services.tenant_llm_service import TenantService
|
||||
|
||||
exist, tenant = TenantService.get_by_id(tenant_id)
|
||||
if not exist:
|
||||
raise LookupError("Tenant not found!")
|
||||
@@ -523,19 +513,11 @@ def _load_session_module(monkeypatch):
|
||||
raise Exception("OCR model name is required")
|
||||
if not model_name:
|
||||
# Use friendly model type names
|
||||
friendly_names = {
|
||||
"embedding": "Embedding",
|
||||
"speech2text": "ASR",
|
||||
"image2text": "Image2Text",
|
||||
"chat": "Chat",
|
||||
"rerank": "Rerank",
|
||||
"tts": "TTS",
|
||||
"ocr": "OCR"
|
||||
}
|
||||
friendly_names = {"embedding": "Embedding", "speech2text": "ASR", "image2text": "Image2Text", "chat": "Chat", "rerank": "Rerank", "tts": "TTS", "ocr": "OCR"}
|
||||
friendly_name = friendly_names.get(model_type_val, model_type_val)
|
||||
raise Exception(f"No default {friendly_name} model is set")
|
||||
return _MockModelConfig2(tenant_id, model_name, model_type_val).to_dict()
|
||||
|
||||
|
||||
tenant_model_service_mod.get_model_config_by_id = _get_model_config_by_id
|
||||
tenant_model_service_mod.get_model_config_from_provider_instance = _get_model_config_from_provider_instance
|
||||
tenant_model_service_mod.get_tenant_default_model_by_type = _get_tenant_default_model_by_type
|
||||
@@ -710,7 +692,7 @@ def _load_session_module(monkeypatch):
|
||||
module.manager = _DummyManager()
|
||||
monkeypatch.setitem(sys.modules, "test_session_sdk_routes_unit_module", module)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
|
||||
# Add TenantService to module for test compatibility
|
||||
class _StubTenantServiceForTest:
|
||||
@staticmethod
|
||||
@@ -721,18 +703,10 @@ def _load_session_module(monkeypatch):
|
||||
@staticmethod
|
||||
def get_by_id(tenant_id):
|
||||
# Return mock tenant by id
|
||||
return True, SimpleNamespace(
|
||||
id=tenant_id,
|
||||
llm_id="chat-model",
|
||||
embd_id="embd-model",
|
||||
asr_id="asr-model",
|
||||
img2txt_id="img2txt-model",
|
||||
rerank_id="rerank-model",
|
||||
tts_id="tts-model"
|
||||
)
|
||||
return True, SimpleNamespace(id=tenant_id, llm_id="chat-model", embd_id="embd-model", asr_id="asr-model", img2txt_id="img2txt-model", rerank_id="rerank-model", tts_id="tts-model")
|
||||
|
||||
module.TenantService = _StubTenantServiceForTest
|
||||
|
||||
|
||||
return module
|
||||
|
||||
|
||||
@@ -2202,6 +2176,7 @@ def test_build_reference_chunks_metadata_matrix_unit(monkeypatch):
|
||||
# chat_api unit tests — session user-id spoofing fix
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _load_chat_api_module(monkeypatch):
|
||||
"""Load api/apps/restful_apis/chat_api.py with all heavy dependencies mocked."""
|
||||
repo_root = Path(__file__).resolve().parents[4]
|
||||
@@ -2309,13 +2284,16 @@ def _load_chat_api_module(monkeypatch):
|
||||
dialog_svc_mod.DialogService = SimpleNamespace(
|
||||
model=SimpleNamespace(_meta=SimpleNamespace(fields=[])),
|
||||
query=lambda **_k: [SimpleNamespace(id="chat-1", icon="")],
|
||||
get_by_id=lambda _id: (True, SimpleNamespace(
|
||||
prompt_config={"prologue": ""},
|
||||
tenant_id="tenant-1",
|
||||
llm_id="model",
|
||||
kb_ids=[],
|
||||
id=_id,
|
||||
)),
|
||||
get_by_id=lambda _id: (
|
||||
True,
|
||||
SimpleNamespace(
|
||||
prompt_config={"prologue": ""},
|
||||
tenant_id="tenant-1",
|
||||
llm_id="model",
|
||||
kb_ids=[],
|
||||
id=_id,
|
||||
),
|
||||
),
|
||||
)
|
||||
dialog_svc_mod.async_chat = lambda *_a, **_k: None
|
||||
dialog_svc_mod.gen_mindmap = lambda *_a, **_k: None
|
||||
@@ -2355,7 +2333,7 @@ def _load_chat_api_module(monkeypatch):
|
||||
api_utils_mod.get_json_result = lambda code=_RetCode.SUCCESS, message="success", data=None: {"code": code, "message": message, "data": data}
|
||||
api_utils_mod.get_request_json = lambda: _AwaitableValue({})
|
||||
api_utils_mod.server_error_response = lambda e: {"code": _RetCode.SERVER_ERROR, "message": str(e)}
|
||||
api_utils_mod.validate_request = lambda *_a, **_k: (lambda func: func)
|
||||
api_utils_mod.validate_request = lambda *_a, **_k: lambda func: func
|
||||
monkeypatch.setitem(sys.modules, "api.utils.api_utils", api_utils_mod)
|
||||
|
||||
rag_gen_mod = ModuleType("rag.prompts.generator")
|
||||
@@ -2417,12 +2395,14 @@ def test_session_completion_user_id_not_spoofable(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"chat_id": "chat-1",
|
||||
"user_id": "attacker-id",
|
||||
"stream": False,
|
||||
}),
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"chat_id": "chat-1",
|
||||
"user_id": "attacker-id",
|
||||
"stream": False,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
_run(inspect.unwrap(module.session_completion)())
|
||||
@@ -2468,16 +2448,18 @@ def test_session_completion_uses_server_history_by_default(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{"role": "user", "content": "client old question", "id": "client-old"},
|
||||
{"role": "assistant", "content": "client old answer", "id": "client-old"},
|
||||
{"role": "user", "content": "latest question", "id": "latest"},
|
||||
],
|
||||
}),
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{"role": "user", "content": "client old question", "id": "client-old"},
|
||||
{"role": "assistant", "content": "client old answer", "id": "client-old"},
|
||||
{"role": "user", "content": "latest question", "id": "latest"},
|
||||
],
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
res = _run(inspect.unwrap(module.session_completion)())
|
||||
@@ -2512,15 +2494,17 @@ def test_session_completion_preserves_zero_generation_params(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({
|
||||
"stream": False,
|
||||
"messages": [{"role": "user", "content": "latest question"}],
|
||||
"temperature": 0,
|
||||
"top_p": 0,
|
||||
"frequency_penalty": 0,
|
||||
"presence_penalty": 0,
|
||||
"max_tokens": 0,
|
||||
}),
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"stream": False,
|
||||
"messages": [{"role": "user", "content": "latest question"}],
|
||||
"temperature": 0,
|
||||
"top_p": 0,
|
||||
"frequency_penalty": 0,
|
||||
"presence_penalty": 0,
|
||||
"max_tokens": 0,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
res = _run(inspect.unwrap(module.session_completion)())
|
||||
@@ -2585,14 +2569,16 @@ def test_session_completion_merges_generation_params_for_existing_chat(monkeypat
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"messages": [{"role": "user", "content": "latest question"}],
|
||||
"temperature": 0,
|
||||
"presence_penalty": 0,
|
||||
}),
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"messages": [{"role": "user", "content": "latest question"}],
|
||||
"temperature": 0,
|
||||
"presence_penalty": 0,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
res = _run(inspect.unwrap(module.session_completion)())
|
||||
@@ -2644,17 +2630,19 @@ def test_session_completion_can_use_submitted_full_history(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"pass_all_history_messages": True,
|
||||
"messages": [
|
||||
{"role": "user", "content": "client old question", "id": "client-old"},
|
||||
{"role": "assistant", "content": "client old answer", "id": "client-old"},
|
||||
{"role": "user", "content": "latest question", "id": "latest"},
|
||||
],
|
||||
}),
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"pass_all_history_messages": True,
|
||||
"messages": [
|
||||
{"role": "user", "content": "client old question", "id": "client-old"},
|
||||
{"role": "assistant", "content": "client old answer", "id": "client-old"},
|
||||
{"role": "user", "content": "latest question", "id": "latest"},
|
||||
],
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
res = _run(inspect.unwrap(module.session_completion)())
|
||||
@@ -2706,12 +2694,14 @@ def test_session_completion_accepts_question_payload(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"question": "latest question",
|
||||
}),
|
||||
lambda: _AwaitableValue(
|
||||
{
|
||||
"chat_id": "chat-1",
|
||||
"session_id": "session-1",
|
||||
"stream": False,
|
||||
"question": "latest question",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
res = _run(inspect.unwrap(module.session_completion)())
|
||||
|
||||
Reference in New Issue
Block a user