mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): common_components/check_openapi_schema to shadcn
- @tremor TextInput + antd Input/Input.TextArea/InputNumber/Tooltip + @ant-design InfoCircleOutlined \u2192 shadcn Input/Textarea/Tooltip + NumericalInput shared helper + lucide Info. - antd Select preserved for enum-backed fields. - text-red-500 \u2192 text-destructive / text-gray-500 \u2192 text-muted- foreground. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
7d31cc2ecc
commit
aa7c219fc4
1 changed files with 65 additions and 27 deletions
|
|
@ -1,8 +1,15 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Form, Input, InputNumber, Select } from "antd";
|
||||
import { TextInput } from "@tremor/react";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Tooltip } from "antd";
|
||||
import { Form, Select } from "antd";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Info } from "lucide-react";
|
||||
import NumericalInput from "@/components/shared/numerical_input";
|
||||
import { getOpenAPISchema } from "../networking";
|
||||
import { formatLabel } from "@/utils/textUtils";
|
||||
|
||||
|
|
@ -25,24 +32,25 @@ interface OpenAPISchema {
|
|||
interface SchemaFormFieldsProps {
|
||||
schemaComponent: string;
|
||||
excludedFields?: string[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
form: any;
|
||||
overrideLabels?: { [key: string]: string };
|
||||
overrideTooltips?: { [key: string]: string };
|
||||
customValidation?: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: (rule: any, value: any) => Promise<void>;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
defaultValues?: { [key: string]: any };
|
||||
}
|
||||
|
||||
// Define which fields should be parsed as JSON
|
||||
export const jsonFields = ["metadata", "config", "enforced_params", "aliases"];
|
||||
|
||||
// Helper function to determine if a field should be treated as JSON
|
||||
const isJSONField = (key: string, property: SchemaProperty): boolean => {
|
||||
return jsonFields.includes(key) || property.format === "json";
|
||||
};
|
||||
|
||||
// Helper function to validate JSON input
|
||||
const validateJSON = (value: string): boolean => {
|
||||
if (!value) return true;
|
||||
try {
|
||||
|
|
@ -53,8 +61,11 @@ const validateJSON = (value: string): boolean => {
|
|||
}
|
||||
};
|
||||
|
||||
const getFieldHelp = (key: string, property: SchemaProperty, type: string): string => {
|
||||
// Default help text based on type
|
||||
const getFieldHelp = (
|
||||
key: string,
|
||||
property: SchemaProperty,
|
||||
type: string,
|
||||
): string => {
|
||||
const defaultHelp =
|
||||
{
|
||||
string: "Text input",
|
||||
|
|
@ -63,28 +74,28 @@ const getFieldHelp = (key: string, property: SchemaProperty, type: string): stri
|
|||
boolean: "True/False value",
|
||||
}[type] || "Text input";
|
||||
|
||||
// Specific field help text
|
||||
const specificHelp: { [key: string]: string } = {
|
||||
max_budget: "Enter maximum budget in USD (e.g., 100.50)",
|
||||
budget_duration: "Select a time period for budget reset",
|
||||
tpm_limit: "Enter maximum tokens per minute (whole number)",
|
||||
rpm_limit: "Enter maximum requests per minute (whole number)",
|
||||
duration: "Enter duration (e.g., 30s, 24h, 7d)",
|
||||
metadata: 'Enter JSON object with key-value pairs\nExample: {"team": "research", "project": "nlp"}',
|
||||
metadata:
|
||||
'Enter JSON object with key-value pairs\nExample: {"team": "research", "project": "nlp"}',
|
||||
config: 'Enter configuration as JSON object\nExample: {"setting": "value"}',
|
||||
permissions: "Enter comma-separated permission strings",
|
||||
enforced_params: 'Enter parameters as JSON object\nExample: {"param": "value"}',
|
||||
enforced_params:
|
||||
'Enter parameters as JSON object\nExample: {"param": "value"}',
|
||||
blocked: "Enter true/false or specific block conditions",
|
||||
aliases: 'Enter aliases as JSON object\nExample: {"alias1": "value1", "alias2": "value2"}',
|
||||
aliases:
|
||||
'Enter aliases as JSON object\nExample: {"alias1": "value1", "alias2": "value2"}',
|
||||
models: "Select one or more model names",
|
||||
key_alias: "Enter a unique identifier for this key",
|
||||
tags: "Enter comma-separated tag strings",
|
||||
};
|
||||
|
||||
// Get specific help text or use default based on type
|
||||
const helpText = specificHelp[key] || defaultHelp;
|
||||
|
||||
// Add format requirements for special cases
|
||||
if (isJSONField(key, property)) {
|
||||
return `${helpText}\nMust be valid JSON format`;
|
||||
}
|
||||
|
|
@ -105,7 +116,8 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
customValidation = {},
|
||||
defaultValues = {},
|
||||
}) => {
|
||||
const [schemaProperties, setSchemaProperties] = useState<OpenAPISchema | null>(null);
|
||||
const [schemaProperties, setSchemaProperties] =
|
||||
useState<OpenAPISchema | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -120,9 +132,13 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
|
||||
setSchemaProperties(componentSchema);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const defaultFormValues: { [key: string]: any } = {};
|
||||
Object.keys(componentSchema.properties)
|
||||
.filter((key) => !excludedFields.includes(key) && defaultValues[key] !== undefined)
|
||||
.filter(
|
||||
(key) =>
|
||||
!excludedFields.includes(key) && defaultValues[key] !== undefined,
|
||||
)
|
||||
.forEach((key) => {
|
||||
defaultFormValues[key] = defaultValues[key];
|
||||
});
|
||||
|
|
@ -130,11 +146,15 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
form.setFieldsValue(defaultFormValues);
|
||||
} catch (error) {
|
||||
console.error("Schema fetch error:", error);
|
||||
setError(error instanceof Error ? error.message : "Failed to fetch schema");
|
||||
setError(
|
||||
error instanceof Error ? error.message : "Failed to fetch schema",
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
};
|
||||
|
||||
fetchOpenAPISchema();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [schemaComponent, form, excludedFields]);
|
||||
|
||||
const getPropertyType = (property: SchemaProperty): string => {
|
||||
|
|
@ -156,7 +176,8 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
const label = overrideLabels[key] || property.title || formatLabel(key);
|
||||
const tooltip = overrideTooltips[key] || property.description;
|
||||
|
||||
const rules = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const rules: any[] = [];
|
||||
if (isRequired) {
|
||||
rules.push({ required: true, message: `${label} is required` });
|
||||
}
|
||||
|
|
@ -165,6 +186,7 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
}
|
||||
if (isJSONField(key, property)) {
|
||||
rules.push({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
validator: async (_: any, value: string) => {
|
||||
if (value && !validateJSON(value)) {
|
||||
throw new Error("Please enter valid JSON");
|
||||
|
|
@ -176,9 +198,14 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
const formLabel = tooltip ? (
|
||||
<span>
|
||||
{label}{" "}
|
||||
<Tooltip title={tooltip}>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</Tooltip>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="ml-1 h-3 w-3 inline text-muted-foreground" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-xs">{tooltip}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</span>
|
||||
) : (
|
||||
label
|
||||
|
|
@ -186,7 +213,9 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
|
||||
let inputComponent;
|
||||
if (isJSONField(key, property)) {
|
||||
inputComponent = <Input.TextArea rows={4} placeholder="Enter as JSON" className="font-mono" />;
|
||||
inputComponent = (
|
||||
<Textarea rows={4} placeholder="Enter as JSON" className="font-mono" />
|
||||
);
|
||||
} else if (property.enum) {
|
||||
inputComponent = (
|
||||
<Select>
|
||||
|
|
@ -198,11 +227,16 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
</Select>
|
||||
);
|
||||
} else if (type === "number" || type === "integer") {
|
||||
inputComponent = <InputNumber style={{ width: "100%" }} precision={type === "integer" ? 0 : undefined} />;
|
||||
inputComponent = (
|
||||
<NumericalInput
|
||||
style={{ width: "100%" }}
|
||||
precision={type === "integer" ? 0 : undefined}
|
||||
/>
|
||||
);
|
||||
} else if (key === "duration") {
|
||||
inputComponent = <TextInput placeholder="eg: 30s, 30h, 30d" />;
|
||||
inputComponent = <Input placeholder="eg: 30s, 30h, 30d" />;
|
||||
} else {
|
||||
inputComponent = <TextInput placeholder={tooltip || ""} />;
|
||||
inputComponent = <Input placeholder={tooltip || ""} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -213,7 +247,11 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
className="mt-8"
|
||||
rules={rules}
|
||||
initialValue={defaultValues[key]}
|
||||
help={<div className="text-xs text-gray-500">{getFieldHelp(key, property, type)}</div>}
|
||||
help={
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{getFieldHelp(key, property, type)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{inputComponent}
|
||||
</Form.Item>
|
||||
|
|
@ -221,7 +259,7 @@ const SchemaFormFields: React.FC<SchemaFormFieldsProps> = ({
|
|||
};
|
||||
|
||||
if (error) {
|
||||
return <div className="text-red-500">Error: {error}</div>;
|
||||
return <div className="text-destructive">Error: {error}</div>;
|
||||
}
|
||||
|
||||
if (!schemaProperties?.properties) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue