feat(KB-148): display per-task model overrides in agent log viewer

- Add model info header to AgentLogViewer with executor/validator display

- Show provider icons and model IDs when overrides are set

- Display 'Using default' badge when no override is configured

- Update TaskDetailModal to extract and pass model selections

- Add comprehensive tests for model display functionality
This commit is contained in:
gsxdsm
2026-03-30 08:17:21 -07:00
parent 82d33cefde
commit ca162abd16
3 changed files with 220 additions and 2 deletions

View File

@@ -193,4 +193,141 @@ describe("AgentLogViewer", () => {
expect(badges).toHaveLength(0);
});
});
describe("model info header", () => {
it("renders model info header with executor model when set", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer
entries={entries}
loading={false}
executorModel={{ provider: "anthropic", modelId: "claude-sonnet-4-5" }}
/>
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("anthropic/claude-sonnet-4-5");
expect(header!.textContent).toContain("Validator:");
expect(header!.textContent).toContain("Using default");
// Verify ProviderIcon is rendered for executor
expect(container.querySelector('[data-provider="anthropic"]')).toBeTruthy();
});
it("renders 'Using default' when no executor model override is set", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer entries={entries} loading={false} executorModel={null} />
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("Using default");
});
it("renders 'Using default' when executorModel is undefined", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer entries={entries} loading={false} />
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("Using default");
});
it("renders model info header with validator model when set", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer
entries={entries}
loading={false}
validatorModel={{ provider: "openai", modelId: "gpt-4o" }}
/>
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Validator:");
expect(header!.textContent).toContain("openai/gpt-4o");
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("Using default");
// Verify ProviderIcon is rendered for validator
expect(container.querySelector('[data-provider="openai"]')).toBeTruthy();
});
it("renders 'Using default' when no validator model override is set", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer entries={entries} loading={false} validatorModel={null} />
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Validator:");
expect(header!.textContent).toContain("Using default");
});
it("renders both models when both are configured", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer
entries={entries}
loading={false}
executorModel={{ provider: "anthropic", modelId: "claude-opus-4" }}
validatorModel={{ provider: "openai", modelId: "gpt-4o" }}
/>
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("anthropic/claude-opus-4");
expect(header!.textContent).toContain("Validator:");
expect(header!.textContent).toContain("openai/gpt-4o");
// Verify both ProviderIcons are rendered
expect(container.querySelector('[data-provider="anthropic"]')).toBeTruthy();
expect(container.querySelector('[data-provider="openai"]')).toBeTruthy();
});
it("renders header with 'Using default' for both models when both are null/undefined", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer entries={entries} loading={false} />
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("Using default");
expect(header!.textContent).toContain("Validator:");
expect(header!.textContent).toContain("Using default");
});
it("shows 'Using default' when executorModel has only provider but no modelId", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer
entries={entries}
loading={false}
executorModel={{ provider: "anthropic" }}
/>
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("Using default");
});
it("shows 'Using default' when executorModel has only modelId but no provider", () => {
const entries = [makeEntry()];
const { container } = render(
<AgentLogViewer
entries={entries}
loading={false}
executorModel={{ modelId: "claude-sonnet-4-5" }}
/>
);
const header = container.querySelector("[data-testid='agent-log-model-header']");
expect(header).toBeTruthy();
expect(header!.textContent).toContain("Executor:");
expect(header!.textContent).toContain("Using default");
});
});
});