lili 675a8e688b feat(cheap-worker): 便宜档 Node→Python 重写进 AgentScope spike — 范式可移植坐实
WU-A 迁移面 spike:便宜档生成核心(多轮 ReAct + 五工具沙箱 + done 门)用 Python 在新建 cheap-worker/
重写成 AgentScope agent,import 复用 tier2 框架层(model client/四道熔断/历史压缩/observability),
check/build/九门 shell-out 现有 node/cjs(不重写 esbuild/CDP/形状门)。

- U1 地基:跨包 sys.path import tier2 worker + 代理旁路 + key 从内网文档注入 env + M3 装配
- U2 五工具:read/list/write Python 实现 + check/build shell-out(形状门三态与 Node 一致)
- U3 LittleJS system prompt:prompt.mjs Python 化(两条 footgun 正反例 + 输入契约)
- U4 scaffold/stage/play 薄壳 + ensure_play_spec(据 state 形态产 play-spec 治链路假绿)
- U5 ReAct 主编排:内置 Agent+ReActConfig+外层有界 resume + done 门 + 收口 stage/smoke/九门
- U6 对照 Node:产物形态 + 九门逐门 + 过门率对比

验收:同 brief Python 新路 vs Node 旧路,产物都真 src/ 6 文件、九门都 9/9 全 PASS、过门率 1.0=1.0
→ spikePass=True。端到端单局 attempt 1 一次收敛、148s。落点 cheap-worker/ 对 tier2/amodel-gen
零改动(结构性零回归)。测试 38/38 全绿。

origin: docs/plans/2026-06-26-001-feat-cheap-worker-python-spike-plan.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 02:42:14 -07:00

65 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
test_bootstrap.py — U1 地基验证:跨包 import + 代理旁路 + key 注入 + M3 model 装配。
须用 venv python 跑import agentscope
cheap-worker/.venv/bin/python cheap-worker/tests/test_bootstrap.py
"""
import os
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1])) # → cheap-worker/
import _bootstrap
def test_cross_package_import():
"""加 sys.path 后 from worker import config, client 不抛 ImportError且拿到关键 API。"""
fw = _bootstrap.load_framework()
assert {"config", "client", "middleware"} <= set(fw)
from worker import config, client # noqa: E402
assert hasattr(config, "build_model_openai")
assert hasattr(client, "install_proxy_bypass")
assert hasattr(client, "get_api_key")
def test_proxy_bypass():
"""install_proxy_bypass 把内网网关 host 并入 NO_PROXY / no_proxy 两个 env。"""
from worker import client # noqa: E402
host = client.install_proxy_bypass("http://100.64.0.8:3000")
assert host == "100.64.0.8"
assert "100.64.0.8" in os.environ.get("NO_PROXY", "")
assert "100.64.0.8" in os.environ.get("no_proxy", "")
def test_api_key_from_doc():
"""key 缺 env 时从 docs/内网凭据与端点.md 解析注入(内网单一事实源)。"""
# 清掉 env 模拟"未设",强制走文档解析路径
os.environ.pop("NEWAPI_KEY", None)
key = _bootstrap.ensure_api_key_env()
assert key.startswith("sk-")
assert os.environ.get("NEWAPI_KEY", "").startswith("sk-")
def test_model_assembly():
"""build_cheap_model 返回 OpenAIChatModelbase_url 带 /v1 由 build_model_openai 保证model 名为 MiniMax-M3。"""
model = _bootstrap.build_cheap_model(max_tokens=8000)
from agentscope.model import OpenAIChatModel # noqa: E402
assert isinstance(model, OpenAIChatModel)
# 模型名固定 M3KTD6。属性名在不同小版本可能差异宽松双路断言。
assert getattr(model, "model", None) == _bootstrap.SPIKE_MODEL or _bootstrap.SPIKE_MODEL in repr(model)
if __name__ == "__main__":
_fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)]
_failed = 0
for _fn in _fns:
try:
_fn()
print(f" PASS {_fn.__name__}")
except Exception as e: # noqa: BLE001 地基测试要看到任何异常类型
_failed += 1
print(f" FAIL {_fn.__name__}: {type(e).__name__}: {e}")
print(f"\n{len(_fns) - _failed}/{len(_fns)} passed")
sys.exit(1 if _failed else 0)