feat: add support for custom provider configuration in the user config (#537)
### What - Add support for loading and merging custom provider configurations from a local `providers.json` file. - Allow users to override or extend default providers with their own settings. ### Why This change enables users to flexibly customize and extend provider endpoints and API keys without modifying the codebase, making the CLI more adaptable for various LLM backends and enterprise use cases. ### How - Introduced `loadProvidersFromFile` and `getMergedProviders` in config logic. - Added/updated related tests in [tests/config.test.tsx] ### Checklist - [x] Lint passes for changed files - [x] Tests pass for all files - [x] Documentation/comments updated as needed --------- Co-authored-by: Thibault Sottiaux <tibo@openai.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { AutoApprovalMode } from "../src/utils/auto-approval-mode.js";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { test, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { providers as defaultProviders } from "../src/utils/providers";
|
||||
|
||||
// In‑memory FS store
|
||||
let memfs: Record<string, string> = {};
|
||||
@@ -148,3 +149,88 @@ test("loads and saves approvalMode correctly", () => {
|
||||
});
|
||||
expect(reloadedConfig.approvalMode).toBe(AutoApprovalMode.FULL_AUTO);
|
||||
});
|
||||
|
||||
test("loads and saves providers correctly", () => {
|
||||
// Setup custom providers configuration
|
||||
const customProviders = {
|
||||
openai: {
|
||||
name: "Custom OpenAI",
|
||||
baseURL: "https://custom-api.openai.com/v1",
|
||||
envKey: "CUSTOM_OPENAI_API_KEY",
|
||||
},
|
||||
anthropic: {
|
||||
name: "Anthropic",
|
||||
baseURL: "https://api.anthropic.com",
|
||||
envKey: "ANTHROPIC_API_KEY",
|
||||
},
|
||||
};
|
||||
|
||||
// Create config with providers
|
||||
const testConfig = {
|
||||
model: "test-model",
|
||||
provider: "anthropic",
|
||||
providers: customProviders,
|
||||
instructions: "test instructions",
|
||||
notify: false,
|
||||
};
|
||||
|
||||
// Save the config
|
||||
saveConfig(testConfig, testConfigPath, testInstructionsPath);
|
||||
|
||||
// Verify saved config contains providers
|
||||
expect(memfs[testConfigPath]).toContain(`"providers"`);
|
||||
expect(memfs[testConfigPath]).toContain(`"Custom OpenAI"`);
|
||||
expect(memfs[testConfigPath]).toContain(`"Anthropic"`);
|
||||
expect(memfs[testConfigPath]).toContain(`"provider": "anthropic"`);
|
||||
|
||||
// Load config and verify providers were loaded correctly
|
||||
const loadedConfig = loadConfig(testConfigPath, testInstructionsPath, {
|
||||
disableProjectDoc: true,
|
||||
});
|
||||
|
||||
// Check providers were loaded correctly
|
||||
expect(loadedConfig.provider).toBe("anthropic");
|
||||
expect(loadedConfig.providers).toEqual({
|
||||
...defaultProviders,
|
||||
...customProviders,
|
||||
});
|
||||
|
||||
// Test merging with built-in providers
|
||||
// Create a config with only one custom provider
|
||||
const partialProviders = {
|
||||
customProvider: {
|
||||
name: "Custom Provider",
|
||||
baseURL: "https://custom-api.example.com",
|
||||
envKey: "CUSTOM_API_KEY",
|
||||
},
|
||||
};
|
||||
|
||||
const partialConfig = {
|
||||
model: "test-model",
|
||||
provider: "customProvider",
|
||||
providers: partialProviders,
|
||||
instructions: "test instructions",
|
||||
notify: false,
|
||||
};
|
||||
|
||||
// Save the partial config
|
||||
saveConfig(partialConfig, testConfigPath, testInstructionsPath);
|
||||
|
||||
// Load config and verify providers were merged with built-in providers
|
||||
const mergedConfig = loadConfig(testConfigPath, testInstructionsPath, {
|
||||
disableProjectDoc: true,
|
||||
});
|
||||
|
||||
// Check providers is defined
|
||||
expect(mergedConfig.providers).toBeDefined();
|
||||
|
||||
// Use bracket notation to access properties
|
||||
if (mergedConfig.providers) {
|
||||
expect(mergedConfig.providers["customProvider"]).toBeDefined();
|
||||
expect(mergedConfig.providers["customProvider"]).toEqual(
|
||||
partialProviders.customProvider,
|
||||
);
|
||||
// Built-in providers should still be there (like openai)
|
||||
expect(mergedConfig.providers["openai"]).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user