fix(model): update gemini apikey modify function (#119)
This commit is contained in:
@@ -65,7 +65,7 @@ export function registerIpcHandlers(
|
|||||||
registerOpenClawHandlers();
|
registerOpenClawHandlers();
|
||||||
|
|
||||||
// Provider handlers
|
// Provider handlers
|
||||||
registerProviderHandlers();
|
registerProviderHandlers(gatewayManager);
|
||||||
|
|
||||||
// Shell handlers
|
// Shell handlers
|
||||||
registerShellHandlers();
|
registerShellHandlers();
|
||||||
@@ -780,7 +780,7 @@ function registerWhatsAppHandlers(mainWindow: BrowserWindow): void {
|
|||||||
/**
|
/**
|
||||||
* Provider-related IPC handlers
|
* Provider-related IPC handlers
|
||||||
*/
|
*/
|
||||||
function registerProviderHandlers(): void {
|
function registerProviderHandlers(gatewayManager: GatewayManager): void {
|
||||||
// Get all providers with key info
|
// Get all providers with key info
|
||||||
ipcMain.handle('provider:list', async () => {
|
ipcMain.handle('provider:list', async () => {
|
||||||
return await getAllProvidersWithKeyInfo();
|
return await getAllProvidersWithKeyInfo();
|
||||||
@@ -976,6 +976,16 @@ function registerProviderHandlers(): void {
|
|||||||
if (providerKey) {
|
if (providerKey) {
|
||||||
saveProviderKeyToOpenClaw(provider.type, 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) {
|
} catch (err) {
|
||||||
console.warn('Failed to set OpenClaw default model:', err);
|
console.warn('Failed to set OpenClaw default model:', err);
|
||||||
}
|
}
|
||||||
@@ -1254,12 +1264,8 @@ async function validateGoogleQueryKey(
|
|||||||
apiKey: string,
|
apiKey: string,
|
||||||
baseUrl?: string
|
baseUrl?: string
|
||||||
): Promise<{ valid: boolean; error?: string }> {
|
): Promise<{ valid: boolean; error?: string }> {
|
||||||
const trimmedBaseUrl = baseUrl?.trim();
|
// Default to the official Google Gemini API base URL if none is provided
|
||||||
if (!trimmedBaseUrl) {
|
const base = normalizeBaseUrl(baseUrl || 'https://generativelanguage.googleapis.com/v1beta');
|
||||||
return { valid: false, error: `Base URL is required for provider "${providerType}" validation` };
|
|
||||||
}
|
|
||||||
|
|
||||||
const base = normalizeBaseUrl(trimmedBaseUrl);
|
|
||||||
const url = `${base}/models?pageSize=1&key=${encodeURIComponent(apiKey)}`;
|
const url = `${base}/models?pageSize=1&key=${encodeURIComponent(apiKey)}`;
|
||||||
return await performProviderValidationRequest(providerType, url, {});
|
return await performProviderValidationRequest(providerType, url, {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,8 +212,8 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
|
|||||||
config.agents = agents;
|
config.agents = agents;
|
||||||
|
|
||||||
// Configure models.providers for providers that need explicit registration.
|
// Configure models.providers for providers that need explicit registration.
|
||||||
// For built-in providers this comes from registry; for custom/ollama-like
|
// Built-in providers (anthropic, google) are part of OpenClaw's pi-ai catalog
|
||||||
// providers callers can supply runtime overrides.
|
// and must NOT have a models.providers entry — it would override the built-in.
|
||||||
const providerCfg = getProviderConfig(provider);
|
const providerCfg = getProviderConfig(provider);
|
||||||
if (providerCfg) {
|
if (providerCfg) {
|
||||||
const models = (config.models || {}) as Record<string, unknown>;
|
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>>;
|
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];
|
const mergedModels = [...registryModels];
|
||||||
for (const item of existingModels) {
|
for (const item of existingModels) {
|
||||||
const id = typeof item?.id === 'string' ? item.id : '';
|
const id = typeof item?.id === 'string' ? item.id : '';
|
||||||
@@ -245,13 +244,25 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
|
|||||||
...existingProvider,
|
...existingProvider,
|
||||||
baseUrl: providerCfg.baseUrl,
|
baseUrl: providerCfg.baseUrl,
|
||||||
api: providerCfg.api,
|
api: providerCfg.api,
|
||||||
apiKey: providerCfg.apiKeyEnv,
|
apiKey: `\${${providerCfg.apiKeyEnv}}`,
|
||||||
models: mergedModels,
|
models: mergedModels,
|
||||||
};
|
};
|
||||||
console.log(`Configured models.providers.${provider} with baseUrl=${providerCfg.baseUrl}, model=${modelId}`);
|
console.log(`Configured models.providers.${provider} with baseUrl=${providerCfg.baseUrl}, model=${modelId}`);
|
||||||
|
|
||||||
models.providers = providers;
|
models.providers = providers;
|
||||||
config.models = models;
|
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
|
// Ensure gateway mode is set
|
||||||
@@ -338,7 +349,7 @@ export function setOpenClawDefaultModelWithOverride(
|
|||||||
models: mergedModels,
|
models: mergedModels,
|
||||||
};
|
};
|
||||||
if (override.apiKeyEnv) {
|
if (override.apiKeyEnv) {
|
||||||
nextProvider.apiKey = override.apiKeyEnv;
|
nextProvider.apiKey = `\${${override.apiKeyEnv}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
providers[provider] = nextProvider;
|
providers[provider] = nextProvider;
|
||||||
|
|||||||
@@ -53,11 +53,8 @@ const REGISTRY: Record<string, ProviderBackendMeta> = {
|
|||||||
google: {
|
google: {
|
||||||
envVar: 'GEMINI_API_KEY',
|
envVar: 'GEMINI_API_KEY',
|
||||||
defaultModel: 'google/gemini-3-pro-preview',
|
defaultModel: 'google/gemini-3-pro-preview',
|
||||||
providerConfig: {
|
// google is built-in to OpenClaw's pi-ai catalog, no providerConfig needed.
|
||||||
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
// Adding models.providers.google overrides the built-in and can break Gemini.
|
||||||
api: 'google',
|
|
||||||
apiKeyEnv: 'GEMINI_API_KEY',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
openrouter: {
|
openrouter: {
|
||||||
envVar: 'OPENROUTER_API_KEY',
|
envVar: 'OPENROUTER_API_KEY',
|
||||||
|
|||||||
@@ -1150,6 +1150,31 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
|||||||
} else {
|
} else {
|
||||||
// No runId from gateway; keep sending state and wait for events.
|
// 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) {
|
} catch (err) {
|
||||||
set({ error: String(err), sending: false });
|
set({ error: String(err), sending: false });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user