mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-14 04:09:09 +00:00
feat(ui): replace Reset example with an Example preset dropdown and a Clear button on the Decisions tab (#45848)
Backport of #45848 to rc/1.106.0.
Cherry-picked from dc77381956 (main).
This commit is contained in:
parent
7318f83976
commit
ad4d9b1a92
4 changed files with 340 additions and 45 deletions
|
|
@ -71,6 +71,12 @@ const selectEndpoint = async (endpoint: "/v1/decisions" | "/v1/systemone") => {
|
|||
await userEvent.click(await screen.findByRole("option", { name: endpoint, exact: true }));
|
||||
};
|
||||
|
||||
const selectPreset = async (label: string) => {
|
||||
screen.getByRole("combobox", { name: "Example" }).focus();
|
||||
await userEvent.keyboard("{ArrowDown}");
|
||||
await userEvent.click(await screen.findByRole("option", { name: label }));
|
||||
};
|
||||
|
||||
const createResponse = (body: SystemOneResponse | OpenAIDecisionsResponse, status = 200, errorText = "") =>
|
||||
({
|
||||
ok: status >= 200 && status < 300,
|
||||
|
|
@ -99,20 +105,81 @@ describe("SystemOneUI integration", () => {
|
|||
document.cookie = "token=; path=/; max-age=0";
|
||||
});
|
||||
|
||||
it("restores the example after the request is edited", async () => {
|
||||
const user = userEvent.setup();
|
||||
it("switches between example presets and reads Custom once the request is edited", async () => {
|
||||
render(<SystemOneUI accessToken="session-key" />);
|
||||
const editor = await openJsonEditor();
|
||||
const example = (editor as HTMLTextAreaElement).value;
|
||||
const resetButton = screen.getByRole("button", { name: "Reset example" });
|
||||
expect(resetButton).toBeDisabled();
|
||||
const picker = screen.getByRole("combobox", { name: "Example" });
|
||||
expect(picker).toHaveTextContent("Triage a bug report");
|
||||
|
||||
fireEvent.change(editor, { target: { value: "{}" } });
|
||||
expect(resetButton).toBeEnabled();
|
||||
await user.click(resetButton);
|
||||
|
||||
expect(picker).toHaveTextContent("Custom");
|
||||
await selectPreset("Triage a bug report");
|
||||
expect(editor).toHaveValue(example);
|
||||
expect(resetButton).toBeDisabled();
|
||||
expect(picker).toHaveTextContent("Triage a bug report");
|
||||
|
||||
await selectPreset("Score a sales lead");
|
||||
expect(picker).toHaveTextContent("Score a sales lead");
|
||||
expect(JSON.parse((editor as HTMLTextAreaElement).value)).toMatchObject({ questions: { fit: { type: "score" } } });
|
||||
});
|
||||
|
||||
it("clears the whole request and lets a preset be picked from nothing", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SystemOneUI accessToken="session-key" />);
|
||||
const clear = screen.getByRole("button", { name: "Clear" });
|
||||
expect(screen.getByRole("textbox", { name: "Input" })).toHaveDisplayValue(/streaming responses/);
|
||||
|
||||
await user.click(clear);
|
||||
expect(screen.getByRole("textbox", { name: "Input" })).toHaveValue("");
|
||||
expect(screen.queryAllByRole("group", { name: /Question / })).toHaveLength(0);
|
||||
expect(screen.getByRole("button", { name: "Send" })).toBeDisabled();
|
||||
expect(clear).toBeDisabled();
|
||||
expect(screen.getByRole("combobox", { name: "Example" })).toHaveTextContent("Custom");
|
||||
|
||||
await selectPreset("Route a support ticket");
|
||||
expect(screen.getByRole("textbox", { name: "Input" })).toHaveDisplayValue(/charged twice/);
|
||||
expect(screen.getAllByRole("group", { name: /Question / })).toHaveLength(1);
|
||||
expect(screen.getByRole("button", { name: "Send" })).toBeEnabled();
|
||||
});
|
||||
|
||||
it("keeps the preset label after picking a model and leaves an omitted model out when clearing", async () => {
|
||||
mockModelLookup.mockResolvedValue(modelGroupInfoResponse(DECISION_AND_CHAT_MODELS));
|
||||
render(<SystemOneUI accessToken="session-key" />);
|
||||
await waitFor(() => expect(screen.getByRole("combobox", { name: "Decision model" })).toHaveValue("jev-latest"));
|
||||
const picker = screen.getByRole("combobox", { name: "Example" });
|
||||
const editor = await openJsonEditor();
|
||||
const example = JSON.parse((editor as HTMLTextAreaElement).value) as Record<string, unknown>;
|
||||
const { model: _model, ...withoutModel } = example;
|
||||
|
||||
fireEvent.change(editor, {
|
||||
target: { value: JSON.stringify({ ...withoutModel, model: "pplx-decider" }, null, 2) },
|
||||
});
|
||||
expect(picker).toHaveTextContent("Triage a bug report");
|
||||
|
||||
fireEvent.change(editor, { target: { value: JSON.stringify(withoutModel, null, 2) } });
|
||||
expect(picker).toHaveTextContent("Triage a bug report");
|
||||
await userEvent.click(screen.getByRole("button", { name: "Clear" }));
|
||||
expect(JSON.parse((editor as HTMLTextAreaElement).value)).toEqual({ state: "", questions: {} });
|
||||
await selectPreset("Score a sales lead");
|
||||
expect(JSON.parse((editor as HTMLTextAreaElement).value)).not.toHaveProperty("model");
|
||||
});
|
||||
|
||||
it("offers the same presets on /v1/decisions in the OpenAI shape and clears to an empty input", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SystemOneUI accessToken="session-key" />);
|
||||
await selectEndpoint("/v1/decisions");
|
||||
await selectPreset("Moderate a forum comment");
|
||||
await user.click(screen.getByRole("tab", { name: "JSON" }));
|
||||
const editor = screen.getByRole("textbox", { name: "Decisions JSON payload" }) as HTMLTextAreaElement;
|
||||
const payload = JSON.parse(editor.value);
|
||||
expect(payload.input).toMatch(/stream=false/);
|
||||
expect(payload.questions).toEqual([
|
||||
{ type: "predicate", name: "breaks_rules", instructions: "Does this comment break the community guidelines?" },
|
||||
]);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Clear" }));
|
||||
expect(JSON.parse(editor.value)).toEqual({ input: "", questions: [] });
|
||||
expect(screen.getByRole("button", { name: "Send" })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("offers both literal proxy endpoints without TypeSafe or product prefixes", async () => {
|
||||
|
|
@ -227,7 +294,7 @@ describe("SystemOneUI integration", () => {
|
|||
|
||||
await selectEndpoint("/v1/decisions");
|
||||
expect(screen.getByRole("textbox", { name: "Decisions JSON payload" })).toHaveValue(JSON.stringify(payload));
|
||||
await user.click(screen.getByRole("button", { name: "Reset example" }));
|
||||
await selectPreset("Triage a bug report");
|
||||
expect(screen.getByRole("textbox", { name: "Decisions JSON payload" })).toHaveValue(nativeExample);
|
||||
await selectEndpoint("/v1/systemone");
|
||||
expect(screen.getByRole("textbox", { name: "System One JSON payload" })).toHaveValue(systemOneDraft);
|
||||
|
|
@ -403,7 +470,7 @@ describe("SystemOneUI integration", () => {
|
|||
);
|
||||
expect(next).toEqual({ ...payload, questions: [{ ...payload.questions[0], instructions: "Is it urgent?" }] });
|
||||
await user.click(screen.getByRole("tab", { name: "Form" }));
|
||||
await user.click(screen.getByRole("button", { name: "Reset example" }));
|
||||
await selectPreset("Triage a bug report");
|
||||
expect(screen.getByRole("textbox", { name: "Input" })).toBeInTheDocument();
|
||||
expect(screen.getAllByRole("group", { name: /Question \d/ })).toHaveLength(3);
|
||||
await selectEndpoint("/v1/systemone");
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import { SearchSelect } from "@/components/shared/SearchSelect";
|
|||
import { DECISIONS_DOCS_URL } from "@/lib/decisionModels";
|
||||
import { uiHref } from "@/utils/uiHref";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Code, Info, LoaderCircle, RotateCcw, Send } from "lucide-react";
|
||||
import { Code, Eraser, Info, LoaderCircle, Send } from "lucide-react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { makePlaygroundDecisionRequest, type PlaygroundDecisionRequest } from "../../llm_calls/system_one";
|
||||
import { decisionsExample, openAIDecisionsExample } from "./lib/example";
|
||||
import { DECISION_PRESETS, emptyPayload, matchPreset, presetPayload, type DecisionPreset } from "./lib/example";
|
||||
import { payloadModel, withPayloadModel } from "./lib/payloadModel";
|
||||
import JsonEditor from "./JsonEditor";
|
||||
import QuestionBreakdown from "./QuestionBreakdown";
|
||||
|
|
@ -35,8 +35,36 @@ interface SystemOneSendVariables {
|
|||
signal: AbortSignal;
|
||||
}
|
||||
|
||||
const CUSTOM_PRESET = "custom";
|
||||
const DECISION_MODELS_DISCUSSION_URL = "https://github.com/BerriAI/litellm/discussions/44231";
|
||||
|
||||
interface PresetPickerProps {
|
||||
activePreset: DecisionPreset | undefined;
|
||||
onPick: (id: string | null) => void;
|
||||
}
|
||||
|
||||
function PresetPicker({ activePreset, onPick }: PresetPickerProps) {
|
||||
return (
|
||||
<Select value={activePreset?.id ?? CUSTOM_PRESET} onValueChange={onPick}>
|
||||
<SelectTrigger className="w-56" aria-label="Example">
|
||||
<SelectValue>{activePreset?.label ?? "Custom"}</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{activePreset === undefined && (
|
||||
<SelectItem value={CUSTOM_PRESET} disabled>
|
||||
Custom
|
||||
</SelectItem>
|
||||
)}
|
||||
{DECISION_PRESETS.map((preset) => (
|
||||
<SelectItem key={preset.id} value={preset.id}>
|
||||
{preset.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
function getCustomProxyBaseUrl(): string | undefined {
|
||||
return typeof window === "undefined" ? undefined : window.sessionStorage.getItem("customProxyBaseUrl") || undefined;
|
||||
}
|
||||
|
|
@ -53,9 +81,10 @@ export default function SystemOneUI({ accessToken, disabledPersonalKeyCreation =
|
|||
const [endpoint, setEndpoint] = useState<PlaygroundDecisionRequest["endpoint"]>("/v1/systemone");
|
||||
const [drafts, setDrafts] = useState<Partial<Record<PlaygroundDecisionRequest["endpoint"], string>>>({});
|
||||
const [view, setView] = useState<EditorView>("form");
|
||||
const example = endpoint === "/v1/decisions" ? openAIDecisionsExample : decisionsExample;
|
||||
const examplePayload = JSON.stringify(example(decisionModels[0]), null, 2);
|
||||
const rawPayload = drafts[endpoint] ?? examplePayload;
|
||||
const rawPayload = drafts[endpoint] ?? presetPayload(DECISION_PRESETS[0], endpoint, decisionModels[0]);
|
||||
const draftModel = drafts[endpoint] === undefined ? decisionModels[0] : payloadModel(rawPayload);
|
||||
const activePreset = matchPreset(endpoint, rawPayload);
|
||||
const clearedPayload = emptyPayload(endpoint, draftModel);
|
||||
const activeController = useRef<AbortController | null>(null);
|
||||
const validated = useMemo(
|
||||
() =>
|
||||
|
|
@ -95,9 +124,11 @@ export default function SystemOneUI({ accessToken, disabledPersonalKeyCreation =
|
|||
}
|
||||
}
|
||||
|
||||
function handleResetExample() {
|
||||
clearRequestState();
|
||||
setDrafts((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== endpoint)));
|
||||
function handlePresetPick(id: string | null) {
|
||||
const preset = DECISION_PRESETS.find((candidate) => candidate.id === id);
|
||||
if (preset !== undefined) {
|
||||
handlePayloadChange(presetPayload(preset, endpoint, draftModel));
|
||||
}
|
||||
}
|
||||
|
||||
function handleModelPick(model: string | null) {
|
||||
|
|
@ -204,9 +235,14 @@ export default function SystemOneUI({ accessToken, disabledPersonalKeyCreation =
|
|||
)}
|
||||
</div>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Button variant="outline" onClick={handleResetExample} disabled={rawPayload === examplePayload}>
|
||||
<RotateCcw />
|
||||
Reset example
|
||||
<PresetPicker activePreset={activePreset} onPick={handlePresetPick} />
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handlePayloadChange(clearedPayload)}
|
||||
disabled={rawPayload === clearedPayload}
|
||||
>
|
||||
<Eraser />
|
||||
Clear
|
||||
</Button>
|
||||
{view === "json" && (
|
||||
<Button variant="outline" onClick={handleFormatJson} disabled={!rawPayload.trim() || hasSyntaxError}>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
DECISION_PRESETS,
|
||||
emptyPayload,
|
||||
openAIDecisionsExample,
|
||||
presetPayload,
|
||||
toOpenAIDecisionsRequest,
|
||||
matchPreset,
|
||||
} from "./example";
|
||||
import { validateOpenAIDecisionsPayload, validateSystemOnePayload } from "./validatePayload";
|
||||
|
||||
describe("decision presets", () => {
|
||||
it.each(DECISION_PRESETS.map((preset) => [preset.label, preset] as const))(
|
||||
"%s is a valid request on both endpoints",
|
||||
(_label, preset) => {
|
||||
expect(
|
||||
validateSystemOnePayload(presetPayload(preset, "/v1/systemone", "jev-latest"), "/v1/systemone"),
|
||||
).toMatchObject({ isValid: true, issues: [] });
|
||||
expect(validateOpenAIDecisionsPayload(presetPayload(preset, "/v1/decisions", "jev-latest"))).toMatchObject({
|
||||
isValid: true,
|
||||
issues: [],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("covers every question type across the presets", () => {
|
||||
const types = new Set(
|
||||
DECISION_PRESETS.flatMap((preset) => Object.values(preset.request.questions).map((question) => question.type)),
|
||||
);
|
||||
expect([...types].sort()).toEqual(["choice", "noul", "score"]);
|
||||
});
|
||||
|
||||
it("maps System One questions onto the OpenAI shape by name", () => {
|
||||
const request = toOpenAIDecisionsRequest(DECISION_PRESETS[0].request, "jev-latest");
|
||||
expect(request.questions.map((question) => [question.type, question.name])).toEqual([
|
||||
["choice", "area"],
|
||||
["predicate", "has_repro_steps"],
|
||||
["score", "severity"],
|
||||
]);
|
||||
expect(request).toEqual(openAIDecisionsExample("jev-latest"));
|
||||
});
|
||||
|
||||
it("recognises a preset whatever the model key holds or where it sits", () => {
|
||||
const lead = DECISION_PRESETS.find((preset) => preset.id === "lead-scoring");
|
||||
const parsed = JSON.parse(presetPayload(lead!, "/v1/systemone", "jev-latest")) as Record<string, unknown>;
|
||||
const { model: _model, ...rest } = parsed;
|
||||
expect(matchPreset("/v1/systemone", JSON.stringify({ ...rest, model: "pplx-decider" }))).toBe(lead);
|
||||
expect(matchPreset("/v1/systemone", JSON.stringify(rest))).toBe(lead);
|
||||
expect(matchPreset("/v1/decisions", presetPayload(lead!, "/v1/decisions"))).toBe(lead);
|
||||
expect(matchPreset("/v1/decisions", presetPayload(lead!, "/v1/systemone"))).toBeUndefined();
|
||||
expect(matchPreset("/v1/systemone", JSON.stringify({ ...rest, state: "edited" }))).toBeUndefined();
|
||||
expect(matchPreset("/v1/systemone", "{")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("omits the model from payloads when none is known", () => {
|
||||
expect(JSON.parse(presetPayload(DECISION_PRESETS[1], "/v1/systemone"))).not.toHaveProperty("model");
|
||||
expect(JSON.parse(emptyPayload("/v1/decisions"))).toEqual({ input: "", questions: [] });
|
||||
expect(validateSystemOnePayload(emptyPayload("/v1/systemone"), "/v1/systemone").isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,39 @@
|
|||
import type { DecisionRequest, SystemOneRequest } from "./schemas";
|
||||
import type { OpenAIDecisionsRequest } from "./openAIDecisions";
|
||||
import { parseJson } from "./validatePayload";
|
||||
|
||||
type PresetChoice = {
|
||||
type: "choice";
|
||||
instructions: string;
|
||||
criteria: Record<string, string>;
|
||||
};
|
||||
|
||||
type PresetNoul = {
|
||||
type: "noul";
|
||||
instructions: string;
|
||||
criteria?: { true: string; false: string };
|
||||
};
|
||||
|
||||
type PresetScore = {
|
||||
type: "score";
|
||||
instructions: string;
|
||||
criteria: string[];
|
||||
};
|
||||
|
||||
export type PresetQuestion = PresetChoice | PresetNoul | PresetScore;
|
||||
|
||||
export type PresetRequest = {
|
||||
state: string;
|
||||
questions: Record<string, PresetQuestion>;
|
||||
};
|
||||
|
||||
type PresetEndpoint = "/v1/decisions" | "/v1/systemone";
|
||||
|
||||
export interface DecisionPreset {
|
||||
id: string;
|
||||
label: string;
|
||||
request: PresetRequest;
|
||||
}
|
||||
|
||||
export const SYSTEM_ONE_EXAMPLE = {
|
||||
model: "jev-latest",
|
||||
|
|
@ -38,34 +72,132 @@ export const SYSTEM_ONE_EXAMPLE = {
|
|||
},
|
||||
} satisfies SystemOneRequest;
|
||||
|
||||
export const decisionsExample = (model?: string): DecisionRequest => ({ ...SYSTEM_ONE_EXAMPLE, model });
|
||||
const { model: _exampleModel, ...bugTriage }: SystemOneRequest & PresetRequest = SYSTEM_ONE_EXAMPLE;
|
||||
|
||||
export const openAIDecisionsExample = (model?: string): OpenAIDecisionsRequest => ({
|
||||
model,
|
||||
input: SYSTEM_ONE_EXAMPLE.state,
|
||||
questions: [
|
||||
{
|
||||
const supportRouting = {
|
||||
state:
|
||||
"Hi, I was charged twice for my team plan this month and the second charge pushed my card over its limit. I need the duplicate refunded today and want to know why it happened.",
|
||||
questions: {
|
||||
department: {
|
||||
type: "choice",
|
||||
name: "area",
|
||||
instructions: SYSTEM_ONE_EXAMPLE.questions.area.instructions,
|
||||
choices: Object.entries(SYSTEM_ONE_EXAMPLE.questions.area.criteria).map(([value, description]) => ({
|
||||
value,
|
||||
description,
|
||||
})),
|
||||
instructions: "Which team should handle this ticket?",
|
||||
criteria: {
|
||||
billing: "Invoices, charges, refunds, and plan changes",
|
||||
technical: "Bugs, errors, outages, and integration help",
|
||||
account: "Login, permissions, and profile settings",
|
||||
sales: "Pricing questions and new purchases",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "predicate",
|
||||
name: "has_repro_steps",
|
||||
instructions: SYSTEM_ONE_EXAMPLE.questions.has_repro_steps.instructions,
|
||||
},
|
||||
} satisfies PresetRequest;
|
||||
|
||||
const moderation = {
|
||||
state:
|
||||
"This guide is garbage and whoever wrote it should be fired. The fix that actually works is setting stream=false, took me two days to figure that out.",
|
||||
questions: {
|
||||
breaks_rules: {
|
||||
type: "noul",
|
||||
instructions: "Does this comment break the community guidelines?",
|
||||
criteria: {
|
||||
true: "Insults, harassment, slurs, or threats aimed at a person",
|
||||
false: "Blunt or frustrated but still about the content",
|
||||
},
|
||||
},
|
||||
{
|
||||
},
|
||||
} satisfies PresetRequest;
|
||||
|
||||
const leadScoring = {
|
||||
state:
|
||||
"Inbound form: VP Engineering at a 400 person fintech. Running a self hosted gateway today, evaluating replacements this quarter, budget approved, needs SSO and audit logs.",
|
||||
questions: {
|
||||
fit: {
|
||||
type: "score",
|
||||
name: "severity",
|
||||
instructions: SYSTEM_ONE_EXAMPLE.questions.severity.instructions,
|
||||
levels: SYSTEM_ONE_EXAMPLE.questions.severity.criteria.map((description, index) => ({
|
||||
label: String(index),
|
||||
description,
|
||||
})),
|
||||
instructions: "How well does this lead fit our ideal customer?",
|
||||
criteria: [
|
||||
"No fit, wrong market or no budget",
|
||||
"Weak fit, early curiosity only",
|
||||
"Possible fit, needs discovery",
|
||||
"Strong fit, active evaluation",
|
||||
"Ideal fit, ready to buy this quarter",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
} satisfies PresetRequest;
|
||||
|
||||
export const DECISION_PRESETS: readonly DecisionPreset[] = [
|
||||
{ id: "bug-triage", label: "Triage a bug report", request: bugTriage },
|
||||
{ id: "support-routing", label: "Route a support ticket", request: supportRouting },
|
||||
{ id: "moderation", label: "Moderate a forum comment", request: moderation },
|
||||
{ id: "lead-scoring", label: "Score a sales lead", request: leadScoring },
|
||||
];
|
||||
|
||||
const toOpenAIQuestion = (name: string, question: PresetQuestion): OpenAIDecisionsRequest["questions"][number] => {
|
||||
switch (question.type) {
|
||||
case "choice":
|
||||
return {
|
||||
type: "choice",
|
||||
name,
|
||||
instructions: question.instructions,
|
||||
choices: Object.entries(question.criteria).map(([value, description]) => ({ value, description })),
|
||||
};
|
||||
case "noul":
|
||||
return { type: "predicate", name, instructions: question.instructions };
|
||||
case "score":
|
||||
return {
|
||||
type: "score",
|
||||
name,
|
||||
instructions: question.instructions,
|
||||
levels: question.criteria.map((description, index) => ({ label: String(index), description })),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const toSystemOneRequest = (request: PresetRequest, model?: string): DecisionRequest => ({ model, ...request });
|
||||
|
||||
export const toOpenAIDecisionsRequest = (request: PresetRequest, model?: string): OpenAIDecisionsRequest => ({
|
||||
model,
|
||||
input: request.state,
|
||||
questions: Object.entries(request.questions).map(([name, question]) => toOpenAIQuestion(name, question)),
|
||||
});
|
||||
|
||||
export const presetPayload = (preset: DecisionPreset, endpoint: PresetEndpoint, model?: string): string =>
|
||||
JSON.stringify(
|
||||
endpoint === "/v1/decisions"
|
||||
? toOpenAIDecisionsRequest(preset.request, model)
|
||||
: toSystemOneRequest(preset.request, model),
|
||||
null,
|
||||
2,
|
||||
);
|
||||
|
||||
const stableJson = (value: unknown): string =>
|
||||
JSON.stringify(value, (_key, item: unknown) =>
|
||||
item !== null && typeof item === "object" && !Array.isArray(item)
|
||||
? Object.fromEntries(Object.entries(item as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b)))
|
||||
: item,
|
||||
);
|
||||
|
||||
const withoutModel = (value: unknown): string => {
|
||||
const { model: _model, ...rest } = (value ?? {}) as Record<string, unknown>;
|
||||
return stableJson(rest);
|
||||
};
|
||||
|
||||
export const matchPreset = (endpoint: PresetEndpoint, raw: string): DecisionPreset | undefined => {
|
||||
const json = parseJson(raw);
|
||||
if (!json.ok) {
|
||||
return undefined;
|
||||
}
|
||||
const current = withoutModel(json.value);
|
||||
return DECISION_PRESETS.find((preset) => withoutModel(JSON.parse(presetPayload(preset, endpoint))) === current);
|
||||
};
|
||||
|
||||
export const emptyPayload = (endpoint: PresetEndpoint, model?: string): string =>
|
||||
JSON.stringify(
|
||||
endpoint === "/v1/decisions" ? { model, input: "", questions: [] } : { model, state: "", questions: {} },
|
||||
null,
|
||||
2,
|
||||
);
|
||||
|
||||
export const decisionsExample = (model?: string): DecisionRequest => toSystemOneRequest(bugTriage, model);
|
||||
|
||||
export const openAIDecisionsExample = (model?: string): OpenAIDecisionsRequest =>
|
||||
toOpenAIDecisionsRequest(bugTriage, model);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue