mirror of
https://github.com/TencentCloud/Octop.git
synced 2026-10-20 06:19:17 +08:00
* fix: inject Admin env into running agents and sum turn token usage Running shells and Docker sandboxes now pick up ~/.octop/env (and workspace .env) without a full reload; MCP stdio no longer inherits the entire host environment. Usage accounting adds every model call in the turn, and workspace I/O survives the harness rebuild window. Co-authored-by: Cursor <cursoragent@cursor.com> * feat: schema v7 identity, expert profiles, chat artifacts, and KB citations (#346) * feat: schema v7 identity, expert profiles, chat artifacts, and KB citations Fold integer PKs plus public string ids, document folders, agent profile columns, and thread artifacts into v7 SQL (not only Python helpers). Experts get workspace avatars and a single welcome_message; chat keeps file changes across sessions, shows knowledge citations, and can drag workspace files. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: drop cron JobDrawer UX from the schema v7 PR Co-authored-by: Cursor <cursoragent@cursor.com> * fix: prefer tool-arg paths when recording chat artifacts Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: jubaoliang <jubaoliang@tencent.com> Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: jubaoliang <jubaoliang@tencent.com> Co-authored-by: Cursor <cursoragent@cursor.com>
82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
"""Unit tests for knowledge retrieval during a chat turn."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from types import SimpleNamespace
|
|
|
|
from octop.infra.db.migrate import run_migrations
|
|
from octop.infra.db.pool import SqlitePool
|
|
from octop.infra.db.repos.knowledge import KnowledgeRepo
|
|
from octop.infra.db.repos.settings import SettingsRepo
|
|
from octop.infra.db.repos.users import UserRepo
|
|
from octop.infra.knowledge import retrieve as retrieve_module
|
|
from octop.infra.knowledge.citations import CITATIONS_MARKER_PREFIX
|
|
from octop.infra.knowledge.index import KnowledgeIndex
|
|
|
|
|
|
def test_retrieve_context_filters_unreadable_knowledge_base(tmp_path, monkeypatch) -> None:
|
|
monkeypatch.setenv("OCTOP_HOME", str(tmp_path / "home"))
|
|
pool = SqlitePool(tmp_path / "octop.db")
|
|
run_migrations(pool)
|
|
services = SimpleNamespace(
|
|
knowledge_repo=KnowledgeRepo(pool),
|
|
settings_repo=SettingsRepo(pool),
|
|
user_repo=UserRepo(pool),
|
|
)
|
|
reader = services.user_repo.create(username="reader", password_hash="h", role="user")
|
|
owner = services.user_repo.create(username="owner", password_hash="h", role="user")
|
|
allowed = services.knowledge_repo.create_base(owner_user_id=reader, name="Allowed")
|
|
hidden = services.knowledge_repo.create_base(owner_user_id=owner, name="Hidden")
|
|
allowed_doc = services.knowledge_repo.create_document(
|
|
kb_id=allowed.id,
|
|
filename="allowed.md",
|
|
content_type="text/markdown",
|
|
byte_size=1,
|
|
status="ready",
|
|
)
|
|
hidden_doc = services.knowledge_repo.create_document(
|
|
kb_id=hidden.id,
|
|
filename="hidden.md",
|
|
content_type="text/markdown",
|
|
byte_size=1,
|
|
status="ready",
|
|
)
|
|
KnowledgeIndex(allowed.id).replace_doc_chunks(allowed_doc.id, ["allowed fact"], [[1.0, 0.0]])
|
|
KnowledgeIndex(hidden.id).replace_doc_chunks(hidden_doc.id, ["secret fact"], [[1.0, 0.0]])
|
|
services.settings_repo.set("knowledge_embedding_model", "test-model")
|
|
monkeypatch.setattr(retrieve_module, "assert_knowledge_usable", lambda *_args: None)
|
|
monkeypatch.setattr(
|
|
retrieve_module, "embed_knowledge_texts", lambda _services, _texts: [[1.0, 0.0]]
|
|
)
|
|
|
|
context = asyncio.run(
|
|
retrieve_module.retrieve_context(
|
|
services,
|
|
user_id=reader,
|
|
is_admin=False,
|
|
query="What facts are available?",
|
|
knowledge_base_ids=[allowed.id, hidden.id],
|
|
)
|
|
)
|
|
|
|
assert "allowed fact" in context
|
|
assert "allowed.md" in context
|
|
assert CITATIONS_MARKER_PREFIX in context
|
|
assert "secret fact" not in context
|
|
assert "hidden.md" not in context
|
|
|
|
|
|
def test_retrieve_context_skips_empty_non_text_turn() -> None:
|
|
context = asyncio.run(
|
|
retrieve_module.retrieve_context(
|
|
SimpleNamespace(),
|
|
user_id=1,
|
|
is_admin=False,
|
|
query=None, # type: ignore[arg-type]
|
|
knowledge_base_ids=["kb-1"],
|
|
)
|
|
)
|
|
|
|
assert context == ""
|