mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-29 04:02:57 +08:00
* refactor(client): remove Python embedded mode Consolidate Python consumers on the HTTP SDK while keeping shared server and storage capabilities unchanged. * refactor(client): remove obsolete embedded leftovers
179 lines
5.7 KiB
Python
179 lines
5.7 KiB
Python
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
|
|
# SPDX-License-Identifier: AGPL-3.0
|
|
|
|
"""Global test fixtures"""
|
|
|
|
import asyncio
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import AsyncGenerator, Generator
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
|
|
from openviking.models.embedder.base import DenseEmbedderBase, EmbedResult
|
|
from openviking.server.identity import RequestContext, Role
|
|
from openviking.service.core import OpenVikingService
|
|
from openviking.service.task_tracker import set_task_tracker
|
|
from openviking.storage import viking_fs as viking_fs_module
|
|
from openviking_cli.session.user_id import UserIdentifier
|
|
from openviking_cli.utils.config.embedding_config import EmbeddingConfig
|
|
from openviking_cli.utils.config.open_viking_config import OpenVikingConfigSingleton
|
|
from tests.utils.mock_agfs import MockLocalAGFS
|
|
|
|
|
|
# ── Workaround: local .so may lack AGFS_Grep symbol (new in latest source) ──
|
|
def _patch_agfs_grep_if_missing():
|
|
"""Wrap _setup_functions to catch missing AGFS_Grep and skip its binding."""
|
|
try:
|
|
from openviking.pyagfs.binding_client import BindingLib
|
|
|
|
_orig_setup = BindingLib._setup_functions
|
|
|
|
def _safe_setup(self):
|
|
try:
|
|
_orig_setup(self)
|
|
except AttributeError as e:
|
|
if "AGFS_Grep" not in str(e):
|
|
raise
|
|
# Re-implement _setup_functions but skip AGFS_Grep lines.
|
|
# We do this by temporarily removing the Grep lines from the
|
|
# source, but since we can't edit .so, we monkey-patch the lib
|
|
# object's __getattr__ to not fail on AGFS_Grep.
|
|
import ctypes
|
|
|
|
class _GrepStub:
|
|
"""Fake ctypes function descriptor for AGFS_Grep."""
|
|
|
|
argtypes = [
|
|
ctypes.c_int64,
|
|
ctypes.c_char_p,
|
|
ctypes.c_char_p,
|
|
ctypes.c_int,
|
|
ctypes.c_int,
|
|
ctypes.c_int,
|
|
ctypes.c_int,
|
|
]
|
|
restype = ctypes.c_char_p
|
|
|
|
def __call__(self, *args):
|
|
return b'{"error":"AGFS_Grep not available in this .so version"}'
|
|
|
|
# Patch at the CDLL instance level by overriding __getattr__
|
|
orig_class = type(self.lib)
|
|
orig_getattr = orig_class.__getattr__
|
|
|
|
def patched_getattr(cdll_self, name):
|
|
if name == "AGFS_Grep":
|
|
return _GrepStub()
|
|
return orig_getattr(cdll_self, name)
|
|
|
|
orig_class.__getattr__ = patched_getattr
|
|
try:
|
|
_orig_setup(self)
|
|
finally:
|
|
orig_class.__getattr__ = orig_getattr
|
|
|
|
BindingLib._setup_functions = _safe_setup
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
_patch_agfs_grep_if_missing()
|
|
|
|
# Test data root directory
|
|
PROJECT_ROOT = Path(__file__).parent.parent
|
|
TEST_TMP_DIR = PROJECT_ROOT / "test_data" / "tmp"
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def event_loop():
|
|
"""Create session-level event loop"""
|
|
loop = asyncio.new_event_loop()
|
|
yield loop
|
|
loop.close()
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def temp_dir() -> Generator[Path, None, None]:
|
|
"""Create temp directory, auto-cleanup before and after test"""
|
|
shutil.rmtree(TEST_TMP_DIR, ignore_errors=True)
|
|
TEST_TMP_DIR.mkdir(parents=True, exist_ok=True)
|
|
yield TEST_TMP_DIR
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def test_data_dir(temp_dir: Path) -> Path:
|
|
"""Create test data directory"""
|
|
data_dir = temp_dir / "data"
|
|
data_dir.mkdir(parents=True, exist_ok=True)
|
|
return data_dir
|
|
|
|
|
|
# ============ Service Fixtures ============
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def service(
|
|
test_data_dir: Path,
|
|
monkeypatch,
|
|
) -> AsyncGenerator[OpenVikingService, None]:
|
|
"""Create an initialized service for domain-level tests."""
|
|
|
|
previous_viking_fs = viking_fs_module._instance
|
|
|
|
class FakeEmbedder(DenseEmbedderBase):
|
|
def __init__(self):
|
|
super().__init__(model_name="test-fake-embedder")
|
|
|
|
def embed(self, text: str, is_query: bool = False) -> EmbedResult:
|
|
return EmbedResult(dense_vector=[0.1] * 1024)
|
|
|
|
def get_dimension(self) -> int:
|
|
return 1024
|
|
|
|
monkeypatch.setattr(EmbeddingConfig, "get_embedder", lambda self: FakeEmbedder())
|
|
mock_agfs = MockLocalAGFS(root_path=test_data_dir / "mock_agfs_root")
|
|
monkeypatch.setattr(
|
|
"openviking.utils.agfs_utils.create_agfs_client",
|
|
lambda *args, **kwargs: mock_agfs,
|
|
)
|
|
OpenVikingConfigSingleton.reset_instance()
|
|
OpenVikingConfigSingleton.initialize(
|
|
config_dict={
|
|
"storage": {
|
|
"workspace": str(test_data_dir),
|
|
"agfs": {"backend": "local"},
|
|
"vectordb": {"backend": "local"},
|
|
},
|
|
"embedding": {
|
|
"dense": {
|
|
"provider": "openai",
|
|
"model": "test-embedder",
|
|
"api_key": "test-key",
|
|
"dimension": 1024,
|
|
}
|
|
},
|
|
}
|
|
)
|
|
instance = OpenVikingService(
|
|
path=str(test_data_dir),
|
|
user=UserIdentifier.the_default_user(),
|
|
)
|
|
try:
|
|
await instance.initialize()
|
|
yield instance
|
|
finally:
|
|
await instance.close()
|
|
set_task_tracker(None)
|
|
viking_fs_module._instance = previous_viking_fs
|
|
OpenVikingConfigSingleton.reset_instance()
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def request_context() -> RequestContext:
|
|
return RequestContext(
|
|
user=UserIdentifier.the_default_user(),
|
|
role=Role.USER,
|
|
)
|