fix(web/config): preserve and update builtin web provider API keys
This commit is contained in:
parent
935d5f8660
commit
9db32d996b
7 changed files with 181 additions and 14 deletions
|
|
@ -191,6 +191,9 @@ func (h *Handler) handlePatchConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type webSearchAPIKeyPatch struct {
|
type webSearchAPIKeyPatch struct {
|
||||||
|
braveAPIKey *string
|
||||||
|
tavilyAPIKey *string
|
||||||
|
perplexityAPIKey *string
|
||||||
glmSearchAPIKey *string
|
glmSearchAPIKey *string
|
||||||
baiduSearchAPIKey *string
|
baiduSearchAPIKey *string
|
||||||
}
|
}
|
||||||
|
|
@ -199,6 +202,15 @@ func parseWebSearchAPIKeyPatch(body []byte) webSearchAPIKeyPatch {
|
||||||
var raw struct {
|
var raw struct {
|
||||||
Tools *struct {
|
Tools *struct {
|
||||||
Web *struct {
|
Web *struct {
|
||||||
|
Brave *struct {
|
||||||
|
APIKey *string `json:"api_key"`
|
||||||
|
} `json:"brave"`
|
||||||
|
Tavily *struct {
|
||||||
|
APIKey *string `json:"api_key"`
|
||||||
|
} `json:"tavily"`
|
||||||
|
Perplexity *struct {
|
||||||
|
APIKey *string `json:"api_key"`
|
||||||
|
} `json:"perplexity"`
|
||||||
GLMSearch *struct {
|
GLMSearch *struct {
|
||||||
APIKey *string `json:"api_key"`
|
APIKey *string `json:"api_key"`
|
||||||
} `json:"glm_search"`
|
} `json:"glm_search"`
|
||||||
|
|
@ -214,6 +226,15 @@ func parseWebSearchAPIKeyPatch(body []byte) webSearchAPIKeyPatch {
|
||||||
|
|
||||||
patch := webSearchAPIKeyPatch{}
|
patch := webSearchAPIKeyPatch{}
|
||||||
if raw.Tools != nil && raw.Tools.Web != nil {
|
if raw.Tools != nil && raw.Tools.Web != nil {
|
||||||
|
if raw.Tools.Web.Brave != nil && raw.Tools.Web.Brave.APIKey != nil {
|
||||||
|
patch.braveAPIKey = raw.Tools.Web.Brave.APIKey
|
||||||
|
}
|
||||||
|
if raw.Tools.Web.Tavily != nil && raw.Tools.Web.Tavily.APIKey != nil {
|
||||||
|
patch.tavilyAPIKey = raw.Tools.Web.Tavily.APIKey
|
||||||
|
}
|
||||||
|
if raw.Tools.Web.Perplexity != nil && raw.Tools.Web.Perplexity.APIKey != nil {
|
||||||
|
patch.perplexityAPIKey = raw.Tools.Web.Perplexity.APIKey
|
||||||
|
}
|
||||||
if raw.Tools.Web.GLMSearch != nil && raw.Tools.Web.GLMSearch.APIKey != nil {
|
if raw.Tools.Web.GLMSearch != nil && raw.Tools.Web.GLMSearch.APIKey != nil {
|
||||||
patch.glmSearchAPIKey = raw.Tools.Web.GLMSearch.APIKey
|
patch.glmSearchAPIKey = raw.Tools.Web.GLMSearch.APIKey
|
||||||
}
|
}
|
||||||
|
|
@ -225,6 +246,15 @@ func parseWebSearchAPIKeyPatch(body []byte) webSearchAPIKeyPatch {
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyWebSearchAPIKeyPatch(cfg *config.Config, patch webSearchAPIKeyPatch) {
|
func applyWebSearchAPIKeyPatch(cfg *config.Config, patch webSearchAPIKeyPatch) {
|
||||||
|
if patch.braveAPIKey != nil {
|
||||||
|
cfg.Tools.Web.Brave.SetAPIKey(strings.TrimSpace(*patch.braveAPIKey))
|
||||||
|
}
|
||||||
|
if patch.tavilyAPIKey != nil {
|
||||||
|
cfg.Tools.Web.Tavily.SetAPIKey(strings.TrimSpace(*patch.tavilyAPIKey))
|
||||||
|
}
|
||||||
|
if patch.perplexityAPIKey != nil {
|
||||||
|
cfg.Tools.Web.Perplexity.SetAPIKey(strings.TrimSpace(*patch.perplexityAPIKey))
|
||||||
|
}
|
||||||
if patch.glmSearchAPIKey != nil {
|
if patch.glmSearchAPIKey != nil {
|
||||||
cfg.Tools.Web.GLMSearch.SetAPIKey(strings.TrimSpace(*patch.glmSearchAPIKey))
|
cfg.Tools.Web.GLMSearch.SetAPIKey(strings.TrimSpace(*patch.glmSearchAPIKey))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,32 +160,61 @@ func runConfigRequest(t *testing.T, configPath, method, body string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertWebSearchAPIKeys(t *testing.T, configPath, wantGLMAPIKey, wantBaiduAPIKey string) {
|
type webSearchAPIKeys struct {
|
||||||
|
brave string
|
||||||
|
tavily string
|
||||||
|
perplexity string
|
||||||
|
glm string
|
||||||
|
baidu string
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertWebSearchAPIKeys(t *testing.T, configPath string, want webSearchAPIKeys) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(configPath)
|
cfg, err := config.LoadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LoadConfig() error = %v", err)
|
t.Fatalf("LoadConfig() error = %v", err)
|
||||||
}
|
}
|
||||||
if got := cfg.Tools.Web.GLMSearch.APIKey(); got != wantGLMAPIKey {
|
if got := cfg.Tools.Web.Brave.APIKey(); got != want.brave {
|
||||||
t.Fatalf("tools.web.glm_search.api_key = %q, want %q", got, wantGLMAPIKey)
|
t.Fatalf("tools.web.brave.api_key = %q, want %q", got, want.brave)
|
||||||
}
|
}
|
||||||
if got := cfg.Tools.Web.BaiduSearch.APIKey(); got != wantBaiduAPIKey {
|
if got := cfg.Tools.Web.Tavily.APIKey(); got != want.tavily {
|
||||||
t.Fatalf("tools.web.baidu_search.api_key = %q, want %q", got, wantBaiduAPIKey)
|
t.Fatalf("tools.web.tavily.api_key = %q, want %q", got, want.tavily)
|
||||||
|
}
|
||||||
|
if got := cfg.Tools.Web.Perplexity.APIKey(); got != want.perplexity {
|
||||||
|
t.Fatalf("tools.web.perplexity.api_key = %q, want %q", got, want.perplexity)
|
||||||
|
}
|
||||||
|
if got := cfg.Tools.Web.GLMSearch.APIKey(); got != want.glm {
|
||||||
|
t.Fatalf("tools.web.glm_search.api_key = %q, want %q", got, want.glm)
|
||||||
|
}
|
||||||
|
if got := cfg.Tools.Web.BaiduSearch.APIKey(); got != want.baidu {
|
||||||
|
t.Fatalf("tools.web.baidu_search.api_key = %q, want %q", got, want.baidu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandlePatchConfig_PreservesWebSearchAPIKeysWhenOmitted(t *testing.T) {
|
func TestHandlePatchConfig_PreservesWebSearchAPIKeysWhenOmitted(t *testing.T) {
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
configPath, cleanup := setupOAuthTestEnv(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
seedWebSearchAPIKeys(t, configPath, "glm-existing-key", "baidu-existing-key")
|
seedWebSearchAPIKeys(t, configPath, webSearchAPIKeys{
|
||||||
|
brave: "brave-existing-key",
|
||||||
|
tavily: "tavily-existing-key",
|
||||||
|
perplexity: "perplexity-existing-key",
|
||||||
|
glm: "glm-existing-key",
|
||||||
|
baidu: "baidu-existing-key",
|
||||||
|
})
|
||||||
|
|
||||||
runConfigRequest(t, configPath, http.MethodPatch, `{
|
runConfigRequest(t, configPath, http.MethodPatch, `{
|
||||||
"gateway": {
|
"gateway": {
|
||||||
"log_level": "info"
|
"log_level": "info"
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
assertWebSearchAPIKeys(t, configPath, "glm-existing-key", "baidu-existing-key")
|
assertWebSearchAPIKeys(t, configPath, webSearchAPIKeys{
|
||||||
|
brave: "brave-existing-key",
|
||||||
|
tavily: "tavily-existing-key",
|
||||||
|
perplexity: "perplexity-existing-key",
|
||||||
|
glm: "glm-existing-key",
|
||||||
|
baidu: "baidu-existing-key",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandlePatchConfig_UpdatesWebSearchAPIKeys(t *testing.T) {
|
func TestHandlePatchConfig_UpdatesWebSearchAPIKeys(t *testing.T) {
|
||||||
|
|
@ -195,18 +224,33 @@ func TestHandlePatchConfig_UpdatesWebSearchAPIKeys(t *testing.T) {
|
||||||
runConfigRequest(t, configPath, http.MethodPatch, `{
|
runConfigRequest(t, configPath, http.MethodPatch, `{
|
||||||
"tools": {
|
"tools": {
|
||||||
"web": {
|
"web": {
|
||||||
|
"brave": { "api_key": "brave-updated-key" },
|
||||||
|
"tavily": { "api_key": "tavily-updated-key" },
|
||||||
|
"perplexity": { "api_key": "perplexity-updated-key" },
|
||||||
"glm_search": { "api_key": "glm-updated-key" },
|
"glm_search": { "api_key": "glm-updated-key" },
|
||||||
"baidu_search": { "api_key": "baidu-updated-key" }
|
"baidu_search": { "api_key": "baidu-updated-key" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
assertWebSearchAPIKeys(t, configPath, "glm-updated-key", "baidu-updated-key")
|
assertWebSearchAPIKeys(t, configPath, webSearchAPIKeys{
|
||||||
|
brave: "brave-updated-key",
|
||||||
|
tavily: "tavily-updated-key",
|
||||||
|
perplexity: "perplexity-updated-key",
|
||||||
|
glm: "glm-updated-key",
|
||||||
|
baidu: "baidu-updated-key",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleUpdateConfig_PreservesWebSearchAPIKeysWhenOmitted(t *testing.T) {
|
func TestHandleUpdateConfig_PreservesWebSearchAPIKeysWhenOmitted(t *testing.T) {
|
||||||
configPath, cleanup := setupOAuthTestEnv(t)
|
configPath, cleanup := setupOAuthTestEnv(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
seedWebSearchAPIKeys(t, configPath, "glm-existing-key-via-put", "baidu-existing-key-via-put")
|
seedWebSearchAPIKeys(t, configPath, webSearchAPIKeys{
|
||||||
|
brave: "brave-existing-key-via-put",
|
||||||
|
tavily: "tavily-existing-key-via-put",
|
||||||
|
perplexity: "perplexity-existing-key-via-put",
|
||||||
|
glm: "glm-existing-key-via-put",
|
||||||
|
baidu: "baidu-existing-key-via-put",
|
||||||
|
})
|
||||||
|
|
||||||
runConfigRequest(t, configPath, http.MethodPut, `{
|
runConfigRequest(t, configPath, http.MethodPut, `{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
|
@ -224,7 +268,13 @@ func TestHandleUpdateConfig_PreservesWebSearchAPIKeysWhenOmitted(t *testing.T) {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
assertWebSearchAPIKeys(t, configPath, "glm-existing-key-via-put", "baidu-existing-key-via-put")
|
assertWebSearchAPIKeys(t, configPath, webSearchAPIKeys{
|
||||||
|
brave: "brave-existing-key-via-put",
|
||||||
|
tavily: "tavily-existing-key-via-put",
|
||||||
|
perplexity: "perplexity-existing-key-via-put",
|
||||||
|
glm: "glm-existing-key-via-put",
|
||||||
|
baidu: "baidu-existing-key-via-put",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleUpdateConfig_UpdatesWebSearchAPIKeys(t *testing.T) {
|
func TestHandleUpdateConfig_UpdatesWebSearchAPIKeys(t *testing.T) {
|
||||||
|
|
@ -248,15 +298,24 @@ func TestHandleUpdateConfig_UpdatesWebSearchAPIKeys(t *testing.T) {
|
||||||
],
|
],
|
||||||
"tools": {
|
"tools": {
|
||||||
"web": {
|
"web": {
|
||||||
|
"brave": { "api_key": "brave-updated-key-via-put" },
|
||||||
|
"tavily": { "api_key": "tavily-updated-key-via-put" },
|
||||||
|
"perplexity": { "api_key": "perplexity-updated-key-via-put" },
|
||||||
"glm_search": { "api_key": "glm-updated-key-via-put" },
|
"glm_search": { "api_key": "glm-updated-key-via-put" },
|
||||||
"baidu_search": { "api_key": "baidu-updated-key-via-put" }
|
"baidu_search": { "api_key": "baidu-updated-key-via-put" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
assertWebSearchAPIKeys(t, configPath, "glm-updated-key-via-put", "baidu-updated-key-via-put")
|
assertWebSearchAPIKeys(t, configPath, webSearchAPIKeys{
|
||||||
|
brave: "brave-updated-key-via-put",
|
||||||
|
tavily: "tavily-updated-key-via-put",
|
||||||
|
perplexity: "perplexity-updated-key-via-put",
|
||||||
|
glm: "glm-updated-key-via-put",
|
||||||
|
baidu: "baidu-updated-key-via-put",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func seedWebSearchAPIKeys(t *testing.T, configPath, glmAPIKey, baiduAPIKey string) {
|
func seedWebSearchAPIKeys(t *testing.T, configPath string, keys webSearchAPIKeys) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
cfg, err := config.LoadConfig(configPath)
|
cfg, err := config.LoadConfig(configPath)
|
||||||
|
|
@ -264,8 +323,11 @@ func seedWebSearchAPIKeys(t *testing.T, configPath, glmAPIKey, baiduAPIKey strin
|
||||||
t.Fatalf("LoadConfig() error = %v", err)
|
t.Fatalf("LoadConfig() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.Tools.Web.GLMSearch.SetAPIKey(glmAPIKey)
|
cfg.Tools.Web.Brave.SetAPIKey(keys.brave)
|
||||||
cfg.Tools.Web.BaiduSearch.SetAPIKey(baiduAPIKey)
|
cfg.Tools.Web.Tavily.SetAPIKey(keys.tavily)
|
||||||
|
cfg.Tools.Web.Perplexity.SetAPIKey(keys.perplexity)
|
||||||
|
cfg.Tools.Web.GLMSearch.SetAPIKey(keys.glm)
|
||||||
|
cfg.Tools.Web.BaiduSearch.SetAPIKey(keys.baidu)
|
||||||
|
|
||||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
t.Fatalf("SaveConfig() error = %v", err)
|
t.Fatalf("SaveConfig() error = %v", err)
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,9 @@ export function ConfigPage() {
|
||||||
"Cron exec timeout",
|
"Cron exec timeout",
|
||||||
{ min: 0 },
|
{ min: 0 },
|
||||||
)
|
)
|
||||||
|
const braveAPIKey = form.braveAPIKey.trim()
|
||||||
|
const tavilyAPIKey = form.tavilyAPIKey.trim()
|
||||||
|
const perplexityAPIKey = form.perplexityAPIKey.trim()
|
||||||
const glmSearchAPIKey = form.glmSearchAPIKey.trim()
|
const glmSearchAPIKey = form.glmSearchAPIKey.trim()
|
||||||
const baiduSearchAPIKey = form.baiduSearchAPIKey.trim()
|
const baiduSearchAPIKey = form.baiduSearchAPIKey.trim()
|
||||||
const execConfigPatch: Record<string, unknown> = {
|
const execConfigPatch: Record<string, unknown> = {
|
||||||
|
|
@ -209,6 +212,15 @@ export function ConfigPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (braveAPIKey !== "") {
|
||||||
|
webConfigPatch.brave = { api_key: braveAPIKey }
|
||||||
|
}
|
||||||
|
if (tavilyAPIKey !== "") {
|
||||||
|
webConfigPatch.tavily = { api_key: tavilyAPIKey }
|
||||||
|
}
|
||||||
|
if (perplexityAPIKey !== "") {
|
||||||
|
webConfigPatch.perplexity = { api_key: perplexityAPIKey }
|
||||||
|
}
|
||||||
if (glmSearchAPIKey !== "") {
|
if (glmSearchAPIKey !== "") {
|
||||||
webConfigPatch.glm_search = { api_key: glmSearchAPIKey }
|
webConfigPatch.glm_search = { api_key: glmSearchAPIKey }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -521,6 +521,45 @@ export function WebSearchSection({
|
||||||
title={t("pages.config.sections.web_search")}
|
title={t("pages.config.sections.web_search")}
|
||||||
description={t("pages.config.web_search_section_hint")}
|
description={t("pages.config.web_search_section_hint")}
|
||||||
>
|
>
|
||||||
|
<Field
|
||||||
|
label={t("pages.config.brave_api_key")}
|
||||||
|
hint={t("pages.config.brave_api_key_hint")}
|
||||||
|
layout="setting-row"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
value={form.braveAPIKey}
|
||||||
|
placeholder={t("pages.config.secret_placeholder")}
|
||||||
|
onChange={(e) => onFieldChange("braveAPIKey", e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label={t("pages.config.tavily_api_key")}
|
||||||
|
hint={t("pages.config.tavily_api_key_hint")}
|
||||||
|
layout="setting-row"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
value={form.tavilyAPIKey}
|
||||||
|
placeholder={t("pages.config.secret_placeholder")}
|
||||||
|
onChange={(e) => onFieldChange("tavilyAPIKey", e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
label={t("pages.config.perplexity_api_key")}
|
||||||
|
hint={t("pages.config.perplexity_api_key_hint")}
|
||||||
|
layout="setting-row"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
value={form.perplexityAPIKey}
|
||||||
|
placeholder={t("pages.config.secret_placeholder")}
|
||||||
|
onChange={(e) => onFieldChange("perplexityAPIKey", e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
label={t("pages.config.glm_search_api_key")}
|
label={t("pages.config.glm_search_api_key")}
|
||||||
hint={t("pages.config.glm_search_api_key_hint")}
|
hint={t("pages.config.glm_search_api_key_hint")}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ export interface CoreConfigForm {
|
||||||
execTimeoutSeconds: string
|
execTimeoutSeconds: string
|
||||||
allowCommand: boolean
|
allowCommand: boolean
|
||||||
cronExecTimeoutMinutes: string
|
cronExecTimeoutMinutes: string
|
||||||
|
braveAPIKey: string
|
||||||
|
tavilyAPIKey: string
|
||||||
|
perplexityAPIKey: string
|
||||||
glmSearchAPIKey: string
|
glmSearchAPIKey: string
|
||||||
baiduSearchAPIKey: string
|
baiduSearchAPIKey: string
|
||||||
maxTokens: string
|
maxTokens: string
|
||||||
|
|
@ -79,6 +82,9 @@ export const EMPTY_FORM: CoreConfigForm = {
|
||||||
execTimeoutSeconds: "0",
|
execTimeoutSeconds: "0",
|
||||||
allowCommand: true,
|
allowCommand: true,
|
||||||
cronExecTimeoutMinutes: "5",
|
cronExecTimeoutMinutes: "5",
|
||||||
|
braveAPIKey: "",
|
||||||
|
tavilyAPIKey: "",
|
||||||
|
perplexityAPIKey: "",
|
||||||
glmSearchAPIKey: "",
|
glmSearchAPIKey: "",
|
||||||
baiduSearchAPIKey: "",
|
baiduSearchAPIKey: "",
|
||||||
maxTokens: "32768",
|
maxTokens: "32768",
|
||||||
|
|
@ -133,6 +139,9 @@ export function buildFormFromConfig(config: unknown): CoreConfigForm {
|
||||||
const devices = asRecord(root.devices)
|
const devices = asRecord(root.devices)
|
||||||
const tools = asRecord(root.tools)
|
const tools = asRecord(root.tools)
|
||||||
const web = asRecord(tools.web)
|
const web = asRecord(tools.web)
|
||||||
|
const brave = asRecord(web.brave)
|
||||||
|
const tavily = asRecord(web.tavily)
|
||||||
|
const perplexity = asRecord(web.perplexity)
|
||||||
const glmSearch = asRecord(web.glm_search)
|
const glmSearch = asRecord(web.glm_search)
|
||||||
const baiduSearch = asRecord(web.baidu_search)
|
const baiduSearch = asRecord(web.baidu_search)
|
||||||
const cron = asRecord(tools.cron)
|
const cron = asRecord(tools.cron)
|
||||||
|
|
@ -191,6 +200,9 @@ export function buildFormFromConfig(config: unknown): CoreConfigForm {
|
||||||
cron.exec_timeout_minutes,
|
cron.exec_timeout_minutes,
|
||||||
EMPTY_FORM.cronExecTimeoutMinutes,
|
EMPTY_FORM.cronExecTimeoutMinutes,
|
||||||
),
|
),
|
||||||
|
braveAPIKey: asString(brave.api_key),
|
||||||
|
tavilyAPIKey: asString(tavily.api_key),
|
||||||
|
perplexityAPIKey: asString(perplexity.api_key),
|
||||||
glmSearchAPIKey: asString(glmSearch.api_key),
|
glmSearchAPIKey: asString(glmSearch.api_key),
|
||||||
baiduSearchAPIKey: asString(baiduSearch.api_key),
|
baiduSearchAPIKey: asString(baiduSearch.api_key),
|
||||||
maxTokens: asNumberString(defaults.max_tokens, EMPTY_FORM.maxTokens),
|
maxTokens: asNumberString(defaults.max_tokens, EMPTY_FORM.maxTokens),
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,12 @@
|
||||||
"cron_exec_timeout": "Scheduled Command Timeout (minutes)",
|
"cron_exec_timeout": "Scheduled Command Timeout (minutes)",
|
||||||
"cron_exec_timeout_hint": "Maximum runtime for scheduled commands. Set to 0 to disable the timeout.",
|
"cron_exec_timeout_hint": "Maximum runtime for scheduled commands. Set to 0 to disable the timeout.",
|
||||||
"web_search_section_hint": "Manage API keys for built-in web search providers. Leave a field blank to keep the current key unchanged.",
|
"web_search_section_hint": "Manage API keys for built-in web search providers. Leave a field blank to keep the current key unchanged.",
|
||||||
|
"brave_api_key": "Brave Search API Key",
|
||||||
|
"brave_api_key_hint": "Used by the built-in Brave web search provider.",
|
||||||
|
"tavily_api_key": "Tavily Search API Key",
|
||||||
|
"tavily_api_key_hint": "Used by the built-in Tavily web search provider.",
|
||||||
|
"perplexity_api_key": "Perplexity Search API Key",
|
||||||
|
"perplexity_api_key_hint": "Used by the built-in Perplexity web search provider.",
|
||||||
"glm_search_api_key": "GLM Search API Key",
|
"glm_search_api_key": "GLM Search API Key",
|
||||||
"glm_search_api_key_hint": "Used by the built-in GLM web search provider.",
|
"glm_search_api_key_hint": "Used by the built-in GLM web search provider.",
|
||||||
"baidu_search_api_key": "Baidu Search API Key",
|
"baidu_search_api_key": "Baidu Search API Key",
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,12 @@
|
||||||
"cron_exec_timeout": "定时命令超时(分钟)",
|
"cron_exec_timeout": "定时命令超时(分钟)",
|
||||||
"cron_exec_timeout_hint": "定时任务中命令的最长运行时间。设置为 0 表示不限制超时。",
|
"cron_exec_timeout_hint": "定时任务中命令的最长运行时间。设置为 0 表示不限制超时。",
|
||||||
"web_search_section_hint": "管理内置网页搜索服务的 API Key。留空表示保持当前 Key 不变。",
|
"web_search_section_hint": "管理内置网页搜索服务的 API Key。留空表示保持当前 Key 不变。",
|
||||||
|
"brave_api_key": "Brave 搜索 API Key",
|
||||||
|
"brave_api_key_hint": "用于内置的 Brave 网页搜索服务。",
|
||||||
|
"tavily_api_key": "Tavily 搜索 API Key",
|
||||||
|
"tavily_api_key_hint": "用于内置的 Tavily 网页搜索服务。",
|
||||||
|
"perplexity_api_key": "Perplexity 搜索 API Key",
|
||||||
|
"perplexity_api_key_hint": "用于内置的 Perplexity 网页搜索服务。",
|
||||||
"glm_search_api_key": "GLM 搜索 API Key",
|
"glm_search_api_key": "GLM 搜索 API Key",
|
||||||
"glm_search_api_key_hint": "用于内置的 GLM 网页搜索服务。",
|
"glm_search_api_key_hint": "用于内置的 GLM 网页搜索服务。",
|
||||||
"baidu_search_api_key": "百度搜索 API Key",
|
"baidu_search_api_key": "百度搜索 API Key",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue