mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for prompt editor components
This commit is contained in:
parent
06e934a367
commit
524569b9ae
12 changed files with 275 additions and 68 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Modal, Form, Select, Upload, Button, Divider } from "antd";
|
||||
import { TextInput } from "@tremor/react";
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
|
|
@ -22,6 +23,7 @@ interface PromptFormData {
|
|||
}
|
||||
|
||||
const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessToken, onSuccess }) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||
|
|
@ -40,12 +42,12 @@ const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessT
|
|||
|
||||
console.log("values: ", values);
|
||||
if (!accessToken) {
|
||||
NotificationsManager.fromBackend("Access token is required");
|
||||
NotificationsManager.fromBackend(t("promptsPage.addPromptForm.accessTokenRequired"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (promptIntegration === "dotprompt" && fileList.length === 0) {
|
||||
NotificationsManager.fromBackend("Please upload a .prompt file");
|
||||
NotificationsManager.fromBackend(t("promptsPage.addPromptForm.uploadPromptFileRequired"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +77,7 @@ const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessT
|
|||
};
|
||||
} catch (conversionError) {
|
||||
console.error("Error converting prompt file:", conversionError);
|
||||
NotificationsManager.fromBackend("Failed to convert prompt file to JSON");
|
||||
NotificationsManager.fromBackend(t("promptsPage.addPromptForm.conversionFailed"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -84,12 +86,12 @@ const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessT
|
|||
// Create the prompt
|
||||
try {
|
||||
await createPromptCall(accessToken, promptData);
|
||||
NotificationsManager.success("Prompt created successfully!");
|
||||
NotificationsManager.success(t("promptsPage.addPromptForm.createSuccess"));
|
||||
handleCancel();
|
||||
onSuccess();
|
||||
} catch (createError) {
|
||||
console.error("Error creating prompt:", createError);
|
||||
NotificationsManager.fromBackend("Failed to create prompt");
|
||||
NotificationsManager.fromBackend(t("promptsPage.addPromptForm.createFailed"));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Form validation error:", error);
|
||||
|
|
@ -101,10 +103,10 @@ const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessT
|
|||
const uploadProps: UploadProps = {
|
||||
beforeUpload: (file) => {
|
||||
if (!file.name.endsWith(".prompt")) {
|
||||
NotificationsManager.fromBackend("Please upload a .prompt file");
|
||||
NotificationsManager.fromBackend(t("promptsPage.addPromptForm.uploadPromptFileRequired"));
|
||||
return false;
|
||||
}
|
||||
return false; // Prevent automatic upload
|
||||
return false;
|
||||
},
|
||||
fileList,
|
||||
onChange: ({ fileList: newFileList }) => {
|
||||
|
|
@ -117,35 +119,39 @@ const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessT
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Add New Prompt"
|
||||
title={t("promptsPage.addPromptForm.title")}
|
||||
open={visible}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={handleCancel}>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>,
|
||||
<Button key="submit" loading={loading} onClick={handleSubmit}>
|
||||
Create Prompt
|
||||
{t("promptsPage.addPromptForm.createPrompt")}
|
||||
</Button>,
|
||||
]}
|
||||
width={600}
|
||||
>
|
||||
<Form form={form} layout="vertical" requiredMark={false}>
|
||||
<Form.Item
|
||||
label="Prompt ID"
|
||||
label={t("promptsPage.addPromptForm.promptIdLabel")}
|
||||
name="prompt_id"
|
||||
rules={[
|
||||
{ required: true, message: "Please enter a prompt ID" },
|
||||
{ required: true, message: t("promptsPage.addPromptForm.promptIdRequired") },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_-]+$/,
|
||||
message: "Prompt ID can only contain letters, numbers, underscores, and hyphens",
|
||||
message: t("promptsPage.addPromptForm.promptIdPattern"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextInput placeholder="Enter unique prompt ID (e.g., my_prompt_id)" />
|
||||
<TextInput placeholder={t("promptsPage.addPromptForm.promptIdPlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Prompt Integration" name="prompt_integration" initialValue="dotprompt">
|
||||
<Form.Item
|
||||
label={t("promptsPage.addPromptForm.integrationLabel")}
|
||||
name="prompt_integration"
|
||||
initialValue="dotprompt"
|
||||
>
|
||||
<Select value={promptIntegration} onChange={setPromptIntegration}>
|
||||
<Option value="dotprompt">dotprompt</Option>
|
||||
</Select>
|
||||
|
|
@ -154,11 +160,18 @@ const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessT
|
|||
{promptIntegration === "dotprompt" && (
|
||||
<>
|
||||
<Divider />
|
||||
<Form.Item label="Prompt File" extra="Upload a .prompt file that follows the Dotprompt specification">
|
||||
<Form.Item
|
||||
label={t("promptsPage.addPromptForm.fileLabel")}
|
||||
extra={t("promptsPage.addPromptForm.fileExtra")}
|
||||
>
|
||||
<Upload {...uploadProps}>
|
||||
<Button icon={<UploadOutlined />}>Select .prompt File</Button>
|
||||
<Button icon={<UploadOutlined />}>{t("promptsPage.addPromptForm.selectFile")}</Button>
|
||||
</Upload>
|
||||
{fileList.length > 0 && <div className="mt-2 text-sm text-gray-600">Selected: {fileList[0].name}</div>}
|
||||
{fileList.length > 0 && (
|
||||
<div className="mt-2 text-sm text-gray-600">
|
||||
{t("promptsPage.addPromptForm.selected", { name: fileList[0].name })}
|
||||
</div>
|
||||
)}
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, Text } from "@tremor/react";
|
||||
import VariableTextArea from "../variable_textarea";
|
||||
|
||||
|
|
@ -8,11 +9,17 @@ interface DeveloperMessageCardProps {
|
|||
}
|
||||
|
||||
const DeveloperMessageCard: React.FC<DeveloperMessageCardProps> = ({ value, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card className="p-3">
|
||||
<Text className="block mb-2 text-sm font-medium">Developer message</Text>
|
||||
<Text className="text-gray-500 text-xs mb-2">Optional system instructions for the model</Text>
|
||||
<VariableTextArea value={value} onChange={onChange} rows={3} placeholder="e.g., You are a helpful assistant..." />
|
||||
<Text className="block mb-2 text-sm font-medium">{t("promptsPage.developerMessageCard.title")}</Text>
|
||||
<Text className="text-gray-500 text-xs mb-2">{t("promptsPage.developerMessageCard.subtitle")}</Text>
|
||||
<VariableTextArea
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
rows={3}
|
||||
placeholder={t("promptsPage.developerMessageCard.placeholder")}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PromptType } from "./types";
|
||||
import { convertToDotPrompt } from "./utils";
|
||||
|
||||
|
|
@ -7,13 +8,14 @@ interface DotpromptViewTabProps {
|
|||
}
|
||||
|
||||
const DotpromptViewTab: React.FC<DotpromptViewTabProps> = ({ prompt }) => {
|
||||
const { t } = useTranslation();
|
||||
const dotpromptContent = convertToDotPrompt(prompt);
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm font-medium text-gray-700 mb-2">Generated .prompt file</h3>
|
||||
<p className="text-xs text-gray-500">This is the dotprompt format that will be saved to the database</p>
|
||||
<h3 className="text-sm font-medium text-gray-700 mb-2">{t("promptsPage.dotpromptViewTab.title")}</h3>
|
||||
<p className="text-xs text-gray-500">{t("promptsPage.dotpromptViewTab.subtitle")}</p>
|
||||
</div>
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 overflow-auto">
|
||||
<pre className="text-sm text-gray-900 font-mono whitespace-pre-wrap">{dotpromptContent}</pre>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Text } from "@tremor/react";
|
||||
import { Input } from "antd";
|
||||
import { SettingsIcon } from "lucide-react";
|
||||
|
|
@ -23,6 +24,7 @@ const ModelConfigCard: React.FC<ModelConfigCardProps> = ({
|
|||
onTemperatureChange,
|
||||
onMaxTokensChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showConfig, setShowConfig] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
@ -36,14 +38,14 @@ const ModelConfigCard: React.FC<ModelConfigCardProps> = ({
|
|||
className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50"
|
||||
>
|
||||
<SettingsIcon size={16} />
|
||||
<span>Parameters</span>
|
||||
<span>{t("promptsPage.modelConfigCard.parameters")}</span>
|
||||
</button>
|
||||
|
||||
{showConfig && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-30">
|
||||
<div className="bg-white rounded-lg shadow-xl p-6 w-96">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold">Model Parameters</h3>
|
||||
<h3 className="text-lg font-semibold">{t("promptsPage.modelConfigCard.modelParameters")}</h3>
|
||||
<button onClick={() => setShowConfig(false)} className="text-gray-400 hover:text-gray-600">
|
||||
✕
|
||||
</button>
|
||||
|
|
@ -51,7 +53,7 @@ const ModelConfigCard: React.FC<ModelConfigCardProps> = ({
|
|||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm text-gray-700">Temperature</Text>
|
||||
<Text className="text-sm text-gray-700">{t("promptsPage.modelConfigCard.temperature")}</Text>
|
||||
<Input
|
||||
type="number"
|
||||
size="small"
|
||||
|
|
@ -66,7 +68,7 @@ const ModelConfigCard: React.FC<ModelConfigCardProps> = ({
|
|||
</div>
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm text-gray-700">Max Tokens</Text>
|
||||
<Text className="text-sm text-gray-700">{t("promptsPage.modelConfigCard.maxTokens")}</Text>
|
||||
<Input
|
||||
type="number"
|
||||
size="small"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Modal, Select, Button as AntdButton, Tabs } from "antd";
|
||||
import { CodeOutlined } from "@ant-design/icons";
|
||||
import { Button as TremorButton, Text } from "@tremor/react";
|
||||
|
|
@ -26,6 +27,7 @@ const PromptCodeSnippets: React.FC<PromptCodeSnippetsProps> = ({
|
|||
version = "1",
|
||||
proxySettings,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<"curl" | "python" | "javascript">("curl");
|
||||
const [selectedTab, setSelectedTab] = useState("basic");
|
||||
|
|
@ -235,13 +237,19 @@ main();`;
|
|||
return (
|
||||
<>
|
||||
<TremorButton variant="secondary" icon={CodeOutlined} onClick={showModal}>
|
||||
Get Code
|
||||
{t("promptsPage.promptCodeSnippets.getCode")}
|
||||
</TremorButton>
|
||||
|
||||
<Modal title="Generated Code" open={isModalVisible} onCancel={handleCancel} footer={null} width={800}>
|
||||
<Modal
|
||||
title={t("promptsPage.promptCodeSnippets.modalTitle")}
|
||||
open={isModalVisible}
|
||||
onCancel={handleCancel}
|
||||
footer={null}
|
||||
width={800}
|
||||
>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div>
|
||||
<Text className="font-medium block mb-1 text-gray-700">Language</Text>
|
||||
<Text className="font-medium block mb-1 text-gray-700">{t("promptsPage.promptCodeSnippets.language")}</Text>
|
||||
<Select
|
||||
value={selectedLanguage}
|
||||
onChange={(value) => setSelectedLanguage(value as "curl" | "python" | "javascript")}
|
||||
|
|
@ -256,10 +264,10 @@ main();`;
|
|||
<AntdButton
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(generatedCode);
|
||||
NotificationsManager.success("Copied to clipboard!");
|
||||
NotificationsManager.success(t("promptsPage.promptCodeSnippets.copiedToClipboard"));
|
||||
}}
|
||||
>
|
||||
Copy to Clipboard
|
||||
{t("promptsPage.promptCodeSnippets.copyToClipboard")}
|
||||
</AntdButton>
|
||||
</div>
|
||||
|
||||
|
|
@ -267,9 +275,9 @@ main();`;
|
|||
activeKey={selectedTab}
|
||||
onChange={setSelectedTab}
|
||||
items={[
|
||||
{ label: "Basic", key: "basic" },
|
||||
{ label: "With Messages", key: "messages" },
|
||||
{ label: "With Version", key: "version" },
|
||||
{ label: t("promptsPage.promptCodeSnippets.tabBasic"), key: "basic" },
|
||||
{ label: t("promptsPage.promptCodeSnippets.tabWithMessages"), key: "messages" },
|
||||
{ label: t("promptsPage.promptCodeSnippets.tabWithVersion"), key: "version" },
|
||||
]}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button as TremorButton } from "@tremor/react";
|
||||
import { Input, Select } from "antd";
|
||||
import { ArrowLeftIcon, SaveIcon, ClockIcon } from "lucide-react";
|
||||
|
|
@ -40,11 +41,12 @@ const PromptEditorHeader: React.FC<PromptEditorHeaderProps> = ({
|
|||
environment,
|
||||
onEnvironmentChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<TremorButton icon={ArrowLeftIcon} variant="light" onClick={onBack} size="xs">
|
||||
Back
|
||||
{t("common.back")}
|
||||
</TremorButton>
|
||||
<Input
|
||||
value={promptName}
|
||||
|
|
@ -61,13 +63,15 @@ const PromptEditorHeader: React.FC<PromptEditorHeaderProps> = ({
|
|||
style={{ width: 140 }}
|
||||
size="small"
|
||||
options={[
|
||||
{ label: "Development", value: "development" },
|
||||
{ label: "Staging", value: "staging" },
|
||||
{ label: "Production", value: "production" },
|
||||
{ label: t("promptsPage.promptEditorHeader.envDevelopment"), value: "development" },
|
||||
{ label: t("promptsPage.promptEditorHeader.envStaging"), value: "staging" },
|
||||
{ label: t("promptsPage.promptEditorHeader.envProduction"), value: "production" },
|
||||
]}
|
||||
/>
|
||||
<span className="px-2 py-0.5 text-xs bg-gray-100 text-gray-600 rounded">Draft</span>
|
||||
<span className="text-xs text-gray-400">Unsaved changes</span>
|
||||
<span className="px-2 py-0.5 text-xs bg-gray-100 text-gray-600 rounded">
|
||||
{t("promptsPage.promptEditorHeader.draft")}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400">{t("promptsPage.promptEditorHeader.unsavedChanges")}</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<PromptCodeSnippets
|
||||
|
|
@ -80,11 +84,11 @@ const PromptEditorHeader: React.FC<PromptEditorHeaderProps> = ({
|
|||
/>
|
||||
{editMode && onShowHistory && (
|
||||
<TremorButton icon={ClockIcon} variant="secondary" onClick={onShowHistory}>
|
||||
History
|
||||
{t("promptsPage.promptEditorHeader.history")}
|
||||
</TremorButton>
|
||||
)}
|
||||
<TremorButton icon={SaveIcon} onClick={onSave} loading={isSaving} disabled={isSaving}>
|
||||
{editMode ? "Update" : "Save"}
|
||||
{editMode ? t("common.update") : t("common.save")}
|
||||
</TremorButton>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { Card, Text } from "@tremor/react";
|
||||
import { Select } from "antd";
|
||||
import { PlusIcon, TrashIcon, GripVerticalIcon } from "lucide-react";
|
||||
|
|
@ -22,6 +23,7 @@ const PromptMessagesCard: React.FC<PromptMessagesCardProps> = ({
|
|||
onRemoveMessage,
|
||||
onMoveMessage,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
|
||||
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
|
||||
|
||||
|
|
@ -51,9 +53,12 @@ const PromptMessagesCard: React.FC<PromptMessagesCardProps> = ({
|
|||
return (
|
||||
<Card className="p-3">
|
||||
<div className="mb-2">
|
||||
<Text className="text-sm font-medium">Prompt messages</Text>
|
||||
<Text className="text-sm font-medium">{t("promptsPage.promptMessagesCard.title")}</Text>
|
||||
<Text className="text-gray-500 text-xs mt-1">
|
||||
Use <code className="bg-gray-100 px-1 rounded text-xs">{"{{variable}}"}</code> syntax for template variables
|
||||
<Trans
|
||||
i18nKey="promptsPage.promptMessagesCard.variableSyntaxHint"
|
||||
components={{ code: <code className="bg-gray-100 px-1 rounded text-xs" /> }}
|
||||
/>
|
||||
</Text>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
|
|
@ -77,9 +82,9 @@ const PromptMessagesCard: React.FC<PromptMessagesCardProps> = ({
|
|||
size="small"
|
||||
bordered={false}
|
||||
>
|
||||
<Option value="user">User</Option>
|
||||
<Option value="assistant">Assistant</Option>
|
||||
<Option value="system">System</Option>
|
||||
<Option value="user">{t("promptsPage.promptMessagesCard.roleUser")}</Option>
|
||||
<Option value="assistant">{t("promptsPage.promptMessagesCard.roleAssistant")}</Option>
|
||||
<Option value="system">{t("promptsPage.promptMessagesCard.roleSystem")}</Option>
|
||||
</Select>
|
||||
<div className="flex items-center gap-1">
|
||||
{messages.length > 1 && (
|
||||
|
|
@ -97,7 +102,7 @@ const PromptMessagesCard: React.FC<PromptMessagesCardProps> = ({
|
|||
value={message.content}
|
||||
onChange={(value) => onUpdateMessage(index, "content", value)}
|
||||
rows={3}
|
||||
placeholder="Enter prompt content..."
|
||||
placeholder={t("promptsPage.promptMessagesCard.contentPlaceholder")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -105,7 +110,7 @@ const PromptMessagesCard: React.FC<PromptMessagesCardProps> = ({
|
|||
</div>
|
||||
<button onClick={onAddMessage} className="mt-2 text-xs text-blue-600 hover:text-blue-700 flex items-center">
|
||||
<PlusIcon size={14} className="mr-1" />
|
||||
Add message
|
||||
{t("promptsPage.promptMessagesCard.addMessage")}
|
||||
</button>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button as TremorButton, Text } from "@tremor/react";
|
||||
import { Input, Modal } from "antd";
|
||||
|
||||
|
|
@ -19,34 +20,33 @@ const PublishModal: React.FC<PublishModalProps> = ({
|
|||
onPublish,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal
|
||||
title="Publish Prompt"
|
||||
title={t("promptsPage.publishModal.title")}
|
||||
open={visible}
|
||||
onCancel={onCancel}
|
||||
footer={[
|
||||
<div key="footer" className="flex justify-end gap-2">
|
||||
<TremorButton variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</TremorButton>
|
||||
<TremorButton onClick={onPublish} loading={isSaving}>
|
||||
Publish
|
||||
{t("promptsPage.publishModal.publish")}
|
||||
</TremorButton>
|
||||
</div>,
|
||||
]}
|
||||
>
|
||||
<div className="py-4">
|
||||
<Text className="mb-2">Name</Text>
|
||||
<Text className="mb-2">{t("common.name")}</Text>
|
||||
<Input
|
||||
value={promptName}
|
||||
onChange={(e) => onNameChange(e.target.value)}
|
||||
placeholder="Enter prompt name"
|
||||
placeholder={t("promptsPage.publishModal.namePlaceholder")}
|
||||
onPressEnter={onPublish}
|
||||
autoFocus
|
||||
/>
|
||||
<Text className="text-gray-500 text-xs mt-2">
|
||||
Published prompts can be used in API calls and are versioned for easy tracking.
|
||||
</Text>
|
||||
<Text className="text-gray-500 text-xs mt-2">{t("promptsPage.publishModal.versionedHint")}</Text>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, Text } from "@tremor/react";
|
||||
import { PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { Tool } from "./types";
|
||||
|
|
@ -11,17 +12,18 @@ interface ToolsCardProps {
|
|||
}
|
||||
|
||||
const ToolsCard: React.FC<ToolsCardProps> = ({ tools, onAddTool, onEditTool, onRemoveTool }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card className="p-3">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm font-medium">Tools</Text>
|
||||
<Text className="text-sm font-medium">{t("promptsPage.toolsCard.title")}</Text>
|
||||
<button onClick={onAddTool} className="text-xs text-blue-600 hover:text-blue-700 flex items-center">
|
||||
<PlusIcon size={14} className="mr-1" />
|
||||
Add
|
||||
{t("common.add")}
|
||||
</button>
|
||||
</div>
|
||||
{tools.length === 0 ? (
|
||||
<Text className="text-gray-500 text-xs">No tools added</Text>
|
||||
<Text className="text-gray-500 text-xs">{t("promptsPage.toolsCard.noTools")}</Text>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{tools.map((tool, index) => (
|
||||
|
|
@ -35,7 +37,7 @@ const ToolsCard: React.FC<ToolsCardProps> = ({ tools, onAddTool, onEditTool, onR
|
|||
</div>
|
||||
<div className="flex items-center space-x-1 ml-2">
|
||||
<button onClick={() => onEditTool(index)} className="text-xs text-blue-600 hover:text-blue-700">
|
||||
Edit
|
||||
{t("common.edit")}
|
||||
</button>
|
||||
<button onClick={() => onRemoveTool(index)} className="text-gray-400 hover:text-red-500">
|
||||
<TrashIcon size={14} />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Drawer, List, Skeleton, Tag, Typography } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getPromptVersions, PromptSpec } from "../../networking";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
|
@ -21,6 +22,7 @@ const VersionHistorySidePanel: React.FC<VersionHistorySidePanelProps> = ({
|
|||
activeVersionId,
|
||||
onSelectVersion,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [versions, setVersions] = useState<PromptSpec[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
|
|
@ -68,18 +70,18 @@ const VersionHistorySidePanel: React.FC<VersionHistorySidePanelProps> = ({
|
|||
|
||||
return (
|
||||
<Drawer
|
||||
title="Version History"
|
||||
title={t("promptsPage.versionHistorySidePanel.title")}
|
||||
placement="right"
|
||||
onClose={onClose}
|
||||
open={isOpen}
|
||||
width={400}
|
||||
mask={false} // Allow interacting with the main editor while drawer is open
|
||||
mask={false}
|
||||
maskClosable={false}
|
||||
>
|
||||
{loading ? (
|
||||
<Skeleton active paragraph={{ rows: 4 }} />
|
||||
) : versions.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-500">No version history available.</div>
|
||||
<div className="text-center py-8 text-gray-500">{t("promptsPage.versionHistorySidePanel.noHistory")}</div>
|
||||
) : (
|
||||
<List
|
||||
dataSource={versions}
|
||||
|
|
@ -113,13 +115,13 @@ const VersionHistorySidePanel: React.FC<VersionHistorySidePanelProps> = ({
|
|||
<Tag className="m-0">{getVersionNumber(item)}</Tag>
|
||||
{index === 0 && (
|
||||
<Tag color="blue" className="m-0">
|
||||
Latest
|
||||
{t("promptsPage.versionHistorySidePanel.latest")}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
{isSelected && (
|
||||
<Tag color="green" className="m-0">
|
||||
Active
|
||||
{t("promptsPage.versionHistorySidePanel.active")}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -127,7 +129,9 @@ const VersionHistorySidePanel: React.FC<VersionHistorySidePanelProps> = ({
|
|||
<div className="flex flex-col gap-1">
|
||||
<Text className="text-sm text-gray-600 font-medium">{formatDate(item.created_at)}</Text>
|
||||
<Text type="secondary" className="text-xs">
|
||||
{item.prompt_info?.prompt_type === "db" ? "Saved to Database" : "Config Prompt"}
|
||||
{item.prompt_info?.prompt_type === "db"
|
||||
? t("promptsPage.versionHistorySidePanel.savedToDatabase")
|
||||
: t("promptsPage.versionHistorySidePanel.configPrompt")}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4486,5 +4486,85 @@
|
|||
"deleteModalMessage": "Are you sure you want to delete this fallback? This action cannot be undone.",
|
||||
"deleteModalResourceTitle": "Fallback Information"
|
||||
}
|
||||
},
|
||||
"promptsPage": {
|
||||
"addPromptForm": {
|
||||
"title": "Add New Prompt",
|
||||
"createPrompt": "Create Prompt",
|
||||
"accessTokenRequired": "Access token is required",
|
||||
"uploadPromptFileRequired": "Please upload a .prompt file",
|
||||
"conversionFailed": "Failed to convert prompt file to JSON",
|
||||
"createSuccess": "Prompt created successfully!",
|
||||
"createFailed": "Failed to create prompt",
|
||||
"promptIdLabel": "Prompt ID",
|
||||
"promptIdRequired": "Please enter a prompt ID",
|
||||
"promptIdPattern": "Prompt ID can only contain letters, numbers, underscores, and hyphens",
|
||||
"promptIdPlaceholder": "Enter unique prompt ID (e.g., my_prompt_id)",
|
||||
"integrationLabel": "Prompt Integration",
|
||||
"fileLabel": "Prompt File",
|
||||
"fileExtra": "Upload a .prompt file that follows the Dotprompt specification",
|
||||
"selectFile": "Select .prompt File",
|
||||
"selected": "Selected: {{name}}"
|
||||
},
|
||||
"developerMessageCard": {
|
||||
"title": "Developer message",
|
||||
"subtitle": "Optional system instructions for the model",
|
||||
"placeholder": "e.g., You are a helpful assistant..."
|
||||
},
|
||||
"dotpromptViewTab": {
|
||||
"title": "Generated .prompt file",
|
||||
"subtitle": "This is the dotprompt format that will be saved to the database"
|
||||
},
|
||||
"modelConfigCard": {
|
||||
"parameters": "Parameters",
|
||||
"modelParameters": "Model Parameters",
|
||||
"temperature": "Temperature",
|
||||
"maxTokens": "Max Tokens"
|
||||
},
|
||||
"promptCodeSnippets": {
|
||||
"getCode": "Get Code",
|
||||
"modalTitle": "Generated Code",
|
||||
"language": "Language",
|
||||
"copyToClipboard": "Copy to Clipboard",
|
||||
"copiedToClipboard": "Copied to clipboard!",
|
||||
"tabBasic": "Basic",
|
||||
"tabWithMessages": "With Messages",
|
||||
"tabWithVersion": "With Version"
|
||||
},
|
||||
"promptEditorHeader": {
|
||||
"envDevelopment": "Development",
|
||||
"envStaging": "Staging",
|
||||
"envProduction": "Production",
|
||||
"draft": "Draft",
|
||||
"unsavedChanges": "Unsaved changes",
|
||||
"history": "History"
|
||||
},
|
||||
"promptMessagesCard": {
|
||||
"title": "Prompt messages",
|
||||
"variableSyntaxHint": "Use <code>{{variable}}</code> syntax for template variables",
|
||||
"roleUser": "User",
|
||||
"roleAssistant": "Assistant",
|
||||
"roleSystem": "System",
|
||||
"contentPlaceholder": "Enter prompt content...",
|
||||
"addMessage": "Add message"
|
||||
},
|
||||
"publishModal": {
|
||||
"title": "Publish Prompt",
|
||||
"publish": "Publish",
|
||||
"namePlaceholder": "Enter prompt name",
|
||||
"versionedHint": "Published prompts can be used in API calls and are versioned for easy tracking."
|
||||
},
|
||||
"toolsCard": {
|
||||
"title": "Tools",
|
||||
"noTools": "No tools added"
|
||||
},
|
||||
"versionHistorySidePanel": {
|
||||
"title": "Version History",
|
||||
"noHistory": "No version history available.",
|
||||
"latest": "Latest",
|
||||
"active": "Active",
|
||||
"savedToDatabase": "Saved to Database",
|
||||
"configPrompt": "Config Prompt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4486,5 +4486,85 @@
|
|||
"deleteModalMessage": "确定要删除此回退吗?此操作无法撤销。",
|
||||
"deleteModalResourceTitle": "回退信息"
|
||||
}
|
||||
},
|
||||
"promptsPage": {
|
||||
"addPromptForm": {
|
||||
"title": "添加新提示词",
|
||||
"createPrompt": "创建提示词",
|
||||
"accessTokenRequired": "访问令牌不能为空",
|
||||
"uploadPromptFileRequired": "请上传 .prompt 文件",
|
||||
"conversionFailed": "提示词文件转换为 JSON 失败",
|
||||
"createSuccess": "提示词创建成功!",
|
||||
"createFailed": "创建提示词失败",
|
||||
"promptIdLabel": "提示词 ID",
|
||||
"promptIdRequired": "请输入提示词 ID",
|
||||
"promptIdPattern": "提示词 ID 只能包含字母、数字、下划线和连字符",
|
||||
"promptIdPlaceholder": "输入唯一提示词 ID(如:my_prompt_id)",
|
||||
"integrationLabel": "提示词集成",
|
||||
"fileLabel": "提示词文件",
|
||||
"fileExtra": "上传符合 Dotprompt 规范的 .prompt 文件",
|
||||
"selectFile": "选择 .prompt 文件",
|
||||
"selected": "已选择:{{name}}"
|
||||
},
|
||||
"developerMessageCard": {
|
||||
"title": "开发者消息",
|
||||
"subtitle": "可选的模型系统指令",
|
||||
"placeholder": "例如:你是一个有帮助的助手..."
|
||||
},
|
||||
"dotpromptViewTab": {
|
||||
"title": "生成的 .prompt 文件",
|
||||
"subtitle": "这是将保存到数据库的 dotprompt 格式"
|
||||
},
|
||||
"modelConfigCard": {
|
||||
"parameters": "参数",
|
||||
"modelParameters": "模型参数",
|
||||
"temperature": "Temperature",
|
||||
"maxTokens": "最大 Token 数"
|
||||
},
|
||||
"promptCodeSnippets": {
|
||||
"getCode": "获取代码",
|
||||
"modalTitle": "生成的代码",
|
||||
"language": "语言",
|
||||
"copyToClipboard": "复制到剪贴板",
|
||||
"copiedToClipboard": "已复制到剪贴板!",
|
||||
"tabBasic": "基础",
|
||||
"tabWithMessages": "含消息",
|
||||
"tabWithVersion": "含版本"
|
||||
},
|
||||
"promptEditorHeader": {
|
||||
"envDevelopment": "开发",
|
||||
"envStaging": "预发布",
|
||||
"envProduction": "生产",
|
||||
"draft": "草稿",
|
||||
"unsavedChanges": "有未保存的更改",
|
||||
"history": "历史"
|
||||
},
|
||||
"promptMessagesCard": {
|
||||
"title": "提示词消息",
|
||||
"variableSyntaxHint": "使用 <code>{{variable}}</code> 语法定义模板变量",
|
||||
"roleUser": "用户",
|
||||
"roleAssistant": "助手",
|
||||
"roleSystem": "系统",
|
||||
"contentPlaceholder": "输入提示词内容...",
|
||||
"addMessage": "添加消息"
|
||||
},
|
||||
"publishModal": {
|
||||
"title": "发布提示词",
|
||||
"publish": "发布",
|
||||
"namePlaceholder": "输入提示词名称",
|
||||
"versionedHint": "已发布的提示词可在 API 调用中使用,并支持版本管理以便追踪。"
|
||||
},
|
||||
"toolsCard": {
|
||||
"title": "工具",
|
||||
"noTools": "暂无工具"
|
||||
},
|
||||
"versionHistorySidePanel": {
|
||||
"title": "版本历史",
|
||||
"noHistory": "暂无版本历史。",
|
||||
"latest": "最新",
|
||||
"active": "当前",
|
||||
"savedToDatabase": "已保存到数据库",
|
||||
"configPrompt": "配置提示词"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue