mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-11 14:40:52 -07:00
fix: cherry-pick commits for release (#12549)
This commit is contained in:
@@ -1137,7 +1137,7 @@ const SETTINGS_SCHEMA = {
|
||||
label: 'Thinking Budget',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: -1,
|
||||
default: 8192,
|
||||
description:
|
||||
'The thinking budget for the Codebase Investigator agent.',
|
||||
showInDialog: false,
|
||||
|
||||
@@ -47,7 +47,7 @@ import {
|
||||
UIActionsContext,
|
||||
type UIActions,
|
||||
} from './contexts/UIActionsContext.js';
|
||||
import { useContext } from 'react';
|
||||
import { useContext, act } from 'react';
|
||||
|
||||
// Mock useStdout to capture terminal title writes
|
||||
let mockStdout: { write: ReturnType<typeof vi.fn> };
|
||||
@@ -1395,5 +1395,41 @@ describe('AppContainer State Management', () => {
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('updates currentModel when ModelChanged event is received', async () => {
|
||||
// Arrange: Mock initial model
|
||||
vi.spyOn(mockConfig, 'getModel').mockReturnValue('initial-model');
|
||||
|
||||
const { unmount } = render(
|
||||
<AppContainer
|
||||
config={mockConfig}
|
||||
settings={mockSettings}
|
||||
version="1.0.0"
|
||||
initializationResult={mockInitResult}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Verify initial model
|
||||
await act(async () => {
|
||||
await vi.waitFor(() => {
|
||||
expect(capturedUIState?.currentModel).toBe('initial-model');
|
||||
});
|
||||
});
|
||||
|
||||
// Get the registered handler for ModelChanged
|
||||
const handler = mockCoreEvents.on.mock.calls.find(
|
||||
(call: unknown[]) => call[0] === CoreEvent.ModelChanged,
|
||||
)?.[1];
|
||||
expect(handler).toBeDefined();
|
||||
|
||||
// Act: Simulate ModelChanged event
|
||||
act(() => {
|
||||
handler({ model: 'new-model' });
|
||||
});
|
||||
|
||||
// Assert: Verify model is updated
|
||||
expect(capturedUIState.currentModel).toBe('new-model');
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
debugLogger,
|
||||
coreEvents,
|
||||
CoreEvent,
|
||||
type ModelChangedPayload,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { validateAuthMethod } from '../config/auth.js';
|
||||
import { loadHierarchicalGeminiMemory } from '../config/config.js';
|
||||
@@ -253,16 +254,22 @@ export const AppContainer = (props: AppContainerProps) => {
|
||||
[historyManager.addItem],
|
||||
);
|
||||
|
||||
// Subscribe to fallback mode changes from core
|
||||
// Subscribe to fallback mode and model changes from core
|
||||
useEffect(() => {
|
||||
const handleFallbackModeChanged = () => {
|
||||
const effectiveModel = getEffectiveModel();
|
||||
setCurrentModel(effectiveModel);
|
||||
};
|
||||
|
||||
const handleModelChanged = (payload: ModelChangedPayload) => {
|
||||
setCurrentModel(payload.model);
|
||||
};
|
||||
|
||||
coreEvents.on(CoreEvent.FallbackModeChanged, handleFallbackModeChanged);
|
||||
coreEvents.on(CoreEvent.ModelChanged, handleModelChanged);
|
||||
return () => {
|
||||
coreEvents.off(CoreEvent.FallbackModeChanged, handleFallbackModeChanged);
|
||||
coreEvents.off(CoreEvent.ModelChanged, handleModelChanged);
|
||||
};
|
||||
}, [getEffectiveModel]);
|
||||
|
||||
|
||||
@@ -139,6 +139,21 @@ vi.mock('../agents/subagent-tool-wrapper.js', () => ({
|
||||
SubagentToolWrapper: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockCoreEvents = vi.hoisted(() => ({
|
||||
emitFeedback: vi.fn(),
|
||||
emitModelChanged: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockSetGlobalProxy = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('../utils/events.js', () => ({
|
||||
coreEvents: mockCoreEvents,
|
||||
}));
|
||||
|
||||
vi.mock('../utils/fetch.js', () => ({
|
||||
setGlobalProxy: mockSetGlobalProxy,
|
||||
}));
|
||||
|
||||
import { BaseLlmClient } from '../core/baseLlmClient.js';
|
||||
import { tokenLimit } from '../core/tokenLimits.js';
|
||||
import { uiTelemetryService } from '../telemetry/index.js';
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
DEFAULT_OTLP_ENDPOINT,
|
||||
uiTelemetryService,
|
||||
} from '../telemetry/index.js';
|
||||
import { coreEvents } from '../utils/events.js';
|
||||
import { tokenLimit } from '../core/tokenLimits.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||
@@ -636,7 +637,10 @@ export class Config {
|
||||
return;
|
||||
}
|
||||
|
||||
this.model = newModel;
|
||||
if (this.model !== newModel) {
|
||||
this.model = newModel;
|
||||
coreEvents.emitModelChanged(newModel);
|
||||
}
|
||||
}
|
||||
|
||||
isInFallbackMode(): boolean {
|
||||
|
||||
@@ -12,8 +12,8 @@ export const DEFAULT_GEMINI_MODEL_AUTO = 'auto';
|
||||
|
||||
export const DEFAULT_GEMINI_EMBEDDING_MODEL = 'gemini-embedding-001';
|
||||
|
||||
// Some thinking models do not default to dynamic thinking which is done by a value of -1
|
||||
export const DEFAULT_THINKING_MODE = -1;
|
||||
// Cap the thinking at 8192 to prevent run-away thinking loops.
|
||||
export const DEFAULT_THINKING_MODE = 8192;
|
||||
|
||||
/**
|
||||
* Determines the effective model to use, applying fallback logic if necessary.
|
||||
|
||||
@@ -156,4 +156,17 @@ describe('CoreEventEmitter', () => {
|
||||
});
|
||||
expect(listener.mock.calls[2][0]).toMatchObject({ message: 'Buffered 2' });
|
||||
});
|
||||
|
||||
describe('ModelChanged Event', () => {
|
||||
it('should emit ModelChanged event with correct payload', () => {
|
||||
const listener = vi.fn();
|
||||
events.on(CoreEvent.ModelChanged, listener);
|
||||
|
||||
const newModel = 'gemini-2.5-pro';
|
||||
events.emitModelChanged(newModel);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith({ model: newModel });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,9 +43,20 @@ export interface FallbackModeChangedPayload {
|
||||
isInFallbackMode: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload for the 'model-changed' event.
|
||||
*/
|
||||
export interface ModelChangedPayload {
|
||||
/**
|
||||
* The new model that was set.
|
||||
*/
|
||||
model: string;
|
||||
}
|
||||
|
||||
export enum CoreEvent {
|
||||
UserFeedback = 'user-feedback',
|
||||
FallbackModeChanged = 'fallback-mode-changed',
|
||||
ModelChanged = 'model-changed',
|
||||
}
|
||||
|
||||
export class CoreEventEmitter extends EventEmitter {
|
||||
@@ -86,6 +97,14 @@ export class CoreEventEmitter extends EventEmitter {
|
||||
this.emit(CoreEvent.FallbackModeChanged, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies subscribers that the model has changed.
|
||||
*/
|
||||
emitModelChanged(model: string): void {
|
||||
const payload: ModelChangedPayload = { model };
|
||||
this.emit(CoreEvent.ModelChanged, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes buffered messages. Call this immediately after primary UI listener
|
||||
* subscribes.
|
||||
@@ -106,6 +125,10 @@ export class CoreEventEmitter extends EventEmitter {
|
||||
event: CoreEvent.FallbackModeChanged,
|
||||
listener: (payload: FallbackModeChangedPayload) => void,
|
||||
): this;
|
||||
override on(
|
||||
event: CoreEvent.ModelChanged,
|
||||
listener: (payload: ModelChangedPayload) => void,
|
||||
): this;
|
||||
override on(
|
||||
event: string | symbol,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -122,6 +145,10 @@ export class CoreEventEmitter extends EventEmitter {
|
||||
event: CoreEvent.FallbackModeChanged,
|
||||
listener: (payload: FallbackModeChangedPayload) => void,
|
||||
): this;
|
||||
override off(
|
||||
event: CoreEvent.ModelChanged,
|
||||
listener: (payload: ModelChangedPayload) => void,
|
||||
): this;
|
||||
override off(
|
||||
event: string | symbol,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -138,6 +165,10 @@ export class CoreEventEmitter extends EventEmitter {
|
||||
event: CoreEvent.FallbackModeChanged,
|
||||
payload: FallbackModeChangedPayload,
|
||||
): boolean;
|
||||
override emit(
|
||||
event: CoreEvent.ModelChanged,
|
||||
payload: ModelChangedPayload,
|
||||
): boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
override emit(event: string | symbol, ...args: any[]): boolean {
|
||||
return super.emit(event, ...args);
|
||||
|
||||
Reference in New Issue
Block a user