feat: OpenMesh 基础平台与 MD/PDF 转换技能
- 后端: coworker 智能体框架, WS API, 文件上传, 附件处理 - 前端: Open WebUI, 文件全量走 upload API (含 MD/TXT/JSON 等文本类) - 技能: md-to-office (pandoc + wkhtmltopdf) - 修复: 上传文件路径丢失, Agent 搜索浪费, 输出文件跑到 uploads/ - 打包: PyInstaller one-dir, 预打包 pandoc/wkhtmltopdf/chromium
This commit is contained in:
99
tests/test_interactions.py
Normal file
99
tests/test_interactions.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""Interactive prompts over messaging: button encoding, block rendering, and the click→resolve path."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from coworker.inbox import InboxStore
|
||||
from coworker.interactions import Button, buttons_for, decode, encode
|
||||
from coworker.connectors.base import InteractionEvent
|
||||
from coworker.connectors.senders import _slack_blocks
|
||||
from coworker.providers import ModelCapabilities, ProviderClient
|
||||
from coworker.server.manager import SessionManager
|
||||
|
||||
|
||||
class ScriptedProvider(ProviderClient):
|
||||
def __init__(self, turns):
|
||||
self._turns = list(turns)
|
||||
|
||||
def complete(self, *, model, messages, tools=None, **settings):
|
||||
return self._turns.pop(0)
|
||||
|
||||
def capabilities(self, model):
|
||||
return ModelCapabilities()
|
||||
|
||||
|
||||
def test_encode_decode_roundtrip():
|
||||
v = encode("abc123", "allow")
|
||||
assert decode(v) == ("abc123", "allow")
|
||||
assert decode("not json") is None
|
||||
assert decode(json.dumps({"nope": 1})) is None
|
||||
|
||||
|
||||
def test_buttons_for_kinds(tmp_path):
|
||||
st = InboxStore(tmp_path / "inbox.json")
|
||||
appr = st.add_approval("s1", "Run `write_file`?")
|
||||
btns = buttons_for(appr)
|
||||
assert [b.label for b in btns] == ["Approve", "Deny"]
|
||||
assert decode(btns[0].value) == (appr.id, "allow")
|
||||
assert decode(btns[1].value) == (appr.id, "deny")
|
||||
|
||||
q = st.add_question("s1", "Which region?", options=["us-east-1", "us-west-2"])
|
||||
qb = buttons_for(q)
|
||||
assert [b.label for b in qb] == ["us-east-1", "us-west-2"]
|
||||
assert decode(qb[0].value) == (q.id, "us-east-1") # resolution IS the option text
|
||||
|
||||
# free-text question (no options) and notifications get no buttons → "open the app"
|
||||
assert buttons_for(st.add_question("s1", "Say something")) == []
|
||||
assert buttons_for(st.add_notification("s1", "FYI")) == []
|
||||
|
||||
|
||||
def test_slack_blocks_shape():
|
||||
blocks = _slack_blocks("Run `x`?", [Button("Approve", "v1"), Button("Deny", "v2")])
|
||||
assert blocks[0]["type"] == "section"
|
||||
els = blocks[1]["elements"]
|
||||
assert [e["text"]["text"] for e in els] == ["Approve", "Deny"]
|
||||
assert [e["value"] for e in els] == ["v1", "v2"]
|
||||
assert [e["action_id"] for e in els] == ["ocw_0", "ocw_1"]
|
||||
# no buttons → just the section, no actions block
|
||||
assert len(_slack_blocks("hi", [])) == 1
|
||||
|
||||
|
||||
def test_interaction_click_resolves_item(tmp_path):
|
||||
mgr = SessionManager(workspace=tmp_path, provider=ScriptedProvider([]))
|
||||
mgr.secrets.put(
|
||||
"slack:default",
|
||||
{
|
||||
"bot_token": "xoxb-test",
|
||||
"app_token": "xapp-test",
|
||||
"allowed_users": ["U_BOB"],
|
||||
"approval_owner_ids": ["U_BOB"],
|
||||
},
|
||||
)
|
||||
item = mgr.inbox.add_approval("sX", "Run `write_file`?")
|
||||
|
||||
resolved: list = []
|
||||
|
||||
async def fake_wait(item_id):
|
||||
# stand in for the suspended agent: record what the item resolved to
|
||||
ev = mgr.inbox._waiters.setdefault(item_id, asyncio.Event())
|
||||
await ev.wait()
|
||||
resolved.append(mgr.inbox.get(item_id).resolution)
|
||||
|
||||
async def scenario():
|
||||
waiter = asyncio.create_task(fake_wait(item.id))
|
||||
await asyncio.sleep(0) # let the waiter register
|
||||
await mgr._on_interaction(
|
||||
InteractionEvent(
|
||||
platform="slack",
|
||||
chat_id="C1",
|
||||
message_id="111.2",
|
||||
value=encode(item.id, "allow"),
|
||||
user_id="U_BOB",
|
||||
user_name="bob",
|
||||
)
|
||||
)
|
||||
await asyncio.wait_for(waiter, timeout=2)
|
||||
|
||||
asyncio.run(scenario())
|
||||
assert resolved == ["allow"]
|
||||
assert mgr.inbox.get(item.id).state == "resolved"
|
||||
Reference in New Issue
Block a user