Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions site/src/pages/AgentsPage/AgentSettingsCompactionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,7 @@ const AgentSettingsCompactionPage: FC = () => {
<AgentSettingsCompactionPageView
models={organizationModels.models}
providerTypeByID={providerTypeByID}
organizationNameByID={
new Map(
organizations.map((organization) => [
organization.id,
organization.display_name || organization.name,
]),
)
}
organizations={organizations}
modelsError={organizationModels.error ?? organizationModels.partialError}
isLoadingModels={organizationModels.isLoading}
thresholds={thresholdsQuery.data?.thresholds}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ const baseArgs: AgentSettingsCompactionPageViewProps = {
},
],
providerTypeByID: new Map<string, string>([["prov-openai", "openai"]]),
organizationNameByID: new Map<string, string>([
[MockDefaultOrganization.id, MockDefaultOrganization.display_name],
]),
organizations: [MockDefaultOrganization],
modelsError: undefined,
isLoadingModels: false,
thresholds: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { UserCompactionThresholdSettings } from "./components/UserCompactionThre
export interface AgentSettingsCompactionPageViewProps {
models: readonly TypesGen.ChatModel[] | undefined;
providerTypeByID: ReadonlyMap<string, string>;
organizationNameByID: ReadonlyMap<string, string>;
organizations: readonly TypesGen.Organization[];
modelsError: unknown;
isLoadingModels: boolean;
thresholds: readonly TypesGen.UserChatCompactionThreshold[] | undefined;
Expand All @@ -24,7 +24,7 @@ export const AgentSettingsCompactionPageView: FC<
> = ({
models,
providerTypeByID,
organizationNameByID,
organizations,
modelsError,
isLoadingModels,
thresholds,
Expand All @@ -42,7 +42,7 @@ export const AgentSettingsCompactionPageView: FC<
<UserCompactionThresholdSettings
models={models ?? []}
providerTypeByID={providerTypeByID}
organizationNameByID={organizationNameByID}
organizations={organizations}
modelsError={modelsError}
isLoadingModels={isLoadingModels}
thresholds={thresholds}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import { expect, fn, userEvent, waitFor, within } from "storybook/test";
import type * as TypesGen from "#/api/typesGenerated";
import { MockChatModel } from "#/testHelpers/chatModels";
import { MockDefaultOrganization, MockUserOwner } from "#/testHelpers/entities";
import {
MockDefaultOrganization,
MockOrganization2,
MockUserOwner,
} from "#/testHelpers/entities";
import {
withAuthProvider,
withDashboardProvider,
} from "#/testHelpers/storybook";
import { UserCompactionThresholdSettings } from "./UserCompactionThresholdSettings";

const modelsOrganization = {
...MockDefaultOrganization,
id: MockChatModel.organization_id,
};

const organizationWithEmptyDisplayName = {
...MockDefaultOrganization,
id: MockChatModel.organization_id,
Expand Down Expand Up @@ -59,9 +68,7 @@ const meta = {
["provider-1", "openai"],
["provider-anthropic", "anthropic"],
]),
organizationNameByID: new Map<string, string>([
[MockChatModel.organization_id, MockDefaultOrganization.display_name],
]),
organizations: [modelsOrganization],
thresholds: [],
isThresholdsLoading: false,
thresholdsError: undefined,
Expand Down Expand Up @@ -116,20 +123,11 @@ export const Default: Story = {

export const EmptyOrganizationDisplayNameFallsBackToName: Story = {
args: {
organizationNameByID: new Map<string, string>([
[
organizationWithEmptyDisplayName.id,
organizationWithEmptyDisplayName.display_name ||
organizationWithEmptyDisplayName.name,
],
]),
organizations: [organizationWithEmptyDisplayName],
thresholds: [{ model_config_id: "model-1", threshold_percent: 90 }],
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(
canvas.getAllByText(organizationWithEmptyDisplayName.name).length,
).toBeGreaterThan(0);
expect(
canvas.getByRole("textbox", {
name: `GPT-4o compaction threshold for ${organizationWithEmptyDisplayName.name}`,
Expand Down Expand Up @@ -320,6 +318,131 @@ export const PartialSaveFailure: Story = {
},
};

export const OrganizationFilter: Story = {
args: {
models: [
mockModels[0],
{
...mockModels[1],
organization_id: MockOrganization2.id,
},
],
organizations: [modelsOrganization, MockOrganization2],
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const filter = await canvas.findByRole("button", {
name: `Organization ${modelsOrganization.display_name}`,
});

expect(canvas.getByText("GPT-4o")).toBeInTheDocument();
expect(canvas.queryByText("Claude Sonnet")).not.toBeInTheDocument();

await userEvent.click(filter);
const option = await within(document.body).findByRole("option", {
name: MockOrganization2.display_name,
});
await userEvent.click(option);

await waitFor(() => {
expect(canvas.queryByText("GPT-4o")).not.toBeInTheDocument();
expect(canvas.getByText("Claude Sonnet")).toBeInTheDocument();
});

expect(
canvas.getByRole("button", {
name: `Organization ${MockOrganization2.display_name}`,
}),
).toBeInTheDocument();
},
};

export const SingleOrganizationHidesFilter: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText("GPT-4o");
expect(
canvas.queryByRole("button", { name: /^Organization / }),
).not.toBeInTheDocument();
},
};

export const OrganizationFilterScopesSaveActions: Story = {
args: {
models: [
mockModels[0],
{
...mockModels[1],
organization_id: MockOrganization2.id,
},
],
organizations: [modelsOrganization, MockOrganization2],
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const gpt4oInput = await canvas.findByRole("textbox", {
name: /GPT-4o compaction threshold/i,
});
await userEvent.type(gpt4oInput, "95");
await canvas.findByRole("button", { name: /Save 1 change/i });

// Switch to the other organization: the draft belongs to a hidden
// row, so the footer must disappear.
await userEvent.click(
canvas.getByRole("button", {
name: `Organization ${modelsOrganization.display_name}`,
}),
);
await userEvent.click(
await within(document.body).findByRole("option", {
name: MockOrganization2.display_name,
}),
);
await waitFor(() => {
expect(canvas.queryByRole("button", { name: /Save/i })).toBeNull();
});

// Editing the visible row saves only that row.
const claudeInput = await canvas.findByRole("textbox", {
name: /Claude Sonnet compaction threshold/i,
});
await userEvent.type(claudeInput, "50");
await userEvent.click(
await canvas.findByRole("button", { name: /Save 1 change/i }),
);
await waitFor(() => {
expect(args.onSaveThreshold).toHaveBeenCalledWith("model-2", 50);
expect(args.onSaveThreshold).not.toHaveBeenCalledWith("model-1", 95);
});

// Switching back restores the hidden draft and its footer.
await userEvent.click(
canvas.getByRole("button", {
name: `Organization ${MockOrganization2.display_name}`,
}),
);
await userEvent.click(
await within(document.body).findByRole("option", {
name: modelsOrganization.display_name,
}),
);
const restoredInput = await canvas.findByRole("textbox", {
name: /GPT-4o compaction threshold/i,
});
expect(restoredInput).toHaveValue("95");
// Wait out the temporary "Saved" footer state (2.5s) before the
// action buttons reappear.
await waitFor(
() => {
expect(
canvas.getByRole("button", { name: /Save 1 change/i }),
).toBeInTheDocument();
},
{ timeout: 5000 },
);
},
};

export const ErrorState: Story = {
name: "Error",
args: {
Expand All @@ -338,9 +461,6 @@ export const PartialModelLoadError: Story = {
expect(
await canvas.findByText("Failed to load models from one organization"),
).toBeVisible();
expect(
canvas.getAllByText(MockDefaultOrganization.display_name).length,
).toBeGreaterThan(0);
expect(
canvas.getByRole("textbox", {
name: `GPT-4o compaction threshold for ${MockDefaultOrganization.display_name}`,
Expand Down
Loading