fix(catalog): corrected moonshot kimi request shaping
Moonshot-native OpenAI-compatible requests now omit OpenAI-only store metadata and use max_tokens for Kimi output budgets.\n\nFixes #2289
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Moonshot/Kimi native OpenAI-compatible request metadata so Kimi K2 uses `max_tokens` and omits OpenAI-only `store`, restoring first-turn output with `MOONSHOT_API_KEY` ([#2289](https://github.com/can1357/oh-my-pi/issues/2289)).
|
||||
|
||||
## [15.11.0] - 2026-06-10
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -102,7 +102,8 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
||||
const isZhipu = modelMatchesHost(hostModel, "zhipu");
|
||||
const isKilo = modelMatchesHost(hostModel, "kilo");
|
||||
const isKimiModel = isKimiModelId(spec.id);
|
||||
const isMoonshotKimi = isKimiModel && modelMatchesHost(hostModel, "moonshotNative");
|
||||
const isMoonshotNative = modelMatchesHost(hostModel, "moonshotNative");
|
||||
const isMoonshotKimi = isKimiModel && isMoonshotNative;
|
||||
const usesMoonshotKimiPreservedThinking = isMoonshotKimi && isKimiK26ModelId(spec.id);
|
||||
const isAnthropicModel =
|
||||
modelMatchesHost(hostModel, "anthropic") || isClaudeModelId(spec.id) || isAnthropicNamespacedModelId(spec.id);
|
||||
@@ -145,11 +146,16 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
||||
isKilo ||
|
||||
isQwen ||
|
||||
isXiaomiHost ||
|
||||
isMoonshotNative ||
|
||||
isOpenCodeHost;
|
||||
const isOpenCodeProvider = provider === "opencode-go" || provider === "opencode-zen";
|
||||
|
||||
const useMaxTokens =
|
||||
isMistral || hostMatchesUrl(baseUrl, "chutes") || hostMatchesUrl(baseUrl, "fireworks") || isDirectDeepseekApi;
|
||||
isMistral ||
|
||||
isMoonshotNative ||
|
||||
hostMatchesUrl(baseUrl, "chutes") ||
|
||||
hostMatchesUrl(baseUrl, "fireworks") ||
|
||||
isDirectDeepseekApi;
|
||||
|
||||
// Hosts whose chat-completions endpoints are known to accept multiple
|
||||
// leading `system`/`developer` messages (preferred for KV-cache reuse).
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* moonshot discovery mapper and stamps default thinking metadata.
|
||||
*/
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { streamOpenAICompletions } from "@oh-my-pi/pi-ai/providers/openai-completions";
|
||||
import { type OpenAICompletionsOptions, streamOpenAICompletions } from "@oh-my-pi/pi-ai/providers/openai-completions";
|
||||
import type { AssistantMessage, Context } from "@oh-my-pi/pi-ai/types";
|
||||
import { buildModel } from "@oh-my-pi/pi-catalog/build";
|
||||
import { Effort } from "@oh-my-pi/pi-catalog/effort";
|
||||
@@ -82,6 +82,7 @@ interface CapturedRequest {
|
||||
|
||||
async function runHiTurn(
|
||||
model: Model<"openai-completions">,
|
||||
options?: Pick<OpenAICompletionsOptions, "reasoning">,
|
||||
): Promise<{ captured: CapturedRequest; assistant: AssistantMessage }> {
|
||||
const captured: CapturedRequest = { url: "", body: {} };
|
||||
const fetchMock = (async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
||||
@@ -92,7 +93,7 @@ async function runHiTurn(
|
||||
return buildMockMoonshotResponse();
|
||||
}) as typeof fetch;
|
||||
|
||||
const stream = streamOpenAICompletions(model, basicContext(), { apiKey: "test-key", fetch: fetchMock });
|
||||
const stream = streamOpenAICompletions(model, basicContext(), { apiKey: "test-key", fetch: fetchMock, ...options });
|
||||
for await (const _ of stream) {
|
||||
// drain until terminal event
|
||||
}
|
||||
@@ -155,6 +156,14 @@ describe("issue #2113 — moonshot kimi-k2.6 discovery and wire format", () => {
|
||||
expect(textBlock).toBeDefined();
|
||||
});
|
||||
|
||||
it("uses Moonshot-native max_tokens and omits OpenAI store control", async () => {
|
||||
const model = moonshotKimiModel("kimi-k2.5", true);
|
||||
const { captured } = await runHiTurn(model, { reasoning: "high" });
|
||||
|
||||
expect(captured.body.max_tokens).toBeDefined();
|
||||
expect(captured.body.max_completion_tokens).toBeUndefined();
|
||||
expect(captured.body.store).toBeUndefined();
|
||||
});
|
||||
it("wire body includes thinking.keep='all' when reasoning is explicitly requested", async () => {
|
||||
const model = moonshotKimiModel("kimi-k2.6", true);
|
||||
const captured: CapturedRequest = { url: "", body: {} };
|
||||
|
||||
Reference in New Issue
Block a user