mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for add_model batch 2
This commit is contained in:
parent
203eaff982
commit
6a21cb423b
8 changed files with 1127 additions and 2180 deletions
|
|
@ -4,6 +4,7 @@ import { Text, Accordion, AccordionHeader, AccordionBody, TextInput } from "@tre
|
|||
import { Row, Col, Typography } from "antd";
|
||||
import TextArea from "antd/es/input/TextArea";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { Team } from "../key_team_helpers/key_list";
|
||||
import CacheControlSettings from "./cache_control_settings";
|
||||
import VectorStoreSelector from "../vector_store_management/VectorStoreSelector";
|
||||
|
|
@ -28,18 +29,18 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
tagsList,
|
||||
accessToken,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [customPricing, setCustomPricing] = React.useState(false);
|
||||
const [pricingModel, setPricingModel] = React.useState<"per_token" | "per_second">("per_token");
|
||||
const [showCacheControl, setShowCacheControl] = React.useState(false);
|
||||
|
||||
// Add validation function for numbers
|
||||
const validateNumber = (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (isNaN(Number(value)) || Number(value) < 0) {
|
||||
return Promise.reject("Please enter a valid positive number");
|
||||
return Promise.reject(t("addModel.advancedSettings.validateNumberError"));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
|
@ -106,19 +107,24 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
<>
|
||||
<Accordion className="mt-2 mb-4">
|
||||
<AccordionHeader>
|
||||
<b>Advanced Settings</b>
|
||||
<b>{t("addModel.advancedSettings.title")}</b>
|
||||
</AccordionHeader>
|
||||
<AccordionBody>
|
||||
<div className="bg-white rounded-lg">
|
||||
<Form.Item label="Custom Pricing" name="custom_pricing" valuePropName="checked" className="mb-4">
|
||||
<Form.Item
|
||||
label={t("addModel.advancedSettings.customPricingLabel")}
|
||||
name="custom_pricing"
|
||||
valuePropName="checked"
|
||||
className="mb-4"
|
||||
>
|
||||
<Switch onChange={handleCustomPricingChange} className="bg-gray-600" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Attached Knowledge Bases (RAG){" "}
|
||||
<Tooltip title="Vector stores to use for RAG. Every request to this model will automatically retrieve context from these knowledge bases.">
|
||||
{t("addModel.advancedSettings.attachedKnowledgeBasesLabel")}{" "}
|
||||
<Tooltip title={t("addModel.advancedSettings.attachedKnowledgeBasesTooltip")}>
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/completion/knowledgebase"
|
||||
target="_blank"
|
||||
|
|
@ -132,25 +138,25 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
}
|
||||
name="vector_store_ids"
|
||||
className="mt-4"
|
||||
help="Select vector stores to attach. Requests to this model will automatically use these for RAG. Set up vector stores in Tools > Vector Stores."
|
||||
help={t("addModel.advancedSettings.attachedKnowledgeBasesHelp")}
|
||||
>
|
||||
<VectorStoreSelector
|
||||
onChange={() => {}}
|
||||
accessToken={accessToken}
|
||||
placeholder="Select knowledge bases (optional)"
|
||||
placeholder={t("addModel.advancedSettings.knowledgeBasesPlaceholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Guardrails{" "}
|
||||
<Tooltip title="Apply safety guardrails to this key to filter content or enforce policies">
|
||||
{t("addModel.advancedSettings.guardrailsLabel")}{" "}
|
||||
<Tooltip title={t("addModel.advancedSettings.guardrailsTooltip")}>
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/guardrails/quick_start"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()} // Prevent accordion from collapsing when clicking link
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</a>
|
||||
|
|
@ -159,21 +165,21 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
}
|
||||
name="guardrails"
|
||||
className="mt-4"
|
||||
help="Select existing guardrails. Go to 'Guardrails' tab to create new guardrails."
|
||||
help={t("addModel.advancedSettings.guardrailsHelp")}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select or enter guardrails"
|
||||
placeholder={t("addModel.advancedSettings.guardrailsPlaceholder")}
|
||||
options={guardrailsList.map((name) => ({ value: name, label: name }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Tags" name="tags" className="mb-4">
|
||||
<Form.Item label={t("addModel.advancedSettings.tagsLabel")} name="tags" className="mb-4">
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select or enter tags"
|
||||
placeholder={t("addModel.advancedSettings.tagsPlaceholder")}
|
||||
options={Object.values(tagsList).map((tag) => ({
|
||||
value: tag.name,
|
||||
label: tag.name,
|
||||
|
|
@ -184,13 +190,17 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
|
||||
{customPricing && (
|
||||
<div className="ml-6 pl-4 border-l-2 border-gray-200">
|
||||
<Form.Item label="Pricing Model" name="pricing_model" className="mb-4">
|
||||
<Form.Item
|
||||
label={t("addModel.advancedSettings.pricingModelLabel")}
|
||||
name="pricing_model"
|
||||
className="mb-4"
|
||||
>
|
||||
<Select
|
||||
defaultValue="per_token"
|
||||
onChange={(value: "per_token" | "per_second") => setPricingModel(value)}
|
||||
options={[
|
||||
{ value: "per_token", label: "Per Million Tokens" },
|
||||
{ value: "per_second", label: "Per Second" },
|
||||
{ value: "per_token", label: t("addModel.advancedSettings.perMillionTokens") },
|
||||
{ value: "per_second", label: t("addModel.advancedSettings.perSecond") },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
@ -198,7 +208,7 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
{pricingModel === "per_token" ? (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Input Cost (per 1M tokens)"
|
||||
label={t("addModel.advancedSettings.inputCostLabel")}
|
||||
name="input_cost_per_token"
|
||||
rules={[{ validator: validateNumber }]}
|
||||
className="mb-4"
|
||||
|
|
@ -206,7 +216,7 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Output Cost (per 1M tokens)"
|
||||
label={t("addModel.advancedSettings.outputCostLabel")}
|
||||
name="output_cost_per_token"
|
||||
rules={[{ validator: validateNumber }]}
|
||||
className="mb-4"
|
||||
|
|
@ -214,27 +224,27 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
<TextInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Cache Read Cost (per 1M tokens)"
|
||||
label={t("addModel.advancedSettings.cacheReadCostLabel")}
|
||||
name="cache_read_input_token_cost"
|
||||
rules={[{ validator: validateNumber }]}
|
||||
tooltip="If left blank, defaults to Input Cost."
|
||||
tooltip={t("addModel.advancedSettings.cacheReadCostTooltip")}
|
||||
className="mb-4"
|
||||
>
|
||||
<TextInput placeholder="Defaults to Input Cost if blank" />
|
||||
<TextInput placeholder={t("addModel.advancedSettings.cacheReadCostPlaceholder")} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Cache Write Cost (per 1M tokens)"
|
||||
label={t("addModel.advancedSettings.cacheWriteCostLabel")}
|
||||
name="cache_creation_input_token_cost"
|
||||
rules={[{ validator: validateNumber }]}
|
||||
tooltip="If left blank, defaults to Input Cost (the backend falls back to input_cost_per_token when no cache-write rate is set)."
|
||||
tooltip={t("addModel.advancedSettings.cacheWriteCostTooltip")}
|
||||
className="mb-4"
|
||||
>
|
||||
<TextInput placeholder="Defaults to Input Cost if blank" />
|
||||
<TextInput placeholder={t("addModel.advancedSettings.cacheWriteCostPlaceholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
) : (
|
||||
<Form.Item
|
||||
label="Cost Per Second"
|
||||
label={t("addModel.advancedSettings.costPerSecondLabel")}
|
||||
name="input_cost_per_second"
|
||||
rules={[{ validator: validateNumber }]}
|
||||
className="mb-4"
|
||||
|
|
@ -246,17 +256,17 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
)}
|
||||
|
||||
<Form.Item
|
||||
label="Use in pass through routes"
|
||||
label={t("addModel.advancedSettings.useInPassThroughLabel")}
|
||||
name="use_in_pass_through"
|
||||
valuePropName="checked"
|
||||
className="mb-4 mt-4"
|
||||
tooltip={
|
||||
<span>
|
||||
Allow using these credentials in pass through routes.{" "}
|
||||
<Link href="https://docs.litellm.ai/docs/pass_through/vertex_ai" target="_blank">
|
||||
Learn more
|
||||
</Link>
|
||||
</span>
|
||||
<Trans
|
||||
i18nKey="addModel.advancedSettings.useInPassThroughTooltip"
|
||||
components={{
|
||||
learnMoreLink: <Link href="https://docs.litellm.ai/docs/pass_through/vertex_ai" target="_blank" />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Switch onChange={handlePassThroughChange} className="bg-gray-600" />
|
||||
|
|
@ -268,9 +278,9 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
onCacheControlChange={handleCacheControlChange}
|
||||
/>
|
||||
<Form.Item
|
||||
label="LiteLLM Params"
|
||||
label={t("addModel.advancedSettings.litellmParamsLabel")}
|
||||
name="litellm_extra_params"
|
||||
tooltip="Optional litellm params used for making a litellm.completion() call."
|
||||
tooltip={t("addModel.advancedSettings.litellmParamsTooltip")}
|
||||
className="mb-4 mt-4"
|
||||
rules={[{ validator: formItemValidateJSON }]}
|
||||
>
|
||||
|
|
@ -287,17 +297,17 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
<Col span={10}></Col>
|
||||
<Col span={10}>
|
||||
<Text className="text-gray-600 text-sm">
|
||||
Pass JSON of litellm supported params{" "}
|
||||
{t("addModel.advancedSettings.litellmParamsHelpText")}{" "}
|
||||
<Link href="https://docs.litellm.ai/docs/completion/input" target="_blank">
|
||||
litellm.completion() call
|
||||
{t("addModel.advancedSettings.litellmParamsHelpLink")}
|
||||
</Link>
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
label="Model Info"
|
||||
label={t("addModel.advancedSettings.modelInfoLabel")}
|
||||
name="model_info_params"
|
||||
tooltip="Optional model info params. Returned when calling `/model/info` endpoint."
|
||||
tooltip={t("addModel.advancedSettings.modelInfoTooltip")}
|
||||
className="mb-0"
|
||||
rules={[{ validator: formItemValidateJSON }]}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { Form, Switch, Select, Typography } from "antd";
|
||||
import { PlusOutlined, MinusCircleOutlined } from "@ant-design/icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import NumericalInput from "../shared/numerical_input";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
|
@ -22,6 +23,8 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
|
|||
showCacheControl,
|
||||
onCacheControlChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const updateCacheControlPoints = (injectionPoints: CacheControlInjectionPoint[]) => {
|
||||
const currentParams = form.getFieldValue("litellm_extra_params");
|
||||
try {
|
||||
|
|
@ -44,21 +47,18 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
|
|||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Cache Control Injection Points"
|
||||
label={t("addModel.cacheControlSettings.injectionPointsLabel")}
|
||||
name="cache_control"
|
||||
valuePropName="checked"
|
||||
className="mb-4"
|
||||
tooltip="Tell litellm where to inject cache control checkpoints. You can specify either by role (to apply to all messages of that role) or by specific message index."
|
||||
tooltip={t("addModel.cacheControlSettings.injectionPointsTooltip")}
|
||||
>
|
||||
<Switch onChange={onCacheControlChange} className="bg-gray-600" />
|
||||
</Form.Item>
|
||||
|
||||
{showCacheControl && (
|
||||
<div className="ml-6 pl-4 border-l-2 border-gray-200">
|
||||
<Text className="text-sm text-gray-500 block mb-4">
|
||||
Providers like Anthropic, Bedrock API require users to specify where to inject cache control checkpoints,
|
||||
litellm can automatically add them for you as a cost saving feature.
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500 block mb-4">{t("addModel.cacheControlSettings.helpText")}</Text>
|
||||
|
||||
<Form.List name="cache_control_injection_points" initialValue={[{ location: "message" }]}>
|
||||
{(fields, { add, remove }) => (
|
||||
|
|
@ -67,30 +67,33 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
|
|||
<div key={field.key} className="flex items-center mb-4 gap-4">
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="Type"
|
||||
label={t("addModel.cacheControlSettings.typeLabel")}
|
||||
name={[field.name, "location"]}
|
||||
initialValue="message"
|
||||
className="mb-0"
|
||||
style={{ width: "180px" }}
|
||||
>
|
||||
<Select disabled options={[{ value: "message", label: "Message" }]} />
|
||||
<Select
|
||||
disabled
|
||||
options={[{ value: "message", label: t("addModel.cacheControlSettings.messageType") }]}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="Role"
|
||||
label={t("addModel.cacheControlSettings.roleLabel")}
|
||||
name={[field.name, "role"]}
|
||||
className="mb-0"
|
||||
style={{ width: "180px" }}
|
||||
tooltip="LiteLLM will mark all messages of this role as cacheable"
|
||||
tooltip={t("addModel.cacheControlSettings.roleTooltip")}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a role"
|
||||
placeholder={t("addModel.cacheControlSettings.rolePlaceholder")}
|
||||
allowClear
|
||||
options={[
|
||||
{ value: "user", label: "User" },
|
||||
{ value: "system", label: "System" },
|
||||
{ value: "assistant", label: "Assistant" },
|
||||
{ value: "user", label: t("addModel.cacheControlSettings.roleUser") },
|
||||
{ value: "system", label: t("addModel.cacheControlSettings.roleSystem") },
|
||||
{ value: "assistant", label: t("addModel.cacheControlSettings.roleAssistant") },
|
||||
]}
|
||||
onChange={() => {
|
||||
const values = form.getFieldValue("cache_control_points");
|
||||
|
|
@ -101,15 +104,15 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
|
|||
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="Index"
|
||||
label={t("addModel.cacheControlSettings.indexLabel")}
|
||||
name={[field.name, "index"]}
|
||||
className="mb-0"
|
||||
style={{ width: "180px" }}
|
||||
tooltip="(Optional) If set litellm will mark the message at this index as cacheable"
|
||||
tooltip={t("addModel.cacheControlSettings.indexTooltip")}
|
||||
>
|
||||
<NumericalInput
|
||||
type="number"
|
||||
placeholder="Optional"
|
||||
placeholder={t("addModel.cacheControlSettings.indexPlaceholder")}
|
||||
step={1}
|
||||
onChange={() => {
|
||||
const values = form.getFieldValue("cache_control_points");
|
||||
|
|
@ -140,7 +143,7 @@ const CacheControlSettings: React.FC<CacheControlSettingsProps> = ({
|
|||
onClick={() => add()}
|
||||
>
|
||||
<PlusOutlined className="mr-2" />
|
||||
Add Injection Point
|
||||
{t("addModel.cacheControlSettings.addInjectionPoint")}
|
||||
</button>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Form, Table } from "antd";
|
||||
import { TextInput } from "@tremor/react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "../atoms/index";
|
||||
import { Providers } from "../provider_info_helpers";
|
||||
|
||||
const ConditionalPublicModelName: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const form = Form.useFormInstance();
|
||||
const [tableKey, setTableKey] = useState(0); // Add a key to force table re-render
|
||||
const [tableKey, setTableKey] = useState(0);
|
||||
|
||||
// Watch the 'model' field for changes and ensure it's always an array
|
||||
const modelValue = Form.useWatch("model", form) || [];
|
||||
|
|
@ -95,30 +97,40 @@ const ConditionalPublicModelName: React.FC = () => {
|
|||
|
||||
const publicNameTooltipContent = (
|
||||
<>
|
||||
<div className="mb-2 font-normal">The name you specify in your API calls to LiteLLM Proxy</div>
|
||||
<div className="mb-2 font-normal">{t("addModel.conditionalPublicModelName.publicModelNameTooltip")}</div>
|
||||
<div className="mb-2 font-normal">
|
||||
<strong>Example:</strong> If you name your public model{" "}
|
||||
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">example-name</code>, and choose{" "}
|
||||
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">openai/qwen-plus-latest</code> as the LiteLLM model
|
||||
<Trans
|
||||
i18nKey="addModel.conditionalPublicModelName.tooltipExample"
|
||||
components={{
|
||||
strong: <strong />,
|
||||
publicName: <code className="bg-gray-700 px-1 py-0.5 rounded text-xs" />,
|
||||
modelName: <code className="bg-gray-700 px-1 py-0.5 rounded text-xs" />,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2 font-normal">
|
||||
<strong>Usage:</strong> You make an API call to the LiteLLM proxy with{" "}
|
||||
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">model = "example-name"</code>
|
||||
<strong>{t("addModel.conditionalPublicModelName.tooltipUsage")}</strong>{" "}
|
||||
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">
|
||||
{t("addModel.conditionalPublicModelName.tooltipUsageCode")}
|
||||
</code>
|
||||
</div>
|
||||
<div className="font-normal">
|
||||
<strong>Result:</strong> LiteLLM sends{" "}
|
||||
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">qwen-plus-latest</code> to the provider
|
||||
<strong>{t("addModel.conditionalPublicModelName.tooltipResult")}</strong>{" "}
|
||||
<code className="bg-gray-700 px-1 py-0.5 rounded text-xs">
|
||||
{t("addModel.conditionalPublicModelName.tooltipResultCode")}
|
||||
</code>{" "}
|
||||
{t("addModel.conditionalPublicModelName.tooltipResultSuffix")}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const liteLLMModelTooltipContent = <div>The model name LiteLLM will send to the LLM API</div>;
|
||||
const liteLLMModelTooltipContent = <div>{t("addModel.conditionalPublicModelName.litellmModelNameTooltip")}</div>;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: (
|
||||
<span className="flex items-center">
|
||||
Public Model Name
|
||||
{t("addModel.conditionalPublicModelName.publicModelNameColumn")}
|
||||
<Tooltip content={publicNameTooltipContent} width="500px" />
|
||||
</span>
|
||||
),
|
||||
|
|
@ -163,7 +175,7 @@ const ConditionalPublicModelName: React.FC = () => {
|
|||
{
|
||||
title: (
|
||||
<span className="flex items-center">
|
||||
LiteLLM Model Name
|
||||
{t("addModel.conditionalPublicModelName.litellmModelNameColumn")}
|
||||
<Tooltip content={liteLLMModelTooltipContent} width="360px" />
|
||||
</span>
|
||||
),
|
||||
|
|
@ -175,9 +187,9 @@ const ConditionalPublicModelName: React.FC = () => {
|
|||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Model Mappings"
|
||||
label={t("addModel.conditionalPublicModelName.modelMappingsLabel")}
|
||||
name="model_mappings"
|
||||
tooltip="Map public model names to LiteLLM model names for load balancing"
|
||||
tooltip={t("addModel.conditionalPublicModelName.modelMappingsTooltip")}
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
|
|
@ -186,21 +198,20 @@ const ConditionalPublicModelName: React.FC = () => {
|
|||
required: true,
|
||||
validator: async (_, value) => {
|
||||
if (!value || value.length === 0) {
|
||||
throw new Error("At least one model mapping is required");
|
||||
throw new Error(t("addModel.conditionalPublicModelName.mappingRequired"));
|
||||
}
|
||||
// Check if all mappings have valid public names
|
||||
const invalidMappings = value.filter(
|
||||
(mapping: any) => !mapping.public_name || mapping.public_name.trim() === "",
|
||||
);
|
||||
if (invalidMappings.length > 0) {
|
||||
throw new Error("All model mappings must have valid public names");
|
||||
throw new Error(t("addModel.conditionalPublicModelName.publicNameRequired"));
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Table
|
||||
key={tableKey} // Add key to force re-render
|
||||
key={tableKey}
|
||||
dataSource={form.getFieldValue("model_mappings")}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import { Form, Select as AntSelect } from "antd";
|
||||
import { TextInput, Text } from "@tremor/react";
|
||||
import { Row, Col } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Providers } from "../provider_info_helpers";
|
||||
|
||||
interface LiteLLMModelNameFieldProps {
|
||||
|
|
@ -15,6 +16,7 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
providerModels,
|
||||
getPlaceholder,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const form = Form.useFormInstance();
|
||||
|
||||
const handleModelChange = (value: string | string[]) => {
|
||||
|
|
@ -101,8 +103,8 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label="LiteLLM Model Name(s)"
|
||||
tooltip="The model name LiteLLM will send to the LLM API"
|
||||
label={t("addModel.litellmModelName.fieldLabel")}
|
||||
tooltip={t("addModel.litellmModelName.fieldTooltip")}
|
||||
className="mb-0"
|
||||
>
|
||||
<Form.Item
|
||||
|
|
@ -110,7 +112,10 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `Please enter ${selectedProvider === Providers.Azure ? "a deployment name" : "at least one model"}.`,
|
||||
message:
|
||||
selectedProvider === Providers.Azure
|
||||
? t("addModel.litellmModelName.deploymentNameRequired")
|
||||
: t("addModel.litellmModelName.modelRequired"),
|
||||
},
|
||||
]}
|
||||
noStyle
|
||||
|
|
@ -130,17 +135,17 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
mode="multiple"
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="Select models"
|
||||
placeholder={t("addModel.litellmModelName.selectModelsPlaceholder")}
|
||||
onChange={handleModelChange}
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) => (option?.label ?? "").toLowerCase().includes(input.toLowerCase())}
|
||||
options={[
|
||||
{
|
||||
label: "Custom Model Name (Enter below)",
|
||||
label: t("addModel.litellmModelName.customModelOption"),
|
||||
value: "custom",
|
||||
},
|
||||
{
|
||||
label: `All ${selectedProvider} Models (Wildcard)`,
|
||||
label: t("addModel.litellmModelName.allModelsWildcard", { provider: selectedProvider }),
|
||||
value: "all-wildcard",
|
||||
},
|
||||
...providerModels.map((model) => ({
|
||||
|
|
@ -155,7 +160,6 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
)}
|
||||
</Form.Item>
|
||||
|
||||
{/* Custom Model Name field */}
|
||||
<Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.model !== currentValues.model}>
|
||||
{({ getFieldValue }) => {
|
||||
const selectedModels = getFieldValue("model") || [];
|
||||
|
|
@ -164,12 +168,14 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
modelArray.includes("custom") && (
|
||||
<Form.Item
|
||||
name="custom_model_name"
|
||||
rules={[{ required: true, message: "Please enter a custom model name." }]}
|
||||
rules={[{ required: true, message: t("addModel.litellmModelName.customModelRequired") }]}
|
||||
className="mt-2"
|
||||
>
|
||||
<TextInput
|
||||
placeholder={
|
||||
selectedProvider === Providers.Azure ? "Enter Azure deployment name" : "Enter custom model name"
|
||||
selectedProvider === Providers.Azure
|
||||
? t("addModel.litellmModelName.enterAzureDeploymentPlaceholder")
|
||||
: t("addModel.litellmModelName.enterCustomModelPlaceholder")
|
||||
}
|
||||
onChange={handleCustomModelNameChange}
|
||||
/>
|
||||
|
|
@ -184,8 +190,8 @@ const LiteLLMModelNameField: React.FC<LiteLLMModelNameFieldProps> = ({
|
|||
<Col span={14}>
|
||||
<Text className="mb-3 mt-1">
|
||||
{selectedProvider === Providers.Azure
|
||||
? "Your deployment name will be saved as the public model name, and LiteLLM will use 'azure/deployment-name' internally"
|
||||
: "The model name LiteLLM will send to the LLM API"}
|
||||
? t("addModel.litellmModelName.azureDeploymentDescription")
|
||||
: t("addModel.litellmModelName.modelDescription")}
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { Typography, Button, Divider } from "antd";
|
||||
import { WarningOutlined, InfoCircleOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { testConnectionRequest } from "../networking";
|
||||
import { prepareModelAddRequest } from "./handle_add_model_submit";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
|
|
@ -23,6 +24,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
|
|||
onClose,
|
||||
onTestComplete,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [error, setError] = React.useState<Error | string | null>(null);
|
||||
const [rawRequest, setRawRequest] = React.useState<any>(null);
|
||||
const [rawResponse, setRawResponse] = React.useState<any>(null);
|
||||
|
|
@ -47,7 +49,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
|
|||
|
||||
if (!result) {
|
||||
console.log("No result from prepareModelAddRequest");
|
||||
setError("Failed to prepare model data. Please check your form inputs.");
|
||||
setError(t("addModel.modelConnectionTest.prepareFailed"));
|
||||
setIsSuccess(false);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
|
|
@ -59,7 +61,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
|
|||
|
||||
const response = await testConnectionRequest(accessToken, litellmParamsObj, modelInfoObj, modelInfoObj?.mode);
|
||||
if (response.status === "success") {
|
||||
NotificationsManager.success("Connection test successful!");
|
||||
NotificationsManager.success(t("addModel.modelConnectionTest.connectionTestSuccessful"));
|
||||
setError(null);
|
||||
setIsSuccess(true);
|
||||
} else {
|
||||
|
|
@ -90,7 +92,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
|
|||
}, []); // Empty dependency array means this runs once on mount
|
||||
|
||||
const getCleanErrorMessage = (errorMsg: string) => {
|
||||
if (!errorMsg) return "Unknown error";
|
||||
if (!errorMsg) return t("addModel.modelConnectionTest.unknownError");
|
||||
|
||||
const mainError = errorMsg.split("stack trace:")[0].trim();
|
||||
|
||||
|
|
@ -104,7 +106,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
|
|||
? getCleanErrorMessage(error)
|
||||
: error?.message
|
||||
? getCleanErrorMessage(error.message)
|
||||
: "Unknown error";
|
||||
: t("addModel.modelConnectionTest.unknownError");
|
||||
|
||||
const formatCurlCommand = (
|
||||
apiBase: string,
|
||||
|
|
@ -154,7 +156,7 @@ ${formattedBody}
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
<Text style={{ fontSize: "16px" }}>Testing connection to {modelName}...</Text>
|
||||
<Text style={{ fontSize: "16px" }}>{t("addModel.modelConnectionTest.testingConnection", { modelName })}</Text>
|
||||
<style jsx>{`
|
||||
@keyframes spin {
|
||||
0% {
|
||||
|
|
@ -186,7 +188,7 @@ ${formattedBody}
|
|||
type="success"
|
||||
style={{ fontSize: "18px", fontWeight: 500, marginLeft: "10px" }}
|
||||
>
|
||||
Connection to {modelName} successful!
|
||||
{t("addModel.modelConnectionTest.connectionSuccess", { modelName })}
|
||||
</Text>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -195,7 +197,7 @@ ${formattedBody}
|
|||
<div style={{ display: "flex", alignItems: "center", marginBottom: "20px" }}>
|
||||
<WarningOutlined style={{ color: "#ff4d4f", fontSize: "24px", marginRight: "12px" }} />
|
||||
<Text data-testid="connection-failure-msg" type="danger" style={{ fontSize: "18px", fontWeight: 500 }}>
|
||||
Connection to {modelName} failed
|
||||
{t("addModel.modelConnectionTest.connectionFailed", { modelName })}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
|
|
@ -210,7 +212,7 @@ ${formattedBody}
|
|||
}}
|
||||
>
|
||||
<Text strong style={{ display: "block", marginBottom: "8px" }}>
|
||||
Error:{" "}
|
||||
{t("addModel.modelConnectionTest.errorLabel")}
|
||||
</Text>
|
||||
<Text type="danger" style={{ fontSize: "14px", lineHeight: "1.5" }}>
|
||||
{errorMessage}
|
||||
|
|
@ -223,7 +225,9 @@ ${formattedBody}
|
|||
onClick={() => setShowDetails(!showDetails)}
|
||||
style={{ paddingLeft: 0, height: "auto" }}
|
||||
>
|
||||
{showDetails ? "Hide Details" : "Show Details"}
|
||||
{showDetails
|
||||
? t("addModel.modelConnectionTest.hideDetails")
|
||||
: t("addModel.modelConnectionTest.showDetails")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -232,7 +236,7 @@ ${formattedBody}
|
|||
{showDetails && (
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<Text strong style={{ display: "block", marginBottom: "8px", fontSize: "15px" }}>
|
||||
Troubleshooting Details
|
||||
{t("addModel.modelConnectionTest.troubleshootingDetails")}
|
||||
</Text>
|
||||
<pre
|
||||
style={{
|
||||
|
|
@ -253,7 +257,7 @@ ${formattedBody}
|
|||
|
||||
<div>
|
||||
<Text strong style={{ display: "block", marginBottom: "8px", fontSize: "15px" }}>
|
||||
API Request
|
||||
{t("addModel.modelConnectionTest.apiRequest")}
|
||||
</Text>
|
||||
<pre
|
||||
style={{
|
||||
|
|
@ -267,17 +271,17 @@ ${formattedBody}
|
|||
lineHeight: "1.5",
|
||||
}}
|
||||
>
|
||||
{curlCommand || "No request data available"}
|
||||
{curlCommand || t("addModel.modelConnectionTest.noRequestData")}
|
||||
</pre>
|
||||
<Button
|
||||
style={{ marginTop: "8px" }}
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(curlCommand || "");
|
||||
NotificationsManager.success("Copied to clipboard");
|
||||
NotificationsManager.success(t("addModel.modelConnectionTest.copiedToClipboard"));
|
||||
}}
|
||||
>
|
||||
Copy to Clipboard
|
||||
{t("addModel.modelConnectionTest.copyToClipboard")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -286,7 +290,7 @@ ${formattedBody}
|
|||
<Divider style={{ margin: "24px 0 16px" }} />
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Button type="link" href="https://docs.litellm.ai/docs/providers" target="_blank" icon={<InfoCircleOutlined />}>
|
||||
View Documentation
|
||||
{t("addModel.modelConnectionTest.viewDocumentation")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { UploadOutlined } from "@ant-design/icons";
|
|||
import { Text, TextInput } from "@tremor/react";
|
||||
import { Button as Button2, Col, Form, Input, Row, Select, Typography, Upload, UploadProps } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CredentialItem, ProviderCredentialFieldMetadata } from "../networking";
|
||||
import { provider_map, Providers } from "../provider_info_helpers";
|
||||
const { Link } = Typography;
|
||||
|
|
@ -93,8 +94,9 @@ export const createCredentialFromModel = (provider: string, modelData: any): Cre
|
|||
};
|
||||
|
||||
const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selectedProvider, uploadProps }) => {
|
||||
const { t } = useTranslation();
|
||||
const selectedProviderEnum = Providers[selectedProvider as keyof typeof Providers] as Providers;
|
||||
const form = Form.useFormInstance(); // Get form instance from context
|
||||
const form = Form.useFormInstance();
|
||||
|
||||
const { data: providerMetadata, isLoading, error: loadError } = useProviderFields();
|
||||
|
||||
|
|
@ -201,7 +203,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
|
|||
{isLoading && allFields.length === 0 && (
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Text className="mb-2">Loading provider fields...</Text>
|
||||
<Text className="mb-2">{t("addModel.providerSpecificFields.loadingFields")}</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
|
|
@ -209,7 +211,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
|
|||
<Row>
|
||||
<Col span={24}>
|
||||
<Text className="mb-2 text-red-500">
|
||||
{loadError instanceof Error ? loadError.message : "Failed to load provider credential fields"}
|
||||
{loadError instanceof Error ? loadError.message : t("addModel.providerSpecificFields.loadFailed")}
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
@ -219,7 +221,9 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
|
|||
<Form.Item
|
||||
label={field.label}
|
||||
name={field.key}
|
||||
rules={field.required ? [{ required: true, message: "Required" }] : undefined}
|
||||
rules={
|
||||
field.required ? [{ required: true, message: t("addModel.providerSpecificFields.required") }] : undefined
|
||||
}
|
||||
tooltip={field.tooltip}
|
||||
className={field.key === "vertex_credentials" ? "mb-0" : undefined}
|
||||
>
|
||||
|
|
@ -247,7 +251,7 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
|
|||
}, 500);
|
||||
}}
|
||||
>
|
||||
<Button2 icon={<UploadOutlined />}>Click to Upload</Button2>
|
||||
<Button2 icon={<UploadOutlined />}>{t("addModel.providerSpecificFields.clickToUpload")}</Button2>
|
||||
</Upload>
|
||||
) : field.type === "textarea" ? (
|
||||
<Input.TextArea
|
||||
|
|
@ -269,23 +273,22 @@ const ProviderSpecificFields: React.FC<ProviderSpecificFieldsProps> = ({ selecte
|
|||
{field.key === "vertex_credentials" && (
|
||||
<Row>
|
||||
<Col>
|
||||
<Text className="mb-3 mt-1">Give a gcp service account(.json file)</Text>
|
||||
<Text className="mb-3 mt-1">{t("addModel.providerSpecificFields.vertexCredentialsHelp")}</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
{/* Special case for Azure Base Model help text */}
|
||||
{field.key === "base_model" && (
|
||||
<Row>
|
||||
<Col span={10}></Col>
|
||||
<Col span={10}>
|
||||
<Text className="mb-2">
|
||||
The actual model your azure deployment uses. Used for accurate cost tracking. Select name from{" "}
|
||||
{t("addModel.providerSpecificFields.baseModelHelp")}{" "}
|
||||
<Link
|
||||
href="https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json"
|
||||
target="_blank"
|
||||
>
|
||||
here
|
||||
{t("addModel.providerSpecificFields.baseModelHelpLink")}
|
||||
</Link>
|
||||
</Text>
|
||||
</Col>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue