chore: updated changelogs & added moonshot tests

This commit is contained in:
can1357
2026-06-21 02:28:24 +02:00
parent c4a02fe714
commit 7fcb85f812
5 changed files with 157 additions and 3 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed the `moonshot` provider being locked to the international Kimi host (`api.moonshot.ai`): OpenAI-completions requests now honor a `MOONSHOT_BASE_URL` override so users can reach the Kimi China platform (`api.moonshot.cn`), which rejects keys issued for the international endpoint. ([#2883](https://github.com/can1357/oh-my-pi/issues/2883))
## [16.1.8] - 2026-06-20
### Changed
@@ -0,0 +1,53 @@
import { afterEach, describe, expect, test } from "bun:test";
import { resolveOpenAIRequestSetup } from "@oh-my-pi/pi-ai/providers/openai-shared";
const ORIGINAL_MOONSHOT_BASE_URL = Bun.env.MOONSHOT_BASE_URL;
function restoreMoonshotBaseUrl(): void {
if (ORIGINAL_MOONSHOT_BASE_URL === undefined) {
delete Bun.env.MOONSHOT_BASE_URL;
return;
}
Bun.env.MOONSHOT_BASE_URL = ORIGINAL_MOONSHOT_BASE_URL;
}
afterEach(() => {
restoreMoonshotBaseUrl();
});
describe("Moonshot China base URL override (issue #2883)", () => {
// Mirrors the bundled `kimi-k2.7-code` catalog entry, whose baseUrl is
// hardcoded to the international platform (`api.moonshot.ai`).
const moonshotModel = {
provider: "moonshot",
id: "kimi-k2.7-code",
baseUrl: "https://api.moonshot.ai/v1",
};
test("redirects the moonshot provider to api.moonshot.cn when MOONSHOT_BASE_URL is set", () => {
Bun.env.MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1";
const setup = resolveOpenAIRequestSetup(moonshotModel, {
apiKey: "sk-china-key",
messages: [],
});
expect(setup.baseUrl).toBe("https://api.moonshot.cn/v1");
});
test("keeps the bundled international endpoint when MOONSHOT_BASE_URL is unset", () => {
delete Bun.env.MOONSHOT_BASE_URL;
const setup = resolveOpenAIRequestSetup(moonshotModel, {
apiKey: "sk-intl-key",
messages: [],
});
expect(setup.baseUrl).toBe("https://api.moonshot.ai/v1");
});
test("does not redirect other openai-completions providers", () => {
Bun.env.MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1";
const setup = resolveOpenAIRequestSetup(
{ provider: "openai", id: "gpt-5.5", baseUrl: "https://api.openai.com/v1" },
{ apiKey: "sk-openai", messages: [] },
);
expect(setup.baseUrl).toBe("https://api.openai.com/v1");
});
});
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed the `moonshot` provider with no path to the Kimi China API: model discovery now honors a `MOONSHOT_BASE_URL` override (redirecting to `api.moonshot.cn`), and `KIMI_API_KEY` resolves as a fallback for `MOONSHOT_API_KEY`. ([#2883](https://github.com/can1357/oh-my-pi/issues/2883))
## [16.1.8] - 2026-06-20
### Fixed
@@ -0,0 +1,96 @@
import { afterEach, describe, expect, test, vi } from "bun:test";
import { getCatalogProviderEntry } from "@oh-my-pi/pi-catalog/provider-models/descriptors";
import { moonshotModelManagerOptions } from "@oh-my-pi/pi-catalog/provider-models/openai-compat";
import type { FetchImpl } from "@oh-my-pi/pi-catalog/types";
import { $pickenv } from "@oh-my-pi/pi-utils";
const MODELS_DEV_URL = "https://models.dev/api.json";
const ORIGINAL_ENV: Record<string, string | undefined> = {
MOONSHOT_BASE_URL: Bun.env.MOONSHOT_BASE_URL,
MOONSHOT_API_KEY: Bun.env.MOONSHOT_API_KEY,
KIMI_API_KEY: Bun.env.KIMI_API_KEY,
};
function restoreEnv(): void {
for (const key in ORIGINAL_ENV) {
const value = ORIGINAL_ENV[key];
if (value === undefined) {
delete Bun.env[key];
} else {
Bun.env[key] = value;
}
}
}
function inputUrl(input: string | URL | Request): string {
if (typeof input === "string") return input;
if (input instanceof URL) return input.toString();
return input.url;
}
function makeFetchMock(calls: string[]): FetchImpl {
return vi.fn(async (input: string | URL | Request) => {
const url = inputUrl(input);
if (url === MODELS_DEV_URL) {
return new Response("{}", { status: 500 });
}
calls.push(url);
return new Response(JSON.stringify({ data: [{ id: "kimi-k2.7-code" }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}) as FetchImpl;
}
afterEach(() => {
restoreEnv();
vi.restoreAllMocks();
});
describe("Moonshot China platform (issue #2883)", () => {
test("moonshot accepts KIMI_API_KEY as an env-key fallback for MOONSHOT_API_KEY", () => {
// `getEnvApiKey("moonshot")` resolves a catalog provider via `$pickenv(...envVars)`
// (see ai/src/stream.ts), so the descriptor's env-var order IS the resolution order.
const envVars = getCatalogProviderEntry("moonshot")?.envVars;
expect(envVars).toEqual(["MOONSHOT_API_KEY", "KIMI_API_KEY"]);
delete Bun.env.MOONSHOT_API_KEY;
Bun.env.KIMI_API_KEY = "kimi-china-key";
expect($pickenv(...(envVars ?? []))).toBe("kimi-china-key");
});
test("MOONSHOT_API_KEY keeps precedence over the KIMI_API_KEY alias", () => {
const envVars = getCatalogProviderEntry("moonshot")?.envVars ?? [];
Bun.env.MOONSHOT_API_KEY = "moonshot-primary-key";
Bun.env.KIMI_API_KEY = "kimi-fallback-key";
expect($pickenv(...envVars)).toBe("moonshot-primary-key");
});
test("discovers models against api.moonshot.cn when MOONSHOT_BASE_URL is set", async () => {
Bun.env.MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1";
const calls: string[] = [];
const options = moonshotModelManagerOptions({ apiKey: "kimi-china-key", fetch: makeFetchMock(calls) });
const models = await options.fetchDynamicModels?.();
expect(calls).toContain("https://api.moonshot.cn/v1/models");
expect(models?.[0]).toMatchObject({
provider: "moonshot",
baseUrl: "https://api.moonshot.cn/v1",
});
});
test("explicit config baseUrl keeps precedence over MOONSHOT_BASE_URL", async () => {
Bun.env.MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1";
const calls: string[] = [];
const options = moonshotModelManagerOptions({
apiKey: "kimi-china-key",
baseUrl: "https://proxy.example/v1",
fetch: makeFetchMock(calls),
});
const models = await options.fetchDynamicModels?.();
expect(calls).toContain("https://proxy.example/v1/models");
expect(models?.[0]?.baseUrl).toBe("https://proxy.example/v1");
});
});
-3
View File
@@ -7,9 +7,6 @@
- Fixed `omp list` and `omp remove` silently starting an interactive agent session (forwarding the bare verb to the model as a prompt) instead of surfacing the real `omp plugin list` / `omp plugin uninstall <name>` commands ([#2935](https://github.com/can1357/oh-my-pi/issues/2935))
- Fixed lazy-initialized LSP servers (basedpyright/pyright, and likely gopls/rust-analyzer) hanging on the first request: the message reader matched incoming messages against pending client requests by id before checking for a `method`, so a server-originated `workspace/configuration` pull whose id collided with an in-flight request was swallowed as a bogus response, leaving the pull unanswered and the server wedged. The reader now routes any message carrying a `method` as a server request before id-matching ([#3001](https://github.com/can1357/oh-my-pi/issues/3001))
- Fixed `omp --approval-mode=yolo acp` and other global option flags placed before a subcommand being rewritten to `launch` with the subcommand swallowed as prompt text; the CLI resolver now skips leading global flags (using the launch parser's value-consumption contract) and dispatches the real subcommand with the flags applied, so ACP mode honors the configured approval policy. ([#2970](https://github.com/can1357/oh-my-pi/issues/2970))
### Fixed
- Fixed `/mcp enable` and `/mcp disable` reconnecting unrelated MCP servers by scoping toggle reconnect/disconnect work to the named server. ([#3157](https://github.com/can1357/oh-my-pi/issues/3157))
## [16.1.8] - 2026-06-20