fix(model): update gemini apikey modify function (#119)
This commit is contained in:
@@ -65,7 +65,7 @@ export function registerIpcHandlers(
|
||||
registerOpenClawHandlers();
|
||||
|
||||
// Provider handlers
|
||||
registerProviderHandlers();
|
||||
registerProviderHandlers(gatewayManager);
|
||||
|
||||
// Shell handlers
|
||||
registerShellHandlers();
|
||||
@@ -780,7 +780,7 @@ function registerWhatsAppHandlers(mainWindow: BrowserWindow): void {
|
||||
/**
|
||||
* Provider-related IPC handlers
|
||||
*/
|
||||
function registerProviderHandlers(): void {
|
||||
function registerProviderHandlers(gatewayManager: GatewayManager): void {
|
||||
// Get all providers with key info
|
||||
ipcMain.handle('provider:list', async () => {
|
||||
return await getAllProvidersWithKeyInfo();
|
||||
@@ -976,6 +976,16 @@ function registerProviderHandlers(): void {
|
||||
if (providerKey) {
|
||||
saveProviderKeyToOpenClaw(provider.type, providerKey);
|
||||
}
|
||||
|
||||
// Restart Gateway so it picks up the new config and env vars.
|
||||
// OpenClaw reads openclaw.json per-request, but env vars (API keys)
|
||||
// are only available if they were injected at process startup.
|
||||
if (gatewayManager.isConnected()) {
|
||||
logger.info(`Restarting Gateway after provider switch to "${provider.type}"`);
|
||||
void gatewayManager.restart().catch((err) => {
|
||||
logger.warn('Gateway restart after provider switch failed:', err);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Failed to set OpenClaw default model:', err);
|
||||
}
|
||||
@@ -1254,12 +1264,8 @@ async function validateGoogleQueryKey(
|
||||
apiKey: string,
|
||||
baseUrl?: string
|
||||
): Promise<{ valid: boolean; error?: string }> {
|
||||
const trimmedBaseUrl = baseUrl?.trim();
|
||||
if (!trimmedBaseUrl) {
|
||||
return { valid: false, error: `Base URL is required for provider "${providerType}" validation` };
|
||||
}
|
||||
|
||||
const base = normalizeBaseUrl(trimmedBaseUrl);
|
||||
// Default to the official Google Gemini API base URL if none is provided
|
||||
const base = normalizeBaseUrl(baseUrl || 'https://generativelanguage.googleapis.com/v1beta');
|
||||
const url = `${base}/models?pageSize=1&key=${encodeURIComponent(apiKey)}`;
|
||||
return await performProviderValidationRequest(providerType, url, {});
|
||||
}
|
||||
|
||||
@@ -212,8 +212,8 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
|
||||
config.agents = agents;
|
||||
|
||||
// Configure models.providers for providers that need explicit registration.
|
||||
// For built-in providers this comes from registry; for custom/ollama-like
|
||||
// providers callers can supply runtime overrides.
|
||||
// Built-in providers (anthropic, google) are part of OpenClaw's pi-ai catalog
|
||||
// and must NOT have a models.providers entry — it would override the built-in.
|
||||
const providerCfg = getProviderConfig(provider);
|
||||
if (providerCfg) {
|
||||
const models = (config.models || {}) as Record<string, unknown>;
|
||||
@@ -229,7 +229,6 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
|
||||
: [];
|
||||
const registryModels = (providerCfg.models ?? []).map((m) => ({ ...m })) as Array<Record<string, unknown>>;
|
||||
|
||||
// Merge model entries by id and ensure the selected/default model id exists.
|
||||
const mergedModels = [...registryModels];
|
||||
for (const item of existingModels) {
|
||||
const id = typeof item?.id === 'string' ? item.id : '';
|
||||
@@ -245,13 +244,25 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
|
||||
...existingProvider,
|
||||
baseUrl: providerCfg.baseUrl,
|
||||
api: providerCfg.api,
|
||||
apiKey: providerCfg.apiKeyEnv,
|
||||
apiKey: `\${${providerCfg.apiKeyEnv}}`,
|
||||
models: mergedModels,
|
||||
};
|
||||
console.log(`Configured models.providers.${provider} with baseUrl=${providerCfg.baseUrl}, model=${modelId}`);
|
||||
|
||||
models.providers = providers;
|
||||
config.models = models;
|
||||
} else {
|
||||
// Built-in provider: remove any stale models.providers entry that may
|
||||
// have been written by an earlier version. Leaving it in place would
|
||||
// override the native pi-ai catalog and can break streaming/auth.
|
||||
const models = (config.models || {}) as Record<string, unknown>;
|
||||
const providers = (models.providers || {}) as Record<string, unknown>;
|
||||
if (providers[provider]) {
|
||||
delete providers[provider];
|
||||
console.log(`Removed stale models.providers.${provider} (built-in provider)`);
|
||||
models.providers = providers;
|
||||
config.models = models;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure gateway mode is set
|
||||
@@ -338,7 +349,7 @@ export function setOpenClawDefaultModelWithOverride(
|
||||
models: mergedModels,
|
||||
};
|
||||
if (override.apiKeyEnv) {
|
||||
nextProvider.apiKey = override.apiKeyEnv;
|
||||
nextProvider.apiKey = `\${${override.apiKeyEnv}}`;
|
||||
}
|
||||
|
||||
providers[provider] = nextProvider;
|
||||
|
||||
@@ -53,11 +53,8 @@ const REGISTRY: Record<string, ProviderBackendMeta> = {
|
||||
google: {
|
||||
envVar: 'GEMINI_API_KEY',
|
||||
defaultModel: 'google/gemini-3-pro-preview',
|
||||
providerConfig: {
|
||||
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
||||
api: 'google',
|
||||
apiKeyEnv: 'GEMINI_API_KEY',
|
||||
},
|
||||
// google is built-in to OpenClaw's pi-ai catalog, no providerConfig needed.
|
||||
// Adding models.providers.google overrides the built-in and can break Gemini.
|
||||
},
|
||||
openrouter: {
|
||||
envVar: 'OPENROUTER_API_KEY',
|
||||
|
||||
@@ -1150,6 +1150,31 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
} else {
|
||||
// No runId from gateway; keep sending state and wait for events.
|
||||
}
|
||||
|
||||
// Safety timeout: if we're still in "sending" state after 90s without
|
||||
// receiving any streaming event, the run likely failed silently (e.g.
|
||||
// provider error not surfaced as a chat event). Surface the error to the
|
||||
// user instead of leaving an infinite spinner.
|
||||
if (result.success) {
|
||||
const sentAt = Date.now();
|
||||
const SAFETY_TIMEOUT_MS = 90_000;
|
||||
const checkStuck = () => {
|
||||
const state = get();
|
||||
if (!state.sending) return;
|
||||
if (state.streamingMessage || state.streamingText) return;
|
||||
if (Date.now() - sentAt < SAFETY_TIMEOUT_MS) {
|
||||
setTimeout(checkStuck, 10_000);
|
||||
return;
|
||||
}
|
||||
set({
|
||||
error: 'No response received from the model. The provider may be unavailable or the API key may have insufficient quota. Please check your provider settings.',
|
||||
sending: false,
|
||||
activeRunId: null,
|
||||
lastUserMessageAt: null,
|
||||
});
|
||||
};
|
||||
setTimeout(checkStuck, 30_000);
|
||||
}
|
||||
} catch (err) {
|
||||
set({ error: String(err), sending: false });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user