fix(news): use task-scoped Skill templates in scenario acceptance
Development CI / check-and-build (push) Successful in 3m23s
GitHub Git mirror / mirror (push) Successful in 6s
Development CI / deploy-development (push) Successful in 2m31s

This commit is contained in:
eezd committed 2026-11-05 21:48:19 +08:00
1 parent 1397a076e1
commit e69b39a0de
2 files changed
+56 -2

No files matched your search

+54
View File
@@ -189,3 +189,57 @@ def test_restore_preserves_extended_cap_and_never_rewinds_counter(scenario):
report['new_cap'] = 119 report['new_cap'] = 119
with pytest.raises(ValueError, match='累计上限'): with pytest.raises(ValueError, match='累计上限'):
recovery.counter_limit(report, 147) recovery.counter_limit(report, 147)
def test_scenario_initialization_freezes_valid_task_skills(scenario, monkeypatch):
import asyncio
from types import SimpleNamespace as N
from unittest.mock import AsyncMock
from alphapulse.models import SkillNode
from alphapulse.news_research.contracts import INDEX_ASSIGN_SKILL, INDEX_SKILL, ASSIGN_SKILL, RESEARCH_SKILL
from alphapulse.news_research.pipeline import BASE
from alphapulse.news_research import service
import restore_news_scenario as recovery
nodes, versions = {}, {}
class Session:
async def __aenter__(self): return self
async def __aexit__(self, *args): pass
async def scalar(self, query): return 0
async def flush(self): pass
async def commit(self): pass
def add(self, row): row.id = len(nodes) + 1
async def get(self, model, identifier):
return (nodes if model is SkillNode else versions)[identifier]
async def create(session, parent, kind, name, scope):
node = N(id=len(nodes)+1, scope=scope, kind=kind, enabled=True, parent_id=parent)
nodes[node.id] = node
return node
async def add_version(session, node, content, *args):
version = N(id=node.id, version_no=1, content=content)
versions[version.id] = version
return version
monkeypatch.setattr(scenario, 'SessionLocal', Session)
monkeypatch.setattr(service, 'provider_for', AsyncMock(return_value=N(
id=1, model='test', reasoning_effort=None, max_tokens=8192, context_window=200000)))
monkeypatch.setattr(scenario, 'create_node', create)
monkeypatch.setattr(scenario, 'add_version', add_version)
fields = dict(protocol='openai', base_url='https://example.invalid', api_key='test-only',
model='test', max_tokens=8192, context_window=200000)
async def check():
assert recovery.initialize is scenario.initialize
config = await recovery.initialize({'index': fields, 'research': fields})
rules = await scenario.freeze_config(config)
assert rules['index']['content'] == INDEX_ASSIGN_SKILL
assert rules['index']['system_prompt'] == BASE + '\n' + INDEX_SKILL
assert rules['research']['system_prompt'] == BASE + '\n' + RESEARCH_SKILL
async with Session() as session:
_, prompt, _ = await service.model_config(session, config, 'assign')
assert prompt == BASE + '\n' + ASSIGN_SKILL
asyncio.run(check())
+2 -2
View File
@@ -16,7 +16,7 @@ from alphapulse.core.crypto import encrypt, decrypt
from alphapulse.core.db import SessionLocal, engine from alphapulse.core.db import SessionLocal, engine
from alphapulse.models import AiProvider, NewsItem, SkillNode from alphapulse.models import AiProvider, NewsItem, SkillNode
from alphapulse.news_research import service from alphapulse.news_research import service
from alphapulse.news_research.contracts import INDEX_SKILL, ASSIGN_SKILL, RESEARCH_SKILL from alphapulse.news_research.contracts import INDEX_ASSIGN_SKILL, RESEARCH_SKILL
from alphapulse.news_research.models import Package, Attempt, Event, Version, Membership, Reference, VocabularyVersion from alphapulse.news_research.models import Package, Attempt, Event, Version, Membership, Reference, VocabularyVersion
from accept_news_research import Budget from accept_news_research import Budget
@@ -72,7 +72,7 @@ async def initialize(credentials):
async with SessionLocal() as session: async with SessionLocal() as session:
if await session.scalar(select(func.count()).select_from(NewsItem)) or await session.scalar(select(func.count()).select_from(SkillNode)): if await session.scalar(select(func.count()).select_from(NewsItem)) or await session.scalar(select(func.count()).select_from(SkillNode)):
raise ValueError('必须使用空的独立CI数据库') raise ValueError('必须使用空的独立CI数据库')
for stage, content in [('index', INDEX_SKILL + '\n' + ASSIGN_SKILL), ('research', RESEARCH_SKILL)]: for stage, content in [('index', INDEX_ASSIGN_SKILL), ('research', RESEARCH_SKILL)]:
fields = credentials[stage] fields = credentials[stage]
provider = AiProvider(name='scenario-' + stage, protocol=fields['protocol'], base_url=fields['base_url'], provider = AiProvider(name='scenario-' + stage, protocol=fields['protocol'], base_url=fields['base_url'],
api_key_encrypted=encrypt(fields['api_key']), api_key_hint='isolated', model=fields['model'], api_key_encrypted=encrypt(fields['api_key']), api_key_hint='isolated', model=fields['model'],