feat: add multi-channel Devii sessions with docs search mode and channel-aware conversation persistence
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
from devplacepy import database
|
||||
from devplacepy.services.manager import service_manager
|
||||
from devplacepy.services.devii import DeviiService
|
||||
from devplacepy.services.devii.session import _parse_topic
|
||||
|
||||
|
||||
def _hub():
|
||||
if service_manager.get_service("devii") is None:
|
||||
service_manager.register(DeviiService())
|
||||
svc = service_manager.get_service("devii")
|
||||
return svc.hub(), svc
|
||||
|
||||
|
||||
def _session(channel, owner_id):
|
||||
hub, svc = _hub()
|
||||
return hub.get_or_create(
|
||||
"guest", owner_id, "guest", "", svc.instance_base_url(), is_admin=False, channel=channel
|
||||
)
|
||||
|
||||
|
||||
def _tool_names(session):
|
||||
return {t["function"]["name"] for t in session.tools}
|
||||
|
||||
|
||||
def test_docs_channel_restricts_to_search_docs(local_db):
|
||||
docs = _session("docs", "sess-docs-tools")
|
||||
assert _tool_names(docs) == {"search_docs"}
|
||||
|
||||
|
||||
def test_docs_channel_uses_docii_prompt_without_behavior_header(local_db):
|
||||
docs = _session("docs", "sess-docs-prompt")
|
||||
prompt = docs._compose_system_prompt()
|
||||
assert prompt.startswith("You are Docii")
|
||||
assert "search_docs" in prompt
|
||||
assert "TRUTH RULES AND BEHAVIOR" not in prompt
|
||||
|
||||
|
||||
def test_main_channel_keeps_full_catalog_and_behavior(local_db):
|
||||
main = _session("main", "sess-main")
|
||||
names = _tool_names(main)
|
||||
assert "search_docs" in names
|
||||
assert len(names) > 1
|
||||
assert "TRUTH RULES AND BEHAVIOR" in main._compose_system_prompt()
|
||||
|
||||
|
||||
def test_docs_channel_does_not_start_scheduler(local_db):
|
||||
docs = _session("docs", "sess-docs-sched")
|
||||
docs.attach(object())
|
||||
assert docs._started is False
|
||||
|
||||
|
||||
def test_user_docs_channel_uses_ephemeral_stores(local_db):
|
||||
hub, svc = _hub()
|
||||
base = svc.instance_base_url()
|
||||
main = hub.get_or_create(
|
||||
"user", "u-iso", "u", "", base, is_admin=False, channel="main"
|
||||
)
|
||||
docs = hub.get_or_create(
|
||||
"user", "u-iso", "u", "", base, is_admin=False, channel="docs"
|
||||
)
|
||||
assert main.store._db is database.db
|
||||
assert docs.store._db is not database.db
|
||||
|
||||
|
||||
def test_parse_topic_new_and_followup():
|
||||
assert _parse_topic('{"decision": "new", "reason": "unrelated"}') == (
|
||||
"new",
|
||||
"unrelated",
|
||||
)
|
||||
assert _parse_topic('{"decision": "follow_up", "reason": "same"}')[0] == "follow_up"
|
||||
|
||||
|
||||
def test_parse_topic_extracts_embedded_json():
|
||||
decision, _ = _parse_topic('Sure. {"decision":"new","reason":"x"} done')
|
||||
assert decision == "new"
|
||||
|
||||
|
||||
def test_parse_topic_defaults_to_followup_on_garbage():
|
||||
assert _parse_topic("not json at all")[0] == "follow_up"
|
||||
assert _parse_topic("")[0] == "follow_up"
|
||||
@@ -0,0 +1,44 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
from devplacepy.services.devii.store import ConversationStore
|
||||
from devplacepy.utils import generate_uid
|
||||
|
||||
|
||||
def _msgs(text):
|
||||
return [{"role": "user", "content": text}]
|
||||
|
||||
|
||||
def test_channels_isolate_one_owner(local_db):
|
||||
store = ConversationStore()
|
||||
owner = f"chan-{generate_uid()[:8]}"
|
||||
store.save("user", owner, _msgs("main thread"), "main")
|
||||
store.save("user", owner, _msgs("docs thread"), "docs")
|
||||
assert store.load("user", owner, "main") == _msgs("main thread")
|
||||
assert store.load("user", owner, "docs") == _msgs("docs thread")
|
||||
|
||||
|
||||
def test_clear_one_channel_keeps_other(local_db):
|
||||
store = ConversationStore()
|
||||
owner = f"chan-{generate_uid()[:8]}"
|
||||
store.save("user", owner, _msgs("main"), "main")
|
||||
store.save("user", owner, _msgs("docs"), "docs")
|
||||
store.clear("user", owner, "docs")
|
||||
assert store.load("user", owner, "docs") is None
|
||||
assert store.load("user", owner, "main") == _msgs("main")
|
||||
|
||||
|
||||
def test_save_updates_in_place_per_channel(local_db):
|
||||
store = ConversationStore()
|
||||
owner = f"chan-{generate_uid()[:8]}"
|
||||
store.save("user", owner, _msgs("first"), "docs")
|
||||
store.save("user", owner, _msgs("second"), "docs")
|
||||
assert store.load("user", owner, "docs") == _msgs("second")
|
||||
|
||||
|
||||
def test_default_channel_is_main(local_db):
|
||||
store = ConversationStore()
|
||||
owner = f"chan-{generate_uid()[:8]}"
|
||||
store.save("user", owner, _msgs("default"))
|
||||
assert store.load("user", owner) == _msgs("default")
|
||||
assert store.load("user", owner, "main") == _msgs("default")
|
||||
assert store.load("user", owner, "docs") is None
|
||||
Reference in New Issue
Block a user