mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
feat(ui): i18n for playground chat UI
This commit is contained in:
parent
da509c009a
commit
c48e2ad23f
3 changed files with 396 additions and 140 deletions
|
|
@ -24,6 +24,7 @@ import {
|
|||
import { Card, Text, TextInput, Title, Button as TremorButton } from "@tremor/react";
|
||||
import { Button, Input, Modal, Popover, Select, Spin, Tooltip, Typography, Upload } from "antd";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { coy } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
|
|
@ -109,6 +110,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
simplified = false,
|
||||
fixedModel,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [mcpServers, setMCPServers] = useState<MCPServer[]>([]);
|
||||
const [mcpToolsets, setMCPToolsets] = useState<MCPToolset[]>([]);
|
||||
const [isToolsetsInfoModalVisible, setIsToolsetsInfoModalVisible] = useState(false);
|
||||
|
|
@ -491,7 +493,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
abortControllerRef.current.abort();
|
||||
abortControllerRef.current = null;
|
||||
setIsLoading(false);
|
||||
NotificationsManager.info("Request cancelled");
|
||||
NotificationsManager.info(t("playground.chatUi.requestCancelled"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -565,19 +567,19 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
|
||||
// For image edits, require both image and prompt
|
||||
if (endpointType === EndpointType.IMAGE_EDITS && uploadedImages.length === 0) {
|
||||
NotificationsManager.fromBackend("Please upload at least one image for editing");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseUploadImage"));
|
||||
return;
|
||||
}
|
||||
|
||||
// For audio transcriptions, require audio file
|
||||
if (endpointType === EndpointType.TRANSCRIPTION && !uploadedAudio) {
|
||||
NotificationsManager.fromBackend("Please upload an audio file for transcription");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseUploadAudio"));
|
||||
return;
|
||||
}
|
||||
|
||||
// For A2A agents, require agent selection
|
||||
if (endpointType === EndpointType.A2A_AGENTS && !selectedAgent) {
|
||||
NotificationsManager.fromBackend("Please select an agent to send a message");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectAgent"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -587,13 +589,13 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
const rawSelected =
|
||||
selectedMCPServers.length === 1 && selectedMCPServers[0] !== "__all__" ? selectedMCPServers[0] : null;
|
||||
if (!rawSelected) {
|
||||
NotificationsManager.fromBackend("Please select an MCP server to test");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectMCPServer"));
|
||||
return;
|
||||
}
|
||||
// Resolve the real server ID (toolsets use toolset: prefix)
|
||||
const mcpServerId = rawSelected.startsWith("toolset:") ? rawSelected : rawSelected;
|
||||
if (!selectedMCPDirectTool) {
|
||||
NotificationsManager.fromBackend("Please select an MCP tool to call");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectMCPTool"));
|
||||
return;
|
||||
}
|
||||
// For toolsets, find the tool in the servers that back this toolset
|
||||
|
|
@ -611,13 +613,15 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
}
|
||||
const mcpTool = searchPool.find((t: any) => t.name === selectedMCPDirectTool);
|
||||
if (!mcpTool) {
|
||||
NotificationsManager.fromBackend("Please wait for tool schema to load");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.waitForToolSchema"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mcpToolArguments = (await mcpToolArgsFormRef.current?.getSubmitValues()) ?? {};
|
||||
} catch (err) {
|
||||
NotificationsManager.fromBackend(err instanceof Error ? err.message : "Please fill in all required parameters");
|
||||
NotificationsManager.fromBackend(
|
||||
err instanceof Error ? err.message : t("playground.chatUi.fillRequiredParameters"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -636,7 +640,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
];
|
||||
|
||||
if (modelRequiredEndpoints.includes(endpointType as EndpointType) && !selectedModel) {
|
||||
NotificationsManager.fromBackend("Please select a model before sending a request");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseSelectModel"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -647,7 +651,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
const effectiveApiKey = simplified ? accessToken : apiKeySource === "session" ? accessToken : apiKey;
|
||||
|
||||
if (!effectiveApiKey) {
|
||||
NotificationsManager.fromBackend("Please provide a Virtual Key or select Current UI Session");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.pleaseProvideVirtualKey"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +667,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
try {
|
||||
newUserMessage = await createMultimodalMessage(inputMessage, responsesUploadedImage);
|
||||
} catch (error) {
|
||||
NotificationsManager.fromBackend("Failed to process image. Please try again.");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.failedToProcessImage"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -672,7 +676,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
try {
|
||||
newUserMessage = await createChatMultimodalMessage(inputMessage, chatUploadedImage);
|
||||
} catch (error) {
|
||||
NotificationsManager.fromBackend("Failed to process image. Please try again.");
|
||||
NotificationsManager.fromBackend(t("playground.chatUi.failedToProcessImage"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -939,7 +943,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
2,
|
||||
)
|
||||
: JSON.stringify(result, null, 2);
|
||||
updateTextUI("assistant", resultText || "Tool executed successfully.");
|
||||
updateTextUI("assistant", resultText || t("playground.chatUi.toolExecutedSuccessfully"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -963,7 +967,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
console.log("Request was cancelled");
|
||||
} else {
|
||||
console.error("Error fetching response", error);
|
||||
updateTextUI("assistant", "Error fetching response:" + error);
|
||||
updateTextUI("assistant", t("playground.chatUi.errorFetchingResponse", { error }));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
|
@ -995,15 +999,15 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
handleRemoveResponsesImage();
|
||||
handleRemoveChatImage();
|
||||
handleRemoveAudio();
|
||||
NotificationsManager.success("Chat history cleared.");
|
||||
NotificationsManager.success(t("playground.chatUi.chatHistoryCleared"));
|
||||
};
|
||||
|
||||
if (userRole && userRole === "Admin Viewer") {
|
||||
const { Title, Paragraph } = Typography;
|
||||
return (
|
||||
<div>
|
||||
<Title level={1}>Access Denied</Title>
|
||||
<Paragraph>Ask your proxy admin for access to test models</Paragraph>
|
||||
<Title level={1}>{t("playground.chatUi.accessDenied")}</Title>
|
||||
<Paragraph>{t("playground.chatUi.askProxyAdminForAccess")}</Paragraph>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1037,11 +1041,11 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
{/* Left Sidebar with Controls - hidden in simplified mode */}
|
||||
{!simplified && (
|
||||
<div className="w-1/4 p-4 bg-gray-50 overflow-y-auto">
|
||||
<Title className="text-xl font-semibold mb-6 mt-2">Configurations</Title>
|
||||
<Title className="text-xl font-semibold mb-6 mt-2">{t("playground.chatUi.configurations")}</Title>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<KeyOutlined className="mr-2" /> Virtual Key Source
|
||||
<KeyOutlined className="mr-2" /> {t("playground.chatUi.virtualKeySource")}
|
||||
</Text>
|
||||
<Select
|
||||
disabled={disabledPersonalKeyCreation}
|
||||
|
|
@ -1051,15 +1055,15 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
setApiKeySource(value as "session" | "custom");
|
||||
}}
|
||||
options={[
|
||||
{ value: "session", label: "Current UI Session" },
|
||||
{ value: "custom", label: "Virtual Key" },
|
||||
{ value: "session", label: t("playground.chatUi.currentUiSession") },
|
||||
{ value: "custom", label: t("playground.chatUi.virtualKeyLabel") },
|
||||
]}
|
||||
className="rounded-md"
|
||||
/>
|
||||
{apiKeySource === "custom" && (
|
||||
<TextInput
|
||||
className="mt-2"
|
||||
placeholder="Enter custom Virtual Key"
|
||||
placeholder={t("playground.chatUi.enterCustomVirtualKey")}
|
||||
type="password"
|
||||
onValueChange={setApiKey}
|
||||
value={apiKey}
|
||||
|
|
@ -1071,7 +1075,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="font-medium block text-gray-700 flex items-center">
|
||||
<SettingOutlined className="mr-2" /> Custom Proxy Base URL
|
||||
<SettingOutlined className="mr-2" /> {t("playground.chatUi.customProxyBaseUrl")}
|
||||
</Text>
|
||||
{proxySettings?.LITELLM_UI_API_DOC_BASE_URL && !customProxyBaseUrl && (
|
||||
<Button
|
||||
|
|
@ -1084,7 +1088,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
}}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
Fill
|
||||
{t("playground.chatUi.fill")}
|
||||
</Button>
|
||||
)}
|
||||
{customProxyBaseUrl && (
|
||||
|
|
@ -1098,12 +1102,12 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
}}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
Clear
|
||||
{t("common.clear")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<TextInput
|
||||
placeholder="Optional: Enter custom proxy URL (e.g., http://localhost:5000)"
|
||||
placeholder={t("playground.chatUi.customProxyUrlPlaceholder")}
|
||||
onValueChange={(value) => {
|
||||
setCustomProxyBaseUrl(value);
|
||||
sessionStorage.setItem("customProxyBaseUrl", value);
|
||||
|
|
@ -1112,13 +1116,15 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
icon={ApiOutlined}
|
||||
/>
|
||||
{customProxyBaseUrl && (
|
||||
<Text className="text-xs text-gray-500 mt-1">API calls will be sent to: {customProxyBaseUrl}</Text>
|
||||
<Text className="text-xs text-gray-500 mt-1">
|
||||
{t("playground.chatUi.apiCallsSentTo", { url: customProxyBaseUrl })}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<ApiOutlined className="mr-2" /> Endpoint Type
|
||||
<ApiOutlined className="mr-2" /> {t("playground.chatUi.endpointTypeLabel")}
|
||||
</Text>
|
||||
<EndpointSelector
|
||||
endpointType={endpointType}
|
||||
|
|
@ -1146,7 +1152,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<div className="mb-4">
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<SoundOutlined className="mr-2" />
|
||||
Voice
|
||||
{t("playground.chatUi.voiceLabel")}
|
||||
</Text>
|
||||
<Select
|
||||
value={selectedVoice}
|
||||
|
|
@ -1175,7 +1181,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center justify-between">
|
||||
<span className="flex items-center">
|
||||
<RobotOutlined className="mr-2" /> Select Model
|
||||
<RobotOutlined className="mr-2" /> {t("playground.chatUi.selectModelLabel")}
|
||||
</span>
|
||||
{isChatModel() ? (
|
||||
<Popover
|
||||
|
|
@ -1191,7 +1197,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
onMockTestFallbacksChange={setMockTestFallbacks}
|
||||
/>
|
||||
}
|
||||
title="Model Settings"
|
||||
title={t("playground.chatUi.modelSettings")}
|
||||
trigger="click"
|
||||
placement="right"
|
||||
>
|
||||
|
|
@ -1200,12 +1206,12 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
size="small"
|
||||
icon={<SettingOutlined />}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
aria-label="Model Settings"
|
||||
aria-label={t("playground.chatUi.modelSettings")}
|
||||
data-testid="model-settings-button"
|
||||
/>
|
||||
</Popover>
|
||||
) : (
|
||||
<Tooltip title="Advanced parameters are only supported for chat models currently">
|
||||
<Tooltip title={t("playground.chatUi.advancedParamsTooltip")}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
|
|
@ -1218,10 +1224,10 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
</Text>
|
||||
<Select
|
||||
value={selectedModel}
|
||||
placeholder="Select a Model"
|
||||
placeholder={t("playground.chatUi.selectModelPlaceholder")}
|
||||
onChange={onModelChange}
|
||||
options={[
|
||||
{ value: "custom", label: "Enter custom model", key: "custom" },
|
||||
{ value: "custom", label: t("playground.chatUi.enterCustomModel"), key: "custom" },
|
||||
...Array.from(
|
||||
new Set(
|
||||
modelInfo
|
||||
|
|
@ -1260,7 +1266,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
{showCustomModelInput && (
|
||||
<TextInput
|
||||
className="mt-2"
|
||||
placeholder="Enter custom model name"
|
||||
placeholder={t("playground.chatUi.enterCustomModelName")}
|
||||
onValueChange={(value) => {
|
||||
// Using setTimeout to create a simple debounce effect
|
||||
if (customModelTimeout.current) {
|
||||
|
|
@ -1280,11 +1286,11 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
{endpointType === EndpointType.A2A_AGENTS && (
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<RobotOutlined className="mr-2" /> Select Agent
|
||||
<RobotOutlined className="mr-2" /> {t("playground.chatUi.selectAgentLabel")}
|
||||
</Text>
|
||||
<Select
|
||||
value={selectedAgent}
|
||||
placeholder="Select an Agent"
|
||||
placeholder={t("playground.chatUi.selectAgentPlaceholder")}
|
||||
onChange={(value) => setSelectedAgent(value)}
|
||||
options={agentInfo.map((agent) => ({
|
||||
value: agent.agent_name,
|
||||
|
|
@ -1312,16 +1318,14 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
))}
|
||||
</Select>
|
||||
{agentInfo.length === 0 && (
|
||||
<Text className="text-xs text-gray-500 mt-2 block">
|
||||
No agents found. Create agents via /v1/agents endpoint.
|
||||
</Text>
|
||||
<Text className="text-xs text-gray-500 mt-2 block">{t("playground.chatUi.noAgentsFound")}</Text>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<TagsOutlined className="mr-2" /> Tags
|
||||
<TagsOutlined className="mr-2" /> {t("playground.chatUi.tagsLabel")}
|
||||
</Text>
|
||||
<TagSelector
|
||||
value={selectedTags}
|
||||
|
|
@ -1335,13 +1339,15 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<ToolOutlined className="mr-2" />
|
||||
{endpointType === EndpointType.MCP ? "MCP Server" : "MCP Servers"}
|
||||
{endpointType === EndpointType.MCP
|
||||
? t("playground.chatUi.mcpServerLabel")
|
||||
: t("playground.chatUi.mcpServersLabel")}
|
||||
<Tooltip
|
||||
className="ml-1"
|
||||
title={
|
||||
endpointType === EndpointType.MCP
|
||||
? "Select an MCP server or toolset to test tools directly."
|
||||
: "Select MCP servers or toolsets to use in your conversation."
|
||||
? t("playground.chatUi.mcpServerTooltipDirect")
|
||||
: t("playground.chatUi.mcpServerTooltip")
|
||||
}
|
||||
>
|
||||
<InfoCircleOutlined
|
||||
|
|
@ -1353,7 +1359,11 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<Select
|
||||
mode={endpointType === EndpointType.MCP ? undefined : "multiple"}
|
||||
style={{ width: "100%" }}
|
||||
placeholder={endpointType === EndpointType.MCP ? "Select MCP server" : "Select MCP servers"}
|
||||
placeholder={
|
||||
endpointType === EndpointType.MCP
|
||||
? t("playground.chatUi.selectMCPServerPlaceholder")
|
||||
: t("playground.chatUi.selectMCPServersPlaceholder")
|
||||
}
|
||||
value={
|
||||
endpointType === EndpointType.MCP
|
||||
? selectedMCPServers[0] !== "__all__" && selectedMCPServers.length === 1
|
||||
|
|
@ -1399,7 +1409,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
maxTagCount={endpointType === EndpointType.MCP ? 1 : "responsive"}
|
||||
filterOption={(input, option) => {
|
||||
if (option?.value === "__all__") {
|
||||
return "All MCP Servers".toLowerCase().includes(input.toLowerCase());
|
||||
return t("playground.chatUi.allMCPServers").toLowerCase().includes(input.toLowerCase());
|
||||
}
|
||||
const val = option?.value as string | undefined;
|
||||
if (val?.startsWith("toolset:")) {
|
||||
|
|
@ -1423,17 +1433,17 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
>
|
||||
{/* All MCP Servers option - hidden for MCP direct mode */}
|
||||
{endpointType !== EndpointType.MCP && (
|
||||
<Select.Option key="__all__" value="__all__" label="All MCP Servers">
|
||||
<Select.Option key="__all__" value="__all__" label={t("playground.chatUi.allMCPServers")}>
|
||||
<div className="flex flex-col py-1">
|
||||
<span className="font-medium">All MCP Servers</span>
|
||||
<span className="text-xs text-gray-500 mt-1">Use all available MCP servers</span>
|
||||
<span className="font-medium">{t("playground.chatUi.allMCPServers")}</span>
|
||||
<span className="text-xs text-gray-500 mt-1">{t("playground.chatUi.useAllMCPServers")}</span>
|
||||
</div>
|
||||
</Select.Option>
|
||||
)}
|
||||
|
||||
{/* Toolsets (purple badge) */}
|
||||
{mcpToolsets.length > 0 && (
|
||||
<Select.OptGroup label="Toolsets">
|
||||
<Select.OptGroup label={t("playground.chatUi.toolsetsGroup")}>
|
||||
{mcpToolsets.map((toolset) => (
|
||||
<Select.Option
|
||||
key={`toolset:${toolset.toolset_id}`}
|
||||
|
|
@ -1450,9 +1460,11 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
className="text-xs px-1 rounded"
|
||||
style={{ background: "#ede9fe", color: "#7c3aed" }}
|
||||
>
|
||||
Toolset
|
||||
{t("playground.chatUi.toolsetBadge")}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
{t("playground.chatUi.toolCount", { count: toolset.tools.length })}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">({toolset.tools.length} tools)</span>
|
||||
</div>
|
||||
{toolset.description && (
|
||||
<span className="text-xs text-gray-500 mt-1">{toolset.description}</span>
|
||||
|
|
@ -1465,7 +1477,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
|
||||
{/* Individual servers */}
|
||||
{mcpServers.length > 0 && (
|
||||
<Select.OptGroup label="Servers">
|
||||
<Select.OptGroup label={t("playground.chatUi.serversGroup")}>
|
||||
{mcpServers.map((server) => (
|
||||
<Select.Option
|
||||
key={server.server_id}
|
||||
|
|
@ -1514,10 +1526,12 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
}
|
||||
return (
|
||||
<div className="mt-3">
|
||||
<Text className="text-xs text-gray-600 mb-1 block">Select Tool</Text>
|
||||
<Text className="text-xs text-gray-600 mb-1 block">
|
||||
{t("playground.chatUi.selectToolLabel")}
|
||||
</Text>
|
||||
<Select
|
||||
style={{ width: "100%" }}
|
||||
placeholder="Select a tool to call"
|
||||
placeholder={t("playground.chatUi.selectToolPlaceholder")}
|
||||
value={selectedMCPDirectTool}
|
||||
onChange={(value) => setSelectedMCPDirectTool(value)}
|
||||
options={toolOptions}
|
||||
|
|
@ -1542,13 +1556,15 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
return (
|
||||
<div key={serverId} className="border rounded p-2">
|
||||
<Text className="text-xs text-gray-600 mb-1">
|
||||
Limit tools for {server?.alias || server?.server_name || serverId}:
|
||||
{t("playground.chatUi.limitToolsFor", {
|
||||
serverName: server?.alias || server?.server_name || serverId,
|
||||
})}
|
||||
</Text>
|
||||
<Select
|
||||
mode="multiple"
|
||||
size="small"
|
||||
style={{ width: "100%" }}
|
||||
placeholder="All tools (default)"
|
||||
placeholder={t("playground.chatUi.allToolsDefault")}
|
||||
value={mcpServerToolRestrictions[serverId] || []}
|
||||
onChange={(selectedTools) => {
|
||||
setMCPServerToolRestrictions((prev) => ({
|
||||
|
|
@ -1585,17 +1601,19 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
key={serverId}
|
||||
className="border border-blue-100 rounded p-2 bg-blue-50 flex items-center justify-between"
|
||||
>
|
||||
<Text className="text-xs text-blue-700">{serverName} requires your API key</Text>
|
||||
<Text className="text-xs text-blue-700">
|
||||
{t("playground.chatUi.serverRequiresApiKey", { serverName })}
|
||||
</Text>
|
||||
{server.has_user_credential ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-green-600 text-xs font-medium flex items-center gap-1">
|
||||
<KeyOutlined /> Connected
|
||||
<KeyOutlined /> {t("playground.chatUi.connected")}
|
||||
</span>
|
||||
<button
|
||||
className="text-xs text-gray-400 hover:text-blue-500 underline"
|
||||
onClick={() => setByokModalServer(server)}
|
||||
>
|
||||
Reconnect
|
||||
{t("playground.chatUi.reconnect")}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -1603,7 +1621,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
className="text-xs bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded-lg font-medium"
|
||||
onClick={() => setByokModalServer(server)}
|
||||
>
|
||||
Connect
|
||||
{t("playground.chatUi.connectButton")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1615,17 +1633,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<DatabaseOutlined className="mr-2" /> Vector Store
|
||||
<DatabaseOutlined className="mr-2" /> {t("playground.chatUi.vectorStoreLabel")}
|
||||
<Tooltip
|
||||
className="ml-1"
|
||||
title={
|
||||
<span>
|
||||
Select vector store(s) to use for this LLM API call. You can set up your vector store{" "}
|
||||
<a href="?page=vector-stores" style={{ color: "#1890ff" }}>
|
||||
here
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
<Trans
|
||||
i18nKey="playground.chatUi.vectorStoreTooltip"
|
||||
components={{
|
||||
setupLink: <a href="?page=vector-stores" style={{ color: "#1890ff" }} />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InfoCircleOutlined />
|
||||
|
|
@ -1641,17 +1658,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<SafetyOutlined className="mr-2" /> Guardrails
|
||||
<SafetyOutlined className="mr-2" /> {t("playground.chatUi.guardrailsLabel")}
|
||||
<Tooltip
|
||||
className="ml-1"
|
||||
title={
|
||||
<span>
|
||||
Select guardrail(s) to use for this LLM API call. You can set up your guardrails{" "}
|
||||
<a href="?page=guardrails" style={{ color: "#1890ff" }}>
|
||||
here
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
<Trans
|
||||
i18nKey="playground.chatUi.guardrailsTooltip"
|
||||
components={{
|
||||
setupLink: <a href="?page=guardrails" style={{ color: "#1890ff" }} />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InfoCircleOutlined />
|
||||
|
|
@ -1667,18 +1683,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
|
||||
<div>
|
||||
<Text className="font-medium block mb-2 text-gray-700 flex items-center">
|
||||
<SafetyOutlined className="mr-2" /> Policies
|
||||
<SafetyOutlined className="mr-2" /> {t("playground.chatUi.policiesLabel")}
|
||||
<Tooltip
|
||||
className="ml-1"
|
||||
title={
|
||||
<span>
|
||||
Select policy/policies to apply to this LLM API call. Policies define which guardrails are
|
||||
applied based on conditions. You can set up your policies{" "}
|
||||
<a href="?page=policies" style={{ color: "#1890ff" }}>
|
||||
here
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
<Trans
|
||||
i18nKey="playground.chatUi.policiesTooltip"
|
||||
components={{
|
||||
setupLink: <a href="?page=policies" style={{ color: "#1890ff" }} />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<InfoCircleOutlined />
|
||||
|
|
@ -1721,14 +1735,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
) : (
|
||||
<>
|
||||
<div className="p-4 border-b border-gray-200 flex justify-between items-center">
|
||||
<Title className="text-xl font-semibold mb-0">{simplified ? "Chat" : "Test Key"}</Title>
|
||||
<Title className="text-xl font-semibold mb-0">
|
||||
{simplified ? t("playground.chatUi.chatTitle") : t("playground.chatUi.testKeyTitle")}
|
||||
</Title>
|
||||
<div className="flex gap-2">
|
||||
<TremorButton
|
||||
onClick={clearChatHistory}
|
||||
className="bg-gray-100 hover:bg-gray-200 text-gray-700 border-gray-300"
|
||||
icon={ClearOutlined}
|
||||
>
|
||||
Clear Chat
|
||||
{t("playground.chatUi.clearChat")}
|
||||
</TremorButton>
|
||||
{!simplified && (
|
||||
<TremorButton
|
||||
|
|
@ -1736,7 +1752,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
className="bg-gray-100 hover:bg-gray-200 text-gray-700 border-gray-300"
|
||||
icon={CodeOutlined}
|
||||
>
|
||||
Get Code
|
||||
{t("playground.chatUi.getCode")}
|
||||
</TremorButton>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1745,7 +1761,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
{chatHistory.length === 0 && (
|
||||
<div className="h-full flex flex-col items-center justify-center text-gray-400">
|
||||
<RobotOutlined style={{ fontSize: "48px", marginBottom: "16px" }} />
|
||||
<Text>Start a conversation, generate an image, or handle audio</Text>
|
||||
<Text>{t("playground.chatUi.emptyStateHint")}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -1786,7 +1802,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
>
|
||||
<RobotOutlined style={{ fontSize: "12px", color: "#4b5563" }} />
|
||||
</div>
|
||||
<strong className="text-sm capitalize">Assistant</strong>
|
||||
<strong className="text-sm capitalize">{t("playground.chatUi.assistant")}</strong>
|
||||
</div>
|
||||
<MCPEventsDisplay events={mcpEvents} />
|
||||
</div>
|
||||
|
|
@ -1810,9 +1826,9 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<p className="ant-upload-drag-icon">
|
||||
<PictureOutlined style={{ fontSize: "24px", color: "#666" }} />
|
||||
</p>
|
||||
<p className="ant-upload-text text-sm">Click or drag images to upload</p>
|
||||
<p className="ant-upload-text text-sm">{t("playground.chatUi.dragImagesToUpload")}</p>
|
||||
<p className="ant-upload-hint text-xs text-gray-500">
|
||||
Support for PNG, JPG, JPEG formats. Multiple images supported.
|
||||
{t("playground.chatUi.imageUploadHint")}
|
||||
</p>
|
||||
</Dragger>
|
||||
) : (
|
||||
|
|
@ -1830,7 +1846,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
return "";
|
||||
}
|
||||
})()}
|
||||
alt={`Upload preview ${index + 1}`}
|
||||
alt={t("playground.chatUi.uploadPreviewAlt", { index: index + 1 })}
|
||||
className="max-w-32 max-h-32 rounded-md border border-gray-200 object-cover"
|
||||
/>
|
||||
<button
|
||||
|
|
@ -1848,7 +1864,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
>
|
||||
<div className="text-center">
|
||||
<PictureOutlined style={{ fontSize: "24px", color: "#666" }} />
|
||||
<p className="text-xs text-gray-500 mt-1">Add more</p>
|
||||
<p className="text-xs text-gray-500 mt-1">{t("playground.chatUi.addMore")}</p>
|
||||
</div>
|
||||
<input
|
||||
id="additional-image-upload"
|
||||
|
|
@ -1879,9 +1895,9 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<p className="ant-upload-drag-icon">
|
||||
<SoundOutlined style={{ fontSize: "24px", color: "#666" }} />
|
||||
</p>
|
||||
<p className="ant-upload-text text-sm">Click or drag audio file to upload</p>
|
||||
<p className="ant-upload-text text-sm">{t("playground.chatUi.dragAudioToUpload")}</p>
|
||||
<p className="ant-upload-hint text-xs text-gray-500">
|
||||
Support for MP3, MP4, MPEG, MPGA, M4A, WAV, WEBM formats. Max file size: 25 MB.
|
||||
{t("playground.chatUi.audioUploadHint")}
|
||||
</p>
|
||||
</Dragger>
|
||||
) : (
|
||||
|
|
@ -1897,7 +1913,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
className="bg-white shadow-sm border border-gray-200 rounded px-2 py-1 text-red-500 hover:bg-red-50 text-xs"
|
||||
onClick={handleRemoveAudio}
|
||||
>
|
||||
<DeleteOutlined /> Remove
|
||||
<DeleteOutlined /> {t("playground.chatUi.removeButton")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1929,12 +1945,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
{isLoading ? (
|
||||
<>
|
||||
<LoadingOutlined className="text-blue-500" spin />
|
||||
<span className="text-sm text-blue-700 font-medium">Running Python code...</span>
|
||||
<span className="text-sm text-blue-700 font-medium">
|
||||
{t("playground.chatUi.runningPythonCode")}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CodeOutlined className="text-blue-500" />
|
||||
<span className="text-sm text-blue-700 font-medium">Code Interpreter Active</span>
|
||||
<span className="text-sm text-blue-700 font-medium">
|
||||
{t("playground.chatUi.codeInterpreterActive")}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1942,16 +1962,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
className="text-xs text-blue-500 hover:text-blue-700"
|
||||
onClick={() => codeInterpreter.setEnabled(false)}
|
||||
>
|
||||
Disable
|
||||
{t("playground.chatUi.disableButton")}
|
||||
</button>
|
||||
</div>
|
||||
{/* Sample prompts - only show when not loading */}
|
||||
{!isLoading && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{[
|
||||
"Generate sample sales data CSV and create a chart",
|
||||
"Create a PNG bar chart comparing AI gateway providers including LiteLLM",
|
||||
"Generate a CSV of LLM pricing data and visualize it as a line chart",
|
||||
t("playground.chatUi.samplePrompt1"),
|
||||
t("playground.chatUi.samplePrompt2"),
|
||||
t("playground.chatUi.samplePrompt3"),
|
||||
].map((prompt, idx) => (
|
||||
<button
|
||||
key={idx}
|
||||
|
|
@ -1970,8 +1990,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
{chatHistory.length === 0 && !isLoading && endpointType !== EndpointType.MCP && (
|
||||
<div className="flex items-center gap-2 mb-3 overflow-x-auto">
|
||||
{(endpointType === EndpointType.A2A_AGENTS
|
||||
? ["What can you help me with?", "Tell me about yourself", "What tasks can you perform?"]
|
||||
: ["Write me a poem", "Explain quantum computing", "Draft a polite email requesting a meeting"]
|
||||
? [
|
||||
t("playground.chatUi.suggestPromptA2A1"),
|
||||
t("playground.chatUi.suggestPromptA2A2"),
|
||||
t("playground.chatUi.suggestPromptA2A3"),
|
||||
]
|
||||
: [
|
||||
t("playground.chatUi.suggestPrompt1"),
|
||||
t("playground.chatUi.suggestPrompt2"),
|
||||
t("playground.chatUi.suggestPrompt3"),
|
||||
]
|
||||
).map((prompt) => (
|
||||
<button
|
||||
key={prompt}
|
||||
|
|
@ -2010,8 +2038,8 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<Tooltip
|
||||
title={
|
||||
codeInterpreter.enabled
|
||||
? "Code Interpreter enabled (click to disable)"
|
||||
: "Enable Code Interpreter"
|
||||
? t("playground.chatUi.codeInterpreterEnabledTooltip")
|
||||
: t("playground.chatUi.enableCodeInterpreterTooltip")
|
||||
}
|
||||
>
|
||||
<button
|
||||
|
|
@ -2023,7 +2051,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
onClick={() => {
|
||||
codeInterpreter.toggle();
|
||||
if (!codeInterpreter.enabled) {
|
||||
NotificationsManager.success("Code Interpreter enabled!");
|
||||
NotificationsManager.success(t("playground.chatUi.codeInterpreterEnabledNotice"));
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -2059,7 +2087,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
<MCPToolArgumentsForm ref={mcpToolArgsFormRef} tool={mcpTool} className="space-y-2" />
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-10 text-sm text-gray-500">
|
||||
Loading tool schema...
|
||||
{t("playground.chatUi.loadingToolSchema")}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
|
@ -2075,16 +2103,16 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
endpointType === EndpointType.RESPONSES ||
|
||||
endpointType === EndpointType.ANTHROPIC_MESSAGES ||
|
||||
endpointType === EndpointType.INTERACTIONS
|
||||
? "Type your message... (Shift+Enter for new line)"
|
||||
? t("playground.chatUi.placeholderChat")
|
||||
: endpointType === EndpointType.A2A_AGENTS
|
||||
? "Send a message to the A2A agent..."
|
||||
? t("playground.chatUi.placeholderA2A")
|
||||
: endpointType === EndpointType.IMAGE_EDITS
|
||||
? "Describe how you want to edit the image..."
|
||||
? t("playground.chatUi.placeholderImageEdits")
|
||||
: endpointType === EndpointType.SPEECH
|
||||
? "Enter text to convert to speech..."
|
||||
? t("playground.chatUi.placeholderSpeech")
|
||||
: endpointType === EndpointType.TRANSCRIPTION
|
||||
? "Optional: Add context or prompt for transcription..."
|
||||
: "Describe the image you want to generate..."
|
||||
? t("playground.chatUi.placeholderTranscription")
|
||||
: t("playground.chatUi.placeholderImage")
|
||||
}
|
||||
disabled={isLoading}
|
||||
className="flex-1"
|
||||
|
|
@ -2128,7 +2156,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
className="bg-red-50 hover:bg-red-100 text-red-600 border-red-200"
|
||||
icon={DeleteOutlined}
|
||||
>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</TremorButton>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -2139,7 +2167,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
</div>
|
||||
</Card>
|
||||
<Modal
|
||||
title="Generated Code"
|
||||
title={t("playground.chatUi.generatedCodeTitle")}
|
||||
open={isGetCodeModalVisible}
|
||||
onCancel={() => setIsGetCodeModalVisible(false)}
|
||||
footer={null}
|
||||
|
|
@ -2147,24 +2175,24 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
>
|
||||
<div className="flex justify-between items-end my-4">
|
||||
<div>
|
||||
<Text className="font-medium block mb-1 text-gray-700">SDK Type</Text>
|
||||
<Text className="font-medium block mb-1 text-gray-700">{t("playground.chatUi.sdkTypeLabel")}</Text>
|
||||
<Select
|
||||
value={selectedSdk}
|
||||
onChange={(value) => setSelectedSdk(value as "openai" | "azure")}
|
||||
style={{ width: 150 }}
|
||||
options={[
|
||||
{ value: "openai", label: "OpenAI SDK" },
|
||||
{ value: "azure", label: "Azure SDK" },
|
||||
{ value: "openai", label: t("playground.chatUi.openAiSdk") },
|
||||
{ value: "azure", label: t("playground.chatUi.azureSdk") },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(generatedCode);
|
||||
NotificationsManager.success("Copied to clipboard!");
|
||||
NotificationsManager.success(t("common.copied"));
|
||||
}}
|
||||
>
|
||||
Copy to Clipboard
|
||||
{t("playground.chatUi.copyToClipboard")}
|
||||
</Button>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
|
|
@ -2198,45 +2226,43 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
|||
|
||||
{/* Toolsets info modal */}
|
||||
<Modal
|
||||
title="How Toolsets Work"
|
||||
title={t("playground.chatUi.toolsetsInfoTitle")}
|
||||
open={isToolsetsInfoModalVisible}
|
||||
onCancel={() => setIsToolsetsInfoModalVisible(false)}
|
||||
footer={[
|
||||
<Button key="close" onClick={() => setIsToolsetsInfoModalVisible(false)}>
|
||||
Close
|
||||
{t("common.close")}
|
||||
</Button>,
|
||||
]}
|
||||
width={600}
|
||||
>
|
||||
<div className="space-y-4 py-2">
|
||||
<p className="text-gray-700">
|
||||
<strong>Toolsets</strong> are named collections of specific tools from one or more MCP servers. Instead of
|
||||
exposing all tools from a server, a toolset gives an agent exactly the tools it needs.
|
||||
<Trans i18nKey="playground.chatUi.toolsetsInfoIntro" components={{ strong: <strong /> }} />
|
||||
</p>
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-800 mb-2">How to use a toolset:</h4>
|
||||
<h4 className="font-semibold text-gray-800 mb-2">{t("playground.chatUi.toolsetsHowToUseTitle")}</h4>
|
||||
<ol className="list-decimal list-inside space-y-2 text-gray-700">
|
||||
<li>
|
||||
Select a <span style={{ color: "#7c3aed", fontWeight: 600 }}>Toolset</span> (purple badge) from the MCP
|
||||
Servers dropdown.
|
||||
<Trans
|
||||
i18nKey="playground.chatUi.toolsetsStep1"
|
||||
components={{ purple: <span style={{ color: "#7c3aed", fontWeight: 600 }} /> }}
|
||||
/>
|
||||
</li>
|
||||
<li>The tool picker will show only the tools included in that toolset.</li>
|
||||
<li>Select a tool and fill in its parameters, then send.</li>
|
||||
<li>The tool call is routed to the correct underlying MCP server automatically.</li>
|
||||
<li>{t("playground.chatUi.toolsetsStep2")}</li>
|
||||
<li>{t("playground.chatUi.toolsetsStep3")}</li>
|
||||
<li>{t("playground.chatUi.toolsetsStep4")}</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div className="bg-purple-50 border border-purple-200 rounded p-3">
|
||||
<p className="text-sm text-purple-800">
|
||||
<strong>Example:</strong> A "GitHub Read-only" toolset might include only{" "}
|
||||
<code>list_repos</code> and <code>get_file</code> from a GitHub MCP server — preventing agents from making
|
||||
writes.
|
||||
<Trans i18nKey="playground.chatUi.toolsetsExample" components={{ strong: <strong />, code: <code /> }} />
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-800 mb-1">Creating toolsets:</h4>
|
||||
<h4 className="font-semibold text-gray-800 mb-1">{t("playground.chatUi.toolsetsCreatingTitle")}</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Admins can create and manage toolsets from the <strong>MCP</strong> page → <strong>Toolsets</strong> tab.
|
||||
Toolsets can then be assigned to keys and teams to scope their tool access.
|
||||
<Trans i18nKey="playground.chatUi.toolsetsCreatingDesc" components={{ strong: <strong /> }} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3857,6 +3857,121 @@
|
|||
},
|
||||
"chatMessageBubble": {
|
||||
"generatedImageAlt": "Generated image"
|
||||
},
|
||||
"chatUi": {
|
||||
"requestCancelled": "Request cancelled",
|
||||
"pleaseUploadImage": "Please upload at least one image for editing",
|
||||
"pleaseUploadAudio": "Please upload an audio file for transcription",
|
||||
"pleaseSelectAgent": "Please select an agent to send a message",
|
||||
"pleaseSelectMCPServer": "Please select an MCP server to test",
|
||||
"pleaseSelectMCPTool": "Please select an MCP tool to call",
|
||||
"waitForToolSchema": "Please wait for tool schema to load",
|
||||
"fillRequiredParameters": "Please fill in all required parameters",
|
||||
"pleaseSelectModel": "Please select a model before sending a request",
|
||||
"pleaseProvideVirtualKey": "Please provide a Virtual Key or select Current UI Session",
|
||||
"failedToProcessImage": "Failed to process image. Please try again.",
|
||||
"toolExecutedSuccessfully": "Tool executed successfully.",
|
||||
"errorFetchingResponse": "Error fetching response: {{error}}",
|
||||
"chatHistoryCleared": "Chat history cleared.",
|
||||
"accessDenied": "Access Denied",
|
||||
"askProxyAdminForAccess": "Ask your proxy admin for access to test models",
|
||||
"configurations": "Configurations",
|
||||
"virtualKeySource": "Virtual Key Source",
|
||||
"currentUiSession": "Current UI Session",
|
||||
"virtualKeyLabel": "Virtual Key",
|
||||
"enterCustomVirtualKey": "Enter custom Virtual Key",
|
||||
"customProxyBaseUrl": "Custom Proxy Base URL",
|
||||
"fill": "Fill",
|
||||
"customProxyUrlPlaceholder": "Optional: Enter custom proxy URL (e.g., http://localhost:5000)",
|
||||
"apiCallsSentTo": "API calls will be sent to: {{url}}",
|
||||
"endpointTypeLabel": "Endpoint Type",
|
||||
"voiceLabel": "Voice",
|
||||
"selectModelLabel": "Select Model",
|
||||
"advancedParamsTooltip": "Advanced parameters are only supported for chat models currently",
|
||||
"modelSettings": "Model Settings",
|
||||
"selectModelPlaceholder": "Select a Model",
|
||||
"enterCustomModel": "Enter custom model",
|
||||
"enterCustomModelName": "Enter custom model name",
|
||||
"selectAgentLabel": "Select Agent",
|
||||
"selectAgentPlaceholder": "Select an Agent",
|
||||
"noAgentsFound": "No agents found. Create agents via /v1/agents endpoint.",
|
||||
"tagsLabel": "Tags",
|
||||
"mcpServerLabel": "MCP Server",
|
||||
"mcpServersLabel": "MCP Servers",
|
||||
"mcpServerTooltipDirect": "Select an MCP server or toolset to test tools directly.",
|
||||
"mcpServerTooltip": "Select MCP servers or toolsets to use in your conversation.",
|
||||
"selectMCPServerPlaceholder": "Select MCP server",
|
||||
"selectMCPServersPlaceholder": "Select MCP servers",
|
||||
"allMCPServers": "All MCP Servers",
|
||||
"useAllMCPServers": "Use all available MCP servers",
|
||||
"toolsetsGroup": "Toolsets",
|
||||
"toolsetBadge": "Toolset",
|
||||
"toolCount": "({{count}} tools)",
|
||||
"serversGroup": "Servers",
|
||||
"selectToolLabel": "Select Tool",
|
||||
"selectToolPlaceholder": "Select a tool to call",
|
||||
"limitToolsFor": "Limit tools for {{serverName}}:",
|
||||
"allToolsDefault": "All tools (default)",
|
||||
"serverRequiresApiKey": "{{serverName}} requires your API key",
|
||||
"connected": "Connected",
|
||||
"reconnect": "Reconnect",
|
||||
"connectButton": "Connect",
|
||||
"vectorStoreLabel": "Vector Store",
|
||||
"vectorStoreTooltip": "Select vector store(s) to use for this LLM API call. You can set up your vector store <setupLink>here</setupLink>.",
|
||||
"guardrailsLabel": "Guardrails",
|
||||
"guardrailsTooltip": "Select guardrail(s) to use for this LLM API call. You can set up your guardrails <setupLink>here</setupLink>.",
|
||||
"policiesLabel": "Policies",
|
||||
"policiesTooltip": "Select policy/policies to apply to this LLM API call. Policies define which guardrails are applied based on conditions. You can set up your policies <setupLink>here</setupLink>.",
|
||||
"testKeyTitle": "Test Key",
|
||||
"chatTitle": "Chat",
|
||||
"clearChat": "Clear Chat",
|
||||
"getCode": "Get Code",
|
||||
"emptyStateHint": "Start a conversation, generate an image, or handle audio",
|
||||
"assistant": "Assistant",
|
||||
"dragImagesToUpload": "Click or drag images to upload",
|
||||
"imageUploadHint": "Support for PNG, JPG, JPEG formats. Multiple images supported.",
|
||||
"uploadPreviewAlt": "Upload preview {{index}}",
|
||||
"addMore": "Add more",
|
||||
"dragAudioToUpload": "Click or drag audio file to upload",
|
||||
"audioUploadHint": "Support for MP3, MP4, MPEG, MPGA, M4A, WAV, WEBM formats. Max file size: 25 MB.",
|
||||
"removeButton": "Remove",
|
||||
"runningPythonCode": "Running Python code...",
|
||||
"codeInterpreterActive": "Code Interpreter Active",
|
||||
"disableButton": "Disable",
|
||||
"samplePrompt1": "Generate sample sales data CSV and create a chart",
|
||||
"samplePrompt2": "Create a PNG bar chart comparing AI gateway providers including LiteLLM",
|
||||
"samplePrompt3": "Generate a CSV of LLM pricing data and visualize it as a line chart",
|
||||
"suggestPromptA2A1": "What can you help me with?",
|
||||
"suggestPromptA2A2": "Tell me about yourself",
|
||||
"suggestPromptA2A3": "What tasks can you perform?",
|
||||
"suggestPrompt1": "Write me a poem",
|
||||
"suggestPrompt2": "Explain quantum computing",
|
||||
"suggestPrompt3": "Draft a polite email requesting a meeting",
|
||||
"codeInterpreterEnabledTooltip": "Code Interpreter enabled (click to disable)",
|
||||
"enableCodeInterpreterTooltip": "Enable Code Interpreter",
|
||||
"codeInterpreterEnabledNotice": "Code Interpreter enabled!",
|
||||
"placeholderChat": "Type your message... (Shift+Enter for new line)",
|
||||
"placeholderA2A": "Send a message to the A2A agent...",
|
||||
"placeholderImageEdits": "Describe how you want to edit the image...",
|
||||
"placeholderSpeech": "Enter text to convert to speech...",
|
||||
"placeholderTranscription": "Optional: Add context or prompt for transcription...",
|
||||
"placeholderImage": "Describe the image you want to generate...",
|
||||
"loadingToolSchema": "Loading tool schema...",
|
||||
"generatedCodeTitle": "Generated Code",
|
||||
"sdkTypeLabel": "SDK Type",
|
||||
"openAiSdk": "OpenAI SDK",
|
||||
"azureSdk": "Azure SDK",
|
||||
"copyToClipboard": "Copy to Clipboard",
|
||||
"toolsetsInfoTitle": "How Toolsets Work",
|
||||
"toolsetsInfoIntro": "<strong>Toolsets</strong> are named collections of specific tools from one or more MCP servers. Instead of exposing all tools from a server, a toolset gives an agent exactly the tools it needs.",
|
||||
"toolsetsHowToUseTitle": "How to use a toolset:",
|
||||
"toolsetsStep1": "Select a <purple>Toolset</purple> (purple badge) from the MCP Servers dropdown.",
|
||||
"toolsetsStep2": "The tool picker will show only the tools included in that toolset.",
|
||||
"toolsetsStep3": "Select a tool and fill in its parameters, then send.",
|
||||
"toolsetsStep4": "The tool call is routed to the correct underlying MCP server automatically.",
|
||||
"toolsetsExample": "<strong>Example:</strong> A \"GitHub Read-only\" toolset might include only <code>list_repos</code> and <code>get_file</code> from a GitHub MCP server — preventing agents from making writes.",
|
||||
"toolsetsCreatingTitle": "Creating toolsets:",
|
||||
"toolsetsCreatingDesc": "Admins can create and manage toolsets from the <strong>MCP</strong> page → <strong>Toolsets</strong> tab. Toolsets can then be assigned to keys and teams to scope their tool access."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3857,6 +3857,121 @@
|
|||
},
|
||||
"chatMessageBubble": {
|
||||
"generatedImageAlt": "生成的图片"
|
||||
},
|
||||
"chatUi": {
|
||||
"requestCancelled": "请求已取消",
|
||||
"pleaseUploadImage": "请至少上传一张图片用于编辑",
|
||||
"pleaseUploadAudio": "请上传一个音频文件用于转录",
|
||||
"pleaseSelectAgent": "请选择一个 Agent 以发送消息",
|
||||
"pleaseSelectMCPServer": "请选择一个 MCP 服务器进行测试",
|
||||
"pleaseSelectMCPTool": "请选择一个 MCP 工具进行调用",
|
||||
"waitForToolSchema": "请等待工具 schema 加载完成",
|
||||
"fillRequiredParameters": "请填写所有必填参数",
|
||||
"pleaseSelectModel": "请在发送请求前选择一个模型",
|
||||
"pleaseProvideVirtualKey": "请提供虚拟密钥或选择当前 UI 会话",
|
||||
"failedToProcessImage": "处理图片失败,请重试。",
|
||||
"toolExecutedSuccessfully": "工具执行成功。",
|
||||
"errorFetchingResponse": "获取响应时出错:{{error}}",
|
||||
"chatHistoryCleared": "聊天记录已清除。",
|
||||
"accessDenied": "拒绝访问",
|
||||
"askProxyAdminForAccess": "请联系代理管理员以获取测试模型的权限",
|
||||
"configurations": "配置",
|
||||
"virtualKeySource": "虚拟密钥来源",
|
||||
"currentUiSession": "当前 UI 会话",
|
||||
"virtualKeyLabel": "虚拟密钥",
|
||||
"enterCustomVirtualKey": "输入自定义虚拟密钥",
|
||||
"customProxyBaseUrl": "自定义代理基础 URL",
|
||||
"fill": "填入",
|
||||
"customProxyUrlPlaceholder": "可选:输入自定义代理 URL(例如:http://localhost:5000)",
|
||||
"apiCallsSentTo": "API 请求将发送至:{{url}}",
|
||||
"endpointTypeLabel": "端点类型",
|
||||
"voiceLabel": "语音",
|
||||
"selectModelLabel": "选择模型",
|
||||
"advancedParamsTooltip": "高级参数目前仅支持聊天模型",
|
||||
"modelSettings": "模型设置",
|
||||
"selectModelPlaceholder": "选择模型",
|
||||
"enterCustomModel": "输入自定义模型",
|
||||
"enterCustomModelName": "输入自定义模型名称",
|
||||
"selectAgentLabel": "选择 Agent",
|
||||
"selectAgentPlaceholder": "选择一个 Agent",
|
||||
"noAgentsFound": "未找到 Agent。请通过 /v1/agents 端点创建 Agent。",
|
||||
"tagsLabel": "标签",
|
||||
"mcpServerLabel": "MCP 服务器",
|
||||
"mcpServersLabel": "MCP 服务器",
|
||||
"mcpServerTooltipDirect": "选择一个 MCP 服务器或工具集以直接测试工具。",
|
||||
"mcpServerTooltip": "选择 MCP 服务器或工具集以在对话中使用。",
|
||||
"selectMCPServerPlaceholder": "选择 MCP 服务器",
|
||||
"selectMCPServersPlaceholder": "选择 MCP 服务器",
|
||||
"allMCPServers": "所有 MCP 服务器",
|
||||
"useAllMCPServers": "使用所有可用的 MCP 服务器",
|
||||
"toolsetsGroup": "工具集",
|
||||
"toolsetBadge": "工具集",
|
||||
"toolCount": "({{count}} 个工具)",
|
||||
"serversGroup": "服务器",
|
||||
"selectToolLabel": "选择工具",
|
||||
"selectToolPlaceholder": "选择要调用的工具",
|
||||
"limitToolsFor": "限制 {{serverName}} 的工具:",
|
||||
"allToolsDefault": "所有工具(默认)",
|
||||
"serverRequiresApiKey": "{{serverName}} 需要您的 API 密钥",
|
||||
"connected": "已连接",
|
||||
"reconnect": "重新连接",
|
||||
"connectButton": "连接",
|
||||
"vectorStoreLabel": "向量存储",
|
||||
"vectorStoreTooltip": "选择此次 LLM API 调用使用的向量存储。您可以在<setupLink>此处</setupLink>设置您的向量存储。",
|
||||
"guardrailsLabel": "护栏",
|
||||
"guardrailsTooltip": "选择此次 LLM API 调用使用的护栏。您可以在<setupLink>此处</setupLink>设置您的护栏。",
|
||||
"policiesLabel": "策略",
|
||||
"policiesTooltip": "选择应用于此次 LLM API 调用的策略。策略根据条件定义要应用的护栏。您可以在<setupLink>此处</setupLink>设置您的策略。",
|
||||
"testKeyTitle": "测试密钥",
|
||||
"chatTitle": "聊天",
|
||||
"clearChat": "清除聊天",
|
||||
"getCode": "获取代码",
|
||||
"emptyStateHint": "开始对话、生成图片或处理音频",
|
||||
"assistant": "助手",
|
||||
"dragImagesToUpload": "点击或拖拽图片到此处上传",
|
||||
"imageUploadHint": "支持 PNG、JPG、JPEG 格式,可上传多张图片。",
|
||||
"uploadPreviewAlt": "上传预览 {{index}}",
|
||||
"addMore": "添加更多",
|
||||
"dragAudioToUpload": "点击或拖拽音频文件到此处上传",
|
||||
"audioUploadHint": "支持 MP3、MP4、MPEG、MPGA、M4A、WAV、WEBM 格式,最大文件大小:25 MB。",
|
||||
"removeButton": "移除",
|
||||
"runningPythonCode": "正在运行 Python 代码...",
|
||||
"codeInterpreterActive": "代码解释器已激活",
|
||||
"disableButton": "禁用",
|
||||
"samplePrompt1": "生成示例销售数据 CSV 并创建图表",
|
||||
"samplePrompt2": "创建一个对比 AI 网关提供商(包括 LiteLLM)的 PNG 柱状图",
|
||||
"samplePrompt3": "生成 LLM 定价数据的 CSV 并将其可视化为折线图",
|
||||
"suggestPromptA2A1": "你能帮我做什么?",
|
||||
"suggestPromptA2A2": "介绍一下你自己",
|
||||
"suggestPromptA2A3": "你能执行哪些任务?",
|
||||
"suggestPrompt1": "给我写一首诗",
|
||||
"suggestPrompt2": "解释量子计算",
|
||||
"suggestPrompt3": "起草一封礼貌地请求会议的邮件",
|
||||
"codeInterpreterEnabledTooltip": "代码解释器已启用(点击可禁用)",
|
||||
"enableCodeInterpreterTooltip": "启用代码解释器",
|
||||
"codeInterpreterEnabledNotice": "代码解释器已启用!",
|
||||
"placeholderChat": "输入消息...(Shift+Enter 换行)",
|
||||
"placeholderA2A": "向 A2A Agent 发送消息...",
|
||||
"placeholderImageEdits": "描述您希望如何编辑图片...",
|
||||
"placeholderSpeech": "输入要转换为语音的文本...",
|
||||
"placeholderTranscription": "可选:为转录添加上下文或提示词...",
|
||||
"placeholderImage": "描述您想要生成的图片...",
|
||||
"loadingToolSchema": "正在加载工具 schema...",
|
||||
"generatedCodeTitle": "生成的代码",
|
||||
"sdkTypeLabel": "SDK 类型",
|
||||
"openAiSdk": "OpenAI SDK",
|
||||
"azureSdk": "Azure SDK",
|
||||
"copyToClipboard": "复制到剪贴板",
|
||||
"toolsetsInfoTitle": "工具集的工作原理",
|
||||
"toolsetsInfoIntro": "<strong>工具集</strong>是来自一个或多个 MCP 服务器的特定工具的命名集合。工具集不会暴露服务器的所有工具,而是为 Agent 提供恰好所需的工具。",
|
||||
"toolsetsHowToUseTitle": "如何使用工具集:",
|
||||
"toolsetsStep1": "从 MCP 服务器下拉列表中选择一个<purple>工具集</purple>(紫色标签)。",
|
||||
"toolsetsStep2": "工具选择器将只显示该工具集包含的工具。",
|
||||
"toolsetsStep3": "选择工具并填写其参数,然后发送。",
|
||||
"toolsetsStep4": "工具调用会自动路由到正确的底层 MCP 服务器。",
|
||||
"toolsetsExample": "<strong>示例:</strong>一个「GitHub 只读」工具集可能只包含来自 GitHub MCP 服务器的 <code>list_repos</code> 和 <code>get_file</code>——防止 Agent 进行写操作。",
|
||||
"toolsetsCreatingTitle": "创建工具集:",
|
||||
"toolsetsCreatingDesc": "管理员可以从 <strong>MCP</strong> 页面 → <strong>工具集</strong>标签页创建和管理工具集。工具集随后可分配给密钥和团队以限定其工具访问范围。"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue