单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
106 lines
No EOL
4.7 KiB
Python
106 lines
No EOL
4.7 KiB
Python
#!/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() |