mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
feat(ui): let an edited tier set write its own classifier opening instructions
The classifier prompt control was replaced by a sentence in custom mode, so an edited tier set could not steer its classifier at all. That control edits classifier_llm_config.system_prompt, a whole-prompt replacement the backend rejects beside tier_definitions, so refusing it there is right. classification_prompt is the field that fits: the router uses it as the opening of the prompt and appends the tier bullets and the injection guard after it, so an operator can state their own instructions and calibration examples without being able to restate or remove either. It has always been accepted beside tier_definitions and the dashboard just never exposed it. Custom mode now gets its own editor for that field, showing the tier bullets it will append underneath. Built-in routers keep the whole-prompt editor. The classifierPrompt restriction goes away with the control it described, and the Restricted helpers move out of ComplexityRouterConfig, which was at its max-lines ceiling.
This commit is contained in:
parent
bbd45bf37d
commit
8921bfb0fc
11 changed files with 232 additions and 28 deletions
|
|
@ -10,6 +10,8 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|||
import { Switch } from "@/components/ui/switch";
|
||||
import React from "react";
|
||||
import ClassifierPromptEditor from "./ClassifierPromptEditor";
|
||||
import ClassificationPromptEditor from "./ClassificationPromptEditor";
|
||||
import { Restricted, restrictedBy } from "./TierRestrictions";
|
||||
import HeuristicScoringConfig from "./HeuristicScoringConfig";
|
||||
import { useComplexityScorerDefaults } from "@/app/(dashboard)/hooks/autoRouter/useComplexityScorerDefaults";
|
||||
import {
|
||||
|
|
@ -32,9 +34,6 @@ import {
|
|||
DEFAULT_HEURISTIC_FIRST_MAX_TIER,
|
||||
HEURISTIC_FIRST_MAX_TIER_KEYS,
|
||||
effectiveClassifierType,
|
||||
heuristicScoringRole,
|
||||
restrictedBy,
|
||||
Restricted,
|
||||
} from "./ComplexityRouterConfig";
|
||||
|
||||
const DEFAULT_SCORING_EXPLANATION =
|
||||
|
|
@ -202,6 +201,10 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
onChange({ ...value, heuristic_first_max_tier: tier });
|
||||
};
|
||||
|
||||
const handleClassificationPromptChange = (classificationPrompt: string | undefined) => {
|
||||
onChange({ ...value, classification_prompt: classificationPrompt });
|
||||
};
|
||||
|
||||
const handleClassifierModelChange = (model: string) => {
|
||||
onChange({
|
||||
...value,
|
||||
|
|
@ -414,7 +417,13 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
</div>
|
||||
<div>
|
||||
<strong className="block mb-1 font-semibold">Classifier Prompt</strong>
|
||||
<Restricted by={restrictedBy(value, "classifierPrompt")}>
|
||||
{value.custom_tier_set ? (
|
||||
<ClassificationPromptEditor
|
||||
classificationPrompt={value.classification_prompt}
|
||||
onChange={handleClassificationPromptChange}
|
||||
tierRows={value.custom_tier_set.tiers}
|
||||
/>
|
||||
) : (
|
||||
<ClassifierPromptEditor
|
||||
systemPrompt={value.classifier_llm_config?.system_prompt}
|
||||
onChange={handleClassifierSystemPromptChange}
|
||||
|
|
@ -422,7 +431,7 @@ const ClassificationMethodConfig: React.FC<ClassificationMethodConfigProps> = ({
|
|||
tierLabels={value.tier_labels}
|
||||
classificationRubric={classificationRubric}
|
||||
/>
|
||||
</Restricted>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<strong className="block mb-1 font-semibold">If the classifier fails</strong>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
import React, { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { TierRow, activeTierName } from "./tier_rows";
|
||||
|
||||
interface ClassificationPromptEditorProps {
|
||||
classificationPrompt: string | undefined;
|
||||
onChange: (classificationPrompt: string | undefined) => void;
|
||||
tierRows: readonly TierRow[];
|
||||
}
|
||||
|
||||
const PLACEHOLDER = `Classify the request into exactly one tier for a payments engineering team.
|
||||
|
||||
Examples:
|
||||
- "bump the copy on the checkout button" -> TRIAGE
|
||||
- "why is our webhook signature check failing" -> SECURITY_REVIEW`;
|
||||
|
||||
const appendedTierBullets = (tierRows: readonly TierRow[]): string =>
|
||||
tierRows.map((row) => `- ${activeTierName(row)}: ${row.definition}`).join("\n");
|
||||
|
||||
const ClassificationPromptEditor: React.FC<ClassificationPromptEditorProps> = ({
|
||||
classificationPrompt,
|
||||
onChange,
|
||||
tierRows,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [draft, setDraft] = useState("");
|
||||
const isOverridden = Boolean(classificationPrompt?.trim());
|
||||
|
||||
const openEditor = () => {
|
||||
setDraft(classificationPrompt ?? "");
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
onChange(draft.trim() || undefined);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button type="button" size="sm" variant="outline" onClick={openEditor}>
|
||||
{isOverridden ? "Edit opening instructions" : "Change default prompt"}
|
||||
</Button>
|
||||
{isOverridden && (
|
||||
<Button type="button" size="sm" variant="link" onClick={() => onChange(undefined)}>
|
||||
Reset to default
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{isOverridden
|
||||
? "This router opens its classifier prompt with your own instructions."
|
||||
: "Replace the opening instructions, and write calibration examples of your own. Your tier definitions and the injection guard are always appended below them."}
|
||||
</p>
|
||||
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Classifier prompt</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Your text is the opening of the classifier prompt, so it is where calibration examples of your own belong.
|
||||
The router appends your tier definitions and its injection guard underneath, and neither can be edited or
|
||||
removed from here. Edit the definitions themselves with Edit tiers above.
|
||||
</p>
|
||||
|
||||
<Textarea
|
||||
value={draft}
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
rows={12}
|
||||
placeholder={PLACEHOLDER}
|
||||
aria-label="Classifier opening instructions"
|
||||
className="mt-3 font-mono text-xs"
|
||||
/>
|
||||
|
||||
<div className="mt-3">
|
||||
<p className="text-xs font-medium">Always appended below your text</p>
|
||||
<pre className="mt-1 overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-muted-foreground">
|
||||
{`Tiers:\n${appendedTierBullets(tierRows)}\n\n<injection guard and closing line>`}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="mt-4">
|
||||
<Button type="button" variant="outline" onClick={() => setIsOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="button" onClick={handleSave}>
|
||||
Save prompt
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClassificationPromptEditor;
|
||||
|
|
@ -1298,6 +1298,26 @@ describe("ComplexityRouterConfig tier editing", () => {
|
|||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("lets an edited tier set write its own opening instructions instead of refusing a prompt outright", () => {
|
||||
renderWithProviders(<ComplexityRouterConfig {...baseProps} value={customValue} onEditingTiersChange={vi.fn()} />);
|
||||
fireEvent.click(screen.getByText("Advanced: Classification Method"));
|
||||
expect(screen.getByText("write calibration examples of your own", { exact: false })).toBeInTheDocument();
|
||||
expect(screen.queryByText("A replacement prompt drops the tier bullets", { exact: false })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps the whole-prompt replacement editor on built-in routers, which the backend still accepts there", () => {
|
||||
renderWithProviders(
|
||||
<ComplexityRouterConfig
|
||||
{...baseProps}
|
||||
value={{ ...defaultValue, classifier_type: "llm", classifier_llm_config: { model: "gpt-4", timeout_ms: 3000 } }}
|
||||
onEditingTiersChange={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByText("Advanced: Classification Method"));
|
||||
expect(screen.getByText("Replace the built-in complexity rubric", { exact: false })).toBeInTheDocument();
|
||||
expect(screen.queryByText("write calibration examples of your own", { exact: false })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("leaves built-in routers with their display-name inputs and no restriction copy", () => {
|
||||
renderWithProviders(<ComplexityRouterConfig {...baseProps} onEditingTiersChange={vi.fn()} />);
|
||||
expect(screen.getByLabelText("Display name for the Simple tier")).toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -13,11 +13,9 @@ import { Input } from "@/components/ui/input";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
type CustomTierSet,
|
||||
type TierRestriction,
|
||||
type ActiveTierRow,
|
||||
type TierRow,
|
||||
rowParamsByTier,
|
||||
CUSTOM_TIER_RESTRICTIONS,
|
||||
MAX_TIER_COUNT,
|
||||
MAX_TIER_DEFINITION_CHARS,
|
||||
MAX_TIER_NAME_CHARS,
|
||||
|
|
@ -37,6 +35,7 @@ import React from "react";
|
|||
import { ModelGroup } from "@/components/llm_calls/fetch_models";
|
||||
import AdaptiveRoutingConfig from "./AdaptiveRoutingConfig";
|
||||
import ClassificationMethodConfig from "./ClassificationMethodConfig";
|
||||
import { Restricted, restrictedBy } from "./TierRestrictions";
|
||||
import {
|
||||
REASONING_EFFORT_OPTIONS,
|
||||
ReasoningEffort,
|
||||
|
|
@ -166,16 +165,6 @@ export const effectiveClassifierType = (
|
|||
value: Pick<ComplexityRouterConfigValue, "custom_tier_set" | "classifier_type">,
|
||||
): ClassifierType => (value.custom_tier_set ? "llm" : value.classifier_type);
|
||||
|
||||
export const restrictedBy = (
|
||||
value: Pick<ComplexityRouterConfigValue, "custom_tier_set">,
|
||||
key: keyof typeof CUSTOM_TIER_RESTRICTIONS,
|
||||
): TierRestriction | undefined => (value.custom_tier_set ? CUSTOM_TIER_RESTRICTIONS[key] : undefined);
|
||||
|
||||
export const Restricted: React.FC<{ by: TierRestriction | undefined; children: React.ReactNode }> = ({
|
||||
by,
|
||||
children,
|
||||
}) => (by ? <span className="block text-sm text-muted-foreground">{by.reason}</span> : <>{children}</>);
|
||||
|
||||
const rowOrigin = (row: TierRow, editing: boolean): string => {
|
||||
if (!editing) return row.id;
|
||||
return isBuiltInTierName(row.name) ? "built-in" : "custom";
|
||||
|
|
@ -219,6 +208,8 @@ export interface ComplexityRouterConfigValue {
|
|||
classifier_context_per_turn_chars?: number;
|
||||
classifier_context_include_assistant_turns?: boolean;
|
||||
classifier_fallback?: ClassifierFallback;
|
||||
/** Opening instructions only; the router appends the tier bullets and the injection guard after them. */
|
||||
classification_prompt?: string;
|
||||
/** Highest tier the scorer may decide alone under heuristic_first. Required by that type, rejected by the others. */
|
||||
heuristic_first_max_tier?: string;
|
||||
session_affinity?: boolean;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react";
|
||||
import { CUSTOM_TIER_RESTRICTIONS, CustomTierSet, TierRestriction } from "./tier_rows";
|
||||
|
||||
export const restrictedBy = (
|
||||
value: { custom_tier_set?: CustomTierSet },
|
||||
key: keyof typeof CUSTOM_TIER_RESTRICTIONS,
|
||||
): TierRestriction | undefined => (value.custom_tier_set ? CUSTOM_TIER_RESTRICTIONS[key] : undefined);
|
||||
|
||||
export const Restricted: React.FC<{ by: TierRestriction | undefined; children: React.ReactNode }> = ({
|
||||
by,
|
||||
children,
|
||||
}) => (by ? <span className="block text-sm text-muted-foreground">{by.reason}</span> : <>{children}</>);
|
||||
|
|
@ -339,6 +339,7 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
|
|||
customTierSet: complexityRouterConfig.custom_tier_set,
|
||||
defaultModel: complexityRouterConfig.default_model,
|
||||
planModeMinTier: complexityRouterConfig.plan_mode_min_tier,
|
||||
classificationPrompt: complexityRouterConfig.classification_prompt,
|
||||
heuristicFirstMaxTier: complexityRouterConfig.heuristic_first_max_tier,
|
||||
tierLabels: complexityRouterConfig.tier_labels,
|
||||
classifierType: complexityRouterConfig.classifier_type,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const baseParams: BuildComplexityRouterConfigParams = {
|
|||
classifierContextWindowSize: undefined,
|
||||
classifierContextBudgetChars: undefined,
|
||||
classifierContextIncludeAssistantTurns: undefined,
|
||||
classificationPrompt: undefined,
|
||||
classifierFallback: undefined,
|
||||
sessionAffinity: false,
|
||||
deploymentAffinity: true,
|
||||
|
|
@ -811,6 +812,25 @@ describe("buildComplexityRouterConfig with an edited tier set", () => {
|
|||
expect(build({ sessionAffinity: true }).session_affinity).toBe(false);
|
||||
});
|
||||
|
||||
it("writes the operator's opening instructions, trimmed, as classification_prompt", () => {
|
||||
expect(build({ classificationPrompt: " Route for a payments team.\n\nExamples:\n- x -> CASUAL " })).toMatchObject(
|
||||
{ classification_prompt: "Route for a payments team.\n\nExamples:\n- x -> CASUAL" },
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps classification_prompt out of the payload when the operator wrote only whitespace", () => {
|
||||
expect(build({ classificationPrompt: " \n " })).not.toHaveProperty("classification_prompt");
|
||||
});
|
||||
|
||||
it("never writes classification_prompt on a built-in router, which the backend rejects without tier_definitions", () => {
|
||||
const payload = buildComplexityRouterConfig({
|
||||
...baseParams,
|
||||
classifierType: "llm",
|
||||
classificationPrompt: "opening instructions",
|
||||
});
|
||||
expect(payload).not.toHaveProperty("classification_prompt");
|
||||
});
|
||||
|
||||
it("omits a definition on a built-in name, letting the backend rubric supply it", () => {
|
||||
const payload = build({
|
||||
customTierSet: {
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ export interface BuildComplexityRouterConfigParams {
|
|||
classifierContextBudgetChars: number | undefined;
|
||||
classifierContextIncludeAssistantTurns: boolean | undefined;
|
||||
classifierFallback: ClassifierFallback | undefined;
|
||||
classificationPrompt: string | undefined;
|
||||
heuristicFirstMaxTier: string | undefined;
|
||||
sessionAffinity: boolean;
|
||||
deploymentAffinity: boolean;
|
||||
|
|
@ -142,6 +143,7 @@ export interface ComplexityRouterConfigPayload {
|
|||
classifier_context_per_turn_chars?: number;
|
||||
classifier_context_include_assistant_turns?: boolean;
|
||||
classifier_fallback?: ClassifierFallback;
|
||||
classification_prompt?: string;
|
||||
heuristic_first_max_tier?: string;
|
||||
session_affinity: boolean;
|
||||
deployment_affinity: boolean;
|
||||
|
|
@ -257,6 +259,7 @@ export const customTierWireFields = (
|
|||
customTierSet: CustomTierSet,
|
||||
classifierLlmConfig: ClassifierLLMConfig | undefined,
|
||||
planModeMinTierId: string | undefined,
|
||||
classificationPrompt: string | undefined,
|
||||
): Partial<ComplexityRouterConfigPayload> => {
|
||||
const rows = customTierSet.tiers;
|
||||
const fallback = tierRowById(rows, customTierSet.fallback_tier_id);
|
||||
|
|
@ -269,13 +272,14 @@ export const customTierWireFields = (
|
|||
})),
|
||||
...(fallback && { fallback_tier: activeTierName(fallback) }),
|
||||
classifier_type: "llm",
|
||||
// Rebuilt from the two fields an edited tier set allows: system_prompt and
|
||||
// classification_rubric are the classifierPrompt and classificationRubric restrictions, and
|
||||
// both live inside this object rather than at the top level the omit list covers.
|
||||
// Rebuilt from the two fields an edited tier set allows. The backend rejects system_prompt and
|
||||
// classification_rubric beside tier_definitions, and both live inside this object rather than at
|
||||
// the top level the omit list covers. The opening instructions ride classification_prompt below.
|
||||
...(classifierLlmConfig && {
|
||||
classifier_llm_config: { model: classifierLlmConfig.model, timeout_ms: classifierLlmConfig.timeout_ms },
|
||||
}),
|
||||
session_affinity: false,
|
||||
...(classificationPrompt?.trim() && { classification_prompt: classificationPrompt.trim() }),
|
||||
...(floor && { plan_mode_min_tier: activeTierName(floor) }),
|
||||
};
|
||||
};
|
||||
|
|
@ -335,6 +339,7 @@ export const buildComplexityRouterConfig = ({
|
|||
classifierContextBudgetChars,
|
||||
classifierContextIncludeAssistantTurns,
|
||||
classifierFallback,
|
||||
classificationPrompt,
|
||||
heuristicFirstMaxTier,
|
||||
sessionAffinity,
|
||||
deploymentAffinity,
|
||||
|
|
@ -425,5 +430,8 @@ export const buildComplexityRouterConfig = ({
|
|||
const kept = Object.fromEntries(
|
||||
Object.entries(payload).filter(([key]) => !CUSTOM_TIER_STRIPPED_KEYS.includes(key)),
|
||||
) as ComplexityRouterConfigPayload;
|
||||
return { ...kept, ...customTierWireFields(customTierSet, classifierLlmConfig, planModeMinTier) };
|
||||
return {
|
||||
...kept,
|
||||
...customTierWireFields(customTierSet, classifierLlmConfig, planModeMinTier, classificationPrompt),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -143,10 +143,6 @@ export const CUSTOM_TIER_RESTRICTIONS = {
|
|||
],
|
||||
reason: "The heuristic scorer never runs under an edited tier set, so its inputs have no effect",
|
||||
},
|
||||
classifierPrompt: {
|
||||
omit: [],
|
||||
reason: "A replacement prompt drops the tier bullets and the injection guard. Your definitions are the rubric",
|
||||
},
|
||||
classificationRubric: {
|
||||
omit: [],
|
||||
reason: "The preset calibration examples are written against the built-in tiers, which your tier set replaces",
|
||||
|
|
|
|||
|
|
@ -476,9 +476,10 @@ describe("managed keys survive an untouched open-and-save", () => {
|
|||
reasoning_override_min_score: 0.3,
|
||||
};
|
||||
|
||||
// tier_definitions and fallback_tier cannot sit beside heuristic_first, which this fixture uses, so
|
||||
// no single stored config can hold every managed key. They get their own round trip below.
|
||||
const CUSTOM_TIER_ONLY_KEYS = new Set(["tier_definitions", "fallback_tier"]);
|
||||
// tier_definitions, fallback_tier and classification_prompt cannot sit beside heuristic_first, which
|
||||
// this fixture uses, so no single stored config can hold every managed key. They get their own round
|
||||
// trip below.
|
||||
const CUSTOM_TIER_ONLY_KEYS = new Set(["tier_definitions", "fallback_tier", "classification_prompt"]);
|
||||
|
||||
it("carries every managed key a built-in router can hold through hydrate then save", () => {
|
||||
const hydrated = hydrateComplexityRouterConfig(STORED_ALL_MANAGED, undefined);
|
||||
|
|
@ -528,6 +529,44 @@ describe("managed keys survive an untouched open-and-save", () => {
|
|||
expect(saved.tiers).toEqual(storedCustom.tiers);
|
||||
});
|
||||
|
||||
it("clears a stored classification_prompt when the operator resets it, rather than preserving it as an unowned key", () => {
|
||||
const storedCustom = {
|
||||
tiers: { CASUAL: ["gpt-4o-mini"], AUDIT: ["o1"] },
|
||||
tier_definitions: [
|
||||
{ name: "CASUAL", description: "small talk" },
|
||||
{ name: "AUDIT", description: "security review" },
|
||||
],
|
||||
fallback_tier: "CASUAL",
|
||||
classifier_type: "llm",
|
||||
classifier_llm_config: { model: "gpt-4o-mini", timeout_ms: 3000 },
|
||||
classification_prompt: "Route for a payments team.",
|
||||
};
|
||||
const hydrated = hydrateComplexityRouterConfig(storedCustom, undefined);
|
||||
const reset = { ...hydrated, classification_prompt: undefined };
|
||||
|
||||
expect(buildUpdatedComplexityRouterConfig(storedCustom, reset)).not.toHaveProperty("classification_prompt");
|
||||
});
|
||||
|
||||
it("round-trips a stored classification_prompt, which an untouched open-and-save must not clear", () => {
|
||||
const storedCustom = {
|
||||
tiers: { CASUAL: ["gpt-4o-mini"], AUDIT: ["o1"] },
|
||||
tier_definitions: [
|
||||
{ name: "CASUAL", description: "small talk" },
|
||||
{ name: "AUDIT", description: "security review" },
|
||||
],
|
||||
fallback_tier: "CASUAL",
|
||||
classifier_type: "llm",
|
||||
classifier_llm_config: { model: "gpt-4o-mini", timeout_ms: 3000 },
|
||||
classification_prompt: "Route for a payments team.\n\nExamples:\n- refund status -> CASUAL",
|
||||
};
|
||||
const hydrated = hydrateComplexityRouterConfig(storedCustom, undefined);
|
||||
|
||||
expect(hydrated.classification_prompt).toBe(storedCustom.classification_prompt);
|
||||
expect(buildUpdatedComplexityRouterConfig(storedCustom, hydrated).classification_prompt).toBe(
|
||||
storedCustom.classification_prompt,
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips the heuristic_first threshold, which save requires and the backend rejects without", () => {
|
||||
const hydrated = hydrateComplexityRouterConfig(STORED_ALL_MANAGED, undefined);
|
||||
expect(hydrated.heuristic_first_max_tier).toBe("SIMPLE");
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ export interface StoredComplexityRouterConfig {
|
|||
tier_model_configs?: unknown;
|
||||
default_model?: string | null;
|
||||
plan_mode_min_tier?: unknown;
|
||||
classification_prompt?: unknown;
|
||||
heuristic_first_max_tier?: unknown;
|
||||
tier_labels?: unknown;
|
||||
classifier_type?: ClassifierType;
|
||||
|
|
@ -153,6 +154,10 @@ export const hydrateComplexityRouterConfig = (
|
|||
parsedConfig.classifier_fallback === "default_model" || parsedConfig.classifier_fallback === "heuristic"
|
||||
? parsedConfig.classifier_fallback
|
||||
: undefined,
|
||||
classification_prompt:
|
||||
typeof parsedConfig.classification_prompt === "string" && parsedConfig.classification_prompt.trim() !== ""
|
||||
? parsedConfig.classification_prompt
|
||||
: undefined,
|
||||
heuristic_first_max_tier:
|
||||
typeof parsedConfig.heuristic_first_max_tier === "string" && parsedConfig.heuristic_first_max_tier.trim() !== ""
|
||||
? parsedConfig.heuristic_first_max_tier
|
||||
|
|
@ -189,6 +194,7 @@ export const MANAGED_COMPLEXITY_ROUTER_KEYS = new Set([
|
|||
"classifier_context_budget_chars",
|
||||
"classifier_context_include_assistant_turns",
|
||||
"classifier_fallback",
|
||||
"classification_prompt",
|
||||
"heuristic_first_max_tier",
|
||||
"session_affinity",
|
||||
"deployment_affinity",
|
||||
|
|
@ -269,6 +275,7 @@ export const buildUpdatedComplexityRouterConfig = (
|
|||
customTierSet: value.custom_tier_set,
|
||||
defaultModel: value.default_model,
|
||||
planModeMinTier: value.plan_mode_min_tier,
|
||||
classificationPrompt: value.classification_prompt,
|
||||
heuristicFirstMaxTier: value.heuristic_first_max_tier,
|
||||
tierLabels: value.tier_labels,
|
||||
classifierType: value.classifier_type,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue