Merge branch 'ji' into master

合并 ji 的功能:开放问答接口(/api/open/chat[/stream] 回传生成文件 + 临时不留存)、
技能地址管理、智能体详情「最近对话」等。
冲突 frontend StrategyRoutes.tsx 已解决:同时保留 admin/roundtable-diagnostics(master)
与 admin/skill-addresses(ji) 两个管理员路由。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.8 committed 2026-06-22 19:39:35 +08:00
commit fbec222eb4
38 files changed
+3365 -94

No files matched your search

@@ -77,3 +77,65 @@ async def test_list_without_tag_store_does_not_break() -> None:
assert [q.id for q in resp.questions] == ["q1", "q2"]
assert all(q.tags == [] for q in resp.questions)
class _FakeSync:
"""Stub position-sync whose ``resolve_visible`` returns a fixed value.
``None`` mimics 默认岗位 / 无标签绑定; a set mimics a position that binds
recommended_question 标签.
"""
def __init__(self, visible: set[str] | None) -> None:
self._visible = visible
async def resolve_visible(self, _user_id, _resource_type): # noqa: ANN001
return self._visible
@pytest.mark.asyncio
async def test_default_position_shows_only_untagged() -> None:
"""默认岗位(resolve_visible → None)只看到未打标的题目。"""
tag_store = MemoryTagStore()
tag = await tag_store.create_tag(
{"id": "t1", "name": "理赔", "tag_type": "recommended_question"}
)
await tag_store.set_target_tags("recommended_question", "q2", [tag["id"]])
req = _request(_FakeQuestionStore(_rows()), tag_store)
req.app.state.position_sync = _FakeSync(None)
resp = await rq_router.list_recommended_questions(req, all=False)
assert [q.id for q in resp.questions] == ["q1"]
@pytest.mark.asyncio
async def test_bound_position_shows_only_its_tagged_questions() -> None:
"""绑定了标签的岗位(resolve_visible → set)只看到对应打标的题目。"""
tag_store = MemoryTagStore()
tag = await tag_store.create_tag(
{"id": "t1", "name": "理赔", "tag_type": "recommended_question"}
)
await tag_store.set_target_tags("recommended_question", "q2", [tag["id"]])
req = _request(_FakeQuestionStore(_rows()), tag_store)
req.app.state.position_sync = _FakeSync({"q2"})
resp = await rq_router.list_recommended_questions(req, all=False)
assert [q.id for q in resp.questions] == ["q2"]
@pytest.mark.asyncio
async def test_admin_view_ignores_position_filter() -> None:
"""``all=True`` 管理视图照旧返回全部(含打标),不受岗位过滤影响。"""
tag_store = MemoryTagStore()
tag = await tag_store.create_tag(
{"id": "t1", "name": "理赔", "tag_type": "recommended_question"}
)
await tag_store.set_target_tags("recommended_question", "q2", [tag["id"]])
req = _request(_FakeQuestionStore(_rows()), tag_store)
req.app.state.position_sync = _FakeSync(None)
resp = await rq_router.list_recommended_questions(req, all=True)
assert [q.id for q in resp.questions] == ["q1", "q2"]