Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ refresh token like a password.

Base URLs (and all credentials) can be set in your environment or stored in `~/.openwiki/.env`.

### OpenRouter provider pinning

When OpenRouter serves a model through multiple upstream providers, set
`OPENWIKI_OPENROUTER_PROVIDER_ONLY` to restrict routing to one provider or a
comma-separated provider allowlist:

```bash
OPENWIKI_PROVIDER=openrouter
OPENROUTER_API_KEY=your-key
OPENWIKI_OPENROUTER_PROVIDER_ONLY=Novita
```

### Provider retry attempts

OpenWiki uses LangChain's built-in retry handling for transient provider errors.
Expand Down
4 changes: 4 additions & 0 deletions src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
providerRequiresRegion,
providerRequiresSecretKey,
resolveConfiguredProvider,
resolveOpenRouterProviderOnly,
resolveProviderBaseUrl,
resolveProviderRegion,
resolveProviderRetryAttempts,
Expand Down Expand Up @@ -544,10 +545,13 @@ function createModel(
}

if (provider === "openrouter") {
const providerOnly = resolveOpenRouterProviderOnly();

return new ChatOpenRouter({
apiKey: process.env[OPENROUTER_API_KEY_ENV_KEY],
baseURL: OPENROUTER_BASE_URL,
model: modelId,
provider: providerOnly ? { only: providerOnly } : undefined,
siteName: "OpenWiki",
...retryOptions,
});
Expand Down
19 changes: 19 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export const OPENAI_CHATGPT_PLAN_ENV_KEY = "OPENAI_CHATGPT_PLAN";
export const ANTHROPIC_API_KEY_ENV_KEY = "ANTHROPIC_API_KEY";
export const ANTHROPIC_BASE_URL_ENV_KEY = "ANTHROPIC_BASE_URL";
export const OPENROUTER_API_KEY_ENV_KEY = "OPENROUTER_API_KEY";
export const OPENWIKI_OPENROUTER_PROVIDER_ONLY_ENV_KEY =
"OPENWIKI_OPENROUTER_PROVIDER_ONLY";
export const BEDROCK_AWS_ACCESS_KEY_ID_ENV_KEY = "BEDROCK_AWS_ACCESS_KEY_ID";
export const BEDROCK_AWS_SECRET_ACCESS_KEY_ENV_KEY =
"BEDROCK_AWS_SECRET_ACCESS_KEY";
Expand Down Expand Up @@ -448,6 +450,23 @@ export function resolveProviderRetryAttempts(
return parsedRetryAttempts;
}

export function resolveOpenRouterProviderOnly(
env: NodeJS.ProcessEnv = process.env,
): string[] | undefined {
const rawProviderOnly = env[OPENWIKI_OPENROUTER_PROVIDER_ONLY_ENV_KEY];

if (rawProviderOnly === undefined) {
return undefined;
}

const providers = rawProviderOnly
.split(",")
.map((provider) => provider.trim())
.filter((provider) => provider.length > 0);

return providers.length > 0 ? providers : undefined;
}

export function normalizeModelId(value: string): string {
return value.trim();
}
Expand Down
3 changes: 3 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
OPENWIKI_NOTION_MCP_REFRESH_TOKEN_ENV_KEY,
OPENROUTER_API_KEY_ENV_KEY,
OPENWIKI_NOTION_TOKEN_ENV_KEY,
OPENWIKI_OPENROUTER_PROVIDER_ONLY_ENV_KEY,
OPENWIKI_SLACK_BOT_TOKEN_ENV_KEY,
OPENWIKI_SLACK_CLIENT_ID_ENV_KEY,
OPENWIKI_SLACK_CLIENT_SECRET_ENV_KEY,
Expand Down Expand Up @@ -91,6 +92,7 @@ export const MANAGED_ENV_KEYS = [
ANTHROPIC_API_KEY_ENV_KEY,
ANTHROPIC_BASE_URL_ENV_KEY,
OPENROUTER_API_KEY_ENV_KEY,
OPENWIKI_OPENROUTER_PROVIDER_ONLY_ENV_KEY,
BEDROCK_AWS_ACCESS_KEY_ID_ENV_KEY,
BEDROCK_AWS_SECRET_ACCESS_KEY_ENV_KEY,
BEDROCK_AWS_REGION_ENV_KEY,
Expand Down Expand Up @@ -273,6 +275,7 @@ function isNonSecretDiagnosticKey(key: string): boolean {
key === OPENWIKI_MODEL_ID_ENV_KEY ||
key === OPENWIKI_PROVIDER_ENV_KEY ||
key === OPENWIKI_PROVIDER_RETRY_ATTEMPTS_ENV_KEY ||
key === OPENWIKI_OPENROUTER_PROVIDER_ONLY_ENV_KEY ||
key === ANTHROPIC_BASE_URL_ENV_KEY ||
key === OPENAI_COMPATIBLE_BASE_URL_ENV_KEY ||
key === BEDROCK_AWS_REGION_ENV_KEY
Expand Down
28 changes: 28 additions & 0 deletions test/constants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
providerRequiresRegion,
providerRequiresSecretKey,
resolveConfiguredProvider,
resolveOpenRouterProviderOnly,
resolveProviderBaseUrl,
resolveProviderRegion,
resolveProviderRetryAttempts,
Expand Down Expand Up @@ -169,6 +170,33 @@ describe("resolveProviderRetryAttempts", () => {
});
});

describe("resolveOpenRouterProviderOnly", () => {
test("returns undefined when no provider pin is configured", () => {
expect(resolveOpenRouterProviderOnly({})).toBeUndefined();
expect(
resolveOpenRouterProviderOnly({
OPENWIKI_OPENROUTER_PROVIDER_ONLY: " ",
}),
).toBeUndefined();
});

test("normalizes a single provider name", () => {
expect(
resolveOpenRouterProviderOnly({
OPENWIKI_OPENROUTER_PROVIDER_ONLY: " Novita ",
}),
).toEqual(["Novita"]);
});

test("normalizes a comma-separated provider allowlist", () => {
expect(
resolveOpenRouterProviderOnly({
OPENWIKI_OPENROUTER_PROVIDER_ONLY: "Novita, Fireworks,, Together",
}),
).toEqual(["Novita", "Fireworks", "Together"]);
});
});

describe("isValidBaseUrl", () => {
test("accepts http and https URLs", () => {
expect(isValidBaseUrl("https://api.example.com/v1")).toBe(true);
Expand Down