feat(recipe-studio): add support for inference_extra_body configuration with collapsible UI and enhanced validation logic
This commit is contained in:
parent
3cafc0506e
commit
cf8cb9109b
7 changed files with 94 additions and 36 deletions
|
|
@ -100,36 +100,37 @@ function normalizeLintDiagnostic(diagnostic) {
|
|||
if (!diagnostic || typeof diagnostic !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const readString = (value) =>
|
||||
typeof value === "string" ? value : null;
|
||||
const readInt = (value) =>
|
||||
Number.isInteger(value) ? value : null;
|
||||
const asObject = (value) =>
|
||||
value && typeof value === "object" ? value : null;
|
||||
|
||||
const message = String(diagnostic.message || "").trim();
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const severityRaw = String(diagnostic.severity || "").trim().toLowerCase();
|
||||
const severity = severityRaw === "error" ? "error" : "warning";
|
||||
const labels = Array.isArray(diagnostic.labels)
|
||||
? diagnostic.labels.map((label) => {
|
||||
const span = label && typeof label === "object" ? label.span : null;
|
||||
const start =
|
||||
span && typeof span === "object" && Number.isInteger(span.offset)
|
||||
? span.offset
|
||||
: null;
|
||||
const length =
|
||||
span && typeof span === "object" && Number.isInteger(span.length)
|
||||
? span.length
|
||||
: null;
|
||||
return {
|
||||
message:
|
||||
label && typeof label === "object" && typeof label.label === "string"
|
||||
? label.label
|
||||
: null,
|
||||
start,
|
||||
end:
|
||||
start !== null && length !== null
|
||||
? start + length
|
||||
: null,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const labels = [];
|
||||
if (Array.isArray(diagnostic.labels)) {
|
||||
for (const label of diagnostic.labels) {
|
||||
const labelObj = asObject(label);
|
||||
const span = asObject(labelObj?.span);
|
||||
const start = readInt(span?.offset);
|
||||
const length = readInt(span?.length);
|
||||
labels.push({
|
||||
message: readString(labelObj?.label),
|
||||
start,
|
||||
end: start !== null && length !== null ? start + length : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const code = typeof diagnostic.code === "string" ? diagnostic.code : null;
|
||||
return {
|
||||
message: code ? `${code}: ${message}` : message,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import {
|
||||
Combobox,
|
||||
|
|
@ -8,7 +13,8 @@ import {
|
|||
ComboboxList,
|
||||
} from "@/components/ui/combobox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { type ReactElement, useRef } from "react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { type ReactElement, useRef, useState } from "react";
|
||||
import type { ModelConfig } from "../../types";
|
||||
import { FieldLabel } from "../shared/field-label";
|
||||
import { NameField } from "../shared/name-field";
|
||||
|
|
@ -24,11 +30,13 @@ export function ModelConfigDialog({
|
|||
providerOptions,
|
||||
onUpdate,
|
||||
}: ModelConfigDialogProps): ReactElement {
|
||||
const [optionalOpen, setOptionalOpen] = useState(false);
|
||||
const modelId = `${config.id}-model`;
|
||||
const providerId = `${config.id}-provider`;
|
||||
const tempId = `${config.id}-temperature`;
|
||||
const topPId = `${config.id}-top-p`;
|
||||
const maxTokensId = `${config.id}-max-tokens`;
|
||||
const extraBodyId = `${config.id}-inference-extra-body`;
|
||||
const providerAnchorRef = useRef<HTMLDivElement>(null);
|
||||
const providerInputRef = useRef(config.provider);
|
||||
const lastProviderRef = useRef(config.provider);
|
||||
|
|
@ -145,15 +153,44 @@ export function ModelConfigDialog({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-xs font-semibold uppercase text-muted-foreground">
|
||||
<Checkbox
|
||||
checked={config.skip_health_check ?? false}
|
||||
onCheckedChange={(value) =>
|
||||
updateField("skip_health_check", Boolean(value))
|
||||
}
|
||||
/>
|
||||
Skip health check
|
||||
</label>
|
||||
<Collapsible open={optionalOpen} onOpenChange={setOptionalOpen}>
|
||||
<CollapsibleTrigger asChild={true}>
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center justify-between text-left text-xs text-muted-foreground"
|
||||
>
|
||||
<span className="font-semibold uppercase">Optional</span>
|
||||
<span>{optionalOpen ? "Hide" : "Show"}</span>
|
||||
</button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="mt-3 space-y-4">
|
||||
<div className="grid gap-2">
|
||||
<FieldLabel
|
||||
label="Inference extra body (JSON)"
|
||||
htmlFor={extraBodyId}
|
||||
hint="Optional request fields merged into inference parameters."
|
||||
/>
|
||||
<Textarea
|
||||
id={extraBodyId}
|
||||
className="corner-squircle nodrag"
|
||||
placeholder='{"top_k": 20, "min_p": 0.0}'
|
||||
value={config.inference_extra_body ?? ""}
|
||||
onChange={(event) =>
|
||||
updateField("inference_extra_body", event.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-xs font-semibold uppercase text-muted-foreground">
|
||||
<Checkbox
|
||||
checked={config.skip_health_check ?? false}
|
||||
onCheckedChange={(value) =>
|
||||
updateField("skip_health_check", Boolean(value))
|
||||
}
|
||||
/>
|
||||
Skip health check
|
||||
</label>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,6 +244,8 @@ export type ModelConfig = {
|
|||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference_max_tokens?: string;
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference_extra_body?: string;
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
skip_health_check?: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -260,6 +260,8 @@ export function makeModelConfig(
|
|||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference_top_p: "",
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference_extra_body: "",
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
skip_health_check: false,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@ export function parseModelConfig(
|
|||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference_max_tokens: readNumberString(inference.max_tokens),
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference_extra_body: isRecord(inference.extra_body)
|
||||
? JSON.stringify(inference.extra_body, null, 2)
|
||||
: "",
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
skip_health_check:
|
||||
typeof model.skip_health_check === "boolean"
|
||||
? model.skip_health_check
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ export function buildRecipePayload(
|
|||
modelProviderConfigs.push(config);
|
||||
continue;
|
||||
}
|
||||
modelConfigs.push(buildModelConfig(config));
|
||||
modelConfigs.push(buildModelConfig(config, errors));
|
||||
modelConfigConfigs.push(config);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,19 @@ export function buildModelProvider(
|
|||
};
|
||||
}
|
||||
|
||||
export function buildModelConfig(config: ModelConfig): Record<string, unknown> {
|
||||
export function buildModelConfig(
|
||||
config: ModelConfig,
|
||||
errors: string[],
|
||||
): Record<string, unknown> {
|
||||
const inference: Record<string, unknown> = {};
|
||||
const temp = config.inference_temperature?.trim();
|
||||
const topP = config.inference_top_p?.trim();
|
||||
const maxTokens = config.inference_max_tokens?.trim();
|
||||
const extraBody = parseJsonObject(
|
||||
config.inference_extra_body,
|
||||
`Model ${config.name} inference extra_body`,
|
||||
errors,
|
||||
);
|
||||
|
||||
if (temp) {
|
||||
const parsed = Number(temp);
|
||||
|
|
@ -57,6 +65,10 @@ export function buildModelConfig(config: ModelConfig): Record<string, unknown> {
|
|||
inference.max_tokens = parsed;
|
||||
}
|
||||
}
|
||||
if (extraBody) {
|
||||
// biome-ignore lint/style/useNamingConvention: api schema
|
||||
inference.extra_body = extraBody;
|
||||
}
|
||||
|
||||
return {
|
||||
alias: config.name,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue