Skip to content
Merged
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
20 changes: 16 additions & 4 deletions frontend/src/components/ui/inspector-shell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,12 @@ async function waitForAnimationFrame() {
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
}

// Lets the focus a freshly mounted layer defers to its next frames run before the test moves on.
async function settleFocus() {
await waitForAnimationFrame();
await waitForAnimationFrame();
}

function setViewportWidth(width: number) {
Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
window.dispatchEvent(new Event("resize"));
Expand Down Expand Up @@ -703,7 +709,10 @@ describe("InspectorShell", () => {
await user.click(screen.getByRole("button", { name: "Deploy" }));

setViewportWidth(720);
await waitForAnimationFrame();
await waitFor(() =>
expect(screen.getByTestId("inspector-content")).not.toHaveAttribute("data-inspector-panel"),
);
await settleFocus();

const inspector = screen.getByTestId("inspector-content");
expect(inspector).toHaveStyle({ zIndex: "calc(var(--layer-modal) - 1)" });
Expand All @@ -717,7 +726,7 @@ describe("InspectorShell", () => {

expect(screen.queryByRole("dialog", { name: "Deploy resource" })).not.toBeInTheDocument();
expect(screen.getByRole("dialog", { name: "Responsive inspector" })).toBeVisible();
expect(screen.getByRole("button", { name: "Deploy" })).toHaveFocus();
await waitFor(() => expect(screen.getByRole("button", { name: "Deploy" })).toHaveFocus());
});

it("keeps an active child dialog isolated when the inspector crosses into wide mode", async () => {
Expand All @@ -729,7 +738,10 @@ describe("InspectorShell", () => {
await user.click(screen.getByRole("button", { name: "Deploy" }));

setViewportWidth(721);
await waitForAnimationFrame();
await waitFor(() =>
expect(screen.getByTestId("inspector-content")).toHaveAttribute("data-inspector-panel"),
);
await settleFocus();

const inspector = screen.getByTestId("inspector-content");
expect(inspector).toHaveAttribute("aria-hidden", "true");
Expand All @@ -742,7 +754,7 @@ describe("InspectorShell", () => {

expect(screen.queryByRole("dialog", { name: "Deploy resource" })).not.toBeInTheDocument();
expect(screen.getByRole("complementary", { name: "Responsive inspector" })).toBeVisible();
expect(screen.getByRole("button", { name: "Deploy" })).toHaveFocus();
await waitFor(() => expect(screen.getByRole("button", { name: "Deploy" })).toHaveFocus());
});

it("gives an active feature modal precedence over inspector dismissal", async () => {
Expand Down
39 changes: 14 additions & 25 deletions frontend/src/features/models/deploy/model-deploy-context-step.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { ReactNode, RefObject } from "react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
DeploymentForm,
DeploymentStepHeading,
} from "@/features/deployments/deployment-form-layout";
import { ContextSizeField } from "@/features/models/runtime/context-size-field";
import type { ContextSizeRange } from "@/features/models/runtime/context-size-input";
import type {
ContextSizeLargestFit,
ContextSizeRange,
} from "@/features/models/runtime/context-size-input";
import {
engineParameterHelp,
maximumContext,
Expand All @@ -28,6 +30,8 @@ interface ModelDeployContextStepProps {
/** The context per request the backend fills while the input is empty, from the latest estimate. */
filledContextPerRequest?: number;
inputs: ReadonlyMap<string, string>;
/** The largest total context that fits the selected target, offered as the total's last chip. */
largestFit?: ContextSizeLargestFit;
model: Model;
onParameterBlur: (parameter: EngineParameterDefinition) => void;
/** Receives each change, and whether it is settled, as a slider or preset is, or typed. */
Expand All @@ -37,11 +41,6 @@ interface ModelDeployContextStepProps {
settled?: boolean,
) => void;
profile?: ModelProfile;
/**
* Where the total context comes from: its label, and a reset back to the default once the
* operator chose it. Absent while the engine default applies.
*/
totalSource?: { label: string; onReset?: () => void };
}

/**
Expand All @@ -57,11 +56,11 @@ export function ModelDeployContextStep({
fieldErrors,
filledContextPerRequest,
inputs,
largestFit,
model,
onParameterBlur,
onParameterChange,
profile,
totalSource,
}: ModelDeployContextStepProps) {
const configuration = model.configurations.find(
(candidate) => candidate.configurationId === profile?.configurationId,
Expand All @@ -75,7 +74,12 @@ export function ModelDeployContextStep({
const sliderRange = contextSliderRange([totalContext, contextPerRequest]);
const contextField = (
parameter: EngineParameterDefinition,
field: { emptyValue?: number; help?: ReactNode; hint?: string; source?: ReactNode } = {},
field: {
emptyValue?: number;
help?: ReactNode;
hint?: string;
largestFit?: ContextSizeLargestFit;
} = {},
) => (
<ContextSizeField
disabled={disabled}
Expand All @@ -102,22 +106,7 @@ export function ModelDeployContextStep({
? contextField(totalContext, {
help: engineParameterHelp(totalContext),
hint: contextHint,
source: totalSource ? (
<>
<Badge variant="outline">{totalSource.label}</Badge>
{totalSource.onReset ? (
<Button
disabled={disabled}
onClick={totalSource.onReset}
size="xs"
type="button"
variant="link"
>
Use largest fit
</Button>
) : null}
</>
) : undefined,
largestFit,
})
: null}
{contextPerRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ export function ModelDeployDialog(props: ModelDeployDialogProps) {
fieldErrors={form.contextFieldErrors}
filledContextPerRequest={memory.estimate.filledContextPerRequest}
inputs={form.engineParameterInputs}
largestFit={form.largestFit}
model={model}
onParameterBlur={form.blurContext}
onParameterChange={form.changeContext}
profile={form.profile}
totalSource={form.totalSource}
/>
) : null}
{step === "Runtime" ? (
Expand Down
Loading