mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-25 12:05:06 +02:00
feat(agent): support custom API key and base URL configuration
Add apiKey and baseUrl options to AgentOptions, with support for: - Explicit values via CLI args (--api-key, --base-url) - Provider-specific environment variables (e.g., OPENAI_BASE_URL) - Generic fallback format (PROVIDER_BASE_URL) Priority: CLI args > env vars > library defaults. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ type CliOptions = {
|
||||
profile?: string | undefined;
|
||||
provider?: string | undefined;
|
||||
model?: string | undefined;
|
||||
apiKey?: string | undefined;
|
||||
baseUrl?: string | undefined;
|
||||
system?: string | undefined;
|
||||
thinking?: string | undefined;
|
||||
cwd?: string | undefined;
|
||||
@@ -21,6 +23,8 @@ function printUsage() {
|
||||
console.log(" --profile ID Load agent profile (identity, soul, tools, memory)");
|
||||
console.log(" --provider NAME LLM provider (e.g., openai, anthropic, kimi)");
|
||||
console.log(" --model NAME Model name");
|
||||
console.log(" --api-key KEY API key (overrides environment variable)");
|
||||
console.log(" --base-url URL Custom base URL for the provider");
|
||||
console.log(" --system TEXT System prompt (ignored if --profile is set)");
|
||||
console.log(" --thinking LEVEL Thinking level");
|
||||
console.log(" --cwd DIR Working directory for commands");
|
||||
@@ -53,6 +57,14 @@ function parseArgs(argv: string[]) {
|
||||
opts.model = args.shift();
|
||||
continue;
|
||||
}
|
||||
if (arg === "--api-key") {
|
||||
opts.apiKey = args.shift();
|
||||
continue;
|
||||
}
|
||||
if (arg === "--base-url") {
|
||||
opts.baseUrl = args.shift();
|
||||
continue;
|
||||
}
|
||||
if (arg === "--system") {
|
||||
opts.system = args.shift();
|
||||
continue;
|
||||
@@ -112,6 +124,8 @@ async function main() {
|
||||
profileId: opts.profile,
|
||||
provider: opts.provider,
|
||||
model: opts.model,
|
||||
apiKey: opts.apiKey,
|
||||
baseUrl: opts.baseUrl,
|
||||
systemPrompt: opts.system,
|
||||
thinkingLevel: opts.thinking as any,
|
||||
cwd: opts.cwd,
|
||||
|
||||
@@ -13,9 +13,12 @@ import {
|
||||
} from "./context-window/index.js";
|
||||
|
||||
/**
|
||||
* Get API Key based on provider
|
||||
* Get API Key based on provider.
|
||||
* Priority: explicit key > provider-specific env var > generic env var format.
|
||||
*/
|
||||
function resolveApiKey(provider: string): string | undefined {
|
||||
function resolveApiKey(provider: string, explicitKey?: string): string | undefined {
|
||||
if (explicitKey) return explicitKey;
|
||||
|
||||
const providerEnvMap: Record<string, string> = {
|
||||
openai: "OPENAI_API_KEY",
|
||||
anthropic: "ANTHROPIC_API_KEY",
|
||||
@@ -39,6 +42,36 @@ function resolveApiKey(provider: string): string | undefined {
|
||||
return process.env[`${normalizedProvider}_API_KEY`];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Base URL based on provider.
|
||||
* Priority: explicit URL > provider-specific env var > generic env var format.
|
||||
*/
|
||||
function resolveBaseUrl(provider: string, explicitUrl?: string): string | undefined {
|
||||
if (explicitUrl) return explicitUrl;
|
||||
|
||||
const providerEnvMap: Record<string, string> = {
|
||||
openai: "OPENAI_BASE_URL",
|
||||
anthropic: "ANTHROPIC_BASE_URL",
|
||||
google: "GOOGLE_BASE_URL",
|
||||
"google-genai": "GOOGLE_BASE_URL",
|
||||
kimi: "MOONSHOT_BASE_URL",
|
||||
"kimi-coding": "MOONSHOT_BASE_URL",
|
||||
deepseek: "DEEPSEEK_BASE_URL",
|
||||
groq: "GROQ_BASE_URL",
|
||||
mistral: "MISTRAL_BASE_URL",
|
||||
together: "TOGETHER_BASE_URL",
|
||||
};
|
||||
|
||||
const envVar = providerEnvMap[provider];
|
||||
if (envVar) {
|
||||
return process.env[envVar];
|
||||
}
|
||||
|
||||
// Try generic format: PROVIDER_BASE_URL
|
||||
const normalizedProvider = provider.toUpperCase().replace(/-/g, "_");
|
||||
return process.env[`${normalizedProvider}_BASE_URL`];
|
||||
}
|
||||
|
||||
export class Agent {
|
||||
private readonly agent: PiAgentCore;
|
||||
private readonly output;
|
||||
@@ -57,7 +90,15 @@ export class Agent {
|
||||
this.output = createAgentOutput({ stdout, stderr });
|
||||
this.debug = options.debug ?? false;
|
||||
|
||||
this.agent = new PiAgentCore();
|
||||
// Resolve provider for API key and base URL
|
||||
const resolvedProvider = options.provider ?? "kimi-coding";
|
||||
const apiKey = resolveApiKey(resolvedProvider, options.apiKey);
|
||||
|
||||
this.agent = new PiAgentCore(
|
||||
apiKey
|
||||
? { getApiKey: (_provider: string) => apiKey }
|
||||
: {},
|
||||
);
|
||||
|
||||
// Load Agent Profile (if profileId is specified)
|
||||
let systemPrompt: string | undefined;
|
||||
@@ -101,12 +142,18 @@ export class Agent {
|
||||
return tempSession.getMeta();
|
||||
})();
|
||||
|
||||
const model = options.provider && options.model ? resolveModel(options) : resolveModel({
|
||||
let model = options.provider && options.model ? resolveModel(options) : resolveModel({
|
||||
...options,
|
||||
provider: storedMeta?.provider,
|
||||
model: storedMeta?.model,
|
||||
});
|
||||
|
||||
// Override base URL if provided via options or environment variable
|
||||
const baseUrl = resolveBaseUrl(model.provider, options.baseUrl);
|
||||
if (baseUrl) {
|
||||
model = { ...model, baseUrl };
|
||||
}
|
||||
|
||||
// === Context Window Guard ===
|
||||
this.contextWindowGuard = checkContextWindow({
|
||||
modelContextWindow: model.contextWindow,
|
||||
@@ -133,7 +180,7 @@ export class Agent {
|
||||
const compactionMode = options.compactionMode ?? "tokens"; // 默认使用 token 模式
|
||||
|
||||
// 获取 API Key(用于 summary 模式)
|
||||
const apiKey = compactionMode === "summary" ? resolveApiKey(model.provider) : undefined;
|
||||
const summaryApiKey = compactionMode === "summary" ? resolveApiKey(model.provider, options.apiKey) : undefined;
|
||||
|
||||
// 创建 SessionManager(带 context window 配置)
|
||||
this.session = new SessionManager({
|
||||
@@ -147,7 +194,7 @@ export class Agent {
|
||||
minKeepMessages: options.minKeepMessages,
|
||||
// Summary 模式参数
|
||||
model: compactionMode === "summary" ? model : undefined,
|
||||
apiKey,
|
||||
apiKey: summaryApiKey,
|
||||
customInstructions: options.summaryInstructions,
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ export type AgentOptions = {
|
||||
profileBaseDir?: string | undefined;
|
||||
provider?: string | undefined;
|
||||
model?: string | undefined;
|
||||
/** Custom API key (overrides environment variable) */
|
||||
apiKey?: string | undefined;
|
||||
/** Custom base URL for the provider endpoint */
|
||||
baseUrl?: string | undefined;
|
||||
/** System prompt, if profileId is set will auto-construct from profile */
|
||||
systemPrompt?: string | undefined;
|
||||
thinkingLevel?: ThinkingLevel | undefined;
|
||||
|
||||
Reference in New Issue
Block a user