



























1+import { readFileSync } from "node:fs";
2+import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
3+import type { Context, Model } from "openclaw/plugin-sdk/llm";
4+import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-runtime";
5+import { buildOpenAICompletionsParams } from "openclaw/plugin-sdk/provider-transport-runtime";
6+import { describe, expect, it } from "vitest";
7+import plugin from "./index.js";
8+import { buildCohereProvider } from "./provider-catalog.js";
9+import { createCohereCompletionsWrapper } from "./stream.js";
10+11+function readManifest() {
12+return JSON.parse(readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8")) as {
13+providerAuthChoices?: Array<{ choiceId?: string; optionKey?: string; cliFlag?: string }>;
14+setup?: { providers?: Array<{ id?: string; envVars?: string[] }> };
15+};
16+}
17+18+function requireCohereModel(): Model<"openai-completions"> {
19+const model = buildCohereProvider().models?.[0];
20+if (!model) {
21+throw new Error("Cohere catalog did not provide a model");
22+}
23+return model as Model<"openai-completions">;
24+}
25+26+function captureCoherePayload(context: Context): Record<string, unknown> {
27+let captured: Record<string, unknown> | undefined;
28+const baseStreamFn: StreamFn = (model, streamContext, options) => {
29+const payload = buildOpenAICompletionsParams(
30+model as Model<"openai-completions">,
31+streamContext,
32+{ maxTokens: 2048 } as never,
33+);
34+options?.onPayload?.(payload, model);
35+captured = payload;
36+return {} as ReturnType<StreamFn>;
37+};
38+39+void createCohereCompletionsWrapper(baseStreamFn)(requireCohereModel(), context, {});
40+if (!captured) {
41+throw new Error("Cohere payload was not captured");
42+}
43+return captured;
44+}
45+46+describe("Cohere provider plugin", () => {
47+it("registers the manifest-owned API key onboarding flow", async () => {
48+const provider = await registerSingleProviderPlugin(plugin);
49+50+expect(provider.auth.map((method) => method.wizard?.choiceId)).toEqual(["cohere-api-key"]);
51+expect(provider).toMatchObject({
52+id: "cohere",
53+envVars: ["COHERE_API_KEY"],
54+});
55+expect(provider.auth[0]).toMatchObject({
56+id: "api-key",
57+kind: "api_key",
58+wizard: { choiceId: "cohere-api-key" },
59+});
60+expect(readManifest().providerAuthChoices).toEqual([
61+expect.objectContaining({
62+choiceId: "cohere-api-key",
63+optionKey: "cohereApiKey",
64+cliFlag: "--cohere-api-key",
65+}),
66+]);
67+expect(readManifest().setup?.providers).toEqual([
68+{ id: "cohere", envVars: ["COHERE_API_KEY"] },
69+]);
70+});
71+72+it("exposes the static Cohere catalog", () => {
73+expect(buildCohereProvider()).toMatchObject({
74+baseUrl: "https://api.cohere.ai/compatibility/v1",
75+api: "openai-completions",
76+models: [
77+expect.objectContaining({
78+id: "command-a-03-2025",
79+compat: {
80+supportsStore: false,
81+supportsUsageInStreaming: false,
82+maxTokensField: "max_tokens",
83+},
84+}),
85+],
86+});
87+});
88+89+it("uses Cohere's OpenAI-compatible completions payload fields", () => {
90+const params = captureCoherePayload({
91+systemPrompt: "system",
92+messages: [],
93+tools: [
94+{
95+name: "lookup",
96+description: "Look up a value",
97+parameters: { type: "object", properties: {} },
98+},
99+],
100+} as Context);
101+102+expect(params.max_tokens).toBe(2048);
103+expect(params).not.toHaveProperty("max_completion_tokens");
104+expect(params).not.toHaveProperty("store");
105+expect(params).not.toHaveProperty("stream_options");
106+expect(params).not.toHaveProperty("tool_choice");
107+});
108+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。