Skip to content

serve: carry Kimi K3 tool-call structure on the shared codec instead of the text channel #1147

Description

@JustVugg

serve: carry Kimi K3 tool-call structure on the shared codec instead of the text channel

Corrected scope. This issue originally proposed doing GLM and Kimi K3 together. That was wrong, and checking the two engines rather than assuming they were symmetric is what showed it:

  • Kimi K3: <|open|>, <|close|>, <|sep|>, <|end_of_msg|> are real special tokens — chat_special() resolves them to ids held in sp[0..3] (kimi_k3.c:2126, :2203). The engine therefore knows whether a structural marker was generated by the model or merely looks like one.
  • GLM: <tool_call> does not appear in colibri.c at all. The markers are ordinary text rendered into the prompt and recovered by parse_tool_calls in openai_server.py. The engine has no ground truth to preserve, so a sideband would carry nothing better than the text already does. GLM's exposure is inherent to its format and can only be narrowed in the parser, which _unclosed_tail already does for the lenient path.

So: K3 only.

What it would fix. #1144 ships K3 tool calling with the structural run re-emitted as literal text for the gateway to pattern-match (option (a) from #1143). That discards a distinction the engine holds: a user prompt that gets the model to echo <|open|>call tool="…"<|sep|> as ordinary characters is, downstream, indistinguishable from a real call. The gateway reports tool_calls and never executes — the client application decides what to run — so this is a trust-boundary weakness rather than code execution, but a client acting on our tool_calls field is acting on a statement of fact we would be getting wrong.

Why the codec makes this cheap. c/serve_codec.h owns both directions since the framing migration finished (#1087, #1090, #1096, #1116), each engine landed behind a byte-exact wire-transcript freeze, and Inkling already rides an engine-specific structured payload on it (audio, as an opaque extension). A tool-call record is the same shape: one file, one new record type, an existing test that proves nothing else moved.

Not urgent, and not user-visible. The wire between engine and gateway is internal; the OpenAI tool_calls contract clients see is identical either way, so this can land whenever without a migration for anyone.

Context: #1143 (design), #1144 (implementation), #1029 (original gap).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions