feat: chatbot model setting + tool-calling toggle, simplify admin AI config (v0.59.0)
Chatbot (general assistant + per-recipe Q&A) now resolves its default model from its own site setting (DEFAULT_CHAT_PROVIDER/MODEL) instead of sharing the generic "text" use case with recipe generation, so admins can point it at a different model. Added AI_TOOL_CALLING_ENABLED: turns off the chatbot's createRecipe/ addToShoppingList tools (and the forced-retry pass) for models that don't reliably support tool calling — it falls back to plain text answers. Simplified the admin AI Configuration page: it showed provider keys and routing settings twice (a read-only card, then an identical edit form). Merged into one editable section; the resolved fallback provider is now a one-line note instead of its own card. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,14 +13,15 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { toast } from "sonner";
|
||||
|
||||
type Provider = "openai" | "anthropic" | "openrouter" | "ollama" | "";
|
||||
|
||||
type UseCase = {
|
||||
key: "text" | "vision" | "mealPlan";
|
||||
providerSetting: "DEFAULT_TEXT_PROVIDER" | "DEFAULT_VISION_PROVIDER" | "DEFAULT_MEAL_PLAN_PROVIDER";
|
||||
modelSetting: "DEFAULT_TEXT_MODEL" | "DEFAULT_VISION_MODEL" | "DEFAULT_MEAL_PLAN_MODEL";
|
||||
key: "text" | "chat" | "vision" | "mealPlan";
|
||||
providerSetting: "DEFAULT_TEXT_PROVIDER" | "DEFAULT_CHAT_PROVIDER" | "DEFAULT_VISION_PROVIDER" | "DEFAULT_MEAL_PLAN_PROVIDER";
|
||||
modelSetting: "DEFAULT_TEXT_MODEL" | "DEFAULT_CHAT_MODEL" | "DEFAULT_VISION_MODEL" | "DEFAULT_MEAL_PLAN_MODEL";
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
@@ -31,7 +32,14 @@ const USE_CASES: UseCase[] = [
|
||||
providerSetting: "DEFAULT_TEXT_PROVIDER",
|
||||
modelSetting: "DEFAULT_TEXT_MODEL",
|
||||
label: "Text generation",
|
||||
description: "Recipe generation, chat, substitutions, pairings, and other text-based AI features.",
|
||||
description: "Recipe generation, substitutions, pairings, and other text-based AI features.",
|
||||
},
|
||||
{
|
||||
key: "chat",
|
||||
providerSetting: "DEFAULT_CHAT_PROVIDER",
|
||||
modelSetting: "DEFAULT_CHAT_MODEL",
|
||||
label: "Chatbot",
|
||||
description: "The general cooking assistant and per-recipe Q&A chat.",
|
||||
},
|
||||
{
|
||||
key: "vision",
|
||||
@@ -71,8 +79,15 @@ const PRESET_MODELS: Record<string, { label: string; models: { value: string; la
|
||||
|
||||
type Values = Partial<Record<UseCase["providerSetting"] | UseCase["modelSetting"], string | null>>;
|
||||
|
||||
export function AdminDefaultModelForm({ initialValues }: { initialValues: Values }) {
|
||||
export function AdminDefaultModelForm({
|
||||
initialValues,
|
||||
initialToolCallingEnabled,
|
||||
}: {
|
||||
initialValues: Values;
|
||||
initialToolCallingEnabled: boolean;
|
||||
}) {
|
||||
const [values, setValues] = useState<Values>(initialValues);
|
||||
const [toolCallingEnabled, setToolCallingEnabled] = useState(initialToolCallingEnabled);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
function setField(key: UseCase["providerSetting"] | UseCase["modelSetting"], value: string | null) {
|
||||
@@ -85,7 +100,7 @@ export function AdminDefaultModelForm({ initialValues }: { initialValues: Values
|
||||
const res = await fetch("/api/v1/admin/settings", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(values),
|
||||
body: JSON.stringify({ ...values, AI_TOOL_CALLING_ENABLED: toolCallingEnabled ? null : "false" }),
|
||||
});
|
||||
if (!res.ok) throw new Error("Save failed");
|
||||
toast.success("Default AI providers saved");
|
||||
@@ -99,7 +114,7 @@ export function AdminDefaultModelForm({ initialValues }: { initialValues: Values
|
||||
return (
|
||||
<section className="rounded-xl border p-6 space-y-4">
|
||||
<div>
|
||||
<h2 className="font-semibold text-lg">Default AI Providers</h2>
|
||||
<h2 className="font-semibold text-lg">Default Models</h2>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
What to use for a given feature when a user hasn't set their own model preference and has no personal API key.
|
||||
Leave a provider unset to fall back to whichever provider key is configured above.
|
||||
@@ -177,6 +192,22 @@ export function AdminDefaultModelForm({ initialValues }: { initialValues: Values
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{key === "chat" && (
|
||||
<div className="flex items-center justify-between border-t pt-3">
|
||||
<div>
|
||||
<Label className="text-xs">Tool calling</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Lets the chatbot draft a recipe or shopping list inline. Turn off for a model that can't reliably call tools (e.g. some local/Ollama models) — it'll fall back to plain text answers.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={toolCallingEnabled}
|
||||
onCheckedChange={setToolCallingEnabled}
|
||||
className="shrink-0"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user