onesvm-browser-server/stacks/smoke/stub_engines.py
chii eb972dfa93 feat: 落地 browser-server 控制面并打通 mgr1 海外订阅
单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 15:05:12 +08:00

106 lines
No EOL
4.7 KiB
Python
Raw Permalink 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.

#!/usr/bin/env python3
"""冒烟专用 stub 引擎(不访问外网;Contract §5-A6.4)。
单进程模拟三引擎形状(T1 契约:形状 = bench 实测样本,数值固定):
- searxng:GET /search?q=...&format=json → bench/searxng-cn/samples/t1-1.excerpt.json 形状(2 条结果)
- trafilatura:POST /v1/read → bench/trafilatura-http/app.py 响应形状(固定 markdown)
- lightpanda/shell 冒烟为降级路径:/json/version 不实现(health 不可达 → 适配器降级),
断言搜索/精读走 searxng/trafilatura stub 仍 200。
仅监听容器内 :80(policy SSRF 契约仅允许 80/443,stub 对齐生产 URL 形状);无外网依赖。
"""
import json
import re
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse, parse_qs
LISTEN = ("0.0.0.0", 80)
# 固定 2 条结果(形状对齐 bench/searxng-cn/samples/t1-1.excerpt.json:title/url/content/engine)。
SEARX_RESULTS = [
{
"title": "stub-海关总署 税务总局关于跨境电子商务出口退运商品税收优惠政策",
"url": "https://hainan.chinatax.gov.cn/xxgk_6_1/06163393.html",
"content": "一、对自2026年1月1日至2027年12月31日期间在跨境电子商务海关监管代码(1210、9610、9710、9810)项下申报出口,因滞销、退货原因,自出口之日起6个月内原状退运进境的商品(不含食品),免征进口关税和进口环节增值税、消费税。",
"engine": "baidu",
},
{
"title": "stub-雨果跨境-跨境电商品牌出海产业互联网平台",
"url": "https://m.cifnews.com/",
"content": "雨果跨境以雨果网作为流量依托,致力于为跨境电商从业者提供全球产业出海,链接全球流通,实现全球品牌的产业互联网平台。",
"engine": "bing",
},
]
# 固定 markdown(形状对齐 bench/trafilatura-http/app.py _extract 返回)。
STUB_MARKDOWN = "# stub 政策公告\n\n为支持跨境电子商务新业态发展,现将出口退运商品税收优惠政策公告如下:\n\n一、对自2026年1月1日至2027年12月31日期间在跨境电子商务海关监管代码项下申报出口的商品,免征进口关税和进口环节增值税、消费税。\n"
STUB_TITLE = "财政部 海关总署 税务总局关于跨境电子商务出口退运商品税收优惠政策的公告(stub)"
class Handler(BaseHTTPRequestHandler):
server_version = "smoke-stub-engines/1.0"
def _json(self, code, obj):
raw = json.dumps(obj, ensure_ascii=False).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def do_GET(self): # noqa: N802
path = self.path.split("?", 1)[0]
if path in ("/healthz", "/health"):
# 同一进程同时顶 searxng(/healthz)与 trafilatura(/health)探活形状。
self._json(200, {"ok": True, "service": "smoke-stub-engines"})
return
if path == "/search":
qs = parse_qs(urlparse(self.path).query)
q = (qs.get("q") or [""])[0]
self._json(200, {
"query": q,
"number_of_results": None,
"unresponsive_engines": [],
"results": SEARX_RESULTS,
})
return
self._json(404, {"ok": False, "error": "not_found"})
def do_POST(self): # noqa: N802
path = self.path.split("?", 1)[0]
length = int(self.headers.get("Content-Length") or 0)
try:
payload = json.loads(self.rfile.read(length) or b"{}")
except json.JSONDecodeError:
self._json(400, {"ok": False, "error": "bad_json"})
return
if path == "/v1/read": # trafilatura 契约(app.py)
max_chars = int(payload.get("max_chars") or 20000)
md = STUB_MARKDOWN
truncated = False
if max_chars and len(md) > max_chars:
md = md[:max_chars]
truncated = True
self._json(200, {
"ok": True,
"title": STUB_TITLE,
"markdown": md,
"char_count": len(md),
"truncated": truncated,
"url": payload.get("url") or "",
})
return
self._json(404, {"ok": False, "error": "not_found"})
def log_message(self, fmt, *args): # 静音(smoke 日志走 smoke-local.sh tee)
pass
def main():
httpd = ThreadingHTTPServer(LISTEN, Handler)
print(f"stub-engines listening {LISTEN}", flush=True)
httpd.serve_forever()
if __name__ == "__main__":
main()