deltafin
§ · Nested detail page

OpenAI-compatible API server 深度解析

拆解 Deltafin 如何把本地慢速 Kimi K3 暴露成 OpenAI SDK 可调用的 HTTP 服务,同时保留单请求锁、流式输出和 reasoning_content 拆分。

§ 00 · IMPORT CHAIN

导入链与契约

▸启动契约

server boot 只加载 tokenizer、layers 和 LazyEmbed,模型生成逻辑仍来自 kimi_run。 入口附近的关键引用是 sources/deltafin/tools/serve_openai.py:42、sources/deltafin/tools/serve_openai.py:45、sources/deltafin/tools/serve_openai.py:65,它们分别说明开关、数据结构和失败边界,而不是孤立函数。

sources/deltafin/tools/serve_openai.py:42-65Python
42MODEL_ID = "deltafin-kimi-k3"
43MAX_TOKENS_CAP = int(os.environ.get("K3_SERVER_MAX_TOKENS", "0"))   # 0 = no cap
44RESPONSE_MEMO_ENTRIES = int(
45    os.environ.get("K3_RESPONSE_MEMO_ENTRIES", "32"))
46RESPONSE_MARKER = "<|open|>response<|sep|>"
47THINK_CLOSE = "<|close|>think<|sep|>"
48
49_lock = threading.Lock()
50_tok = None
51_layers = None
52_embed = None
53_memo = DeterministicResponseMemo(RESPONSE_MEMO_ENTRIES)
54
55
56def _boot():
57    global _tok, _layers, _embed
58    from transformers import AutoTokenizer
59    print("[serve] loading tokenizer + layer skeletons...", flush=True)
60    _tok = AutoTokenizer.from_pretrained(
61        os.path.join(kr.ROOT, "k3-meta"), trust_remote_code=True)
62    kr.check_expert_pool()
63    _layers = kr.build_layers()
64    _embed = kr.LazyEmbed()
65    print("[serve] ready", flush=True)
§ 01 · CORE IMPL

核心实现,逐段拆

▸请求解析

do_POST 同时支持 chat 和 raw completion,max_tokens 语义按两种模式拆开。 这部分应该和 sources/deltafin/tools/serve_openai.py:128、sources/deltafin/tools/serve_openai.py:134、sources/deltafin/tools/serve_openai.py:140、sources/deltafin/tools/serve_openai.py:170 一起看:前半段定义资源边界,后半段才进入执行或回退。

sources/deltafin/tools/serve_openai.py:128-170Python
128    def do_POST(self):
129        try:
130            body = json.loads(self.rfile.read(int(self.headers.get("Content-Length", 0)) or 0) or b"{}")
131        except json.JSONDecodeError:
132            return self._err(400, "invalid JSON body")
133        chat = self.path in ("/v1/chat/completions", "/chat/completions")
134        comp = self.path in ("/v1/completions", "/completions")
135        if not (chat or comp):
136            return self._err(404, f"no route {self.path}")
137
138        if chat:
139            messages = body.get("messages")
140            if not messages:
141                return self._err(400, "messages required")
142            ids = _tok.apply_chat_template(messages, tokenize=True, add_generation_prompt=True)
143        else:
144            prompt = body.get("prompt")
145            if not isinstance(prompt, str):
146                return self._err(400, "prompt (string) required")
147            ids = _tok.encode(prompt)
148        # OpenAI semantics: omitted max_tokens means the model decides. Chat ends
149        # naturally at EOS; raw completions have no terminator, so only THEY get
150        # a default cap (256) — an explicit max_tokens is always honored.
151        req_max = body.get("max_tokens")
152        if req_max:
153            max_new = int(req_max)
154        else:
155            max_new = 1_000_000 if chat else 256
156        if MAX_TOKENS_CAP:
157            max_new = min(max_new, MAX_TOKENS_CAP)
158        stream = bool(body.get("stream"))
159        rid = ("chatcmpl-" if chat else "cmpl-") + uuid.uuid4().hex[:20]
160        created = int(time.time())
161
162        if not _lock.acquire(timeout=5):
163            return self._err(429, "a generation is already running (Deltafin serves one at a time)")
164        try:
165            mode = "chat" if chat else "completion"
166            cached = _memo.get(mode, ids, max_new)
167            if cached is not None:
168                print(f"[serve] deterministic response memo hit "
169                      f"({len(cached.token_ids)} tokens)", flush=True)
170            if stream:

▸流式与非流式响应

streaming 分支写 SSE,非流式分支写 OpenAI 形状的 JSON,并补 usage。 如果只看函数名会误判为普通 helper;结合 sources/deltafin/tools/serve_openai.py:170、sources/deltafin/tools/serve_openai.py:175、sources/deltafin/tools/serve_openai.py:180、sources/deltafin/tools/serve_openai.py:232 可以看到它承担了资源生命周期和数据一致性责任。

sources/deltafin/tools/serve_openai.py:170-232Python
170            if stream:
171                self.send_response(200)
172                self.send_header("Content-Type", "text/event-stream")
173                self.send_header("Cache-Control", "no-cache")
174                self.send_header("Connection", "close")
175                self.end_headers()
176
177                def sse(obj):
178                    self.wfile.write(b"data: " + json.dumps(obj).encode() + b"\n\n")
179                    self.wfile.flush()
180
181                def on_delta(delta):
182                    if chat:
183                        sse({"id": rid, "object": "chat.completion.chunk", "created": created,
184                             "model": MODEL_ID,
185                             "choices": [{"index": 0, "delta": {"content": delta},
186                                          "finish_reason": None}]})
187                    else:
188                        sse({"id": rid, "object": "text_completion", "created": created,
189                             "model": MODEL_ID,
190                             "choices": [{"index": 0, "text": delta, "finish_reason": None}]})
191
192                if cached is None:
193                    out, text, finish = _gen(
194                        ids, max_new, on_delta=on_delta)
195                    _memo.put(mode, ids, max_new, out, text, finish)
196                else:
197                    out = list(cached.token_ids)
198                    text = cached.text
199                    finish = cached.finish_reason
200                    if text:
201                        on_delta(text)
202                key = "delta" if chat else "text"
203                sse({"id": rid, "object": "chat.completion.chunk" if chat else "text_completion",
204                     "created": created, "model": MODEL_ID,
205                     "choices": [{"index": 0, key: {} if chat else "", "finish_reason": finish}]})
206                self.wfile.write(b"data: [DONE]\n\n")
207                self.wfile.flush()
208                return
209
210            if cached is None:
211                out, text, finish = _gen(ids, max_new)
212                _memo.put(mode, ids, max_new, out, text, finish)
213            else:
214                out = list(cached.token_ids)
215                text = cached.text
216                finish = cached.finish_reason
217            usage = {"prompt_tokens": len(ids), "completion_tokens": len(out),
218                     "total_tokens": len(ids) + len(out)}
219            if chat:
220                reasoning, content = _split_reasoning(text)
221                msg = {"role": "assistant", "content": content}
222                if reasoning:
223                    msg["reasoning_content"] = reasoning
224                self._json(200, {"id": rid, "object": "chat.completion", "created": created,
225                                 "model": MODEL_ID, "usage": usage,
226                                 "choices": [{"index": 0, "message": msg,
227                                              "finish_reason": finish}]})
228            else:
229                self._json(200, {"id": rid, "object": "text_completion", "created": created,
230                                 "model": MODEL_ID, "usage": usage,
231                                 "choices": [{"index": 0, "text": text,
232                                              "finish_reason": finish}]})
§ 02 · KEY FLOW

关键流程

HTTP request
  -> validate route/body
  -> tokenize chat or prompt
  -> acquire global generation lock
  -> deterministic memo lookup
  -> generate via kimi_run
  -> SSE chunks or JSON completion

这个流程的关键不是“多一个优化”,而是每一步都有明确退出条件。sources/deltafin/tools/serve_openai.py:43、sources/deltafin/tools/serve_openai.py:129、sources/deltafin/tools/serve_openai.py:171、sources/deltafin/tools/serve_openai.py:231 串起来看,能看到 Deltafin 在慢路径、缓存命中、硬件能力和用户请求之间维持同一套语义。

§ 03 · EDGE CASES

边界 / 失败模式

  • sources/deltafin/tools/serve_openai.py:162、sources/deltafin/tools/serve_openai.py:163 主动用 429 表示已有 generation,避免慢速模型被并发请求压垮。
  • sources/deltafin/tools/serve_openai.py:68、sources/deltafin/tools/serve_openai.py:72 将 K3 的 think/response 标记拆成 reasoning_content 和 content。
  • sources/deltafin/tools/response_memo.py:35、sources/deltafin/tools/response_memo.py:48、sources/deltafin/tools/response_memo.py:64 的 memo key 使用完整 token tuple,不做模糊缓存。
Sub-page · 2026-07-30Source · gavamedia/deltafin↑ deltafin 主页面