mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-10-01 01:38:07 +08:00
* fix(session): 避免归档任务阻塞队列 Worker 后序归档不再占用 Worker 等待前序任务,并根据 QueueFS work 识别和跳过无法恢复的孤儿归档。 * fix(session): 仅调度队首归档任务 同一 Session 只将最早的未完成归档放入 QueueFS,后续归档在前序结束后再依次入队,并兼容升级前已入队任务。 * fix(session): 降低队首归档调度的存储读取 用 QueueFS 运行时索引判断 Session 是否已有归档任务,正常完成后直接调度相邻 Archive,避免每次 Commit 和任务结束都扫描完整历史目录。 * fix(session): 恢复每个归档任务独立入队
167 lines
5.4 KiB
Python
167 lines
5.4 KiB
Python
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
|
|
# SPDX-License-Identifier: AGPL-3.0
|
|
|
|
"""Session test fixtures"""
|
|
|
|
import asyncio
|
|
from functools import partial
|
|
from typing import AsyncGenerator
|
|
|
|
import pytest_asyncio
|
|
|
|
from openviking.message import TextPart, ToolPart
|
|
from openviking.server.identity import RequestContext
|
|
from openviking.service.core import OpenVikingService
|
|
from openviking.service.task_tracker import get_task_tracker
|
|
from openviking.session import Session
|
|
from openviking.storage.queuefs import QueueManager, SessionCommitMsg, get_queue_manager
|
|
from openviking.utils.time_utils import get_current_timestamp
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def client(
|
|
service: OpenVikingService,
|
|
request_context: RequestContext,
|
|
monkeypatch,
|
|
) -> partial:
|
|
"""Bind the shared service's session factory to the test request context."""
|
|
|
|
queue_manager = get_queue_manager()
|
|
original_enqueue = queue_manager.enqueue
|
|
commit_tasks = []
|
|
queued_commit_tasks: set[str] = set()
|
|
tracker = get_task_tracker()
|
|
original_has_work = tracker.has_work
|
|
|
|
monkeypatch.setattr(
|
|
tracker,
|
|
"has_work",
|
|
lambda task_id: task_id in queued_commit_tasks or original_has_work(task_id),
|
|
)
|
|
|
|
async def enqueue_with_session_commit_fallback(queue_name, data):
|
|
if queue_name != QueueManager.SESSION_COMMIT:
|
|
return await original_enqueue(queue_name, data)
|
|
|
|
queued_commit_tasks.add(data["task_id"])
|
|
|
|
async def process_commit():
|
|
message = SessionCommitMsg(**data)
|
|
queued_session = service.sessions.session(
|
|
request_context,
|
|
session_id=message.session_id,
|
|
session_uri=message.session_uri,
|
|
)
|
|
while True:
|
|
phase1 = await queued_session._read_phase1_meta(message.archive_uri)
|
|
if phase1.get("status") == "ready" or await queued_session._archive_file_exists(
|
|
message.archive_uri,
|
|
".failed.json",
|
|
):
|
|
break
|
|
await asyncio.sleep(0)
|
|
await queued_session.load()
|
|
try:
|
|
while not await queued_session.resume_queued_commit(message):
|
|
await asyncio.sleep(0)
|
|
finally:
|
|
queued_commit_tasks.discard(message.task_id)
|
|
|
|
commit_tasks.append(asyncio.create_task(process_commit()))
|
|
return data["task_id"]
|
|
|
|
monkeypatch.setattr(queue_manager, "enqueue", enqueue_with_session_commit_fallback)
|
|
yield partial(service.sessions.session, request_context)
|
|
if commit_tasks:
|
|
await asyncio.gather(*commit_tasks, return_exceptions=True)
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def client_with_resource_sync(
|
|
client,
|
|
service: OpenVikingService,
|
|
request_context: RequestContext,
|
|
):
|
|
uri = "viking://resources/session-active-count.md"
|
|
timestamp = get_current_timestamp()
|
|
vector = service.vikingdb_manager.get_embedder().embed("active count test").dense_vector
|
|
await service.vikingdb_manager.upsert(
|
|
{
|
|
"uri": uri,
|
|
"parent_uri": "viking://resources",
|
|
"is_leaf": True,
|
|
"abstract": "Session active count test resource",
|
|
"context_type": "resource",
|
|
"category": "",
|
|
"created_at": timestamp,
|
|
"updated_at": timestamp,
|
|
"active_count": 0,
|
|
"vector": vector,
|
|
"meta": {},
|
|
"related_uri": [],
|
|
"account_id": request_context.account_id,
|
|
"owner_space": "",
|
|
"level": 2,
|
|
},
|
|
ctx=request_context,
|
|
)
|
|
return service, request_context, uri
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def session(
|
|
client,
|
|
service: OpenVikingService,
|
|
request_context: RequestContext,
|
|
) -> AsyncGenerator[Session, None]:
|
|
"""Create new Session"""
|
|
session = await service.sessions.create(request_context)
|
|
yield session
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def session_with_messages(
|
|
client,
|
|
service: OpenVikingService,
|
|
request_context: RequestContext,
|
|
) -> AsyncGenerator[Session, None]:
|
|
"""Create Session with existing messages"""
|
|
session = await service.sessions.create(
|
|
request_context,
|
|
session_id="test_session_with_messages",
|
|
)
|
|
|
|
session.add_message("user", [TextPart("Hello, this is a test message.")])
|
|
session.add_message("assistant", [TextPart("Hello! How can I help you today?")])
|
|
session.add_message("user", [TextPart("I need help with testing.")])
|
|
session.add_message("assistant", [TextPart("I can help you with testing.")])
|
|
|
|
yield session
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def session_with_tool_call(
|
|
client,
|
|
service: OpenVikingService,
|
|
request_context: RequestContext,
|
|
) -> AsyncGenerator[tuple[Session, str, str], None]:
|
|
"""Create Session with tool call"""
|
|
session = await service.sessions.create(
|
|
request_context,
|
|
session_id="test_session_with_tool",
|
|
)
|
|
|
|
tool_id = "test_tool_001"
|
|
tool_part = ToolPart(
|
|
tool_id=tool_id,
|
|
tool_name="test_tool",
|
|
tool_uri=f"{session.uri}/tools/{tool_id}",
|
|
skill_uri="viking://user/skills/test_skill",
|
|
tool_input={"param": "value"},
|
|
tool_status="running",
|
|
)
|
|
|
|
msg = session.add_message("assistant", [TextPart("Executing tool..."), tool_part])
|
|
|
|
yield session, msg.id, tool_id
|