Skip to content

Commit f821949

Browse files
authored
fix(model): update gemini apikey modify function (#119)
1 parent 1808990 commit f821949

4 files changed

Lines changed: 57 additions & 18 deletions

File tree

electron/main/ipc-handlers.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function registerIpcHandlers(
6565
registerOpenClawHandlers();
6666

6767
// Provider handlers
68-
registerProviderHandlers();
68+
registerProviderHandlers(gatewayManager);
6969

7070
// Shell handlers
7171
registerShellHandlers();
@@ -780,7 +780,7 @@ function registerWhatsAppHandlers(mainWindow: BrowserWindow): void {
780780
/**
781781
* Provider-related IPC handlers
782782
*/
783-
function registerProviderHandlers(): void {
783+
function registerProviderHandlers(gatewayManager: GatewayManager): void {
784784
// Get all providers with key info
785785
ipcMain.handle('provider:list', async () => {
786786
return await getAllProvidersWithKeyInfo();
@@ -976,6 +976,16 @@ function registerProviderHandlers(): void {
976976
if (providerKey) {
977977
saveProviderKeyToOpenClaw(provider.type, providerKey);
978978
}
979+
980+
// Restart Gateway so it picks up the new config and env vars.
981+
// OpenClaw reads openclaw.json per-request, but env vars (API keys)
982+
// are only available if they were injected at process startup.
983+
if (gatewayManager.isConnected()) {
984+
logger.info(`Restarting Gateway after provider switch to "${provider.type}"`);
985+
void gatewayManager.restart().catch((err) => {
986+
logger.warn('Gateway restart after provider switch failed:', err);
987+
});
988+
}
979989
} catch (err) {
980990
console.warn('Failed to set OpenClaw default model:', err);
981991
}
@@ -1254,12 +1264,8 @@ async function validateGoogleQueryKey(
12541264
apiKey: string,
12551265
baseUrl?: string
12561266
): Promise<{ valid: boolean; error?: string }> {
1257-
const trimmedBaseUrl = baseUrl?.trim();
1258-
if (!trimmedBaseUrl) {
1259-
return { valid: false, error: `Base URL is required for provider "${providerType}" validation` };
1260-
}
1261-
1262-
const base = normalizeBaseUrl(trimmedBaseUrl);
1267+
// Default to the official Google Gemini API base URL if none is provided
1268+
const base = normalizeBaseUrl(baseUrl || 'https://generativelanguage.googleapis.com/v1beta');
12631269
const url = `${base}/models?pageSize=1&key=${encodeURIComponent(apiKey)}`;
12641270
return await performProviderValidationRequest(providerType, url, {});
12651271
}

electron/utils/openclaw-auth.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
212212
config.agents = agents;
213213

214214
// Configure models.providers for providers that need explicit registration.
215-
// For built-in providers this comes from registry; for custom/ollama-like
216-
// providers callers can supply runtime overrides.
215+
// Built-in providers (anthropic, google) are part of OpenClaw's pi-ai catalog
216+
// and must NOT have a models.providers entry — it would override the built-in.
217217
const providerCfg = getProviderConfig(provider);
218218
if (providerCfg) {
219219
const models = (config.models || {}) as Record<string, unknown>;
@@ -229,7 +229,6 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
229229
: [];
230230
const registryModels = (providerCfg.models ?? []).map((m) => ({ ...m })) as Array<Record<string, unknown>>;
231231

232-
// Merge model entries by id and ensure the selected/default model id exists.
233232
const mergedModels = [...registryModels];
234233
for (const item of existingModels) {
235234
const id = typeof item?.id === 'string' ? item.id : '';
@@ -245,13 +244,25 @@ export function setOpenClawDefaultModel(provider: string, modelOverride?: string
245244
...existingProvider,
246245
baseUrl: providerCfg.baseUrl,
247246
api: providerCfg.api,
248-
apiKey: providerCfg.apiKeyEnv,
247+
apiKey: `\${${providerCfg.apiKeyEnv}}`,
249248
models: mergedModels,
250249
};
251250
console.log(`Configured models.providers.${provider} with baseUrl=${providerCfg.baseUrl}, model=${modelId}`);
252251

253252
models.providers = providers;
254253
config.models = models;
254+
} else {
255+
// Built-in provider: remove any stale models.providers entry that may
256+
// have been written by an earlier version. Leaving it in place would
257+
// override the native pi-ai catalog and can break streaming/auth.
258+
const models = (config.models || {}) as Record<string, unknown>;
259+
const providers = (models.providers || {}) as Record<string, unknown>;
260+
if (providers[provider]) {
261+
delete providers[provider];
262+
console.log(`Removed stale models.providers.${provider} (built-in provider)`);
263+
models.providers = providers;
264+
config.models = models;
265+
}
255266
}
256267

257268
// Ensure gateway mode is set
@@ -338,7 +349,7 @@ export function setOpenClawDefaultModelWithOverride(
338349
models: mergedModels,
339350
};
340351
if (override.apiKeyEnv) {
341-
nextProvider.apiKey = override.apiKeyEnv;
352+
nextProvider.apiKey = `\${${override.apiKeyEnv}}`;
342353
}
343354

344355
providers[provider] = nextProvider;

electron/utils/provider-registry.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,8 @@ const REGISTRY: Record<string, ProviderBackendMeta> = {
5353
google: {
5454
envVar: 'GEMINI_API_KEY',
5555
defaultModel: 'google/gemini-3-pro-preview',
56-
providerConfig: {
57-
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
58-
api: 'google',
59-
apiKeyEnv: 'GEMINI_API_KEY',
60-
},
56+
// google is built-in to OpenClaw's pi-ai catalog, no providerConfig needed.
57+
// Adding models.providers.google overrides the built-in and can break Gemini.
6158
},
6259
openrouter: {
6360
envVar: 'OPENROUTER_API_KEY',

src/stores/chat.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,31 @@ export const useChatStore = create<ChatState>((set, get) => ({
11501150
} else {
11511151
// No runId from gateway; keep sending state and wait for events.
11521152
}
1153+
1154+
// Safety timeout: if we're still in "sending" state after 90s without
1155+
// receiving any streaming event, the run likely failed silently (e.g.
1156+
// provider error not surfaced as a chat event). Surface the error to the
1157+
// user instead of leaving an infinite spinner.
1158+
if (result.success) {
1159+
const sentAt = Date.now();
1160+
const SAFETY_TIMEOUT_MS = 90_000;
1161+
const checkStuck = () => {
1162+
const state = get();
1163+
if (!state.sending) return;
1164+
if (state.streamingMessage || state.streamingText) return;
1165+
if (Date.now() - sentAt < SAFETY_TIMEOUT_MS) {
1166+
setTimeout(checkStuck, 10_000);
1167+
return;
1168+
}
1169+
set({
1170+
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.',
1171+
sending: false,
1172+
activeRunId: null,
1173+
lastUserMessageAt: null,
1174+
});
1175+
};
1176+
setTimeout(checkStuck, 30_000);
1177+
}
11531178
} catch (err) {
11541179
set({ error: String(err), sending: false });
11551180
}

0 commit comments

Comments
 (0)