Files
e07824e6a9 Fix/agent env usage workspace (#351)
* 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>
2026-08-20 10:17:39 +08:00

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 == ""