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
38 changes: 37 additions & 1 deletion web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"recharts": "^3.10.1",
"rehype-highlight": "^7.0.2",
"remark-gfm": "^4.0.1",
"tailwind-merge": "^3.7.0"
"tailwind-merge": "^3.7.0",
"yaml": "^2.9.1"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.20",
Expand Down
11 changes: 8 additions & 3 deletions web/packages/ui/src/components/code-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ const COLOR: Record<Exclude<Tok["t"], "">, string> = {
w: "var(--code-keyword)",
}

/** 一行的高亮,与 CodeBlock 画法相同。token 拼回去就是原文,所以可以垫在透明 textarea 下面做可编辑的高亮。 */
export function highlightLine(line: string, language: CodeBlockLanguage = "shell"): React.ReactNode[] {
return tokenizeLine(line, language).map((tok, j) =>
tok.t ? <span key={j} style={{ color: COLOR[tok.t] }}>{tok.v}</span> : <React.Fragment key={j}>{tok.v}</React.Fragment>,
)
}

export function CodeBlock({
value,
language = "shell",
Expand Down Expand Up @@ -121,9 +128,7 @@ export function CodeBlock({
</span>
) : null}
<span className="min-w-0 flex-1">
{tokenizeLine(line, language).map((tok, j) =>
tok.t ? <span key={j} style={{ color: COLOR[tok.t] }}>{tok.v}</span> : <React.Fragment key={j}>{tok.v}</React.Fragment>,
)}
{highlightLine(line, language)}
{line === "" ? "​" : null}
</span>
</div>
Expand Down
2 changes: 1 addition & 1 deletion web/packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGr
export { Sheet, SheetBody, SheetCancelButton, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, useSheetClose } from "./components/ui/sheet"
export type { SheetSize } from "./components/ui/sheet"
export { FieldNotice } from "./components/floating-field"
export { CodeBlock } from "./components/code-block"
export { CodeBlock, highlightLine } from "./components/code-block"
export { CodeToolbar } from "./components/code-toolbar"
export type { CodeBlockProps, CodeBlockLanguage } from "./components/code-block"
export type { CodeToolbarProps } from "./components/code-toolbar"
Expand Down
12 changes: 6 additions & 6 deletions web/src/modules/inferences/components/DeployForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
FieldHint,
FieldInput,
FieldSelect,
FieldTextarea,
FloatingField,
FormSection,
Input,
Expand All @@ -23,14 +22,15 @@ import {
cn,
} from "@modelsphere/ui";
import { Plus, RefreshCw, X } from "lucide-react";
import type { ClusterInfo } from "@swiss/lib/api";
import type { ClusterInfo, HelmValues } from "@swiss/lib/api";
import { cacheFieldError, type CacheCard, type CacheDefaults } from "@swiss/lib/cache";
import { unlistedProducts, type GPUCapacity } from "@swiss/lib/gpu";
import { effective, imageChoices, type Form, type Toleration } from "@swiss/components/DeploySettings";
import { MAX_VALUES, rejectKey } from "@swiss/lib/routeDirectives";
import { FALLBACK_ROUTE_OPTIONS, routeDirectives, runtimeValue, type RouteDirective, type RouteOptions } from "@/modules/inferences/routeOptions";
import { useT } from "@/modules/inferences/i18n";
import { isAdaptiveOption, toggleAdaptive } from "../adaptive-concurrency";
import { OverrideYaml } from "./OverrideYaml";
import { NAMED_VALUES, extraValue, fracOutOfRange, serviceIdError, type Section } from "@/modules/inferences/deploy-lib";

const EFFECTS = ["", "NoSchedule", "PreferNoSchedule", "NoExecute"];
Expand All @@ -56,9 +56,11 @@ export interface DeployFormProps {
idPrefix: string;
routeOptions?: RouteOptions | null;
catalogRoute?: string;
// Composes the form with these overrides, storing nothing, for the Override YAML editor.
loadValues?: (edits: string) => Promise<HelmValues>;
}

export function DeployForm({ form, onChange, upgrade, submitted, target, cluster, defaults, gpuOptions, gpuListed, gpuResources, capacity, expanded, onExpand, idPrefix, routeOptions, catalogRoute }: DeployFormProps) {
export function DeployForm({ form, onChange, upgrade, submitted, target, cluster, defaults, gpuOptions, gpuListed, gpuResources, capacity, expanded, onExpand, idPrefix, routeOptions, catalogRoute, loadValues }: DeployFormProps) {
const t = useT();
const on = effective(form);
const f = (k: string) => t(`deploy.fields.${k}`);
Expand Down Expand Up @@ -247,9 +249,7 @@ export function DeployForm({ form, onChange, upgrade, submitted, target, cluster
<FloatingField layout="inline" label={f("skipSchemaValidation")} hint={f("skipSchemaValidationHint")}>
{sw("skipSchemaValidation", f("skipSchemaValidation"))}
</FloatingField>
<FloatingField label={f("edits")} hint={f("editsHint")} multiline>
<FieldTextarea {...text("edits")} rows={10} spellCheck={false} className="font-mono text-xs" />
</FloatingField>
<OverrideYaml value={form.edits} onChange={(edits) => onChange({ edits })} load={loadValues} />
</>,
)}
</div>
Expand Down
23 changes: 15 additions & 8 deletions web/src/modules/inferences/components/DeploySheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { detailPath, sloEnabled } from "@/modules/inferences/lib";
import { SECTIONS, chartChoice, defaultServiceId, pipelineState, serviceIdError, whatMoves, type Section } from "@/modules/inferences/deploy-lib";
import { DeployForm } from "@/modules/inferences/components/DeployForm";
import { ExperimentalBadge } from "@/modules/inferences/components/InstallMethodLabel";
import { AdaptivePlanChangedError, composeAdaptivePlan } from "../adaptive-concurrency";
import { AdaptivePlanChangedError, composeAdaptivePlan, withAdaptiveConcurrency } from "../adaptive-concurrency";
import { readRouteOptions } from "@/modules/inferences/api";

export type DeployTarget =
Expand Down Expand Up @@ -186,16 +186,22 @@ function DeploySheetBody({ target, onClose }: { target: DeployTarget; onClose: (
// A product the variant does not list is one swissd takes only as forced.
const forceGpuProducts = unlistedProducts(v?.requires.gpuProduct, form.gpuProducts).length > 0;
const change = (patch: Partial<Form>) => setForm((f) => ({ ...f, ...patch }));
// What a compose of f sends; the Override YAML editor's preview sends the same.
const requestFor = (f: Form) => planRequest(
{ ...f, serviceId: f.serviceId || (upgrade ? f.serviceId : defaultServiceId(target.model)) },
upgrade
? { model: recorded!.model, version: version || undefined, variant: variant || undefined, catalog: moveTo || undefined, chartVersion: chartVersion || undefined, fromRelease: target.release, cacheUnsupported: !!cache.unsupported, forceGpuProducts }
: { model: target.model, version, variant: keptVariant, catalog: target.catalog, chartVersion: chartVersion || undefined, cacheUnsupported: !!cache.unsupported, forceGpuProducts },
profile.data?.profile,
);
const loadValues = (edits: string) => {
const f = { ...form, edits };
return deployApi.helmValues(withAdaptiveConcurrency(requestFor(f), f, routeOptions.data));
};
const compose = useMutation({
mutationFn: async () => {
try {
return await composeAdaptivePlan(planRequest(
{ ...form, serviceId: form.serviceId || (upgrade ? form.serviceId : defaultServiceId(target.model)) },
upgrade
? { model: recorded!.model, version: version || undefined, variant: variant || undefined, catalog: moveTo || undefined, chartVersion: chartVersion || undefined, fromRelease: target.release, cacheUnsupported: !!cache.unsupported, forceGpuProducts }
: { model: target.model, version, variant: keptVariant, catalog: target.catalog, chartVersion: chartVersion || undefined, cacheUnsupported: !!cache.unsupported, forceGpuProducts },
profile.data?.profile,
), form, routeOptions.data, deployApi.plan, catalogName);
return await composeAdaptivePlan(requestFor(form), form, routeOptions.data, deployApi.plan, catalogName);
} catch (error) {
if (error instanceof AdaptivePlanChangedError) throw new Error(t("deploy.fields.adaptiveChanged"));
throw error;
Expand Down Expand Up @@ -438,6 +444,7 @@ function DeploySheetBody({ target, onClose }: { target: DeployTarget; onClose: (
routeOptions={routeOptions.isSuccess ? routeOptions.data : undefined}
catalogRoute={catalogRoute}
idPrefix={ID_PREFIX}
loadValues={readOnly ? undefined : loadValues}
/>
<div style={{ height: nav.tailSpace }} aria-hidden />
</>
Expand Down
Loading
Loading