#!/usr/bin/env python3 """最小 OpenAI 兼容 embeddings mock —— 无真实 embedding key 时验证 RAG 链路。 确定性"词法向量":把文本的字 + 字bigram 哈希进固定维向量并归一化, 词面重叠越多余弦越高(足以演示"查询召回相关文档")。真实语义需插真实 embedding API。 GET /models → 200 POST /embeddings → {data:[{embedding:[...]}]} 用法: python3 scripts/mock_embeddings.py 11888 """ import hashlib import json import math import sys from http.server import BaseHTTPRequestHandler, HTTPServer DIM = 256 def embed(text: str): vec = [0.0] * DIM t = text.strip() toks = list(t) + [t[i:i + 2] for i in range(len(t) - 1)] # 字 + 字bigram for tok in toks: h = int(hashlib.md5(tok.encode("utf-8")).hexdigest(), 16) vec[h % DIM] += 1.0 norm = math.sqrt(sum(x * x for x in vec)) or 1.0 return [x / norm for x in vec] class H(BaseHTTPRequestHandler): def log_message(self, *a): pass def do_GET(self): if self.path.endswith("/models"): body = json.dumps({"object": "list", "data": [{"id": "mock-embed"}]}).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) else: self.send_response(404) self.end_headers() def do_POST(self): n = int(self.headers.get("Content-Length", 0)) req = json.loads(self.rfile.read(n) or b"{}") inp = req.get("input", []) if isinstance(inp, str): inp = [inp] data = [{"object": "embedding", "index": i, "embedding": embed(t)} for i, t in enumerate(inp)] body = json.dumps({"object": "list", "data": data, "model": req.get("model", "mock-embed")}).encode() sys.stderr.write(f"[mock-embed] /embeddings n={len(inp)} dim={DIM}\n") sys.stderr.flush() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) if __name__ == "__main__": port = int(sys.argv[1]) if len(sys.argv) > 1 else 11888 print(f"[mock-embed] listening on :{port} (dim={DIM})") HTTPServer(("127.0.0.1", port), H).serve_forever()