feat: add emulator mode for mocked tool responses#3062
feat: add emulator mode for mocked tool responses#3062Deeven-Seru wants to merge 1 commit intogoogleapis:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces an emulator mode that allows the server to return mock tool responses from a JSON file instead of calling live backends. It adds new CLI flags, updates server configuration, and implements a tool wrapper that matches input parameters against defined mocks. Feedback focuses on optimizing the emulator's performance by pre-normalizing mock data during initialization and refactoring the invocation logic to minimize redundant JSON serialization.
da8da48 to
64e66d6
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces an emulator mode that allows the toolbox server to return mock tool responses from a JSON file instead of calling live backends. It adds new CLI flags, validation logic for the emulator configuration, and a new emulatorTool wrapper that intercepts tool invocations to match them against provided mocks. Feedback focuses on improving the emulator's flexibility by allowing partial emulation (only wrapping tools that have mocks), making parameter embedding a no-op to simplify mock matching, centralizing validation logic to avoid duplication, and ensuring the server watches the mocks file for dynamic reloads.
| func wrapToolsForEmulator(toolMap map[string]tools.Tool, mocksByTool map[string][]emulatorMock) map[string]tools.Tool { | ||
| wrapped := make(map[string]tools.Tool, len(toolMap)) | ||
| for toolName, t := range toolMap { | ||
| wrapped[toolName] = emulatorTool{ | ||
| name: toolName, | ||
| base: t, | ||
| mocks: mocksByTool[toolName], | ||
| } | ||
| } | ||
| return wrapped | ||
| } |
There was a problem hiding this comment.
The current implementation wraps every single tool in the server when emulator mode is enabled. This means that any tool without a defined mock will return an error upon invocation, effectively breaking all non-mocked tools.
Consider only wrapping tools that actually have mocks defined in the mocksByTool map. This allows for "partial emulation" where some tools are mocked while others continue to use their live backends.
func wrapToolsForEmulator(toolMap map[string]tools.Tool, mocksByTool map[string][]emulatorMock) map[string]tools.Tool {
wrapped := make(map[string]tools.Tool, len(toolMap))
for toolName, t := range toolMap {
if mocks, ok := mocksByTool[toolName]; ok && len(mocks) > 0 {
wrapped[toolName] = emulatorTool{
name: toolName,
base: t,
mocks: mocks,
}
} else {
wrapped[toolName] = t
}
}
return wrapped
}| func (e emulatorTool) EmbedParams(ctx context.Context, params parameters.ParamValues, embeddingModels map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { | ||
| return e.base.EmbedParams(ctx, params, embeddingModels) | ||
| } |
There was a problem hiding this comment.
By delegating EmbedParams to the base tool, the emulator will attempt to call live embedding models if the tool configuration requires it. This makes mocking difficult because the user would need to provide exact embedding vectors in the mock JSON file to match the parameters in Invoke.
It is recommended to make EmbedParams a no-op for the emulatorTool. This ensures that Invoke receives the original, human-readable parameters, making them much easier to match against the mock file.
| func (e emulatorTool) EmbedParams(ctx context.Context, params parameters.ParamValues, embeddingModels map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { | |
| return e.base.EmbedParams(ctx, params, embeddingModels) | |
| } | |
| func (e emulatorTool) EmbedParams(ctx context.Context, params parameters.ParamValues, embeddingModels map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { | |
| return params, nil | |
| } |
Summary
Implements #1243 by adding an emulator mode that intercepts tool execution and returns predefined mock responses from a local JSON file.
What’s included
EmulatorModeEmulatorMocksFile--emulator-mode--emulator-mocks-file--emulator-mocks-filewhen emulator mode is enabled.internal/server/emulator.gothat:parametersresponseinstead of hitting live backendsExample mock file
{ "mocks": [ { "tool_name": "search_users_bq", "parameters": { "id": 123 }, "response": [{ "id": 123, "name": "Alice" }] } ] }Test
go test ./internal/server -run TestEmulatorToolInvoke -count=1