mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for search tools
This commit is contained in:
parent
7ff15f970e
commit
6d04f50e80
7 changed files with 334 additions and 100 deletions
|
|
@ -5,6 +5,7 @@ import { Button, TextInput } from "@tremor/react";
|
|||
import { Form, Input, Modal, Select, Tooltip, Typography } from "antd";
|
||||
import Image from "next/image";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import { createSearchTool, fetchAvailableSearchProviders } from "../networking";
|
||||
import SearchConnectionTest from "./SearchConnectionTest";
|
||||
|
|
@ -60,6 +61,7 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
isModalVisible,
|
||||
setModalVisible,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [formValues, setFormValues] = useState<Record<string, any>>({});
|
||||
|
|
@ -104,14 +106,14 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
if (accessToken != null) {
|
||||
const response = await createSearchTool(accessToken, payload);
|
||||
|
||||
NotificationsManager.success("Search tool created successfully");
|
||||
NotificationsManager.success(t("searchTools.createSearchTools.createSuccess"));
|
||||
form.resetFields();
|
||||
setFormValues({});
|
||||
setModalVisible(false);
|
||||
onCreateSuccess(response);
|
||||
}
|
||||
} catch (error) {
|
||||
NotificationsManager.error("Error creating search tool: " + error);
|
||||
NotificationsManager.error(t("searchTools.createSearchTools.createError", { error }));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
|
@ -134,7 +136,7 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
// Show the modal with the fresh test
|
||||
setIsTestModalVisible(true);
|
||||
} catch (error) {
|
||||
NotificationsManager.error("Please fill in Search Provider and API Key before testing");
|
||||
NotificationsManager.error(t("searchTools.createSearchTools.testFillRequired"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -154,7 +156,7 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
title={
|
||||
<div className="flex items-center space-x-3 pb-4 border-b border-gray-100">
|
||||
<span className="text-2xl">🔍</span>
|
||||
<h2 className="text-xl font-semibold text-gray-900">Add New Search Tool</h2>
|
||||
<h2 className="text-xl font-semibold text-gray-900">{t("searchTools.createSearchTools.modalTitle")}</h2>
|
||||
</div>
|
||||
}
|
||||
open={isModalVisible}
|
||||
|
|
@ -179,23 +181,23 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Search Tool Name
|
||||
<Tooltip title="A unique name to identify this search tool configuration (e.g., 'perplexity-search', 'tavily-news-search').">
|
||||
{t("searchTools.createSearchTools.searchToolNameLabel")}
|
||||
<Tooltip title={t("searchTools.createSearchTools.searchToolNameTooltip")}>
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="search_tool_name"
|
||||
rules={[
|
||||
{ required: true, message: "Please enter a search tool name" },
|
||||
{ required: true, message: t("searchTools.createSearchTools.searchToolNameRequired") },
|
||||
{
|
||||
pattern: /^[a-zA-Z0-9_-]+$/,
|
||||
message: "Name can only contain letters, numbers, hyphens, and underscores",
|
||||
message: t("searchTools.createSearchTools.searchToolNamePattern"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="e.g., perplexity-search, my-tavily-tool"
|
||||
placeholder={t("searchTools.createSearchTools.searchToolNamePlaceholder")}
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
@ -203,17 +205,17 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
Search Provider
|
||||
<Tooltip title="Select the search provider you want to use. Each provider has different capabilities and pricing.">
|
||||
{t("searchTools.createSearchTools.searchProviderLabel")}
|
||||
<Tooltip title={t("searchTools.createSearchTools.searchProviderTooltip")}>
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="search_provider"
|
||||
rules={[{ required: true, message: "Please select a search provider" }]}
|
||||
rules={[{ required: true, message: t("searchTools.createSearchTools.searchProviderRequired") }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a search provider"
|
||||
placeholder={t("searchTools.createSearchTools.searchProviderPlaceholder")}
|
||||
className="rounded-lg"
|
||||
size="large"
|
||||
loading={isLoadingProviders}
|
||||
|
|
@ -244,46 +246,50 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
<Form.Item
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center">
|
||||
API Key
|
||||
<Tooltip title="The API key for authenticating with the search provider. This will be securely stored.">
|
||||
{t("searchTools.createSearchTools.apiKeyLabel")}
|
||||
<Tooltip title={t("searchTools.createSearchTools.apiKeyTooltip")}>
|
||||
<InfoCircleOutlined className="ml-2 text-blue-400 hover:text-blue-600 cursor-help" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="api_key"
|
||||
rules={[{ required: false, message: "Please enter an API key" }]}
|
||||
rules={[{ required: false, message: t("searchTools.createSearchTools.apiKeyRequired") }]}
|
||||
>
|
||||
<TextInput
|
||||
type="password"
|
||||
placeholder="Enter your API key"
|
||||
placeholder={t("searchTools.createSearchTools.apiKeyPlaceholder")}
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={<span className="text-sm font-medium text-gray-700">Description (Optional)</span>}
|
||||
label={
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
{t("searchTools.createSearchTools.descriptionLabel")}
|
||||
</span>
|
||||
}
|
||||
name="description"
|
||||
>
|
||||
<TextArea
|
||||
rows={3}
|
||||
placeholder="Brief description of this search tool's purpose"
|
||||
placeholder={t("searchTools.createSearchTools.descriptionPlaceholder")}
|
||||
className="rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center pt-6 border-t border-gray-100">
|
||||
<Tooltip title="Get help on our github">
|
||||
<Tooltip title={t("searchTools.createSearchTools.needHelpTooltip")}>
|
||||
<Typography.Link href="https://github.com/BerriAI/litellm/issues" target="_blank">
|
||||
Need Help?
|
||||
{t("searchTools.createSearchTools.needHelp")}
|
||||
</Typography.Link>
|
||||
</Tooltip>
|
||||
<div className="space-x-2">
|
||||
<Button onClick={handleTestConnection} loading={isTestingConnection}>
|
||||
Test Connection
|
||||
{t("searchTools.createSearchTools.testConnectionBtn")}
|
||||
</Button>
|
||||
<Button loading={isLoading} type="submit">
|
||||
Add Search Tool
|
||||
{t("searchTools.createSearchTools.addSearchToolBtn")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -292,7 +298,7 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
|
||||
{/* Test Connection Results Modal */}
|
||||
<Modal
|
||||
title="Connection Test Results"
|
||||
title={t("searchTools.createSearchTools.connectionTestResultsTitle")}
|
||||
open={isTestModalVisible}
|
||||
onCancel={() => {
|
||||
setIsTestModalVisible(false);
|
||||
|
|
@ -306,7 +312,7 @@ const CreateSearchTool: React.FC<CreateSearchToolProps> = ({
|
|||
setIsTestingConnection(false);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
{t("common.close")}
|
||||
</Button>,
|
||||
]}
|
||||
width={700}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { InfoCircleOutlined, WarningOutlined } from "@ant-design/icons";
|
||||
import { Button, Divider, Typography } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import { testSearchToolConnection } from "../networking";
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ interface SearchConnectionTestProps {
|
|||
}
|
||||
|
||||
const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmParams, accessToken, onTestComplete }) => {
|
||||
const { t } = useTranslation();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [testResult, setTestResult] = useState<{
|
||||
status: "success" | "error";
|
||||
|
|
@ -30,12 +32,12 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
const result = await testSearchToolConnection(accessToken, litellmParams);
|
||||
setTestResult(result);
|
||||
if (result.status === "success") {
|
||||
NotificationsManager.success("Connection test successful!");
|
||||
NotificationsManager.success(t("searchTools.searchConnectionTest.testSuccessNotification"));
|
||||
}
|
||||
} catch (error) {
|
||||
setTestResult({
|
||||
status: "error",
|
||||
message: error instanceof Error ? error.message : "Unknown error occurred",
|
||||
message: error instanceof Error ? error.message : t("searchTools.searchConnectionTest.unknownErrorOccurred"),
|
||||
error_type: "NetworkError",
|
||||
});
|
||||
} finally {
|
||||
|
|
@ -50,7 +52,7 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
}, [accessToken, litellmParams, onTestComplete]);
|
||||
|
||||
const getCleanErrorMessage = (errorMsg: string) => {
|
||||
if (!errorMsg) return "Unknown error";
|
||||
if (!errorMsg) return t("searchTools.searchConnectionTest.unknownError");
|
||||
|
||||
// Remove stack traces
|
||||
const mainError = errorMsg.split("stack trace:")[0].trim();
|
||||
|
|
@ -70,9 +72,9 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
}
|
||||
// If it's a 401 error
|
||||
if (finalError.includes("401") || finalError.includes("Authorization Required")) {
|
||||
return "Authentication failed: Invalid API key or credentials";
|
||||
return t("searchTools.searchConnectionTest.authFailedInvalidKey");
|
||||
}
|
||||
return "Authentication error - please check your API key";
|
||||
return t("searchTools.searchConnectionTest.authErrorCheckKey");
|
||||
}
|
||||
|
||||
// Limit very long error messages
|
||||
|
|
@ -83,7 +85,9 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
return finalError;
|
||||
};
|
||||
|
||||
const errorMessage = testResult?.message ? getCleanErrorMessage(testResult.message) : "Unknown error";
|
||||
const errorMessage = testResult?.message
|
||||
? getCleanErrorMessage(testResult.message)
|
||||
: t("searchTools.searchConnectionTest.unknownError");
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
|
@ -103,7 +107,9 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
/>
|
||||
</div>
|
||||
<Text style={{ fontSize: "16px" }}>
|
||||
Testing connection to {litellmParams.search_provider || "search provider"}...
|
||||
{t("searchTools.searchConnectionTest.testingConnection", {
|
||||
provider: litellmParams.search_provider || t("searchTools.searchConnectionTest.searchProvider"),
|
||||
})}
|
||||
</Text>
|
||||
<style jsx>{`
|
||||
@keyframes spin {
|
||||
|
|
@ -143,11 +149,11 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
</div>
|
||||
<div style={{ marginLeft: "12px" }}>
|
||||
<Text type="success" style={{ fontSize: "18px", fontWeight: 500, display: "block" }}>
|
||||
Connection to {litellmParams.search_provider} successful!
|
||||
{t("searchTools.searchConnectionTest.connectionSuccess", { provider: litellmParams.search_provider })}
|
||||
</Text>
|
||||
{testResult.test_query && (
|
||||
<Text style={{ fontSize: "14px", color: "#666", marginTop: "8px", display: "block" }}>
|
||||
Test query:{" "}
|
||||
{t("searchTools.searchConnectionTest.testQueryLabel")}{" "}
|
||||
<code style={{ backgroundColor: "#f0f0f0", padding: "2px 6px", borderRadius: "4px" }}>
|
||||
{testResult.test_query}
|
||||
</code>
|
||||
|
|
@ -155,7 +161,7 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
)}
|
||||
{testResult.results_count !== undefined && (
|
||||
<Text style={{ fontSize: "14px", color: "#666", display: "block" }}>
|
||||
Results retrieved: {testResult.results_count}
|
||||
{t("searchTools.searchConnectionTest.resultsRetrieved", { count: testResult.results_count })}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -166,7 +172,9 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
<div style={{ display: "flex", alignItems: "center", marginBottom: "20px" }}>
|
||||
<WarningOutlined style={{ color: "#ff4d4f", fontSize: "24px", marginRight: "12px" }} />
|
||||
<Text type="danger" style={{ fontSize: "18px", fontWeight: 500 }}>
|
||||
Connection to {litellmParams.search_provider || "search provider"} failed
|
||||
{t("searchTools.searchConnectionTest.connectionFailed", {
|
||||
provider: litellmParams.search_provider || t("searchTools.searchConnectionTest.searchProvider"),
|
||||
})}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
|
|
@ -181,7 +189,7 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
}}
|
||||
>
|
||||
<Text strong style={{ display: "block", marginBottom: "8px" }}>
|
||||
Error:{" "}
|
||||
{t("searchTools.searchConnectionTest.errorLabel")}{" "}
|
||||
</Text>
|
||||
<Text type="danger" style={{ fontSize: "14px", lineHeight: "1.5" }}>
|
||||
{errorMessage}
|
||||
|
|
@ -190,7 +198,7 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
{testResult.error_type && (
|
||||
<div style={{ marginTop: "8px" }}>
|
||||
<Text style={{ fontSize: "13px", color: "#666" }}>
|
||||
Error type:{" "}
|
||||
{t("searchTools.searchConnectionTest.errorTypeLabel")}{" "}
|
||||
<code
|
||||
style={{ backgroundColor: "#ffebee", padding: "2px 6px", borderRadius: "4px", color: "#d32f2f" }}
|
||||
>
|
||||
|
|
@ -207,7 +215,9 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
onClick={() => setShowDetails(!showDetails)}
|
||||
style={{ paddingLeft: 0, height: "auto" }}
|
||||
>
|
||||
{showDetails ? "Hide Details" : "Show Details"}
|
||||
{showDetails
|
||||
? t("searchTools.searchConnectionTest.hideDetails")
|
||||
: t("searchTools.searchConnectionTest.showDetails")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -216,7 +226,7 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
{showDetails && (
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<Text strong style={{ display: "block", marginBottom: "8px", fontSize: "15px" }}>
|
||||
Full Error Details
|
||||
{t("searchTools.searchConnectionTest.fullErrorDetails")}
|
||||
</Text>
|
||||
<pre
|
||||
style={{
|
||||
|
|
@ -247,15 +257,13 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
}}
|
||||
>
|
||||
<Text strong style={{ display: "block", marginBottom: "8px", color: "#d48806" }}>
|
||||
Troubleshooting tips:
|
||||
{t("searchTools.searchConnectionTest.troubleshootingTips")}
|
||||
</Text>
|
||||
<ul style={{ margin: "8px 0", paddingLeft: "20px", color: "#ad6800" }}>
|
||||
<li style={{ marginBottom: "6px" }}>Verify your API key is correct and active</li>
|
||||
<li style={{ marginBottom: "6px" }}>Check if the search provider service is operational</li>
|
||||
<li style={{ marginBottom: "6px" }}>Ensure you have sufficient credits/quota with the provider</li>
|
||||
<li style={{ marginBottom: "6px" }}>
|
||||
Review the provider's documentation for any additional requirements
|
||||
</li>
|
||||
<li style={{ marginBottom: "6px" }}>{t("searchTools.searchConnectionTest.tipVerifyApiKey")}</li>
|
||||
<li style={{ marginBottom: "6px" }}>{t("searchTools.searchConnectionTest.tipCheckServiceStatus")}</li>
|
||||
<li style={{ marginBottom: "6px" }}>{t("searchTools.searchConnectionTest.tipCheckCredits")}</li>
|
||||
<li style={{ marginBottom: "6px" }}>{t("searchTools.searchConnectionTest.tipReviewDocs")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -264,7 +272,7 @@ const SearchConnectionTest: React.FC<SearchConnectionTestProps> = ({ litellmPara
|
|||
<Divider style={{ margin: "24px 0 16px" }} />
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Button type="link" href="https://docs.litellm.ai/docs/search" target="_blank" icon={<InfoCircleOutlined />}>
|
||||
View Search Documentation
|
||||
{t("searchTools.searchConnectionTest.viewSearchDocs")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React, { useState } from "react";
|
|||
import { Button, Input, Typography, Spin } from "antd";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import { SearchOutlined, LoadingOutlined } from "@ant-design/icons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { searchToolQueryCall } from "../networking";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import { Card, Title as TremorTitle } from "@tremor/react";
|
||||
|
|
@ -25,6 +26,7 @@ interface SearchToolTesterProps {
|
|||
}
|
||||
|
||||
export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolName, accessToken, className = "" }) => {
|
||||
const { t } = useTranslation();
|
||||
const [query, setQuery] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [searchHistory, setSearchHistory] = useState<
|
||||
|
|
@ -40,7 +42,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
|
||||
const handleSearch = async () => {
|
||||
if (!query.trim()) {
|
||||
MessageManager.warning("Please enter a search query");
|
||||
MessageManager.warning(t("searchTools.searchToolTester.enterQueryWarning"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +65,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
// Don't clear query after search so user can modify it
|
||||
} catch (error) {
|
||||
console.error("Error querying search tool:", error);
|
||||
NotificationsManager.fromBackend("Failed to query search tool");
|
||||
NotificationsManager.fromBackend(t("searchTools.searchToolTester.queryFailed"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
|
@ -76,7 +78,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
const clearHistory = () => {
|
||||
setSearchHistory([]);
|
||||
setExpandedResults({});
|
||||
NotificationsManager.success("Search history cleared");
|
||||
NotificationsManager.success(t("searchTools.searchToolTester.historyCleared"));
|
||||
};
|
||||
|
||||
const toggleResultExpansion = (historyIndex: number, resultIndex: number) => {
|
||||
|
|
@ -94,7 +96,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
return (
|
||||
<Card className="mt-6">
|
||||
<div className="mb-6">
|
||||
<TremorTitle>Test Search Tool</TremorTitle>
|
||||
<TremorTitle>{t("searchTools.searchToolTester.title")}</TremorTitle>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col" style={{ minHeight: "600px" }}>
|
||||
|
|
@ -121,7 +123,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
handleSearch();
|
||||
}
|
||||
}}
|
||||
placeholder="Enter your search query..."
|
||||
placeholder={t("searchTools.searchToolTester.searchPlaceholder")}
|
||||
disabled={isLoading}
|
||||
bordered={false}
|
||||
style={{ fontSize: "15px", padding: 0, height: "100%", boxShadow: "none" }}
|
||||
|
|
@ -145,7 +147,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
||||
}}
|
||||
>
|
||||
Search
|
||||
{t("common.search")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -157,15 +159,17 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
<div className="flex items-center justify-center w-24 h-24 rounded-full bg-gray-100 mb-6">
|
||||
<SearchOutlined style={{ fontSize: "48px", color: "#9ca3af" }} />
|
||||
</div>
|
||||
<Text className="text-lg text-gray-600 font-medium">Test your search tool</Text>
|
||||
<Text className="text-sm text-gray-500 mt-2">Enter a query above to see search results</Text>
|
||||
<Text className="text-lg text-gray-600 font-medium">
|
||||
{t("searchTools.searchToolTester.emptyStateTitle")}
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500 mt-2">{t("searchTools.searchToolTester.emptyStateSubtitle")}</Text>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{isLoading && (
|
||||
<div className="flex flex-col justify-center items-center py-16">
|
||||
<Spin indicator={antIcon} />
|
||||
<Text className="mt-4 text-gray-600 font-medium">Searching...</Text>
|
||||
<Text className="mt-4 text-gray-600 font-medium">{t("searchTools.searchToolTester.searching")}</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -179,7 +183,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<Text className="text-xs font-semibold text-gray-500 uppercase tracking-wide">
|
||||
Search Query
|
||||
{t("searchTools.searchToolTester.searchQueryLabel")}
|
||||
</Text>
|
||||
<div className="text-base font-semibold text-gray-900 mt-1.5">{latestResults.query}</div>
|
||||
</div>
|
||||
|
|
@ -187,8 +191,9 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
<Text className="text-xs text-gray-500">{formatTimestamp(latestResults.timestamp)}</Text>
|
||||
<div className="flex items-center gap-3 mt-1">
|
||||
<div className="text-sm font-semibold text-blue-600">
|
||||
{latestResults.response?.results?.length || 0}{" "}
|
||||
{latestResults.response?.results?.length === 1 ? "result" : "results"}
|
||||
{t("searchTools.searchToolTester.resultCount", {
|
||||
count: latestResults.response?.results?.length || 0,
|
||||
})}
|
||||
</div>
|
||||
{latestResults.latency !== undefined && (
|
||||
<>
|
||||
|
|
@ -280,7 +285,9 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
color: "#3b82f6",
|
||||
}}
|
||||
>
|
||||
{isResultExpanded ? "Show less" : "Show more"}
|
||||
{isResultExpanded
|
||||
? t("searchTools.searchToolTester.showLess")
|
||||
: t("searchTools.searchToolTester.showMore")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -293,8 +300,10 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
<div className="flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mx-auto mb-4">
|
||||
<SearchOutlined style={{ fontSize: "24px", color: "#9ca3af" }} />
|
||||
</div>
|
||||
<Text className="text-gray-600 font-medium">No results found</Text>
|
||||
<Text className="text-sm text-gray-500 mt-1">Try a different search query</Text>
|
||||
<Text className="text-gray-600 font-medium">{t("common.noResults")}</Text>
|
||||
<Text className="text-sm text-gray-500 mt-1">
|
||||
{t("searchTools.searchToolTester.tryDifferentQuery")}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -304,7 +313,9 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
{searchHistory.length > 1 && (
|
||||
<div className="mt-8 pt-6 border-t border-gray-200">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<Text className="text-sm font-semibold text-gray-700">Previous Searches</Text>
|
||||
<Text className="text-sm font-semibold text-gray-700">
|
||||
{t("searchTools.searchToolTester.previousSearches")}
|
||||
</Text>
|
||||
<Button
|
||||
onClick={clearHistory}
|
||||
size="small"
|
||||
|
|
@ -314,7 +325,7 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Clear All
|
||||
{t("searchTools.searchToolTester.clearAll")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
|
|
@ -329,8 +340,9 @@ export const SearchToolTester: React.FC<SearchToolTesterProps> = ({ searchToolNa
|
|||
<div className="text-sm font-medium text-gray-800 truncate">{entry.query}</div>
|
||||
<div className="text-xs text-gray-500 mt-1.5 flex items-center gap-2">
|
||||
<span className="font-medium text-blue-600">
|
||||
{entry.response?.results?.length || 0}{" "}
|
||||
{entry.response?.results?.length === 1 ? "result" : "results"}
|
||||
{t("searchTools.searchToolTester.resultCount", {
|
||||
count: entry.response?.results?.length || 0,
|
||||
})}
|
||||
</span>
|
||||
{entry.latency !== undefined && (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Button, Card, Grid, Text, Title } from "@tremor/react";
|
|||
import { Button as AntdButton } from "antd";
|
||||
import { CheckIcon, CopyIcon } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SearchToolTester } from "./SearchToolTester";
|
||||
import { AvailableSearchProvider, SearchTool } from "./types";
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ export const SearchToolView: React.FC<SearchToolViewProps> = ({
|
|||
accessToken,
|
||||
availableProviders,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
|
||||
|
||||
const copyToClipboard = async (text: string | null | undefined, key: string) => {
|
||||
|
|
@ -44,7 +46,7 @@ export const SearchToolView: React.FC<SearchToolViewProps> = ({
|
|||
<div className="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<Button icon={ArrowLeftIcon} variant="light" className="mb-4" onClick={onBack}>
|
||||
Back to All Search Tools
|
||||
{t("searchTools.searchToolView.backToAll")}
|
||||
</Button>
|
||||
<div className="flex items-center cursor-pointer">
|
||||
<Title>{searchTool.search_tool_name}</Title>
|
||||
|
|
@ -79,30 +81,32 @@ export const SearchToolView: React.FC<SearchToolViewProps> = ({
|
|||
|
||||
<Grid numItems={1} numItemsSm={2} numItemsLg={3} className="gap-6">
|
||||
<Card>
|
||||
<Text>Provider</Text>
|
||||
<Text>{t("searchTools.searchToolView.providerLabel")}</Text>
|
||||
<div className="mt-2">
|
||||
<Title>{getProviderDisplayName(searchTool.litellm_params.search_provider)}</Title>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Text>API Key</Text>
|
||||
<Text>{t("searchTools.searchToolView.apiKeyLabel")}</Text>
|
||||
<div className="mt-2">
|
||||
<Text>{searchTool.litellm_params.api_key ? "****" : "Not set"}</Text>
|
||||
<Text>{searchTool.litellm_params.api_key ? "****" : t("searchTools.searchToolView.notSet")}</Text>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Text>Created At</Text>
|
||||
<Text>{t("common.createdAt")}</Text>
|
||||
<div className="mt-2">
|
||||
<Text>{searchTool.created_at ? new Date(searchTool.created_at).toLocaleString() : "Unknown"}</Text>
|
||||
<Text>
|
||||
{searchTool.created_at ? new Date(searchTool.created_at).toLocaleString() : t("common.unknown")}
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
{searchTool.search_tool_info?.description && (
|
||||
<Card className="mt-6">
|
||||
<Text>Description</Text>
|
||||
<Text>{t("common.description")}</Text>
|
||||
<div className="mt-2">
|
||||
<Text>{searchTool.search_tool_info.description}</Text>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query";
|
|||
import { Button, Text, Title } from "@tremor/react";
|
||||
import { Form, Input, Modal, Select, Spin, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DeleteResourceModal from "../common_components/DeleteResourceModal";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import { deleteSearchTool, fetchAvailableSearchProviders, fetchSearchTools, updateSearchTool } from "../networking";
|
||||
|
|
@ -19,6 +20,7 @@ interface SearchToolsProps {
|
|||
}
|
||||
|
||||
const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID }) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
data: searchTools,
|
||||
isLoading: isLoadingTools,
|
||||
|
|
@ -94,13 +96,13 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteSearchTool(accessToken, toolIdToDelete);
|
||||
NotificationsManager.success("Deleted search tool successfully");
|
||||
NotificationsManager.success(t("searchTools.searchTools.deleteSuccess"));
|
||||
setIsDeleteModalOpen(false);
|
||||
setToolToDelete(null);
|
||||
refetch();
|
||||
} catch (error) {
|
||||
console.error("Error deleting the search tool:", error);
|
||||
NotificationsManager.error("Failed to delete search tool");
|
||||
NotificationsManager.error(t("searchTools.searchTools.deleteFailed"));
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
|
|
@ -143,14 +145,14 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
};
|
||||
|
||||
await updateSearchTool(accessToken, selectedToolId, searchToolData);
|
||||
NotificationsManager.success("Search tool updated successfully");
|
||||
NotificationsManager.success(t("searchTools.searchTools.updateSuccess"));
|
||||
setEditModalVisible(false);
|
||||
form.resetFields();
|
||||
setSelectedToolId(null);
|
||||
refetch();
|
||||
} catch (error) {
|
||||
console.error("Failed to update search tool:", error);
|
||||
NotificationsManager.error("Failed to update search tool");
|
||||
NotificationsManager.error(t("searchTools.searchTools.updateFailed"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -158,18 +160,18 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
<Form form={form} layout="vertical">
|
||||
<Form.Item
|
||||
name="search_tool_name"
|
||||
label="Search Tool Name"
|
||||
rules={[{ required: true, message: "Please enter a search tool name" }]}
|
||||
label={t("searchTools.searchTools.editSearchToolNameLabel")}
|
||||
rules={[{ required: true, message: t("searchTools.createSearchTools.searchToolNameRequired") }]}
|
||||
>
|
||||
<Input placeholder="e.g., my-perplexity-search" />
|
||||
<Input placeholder={t("searchTools.searchTools.editSearchToolNamePlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="search_provider"
|
||||
label="Search Provider"
|
||||
rules={[{ required: true, message: "Please select a search provider" }]}
|
||||
label={t("searchTools.searchTools.editSearchProviderLabel")}
|
||||
rules={[{ required: true, message: t("searchTools.createSearchTools.searchProviderRequired") }]}
|
||||
>
|
||||
<Select placeholder="Select a search provider" loading={isLoadingProviders}>
|
||||
<Select placeholder={t("searchTools.createSearchTools.searchProviderPlaceholder")} loading={isLoadingProviders}>
|
||||
{availableProviders.map((provider) => (
|
||||
<Select.Option key={provider.provider_name} value={provider.provider_name}>
|
||||
{provider.ui_friendly_name}
|
||||
|
|
@ -178,19 +180,23 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="api_key" label="API Key" extra="API key for the search provider">
|
||||
<Input.Password placeholder="Enter API key" />
|
||||
<Form.Item
|
||||
name="api_key"
|
||||
label={t("searchTools.searchTools.editApiKeyLabel")}
|
||||
extra={t("searchTools.searchTools.editApiKeyExtra")}
|
||||
>
|
||||
<Input.Password placeholder={t("searchTools.searchTools.editApiKeyPlaceholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="description" label="Description">
|
||||
<Input.TextArea rows={3} placeholder="Description of this search tool" />
|
||||
<Form.Item name="description" label={t("common.description")}>
|
||||
<Input.TextArea rows={3} placeholder={t("searchTools.searchTools.editDescriptionPlaceholder")} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
||||
if (!accessToken || !userRole || !userID) {
|
||||
console.log("Missing required authentication parameters", { accessToken, userRole, userID });
|
||||
return <div className="p-6 text-center text-gray-500">Missing required authentication parameters.</div>;
|
||||
return <div className="p-6 text-center text-gray-500">{t("searchTools.searchTools.missingAuthParams")}</div>;
|
||||
}
|
||||
|
||||
const ToolsTab = () =>
|
||||
|
|
@ -224,7 +230,7 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
rowKey={(record) => record.search_tool_id || record.search_tool_name}
|
||||
pagination={false}
|
||||
locale={{
|
||||
emptyText: "No search tools configured",
|
||||
emptyText: t("searchTools.searchTools.emptyText"),
|
||||
}}
|
||||
size="small"
|
||||
/>
|
||||
|
|
@ -236,19 +242,19 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
<div className="w-full h-full p-6">
|
||||
<DeleteResourceModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
title="Delete Search Tool"
|
||||
message="Are you sure you want to delete this search tool? This action cannot be undone."
|
||||
resourceInformationTitle="Search Tool Information"
|
||||
title={t("searchTools.searchTools.deleteModalTitle")}
|
||||
message={t("searchTools.searchTools.deleteModalMessage")}
|
||||
resourceInformationTitle={t("searchTools.searchTools.deleteResourceInfoTitle")}
|
||||
resourceInformation={
|
||||
toolToDelete
|
||||
? [
|
||||
{ label: "Name", value: toolToDelete.search_tool_name },
|
||||
{ label: t("common.name"), value: toolToDelete.search_tool_name },
|
||||
{ label: "ID", value: toolToDelete.search_tool_id, code: true },
|
||||
{
|
||||
label: "Provider",
|
||||
label: t("searchTools.searchTools.deleteResourceProviderLabel"),
|
||||
value: providerInfo?.ui_friendly_name || toolToDelete.litellm_params.search_provider,
|
||||
},
|
||||
{ label: "Description", value: toolToDelete.search_tool_info?.description || "-" },
|
||||
{ label: t("common.description"), value: toolToDelete.search_tool_info?.description || "-" },
|
||||
]
|
||||
: []
|
||||
}
|
||||
|
|
@ -267,7 +273,7 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
|
||||
{/* Edit Modal */}
|
||||
<Modal
|
||||
title="Edit Search Tool"
|
||||
title={t("searchTools.searchTools.editModalTitle")}
|
||||
open={isEditModalVisible}
|
||||
onOk={handleEditSubmit}
|
||||
onCancel={() => {
|
||||
|
|
@ -280,11 +286,11 @@ const SearchTools: React.FC<SearchToolsProps> = ({ accessToken, userRole, userID
|
|||
{renderEditForm()}
|
||||
</Modal>
|
||||
|
||||
<Title>Search Tools</Title>
|
||||
<Text className="text-tremor-content mt-2">Configure and manage your search providers</Text>
|
||||
<Title>{t("searchTools.searchTools.pageTitle")}</Title>
|
||||
<Text className="text-tremor-content mt-2">{t("searchTools.searchTools.pageSubtitle")}</Text>
|
||||
{isAdminRole(userRole) && (
|
||||
<Button className="mt-4 mb-4" onClick={() => setCreateModalVisible(true)}>
|
||||
+ Add New Search Tool
|
||||
{t("searchTools.searchTools.addNewBtn")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -7154,5 +7154,104 @@
|
|||
"usageExportHeader": {
|
||||
"exportData": "Export Data"
|
||||
}
|
||||
},
|
||||
"searchTools": {
|
||||
"createSearchTools": {
|
||||
"modalTitle": "Add New Search Tool",
|
||||
"searchToolNameLabel": "Search Tool Name",
|
||||
"searchToolNameTooltip": "A unique name to identify this search tool configuration (e.g., 'perplexity-search', 'tavily-news-search').",
|
||||
"searchToolNameRequired": "Please enter a search tool name",
|
||||
"searchToolNamePattern": "Name can only contain letters, numbers, hyphens, and underscores",
|
||||
"searchToolNamePlaceholder": "e.g., perplexity-search, my-tavily-tool",
|
||||
"searchProviderLabel": "Search Provider",
|
||||
"searchProviderTooltip": "Select the search provider you want to use. Each provider has different capabilities and pricing.",
|
||||
"searchProviderRequired": "Please select a search provider",
|
||||
"searchProviderPlaceholder": "Select a search provider",
|
||||
"apiKeyLabel": "API Key",
|
||||
"apiKeyTooltip": "The API key for authenticating with the search provider. This will be securely stored.",
|
||||
"apiKeyRequired": "Please enter an API key",
|
||||
"apiKeyPlaceholder": "Enter your API key",
|
||||
"descriptionLabel": "Description (Optional)",
|
||||
"descriptionPlaceholder": "Brief description of this search tool's purpose",
|
||||
"needHelpTooltip": "Get help on our github",
|
||||
"needHelp": "Need Help?",
|
||||
"testConnectionBtn": "Test Connection",
|
||||
"addSearchToolBtn": "Add Search Tool",
|
||||
"connectionTestResultsTitle": "Connection Test Results",
|
||||
"createSuccess": "Search tool created successfully",
|
||||
"createError": "Error creating search tool: {{error}}",
|
||||
"testFillRequired": "Please fill in Search Provider and API Key before testing"
|
||||
},
|
||||
"searchConnectionTest": {
|
||||
"testSuccessNotification": "Connection test successful!",
|
||||
"unknownError": "Unknown error",
|
||||
"unknownErrorOccurred": "Unknown error occurred",
|
||||
"authFailedInvalidKey": "Authentication failed: Invalid API key or credentials",
|
||||
"authErrorCheckKey": "Authentication error - please check your API key",
|
||||
"searchProvider": "search provider",
|
||||
"testingConnection": "Testing connection to {{provider}}...",
|
||||
"connectionSuccess": "Connection to {{provider}} successful!",
|
||||
"testQueryLabel": "Test query:",
|
||||
"resultsRetrieved": "Results retrieved: {{count}}",
|
||||
"connectionFailed": "Connection to {{provider}} failed",
|
||||
"errorLabel": "Error:",
|
||||
"errorTypeLabel": "Error type:",
|
||||
"hideDetails": "Hide Details",
|
||||
"showDetails": "Show Details",
|
||||
"fullErrorDetails": "Full Error Details",
|
||||
"troubleshootingTips": "Troubleshooting tips:",
|
||||
"tipVerifyApiKey": "Verify your API key is correct and active",
|
||||
"tipCheckServiceStatus": "Check if the search provider service is operational",
|
||||
"tipCheckCredits": "Ensure you have sufficient credits/quota with the provider",
|
||||
"tipReviewDocs": "Review the provider's documentation for any additional requirements",
|
||||
"viewSearchDocs": "View Search Documentation"
|
||||
},
|
||||
"searchToolTester": {
|
||||
"title": "Test Search Tool",
|
||||
"enterQueryWarning": "Please enter a search query",
|
||||
"queryFailed": "Failed to query search tool",
|
||||
"historyCleared": "Search history cleared",
|
||||
"searchPlaceholder": "Enter your search query...",
|
||||
"emptyStateTitle": "Test your search tool",
|
||||
"emptyStateSubtitle": "Enter a query above to see search results",
|
||||
"searching": "Searching...",
|
||||
"searchQueryLabel": "Search Query",
|
||||
"resultCount_one": "{{count}} result",
|
||||
"resultCount_other": "{{count}} results",
|
||||
"tryDifferentQuery": "Try a different search query",
|
||||
"previousSearches": "Previous Searches",
|
||||
"clearAll": "Clear All",
|
||||
"showLess": "Show less",
|
||||
"showMore": "Show more"
|
||||
},
|
||||
"searchToolView": {
|
||||
"backToAll": "Back to All Search Tools",
|
||||
"providerLabel": "Provider",
|
||||
"apiKeyLabel": "API Key",
|
||||
"notSet": "Not set"
|
||||
},
|
||||
"searchTools": {
|
||||
"pageTitle": "Search Tools",
|
||||
"pageSubtitle": "Configure and manage your search providers",
|
||||
"addNewBtn": "+ Add New Search Tool",
|
||||
"emptyText": "No search tools configured",
|
||||
"missingAuthParams": "Missing required authentication parameters.",
|
||||
"deleteModalTitle": "Delete Search Tool",
|
||||
"deleteModalMessage": "Are you sure you want to delete this search tool? This action cannot be undone.",
|
||||
"deleteResourceInfoTitle": "Search Tool Information",
|
||||
"deleteResourceProviderLabel": "Provider",
|
||||
"deleteSuccess": "Deleted search tool successfully",
|
||||
"deleteFailed": "Failed to delete search tool",
|
||||
"editModalTitle": "Edit Search Tool",
|
||||
"editSearchToolNameLabel": "Search Tool Name",
|
||||
"editSearchToolNamePlaceholder": "e.g., my-perplexity-search",
|
||||
"editSearchProviderLabel": "Search Provider",
|
||||
"editApiKeyLabel": "API Key",
|
||||
"editApiKeyExtra": "API key for the search provider",
|
||||
"editApiKeyPlaceholder": "Enter API key",
|
||||
"editDescriptionPlaceholder": "Description of this search tool",
|
||||
"updateSuccess": "Search tool updated successfully",
|
||||
"updateFailed": "Failed to update search tool"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7154,5 +7154,104 @@
|
|||
"usageExportHeader": {
|
||||
"exportData": "导出数据"
|
||||
}
|
||||
},
|
||||
"searchTools": {
|
||||
"createSearchTools": {
|
||||
"modalTitle": "添加新搜索工具",
|
||||
"searchToolNameLabel": "搜索工具名称",
|
||||
"searchToolNameTooltip": "用于标识此搜索工具配置的唯一名称(例如 'perplexity-search'、'tavily-news-search')。",
|
||||
"searchToolNameRequired": "请输入搜索工具名称",
|
||||
"searchToolNamePattern": "名称只能包含字母、数字、连字符和下划线",
|
||||
"searchToolNamePlaceholder": "例如:perplexity-search、my-tavily-tool",
|
||||
"searchProviderLabel": "搜索提供商",
|
||||
"searchProviderTooltip": "选择要使用的搜索提供商。每个提供商具有不同的功能和定价。",
|
||||
"searchProviderRequired": "请选择搜索提供商",
|
||||
"searchProviderPlaceholder": "选择搜索提供商",
|
||||
"apiKeyLabel": "API 密钥",
|
||||
"apiKeyTooltip": "用于向搜索提供商进行身份验证的 API 密钥,将被安全存储。",
|
||||
"apiKeyRequired": "请输入 API 密钥",
|
||||
"apiKeyPlaceholder": "输入您的 API 密钥",
|
||||
"descriptionLabel": "描述(可选)",
|
||||
"descriptionPlaceholder": "简要描述此搜索工具的用途",
|
||||
"needHelpTooltip": "在我们的 GitHub 上获取帮助",
|
||||
"needHelp": "需要帮助?",
|
||||
"testConnectionBtn": "测试连接",
|
||||
"addSearchToolBtn": "添加搜索工具",
|
||||
"connectionTestResultsTitle": "连接测试结果",
|
||||
"createSuccess": "搜索工具创建成功",
|
||||
"createError": "创建搜索工具时出错:{{error}}",
|
||||
"testFillRequired": "请在测试前填写搜索提供商和 API 密钥"
|
||||
},
|
||||
"searchConnectionTest": {
|
||||
"testSuccessNotification": "连接测试成功!",
|
||||
"unknownError": "未知错误",
|
||||
"unknownErrorOccurred": "发生未知错误",
|
||||
"authFailedInvalidKey": "身份验证失败:API 密钥或凭据无效",
|
||||
"authErrorCheckKey": "身份验证错误 - 请检查您的 API 密钥",
|
||||
"searchProvider": "搜索提供商",
|
||||
"testingConnection": "正在测试与 {{provider}} 的连接...",
|
||||
"connectionSuccess": "与 {{provider}} 连接成功!",
|
||||
"testQueryLabel": "测试查询:",
|
||||
"resultsRetrieved": "已检索结果:{{count}}",
|
||||
"connectionFailed": "与 {{provider}} 连接失败",
|
||||
"errorLabel": "错误:",
|
||||
"errorTypeLabel": "错误类型:",
|
||||
"hideDetails": "隐藏详情",
|
||||
"showDetails": "显示详情",
|
||||
"fullErrorDetails": "完整错误详情",
|
||||
"troubleshootingTips": "故障排除提示:",
|
||||
"tipVerifyApiKey": "确认您的 API 密钥正确且有效",
|
||||
"tipCheckServiceStatus": "检查搜索提供商服务是否正常运行",
|
||||
"tipCheckCredits": "确保您在该提供商处有足够的额度/配额",
|
||||
"tipReviewDocs": "查阅提供商文档了解其他要求",
|
||||
"viewSearchDocs": "查看搜索文档"
|
||||
},
|
||||
"searchToolTester": {
|
||||
"title": "测试搜索工具",
|
||||
"enterQueryWarning": "请输入搜索查询",
|
||||
"queryFailed": "查询搜索工具失败",
|
||||
"historyCleared": "搜索历史已清除",
|
||||
"searchPlaceholder": "输入您的搜索查询...",
|
||||
"emptyStateTitle": "测试您的搜索工具",
|
||||
"emptyStateSubtitle": "在上方输入查询以查看搜索结果",
|
||||
"searching": "搜索中...",
|
||||
"searchQueryLabel": "搜索查询",
|
||||
"resultCount_one": "{{count}} 条结果",
|
||||
"resultCount_other": "{{count}} 条结果",
|
||||
"tryDifferentQuery": "尝试不同的搜索查询",
|
||||
"previousSearches": "历史搜索",
|
||||
"clearAll": "清除全部",
|
||||
"showLess": "收起",
|
||||
"showMore": "展开"
|
||||
},
|
||||
"searchToolView": {
|
||||
"backToAll": "返回所有搜索工具",
|
||||
"providerLabel": "提供商",
|
||||
"apiKeyLabel": "API 密钥",
|
||||
"notSet": "未设置"
|
||||
},
|
||||
"searchTools": {
|
||||
"pageTitle": "搜索工具",
|
||||
"pageSubtitle": "配置和管理您的搜索提供商",
|
||||
"addNewBtn": "+ 添加新搜索工具",
|
||||
"emptyText": "未配置搜索工具",
|
||||
"missingAuthParams": "缺少必要的身份验证参数。",
|
||||
"deleteModalTitle": "删除搜索工具",
|
||||
"deleteModalMessage": "确定要删除此搜索工具吗?此操作无法撤销。",
|
||||
"deleteResourceInfoTitle": "搜索工具信息",
|
||||
"deleteResourceProviderLabel": "提供商",
|
||||
"deleteSuccess": "搜索工具已成功删除",
|
||||
"deleteFailed": "删除搜索工具失败",
|
||||
"editModalTitle": "编辑搜索工具",
|
||||
"editSearchToolNameLabel": "搜索工具名称",
|
||||
"editSearchToolNamePlaceholder": "例如:my-perplexity-search",
|
||||
"editSearchProviderLabel": "搜索提供商",
|
||||
"editApiKeyLabel": "API 密钥",
|
||||
"editApiKeyExtra": "搜索提供商的 API 密钥",
|
||||
"editApiKeyPlaceholder": "输入 API 密钥",
|
||||
"editDescriptionPlaceholder": "此搜索工具的描述",
|
||||
"updateSuccess": "搜索工具更新成功",
|
||||
"updateFailed": "更新搜索工具失败"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue