fix(ai): adjusted llama-cpp base url and refactor token validation

- Update `llama.cpp` base URL to remove the `/v1` suffix.
- Consolidate local provider token validation in `coding-agent` using a `Set`.
- Add test coverage to ensure catalog model IDs are preserved verbatim on the wire.
This commit is contained in:
can1357
2026-06-21 02:45:30 +02:00
parent 13bfc7b9c0
commit 320f514f77
3 changed files with 48 additions and 15 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import type { ProviderDefinition } from "./types";
const PROVIDER_ID = "llama.cpp";
const AUTH_URL = "https://github.com/ggml-org/llama.cpp#quick-start";
const DEFAULT_LOCAL_BASE_URL = "http://127.0.0.1:8080/v1";
const DEFAULT_LOCAL_BASE_URL = "http://127.0.0.1:8080";
const DEFAULT_LOCAL_TOKEN = "llama-cpp-local";
export async function loginLlamaCpp(options: OAuthController): Promise<string> {
+41 -1
View File
@@ -7,10 +7,19 @@
* catalog contract and the case-sensitive id pass-through against the wire.
*/
import { describe, expect, it } from "bun:test";
import { streamOpenAICompletions } from "@oh-my-pi/pi-ai/providers/openai-completions";
import type { Context, FetchImpl, Model } from "@oh-my-pi/pi-ai/types";
import { createModelManager } from "@oh-my-pi/pi-catalog/model-manager";
import { getBundledModel } from "@oh-my-pi/pi-catalog/models";
import { waferServerlessModelManagerOptions } from "@oh-my-pi/pi-catalog/provider-models/openai-compat";
import type { FetchImpl, Model } from "@oh-my-pi/pi-catalog/types";
function sseResponse(events: unknown[]): Response {
const payload = `${events.map(e => `data: ${typeof e === "string" ? e : JSON.stringify(e)}`).join("\n\n")}\n\n`;
return new Response(payload, {
status: 200,
headers: { "content-type": "text/event-stream" },
});
}
describe("Wafer Serverless provider", () => {
it("ships the documented Serverless catalog (GLM-5.1, Qwen3.5, Qwen3.6, Qwen3.7-Max, Kimi-K2.6, DeepSeek V4 Flash/Pro)", () => {
@@ -70,6 +79,37 @@ describe("Wafer Serverless provider", () => {
expect(dsPro.reasoning).toBe(true);
expect(dsPro.compatConfig?.thinkingFormat).toBeUndefined();
});
it("preserves the catalog id verbatim on the wire (no rewrite, case-sensitive)", async () => {
const model = getBundledModel<"openai-completions">("wafer-serverless", "GLM-5.1");
const captured: { url: string | null; body: string | null } = { url: null, body: null };
const fetchMock: FetchImpl = async (input: string | URL | Request, init?: RequestInit) => {
captured.url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
captured.body = typeof init?.body === "string" ? init.body : null;
return sseResponse(["[DONE]"]);
};
const context: Context = {
systemPrompt: ["t"],
messages: [{ role: "user", content: "hi", timestamp: Date.now() }],
};
const stream = streamOpenAICompletions(model as Model<"openai-completions">, context, {
apiKey: "wfr_test",
fetch: fetchMock,
});
for await (const _event of stream) {
/* drain */
}
expect(captured.url).toBe("https://pass.wafer.ai/v1/chat/completions");
expect(captured.body).not.toBeNull();
const parsed = JSON.parse(captured.body ?? "{}") as { model?: unknown };
// Wafer's docs note model names are case-insensitive on input, but the
// canonical id has mixed case; we must round-trip it unchanged so users
// who pin `GLM-5.1` don't end up with usage rows under `glm-5.1` or
// hitting the upstream 404 path.
expect(parsed.model).toBe("GLM-5.1");
});
});
describe("Wafer dynamic discovery mapper", () => {
@@ -24,13 +24,6 @@ import {
resolveVariantAlias,
} from "@oh-my-pi/pi-catalog/variant-collapse";
// Sentinels for local-only OAuth tokens — declared inline to avoid loading
// provider modules at startup. Must match packages/ai/src/registry/llama-cpp.ts,
// packages/ai/src/registry/lm-studio.ts, and packages/ai/src/registry/vllm.ts.
const DEFAULT_LLAMA_CPP_LOCAL_TOKEN = "llama-cpp-local";
const DEFAULT_LOCAL_TOKEN = "lm-studio-local";
const DEFAULT_VLLM_LOCAL_TOKEN = "vllm-local";
const SPECIAL_MODEL_MANAGER_PROVIDER_IDS: readonly string[] = [
"google-antigravity",
"google-gemini-cli",
@@ -42,6 +35,11 @@ const STARTUP_MODEL_CACHE_PROVIDER_IDS: readonly string[] = [
...SPECIAL_MODEL_MANAGER_PROVIDER_IDS,
];
// Sentinels for local-only OAuth tokens — declared inline to avoid loading
// provider modules at startup. Must match packages/ai/src/registry/llama-cpp.ts,
// packages/ai/src/registry/lm-studio.ts, and packages/ai/src/registry/vllm.ts.
const LOCAL_PROVIDER_PLACEHOLDERS = new Set<string>(["llama-cpp-local", "lm-studio-local", "vllm-local"]);
import type { ApiKeyResolver, FetchImpl } from "@oh-my-pi/pi-ai";
import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
@@ -86,12 +84,7 @@ export function isAuthenticated(apiKey: string | undefined | null): apiKey is st
}
function isDiscoveryBearerApiKey(apiKey: string | undefined | null): apiKey is string {
return (
isAuthenticated(apiKey) &&
apiKey !== DEFAULT_LLAMA_CPP_LOCAL_TOKEN &&
apiKey !== DEFAULT_LOCAL_TOKEN &&
apiKey !== DEFAULT_VLLM_LOCAL_TOKEN
);
return isAuthenticated(apiKey) && !LOCAL_PROVIDER_PLACEHOLDERS.has(apiKey);
}
/** Provider override config (baseUrl, headers, apiKey, compat, transport) without custom models */