Compare commits

...
4 Commits
Author SHA1 Message Date
Dayuan Jiang 4cba863fd9 chore: bump version to 0.5.0 (#967) 2026-10-10 12:02:12 +09:00
Dayuan Jiang 7682e479e3 chore: replace the cat example with a new drawing (#966)
Update the README cat image and the cached example response for
"Draw a cat for me" with a new cat diagram.
2026-10-10 10:33:23 +09:00
Dayuan Jiang a7ae114edf fix(chat): keep the request alive while its answer streams, so a stop still reaches the provider (#965)
A Request's signal follows the signal it was created with through a weak
link inside undici: once the Request object is garbage collected, the link is
gone and request.signal never aborts (nodejs/undici#3644). The chat route
passes req.signal to streamText and holds nothing else from the request, so a
GC pause during a stream could leave the model call running after the client
stopped or disconnected, with no onAbort accounting.

The route now keeps each request in a WeakMap keyed by its response, which
Next holds while the body is piped.

This is also the cause of the unit test chat-route-abort hanging on CI (three
times this week, 60 ms locally): the test's Request is dropped as soon as the
route returns, and a GC in that window lost the abort. With a forced GC the
test hangs on Node 20 and 24 without this change and passes with it.
2026-10-10 09:24:25 +09:00
Dayuan Jiang e117095c7b feat(admin): rewrite the admin panel copy in plain words (#963)
Labels are two to four words, descriptions one short line, and the
wording follows the reader rather than the implementation: "服务商"
instead of "provider", "本页设置" instead of "后台设置" for the source
chip (it shared its name with the page title), "需重启" as a badge, a
verb on every switch ("访问内网"), "Langfuse 密钥" instead of "私钥".
Descriptions keep the facts the old ones had (where to change an
env-managed provider, that users must enter an access code), and the
same style is applied to the English, Japanese and Traditional Chinese
dictionaries. Five user-facing strings in the same files are aligned
with the new terms (服务商, token).
2026-10-10 08:38:26 +09:00
9 changed files with 419 additions and 408 deletions
+6
View File
@@ -98,6 +98,11 @@ function createCachedStreamResponse(xml: string): Response {
// Responses streamed from the model, whose trace streamText's callbacks end
const modelStreamResponses = new WeakSet<Response>()
// A Request's signal follows the client's disconnect only while the Request
// object itself is alive: once it is garbage collected, the abort is lost
// (nodejs/undici#3644) and a stopped chat would run on at the provider.
// Each request is kept as long as its answer streams.
const requestOfResponse = new WeakMap<Response, Request>()
// Inner handler function
const DEBUG_LLM_PAYLOAD = process.env.DEBUG_LLM_PAYLOAD === "true"
@@ -787,6 +792,7 @@ Call this tool to get shape names and usage syntax for a specific library.`,
},
})
modelStreamResponses.add(response)
requestOfResponse.set(response, req)
return response
}
+133 -125
View File
@@ -740,146 +740,154 @@ export const CACHED_EXAMPLE_RESPONSES: CachedResponse[] = [
{
promptText: "Draw a cat for me",
hasImage: false,
xml: `<mxCell id="2" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="300" y="150" width="120" height="120" as="geometry"/>
</mxCell>
<mxCell id="3" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;rotation=30;" vertex="1" parent="1">
<mxGeometry x="280" y="120" width="50" height="60" as="geometry"/>
</mxCell>
<mxCell id="4" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;rotation=-30;" vertex="1" parent="1">
<mxGeometry x="390" y="120" width="50" height="60" as="geometry"/>
</mxCell>
<mxCell id="5" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;rotation=30;" vertex="1" parent="1">
<mxGeometry x="290" y="135" width="30" height="35" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;rotation=-30;" vertex="1" parent="1">
<mxGeometry x="400" y="135" width="30" height="35" as="geometry"/>
</mxCell>
<mxCell id="7" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;strokeColor=#000000;" vertex="1" parent="1">
<mxGeometry x="325" y="185" width="15" height="15" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;strokeColor=#000000;" vertex="1" parent="1">
<mxGeometry x="380" y="185" width="15" height="15" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=#000000;strokeWidth=1;rotation=180;" vertex="1" parent="1">
<mxGeometry x="350" y="210" width="20" height="15" as="geometry"/>
</mxCell>
<mxCell id="10" value="" style="curved=1;endArrow=none;html=1;strokeColor=#000000;strokeWidth=2;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="360" y="220" as="sourcePoint"/>
<mxPoint x="340" y="235" as="targetPoint"/>
xml: `<mxCell id="2" edge="1" parent="1" style="curved=1;endArrow=none;html=1;strokeWidth=14;strokeColor=#F4A460;" value="">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="355" y="230"/>
<mxPoint x="580" y="480"/>
<mxPoint x="600" y="380"/>
</Array>
<mxPoint x="490" y="480" as="sourcePoint"/>
<mxPoint x="560" y="330" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="11" value="" style="curved=1;endArrow=none;html=1;strokeColor=#000000;strokeWidth=2;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="360" y="220" as="sourcePoint"/>
<mxPoint x="380" y="235" as="targetPoint"/>
<mxCell id="3" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#F4A460;strokeColor=#8B5A2B;strokeWidth=2;" value="" vertex="1">
<mxGeometry height="190" width="200" x="320" y="320" as="geometry"/>
</mxCell>
<mxCell id="4" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE4C4;strokeColor=none;" value="" vertex="1">
<mxGeometry height="120" width="100" x="370" y="360" as="geometry"/>
</mxCell>
<mxCell id="5" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#F4A460;strokeColor=#8B5A2B;strokeWidth=2;" value="" vertex="1">
<mxGeometry height="40" width="60" x="350" y="480" as="geometry"/>
</mxCell>
<mxCell id="6" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#F4A460;strokeColor=#8B5A2B;strokeWidth=2;" value="" vertex="1">
<mxGeometry height="40" width="60" x="430" y="480" as="geometry"/>
</mxCell>
<mxCell id="7" parent="1" style="triangle;direction=north;whiteSpace=wrap;html=1;fillColor=#F4A460;strokeColor=#8B5A2B;strokeWidth=2;rotation=-15;" value="" vertex="1">
<mxGeometry height="80" width="70" x="310" y="110" as="geometry"/>
</mxCell>
<mxCell id="8" parent="1" style="triangle;direction=north;whiteSpace=wrap;html=1;fillColor=#F4A460;strokeColor=#8B5A2B;strokeWidth=2;rotation=15;" value="" vertex="1">
<mxGeometry height="80" width="70" x="460" y="110" as="geometry"/>
</mxCell>
<mxCell id="9" parent="1" style="triangle;direction=north;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;rotation=-15;" value="" vertex="1">
<mxGeometry height="45" width="36" x="327" y="135" as="geometry"/>
</mxCell>
<mxCell id="10" parent="1" style="triangle;direction=north;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;rotation=15;" value="" vertex="1">
<mxGeometry height="45" width="36" x="477" y="135" as="geometry"/>
</mxCell>
<mxCell id="11" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#F4A460;strokeColor=#8B5A2B;strokeWidth=2;" value="" vertex="1">
<mxGeometry height="200" width="240" x="300" y="140" as="geometry"/>
</mxCell>
<mxCell id="12" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#333333;strokeColor=none;" value="" vertex="1">
<mxGeometry height="44" width="36" x="355" y="200" as="geometry"/>
</mxCell>
<mxCell id="13" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#333333;strokeColor=none;" value="" vertex="1">
<mxGeometry height="44" width="36" x="449" y="200" as="geometry"/>
</mxCell>
<mxCell id="14" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=none;" value="" vertex="1">
<mxGeometry height="13" width="13" x="363" y="207" as="geometry"/>
</mxCell>
<mxCell id="15" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=none;" value="" vertex="1">
<mxGeometry height="13" width="13" x="457" y="207" as="geometry"/>
</mxCell>
<mxCell id="16" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;opacity=70;" value="" vertex="1">
<mxGeometry height="22" width="40" x="325" y="255" as="geometry"/>
</mxCell>
<mxCell id="17" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFB6C1;strokeColor=none;opacity=70;" value="" vertex="1">
<mxGeometry height="22" width="40" x="475" y="255" as="geometry"/>
</mxCell>
<mxCell id="18" parent="1" style="triangle;direction=south;whiteSpace=wrap;html=1;fillColor=#FF69B4;strokeColor=none;" value="" vertex="1">
<mxGeometry height="16" width="24" x="408" y="252" as="geometry"/>
</mxCell>
<mxCell id="19" edge="1" parent="1" style="curved=1;endArrow=none;html=1;strokeWidth=2;strokeColor=#333333;" value="">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="365" y="230"/>
<mxPoint x="410" y="288"/>
</Array>
<mxPoint x="400" y="278" as="sourcePoint"/>
<mxPoint x="420" y="270" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="310" y="200" as="sourcePoint"/>
<mxPoint x="260" y="195" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="13" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="310" y="210" as="sourcePoint"/>
<mxPoint x="260" y="210" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="14" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="310" y="220" as="sourcePoint"/>
<mxPoint x="260" y="225" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="15" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="200" as="sourcePoint"/>
<mxPoint x="460" y="195" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="16" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="210" as="sourcePoint"/>
<mxPoint x="460" y="210" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="17" value="" style="endArrow=none;html=1;strokeColor=#000000;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="220" as="sourcePoint"/>
<mxPoint x="460" y="225" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="18" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="285" y="250" width="150" height="180" as="geometry"/>
</mxCell>
<mxCell id="19" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFFFFF;strokeColor=none;" vertex="1" parent="1">
<mxGeometry x="315" y="280" width="90" height="120" as="geometry"/>
</mxCell>
<mxCell id="20" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="300" y="410" width="40" height="50" as="geometry"/>
</mxCell>
<mxCell id="21" value="" style="ellipse;whiteSpace=wrap;html=1;fillColor=#FFE6CC;strokeColor=#000000;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="380" y="410" width="40" height="50" as="geometry"/>
</mxCell>
<mxCell id="22" value="" style="curved=1;endArrow=none;html=1;strokeColor=#000000;strokeWidth=3;fillColor=#FFE6CC;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="285" y="340" as="sourcePoint"/>
<mxPoint x="240" y="260" as="targetPoint"/>
<mxCell id="20" edge="1" parent="1" style="curved=1;endArrow=none;html=1;strokeWidth=2;strokeColor=#333333;" value="">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="260" y="350"/>
<mxPoint x="240" y="320"/>
<mxPoint x="235" y="290"/>
<mxPoint x="430" y="288"/>
</Array>
<mxPoint x="420" y="270" as="sourcePoint"/>
<mxPoint x="440" y="278" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="21" edge="1" parent="1" style="endArrow=none;html=1;strokeWidth=2;strokeColor=#5C3A1A;" value="">
<mxGeometry relative="1" as="geometry">
<mxPoint x="370" y="262" as="sourcePoint"/>
<mxPoint x="270" y="245" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="22" edge="1" parent="1" style="endArrow=none;html=1;strokeWidth=2;strokeColor=#5C3A1A;" value="">
<mxGeometry relative="1" as="geometry">
<mxPoint x="370" y="272" as="sourcePoint"/>
<mxPoint x="268" y="278" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="23" edge="1" parent="1" style="endArrow=none;html=1;strokeWidth=2;strokeColor=#5C3A1A;" value="">
<mxGeometry relative="1" as="geometry">
<mxPoint x="470" y="262" as="sourcePoint"/>
<mxPoint x="570" y="245" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="24" edge="1" parent="1" style="endArrow=none;html=1;strokeWidth=2;strokeColor=#5C3A1A;" value="">
<mxGeometry relative="1" as="geometry">
<mxPoint x="470" y="272" as="sourcePoint"/>
<mxPoint x="572" y="278" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="25" parent="1" style="text;html=1;align=center;verticalAlign=middle;fontSize=22;fontStyle=1;fontColor=#FF69B4;" value="Meow~ ♥" vertex="1">
<mxGeometry height="40" width="120" x="530" y="120" as="geometry"/>
</mxCell>`,
},
]
+62 -62
View File
@@ -118,7 +118,7 @@
"customSystemMessage": "Custom instructions",
"customSystemMessageDescription": "Add custom instructions appended to the AI's system prompt.",
"customSystemMessagePlaceholder": "e.g., Always use blue color scheme for diagrams...",
"maxOutputTokens": "Max output tokens",
"maxOutputTokens": "Output token limit",
"maxOutputTokensDescription": "Budget for one reply, shared by thinking and the diagram XML. Raise it if the AI keeps thinking and no diagram appears. Leave empty for the default.",
"tabModels": "Models",
"tabAbout": "About",
@@ -482,46 +482,46 @@
},
"admin": {
"title": "Admin Settings",
"loginPrompt": "Enter the admin password (the ADMIN_PASSWORD environment variable) to manage server settings.",
"loginPrompt": "Enter the admin password (the ADMIN_PASSWORD environment variable)",
"password": "Password",
"signIn": "Sign In",
"signingIn": "Signing In…",
"signingIn": "Signing in…",
"loginFailed": "Login failed",
"precedence": "File overrides env · env overrides defaults",
"notWritable": "The settings file is not writable on this deployment (serverless platforms have no persistent disk). Settings are shown read-only — configure via environment variables instead.",
"precedence": "Priority: this page > environment variables > defaults",
"notWritable": "Settings can't be saved on this deployment, only viewed; use environment variables instead",
"settingGroups": "Setting groups",
"enabled": "Enabled",
"disabled": "Disabled",
"enableGroup": "Enable {group}",
"enabled": "On",
"disabled": "Off",
"enableGroup": "Turn on {group}",
"unsavedChanges": "Unsaved changes",
"saved": "Settings saved. Changes apply immediately.",
"saveFailed": "Save failed. Check your connection and try again.",
"invalidSettings": "Some settings are invalid.",
"discard": "Discard",
"saveChanges": "Save Changes",
"saved": "Saved. Changes apply right away, except items marked \"Needs restart\"",
"saveFailed": "Save failed. Check your connection and try again",
"invalidSettings": "Some settings are invalid; see the notes under them",
"discard": "Discard changes",
"saveChanges": "Save changes",
"saving": "Saving…",
"sourceSaved": "Saved",
"sourceSaved": "This page",
"sourceEnv": "Env",
"sourceSavedTitle": "Set in the admin settings file",
"sourceEnvTitle": "Set by an environment variable",
"restartRequired": "Restart Required",
"sourceSavedTitle": "Set on this page",
"sourceEnvTitle": "From an environment variable",
"restartRequired": "Needs restart",
"modified": "Modified",
"notSet": "Not set",
"savedReplace": "Saved ({hint}) — type to replace",
"showValue": "Show value",
"hideValue": "Hide value",
"removeValue": "Remove value",
"removeValueTitle": "Remove the stored value",
"savedReplace": "Saved ({hint}); type a new value to replace it",
"showValue": "Show",
"hideValue": "Hide",
"removeValue": "Clear",
"removeValueTitle": "Clear the saved value",
"resetToDefault": "Reset to default",
"models": "Models",
"modelsDescription": "Server-side providers and models available to all users — no personal API key needed. The default provider's first model is used when users don't pick one.",
"addProviderHint": "Add a provider to offer server-side models to all users.",
"selectProviderHint": "Select or add a provider to configure its credentials and models.",
"addProviderToOfferModels": "Add at least one model to expose this provider to users.",
"managedViaEnv": "(managed via env)",
"envReadOnly": "Defined in AI_MODELS_CONFIG / ai-models.json — read-only here. Edit the environment configuration to change it.",
"modelsDescription": "Models every user can use, no personal API key needed",
"addProviderHint": "Add a provider to offer models to all users",
"selectProviderHint": "Select or add a provider, then enter its key and models",
"addProviderToOfferModels": "Add at least one model before users can see this provider",
"managedViaEnv": "(environment variables)",
"envReadOnly": "Defined in AI_MODELS_CONFIG / ai-models.json; view only here, edit it there",
"defaultModel": "Default Model",
"noModelsConfigured": "No models configured",
"noModelsConfigured": "No models yet",
"modelCount": "{count} model",
"modelCountPlural": "{count} models",
"default": "Default",
@@ -531,65 +531,65 @@
"addModel": "Add model",
"suggested": "Suggested",
"test": "Test",
"testOk": "OK ({ms}ms)",
"testOk": "Passed ({ms} ms)",
"testFailed": "Failed",
"removeModel": "Remove {model}",
"removeModel": "Delete {model}",
"deleteProviderTitle": "Delete {name}?",
"deleteProviderDesc": "Its credentials and models will be removed from the server after you save.",
"deleteProviderDesc": "After you save, its key and models are deleted from the server",
"cancel": "Cancel",
"delete": "Delete",
"groups": {
"generation": {
"title": "Generation",
"description": "Output parameters applied to all chat requests."
"title": "Replies",
"description": "How the AI replies, for every conversation"
},
"access": {
"title": "Access Control",
"description": "Restrict who can use this deployment."
"title": "Access",
"description": "Limit who can use this site"
},
"features": {
"title": "Features",
"description": "Optional features and security toggles."
"title": "Optional Features",
"description": "Extra features and safety options"
},
"observability": {
"title": "Observability",
"description": "Langfuse tracing for LLM calls."
"title": "Call Logs",
"description": "Record model calls with Langfuse"
},
"quota": {
"title": "Quota & Rate Limits",
"description": "Per-IP usage limits. Enforcement requires a DynamoDB table."
"title": "Usage Limits",
"description": "Per-IP limits; they take effect only with a DynamoDB table"
}
},
"settings": {
"TEMPERATURE": {
"label": "Temperature",
"description": "Leave unset for reasoning models that reject temperature."
"description": "Higher is more random; leave empty for reasoning models that don't support it"
},
"MAX_OUTPUT_TOKENS": {
"label": "Max Output Tokens"
"label": "Output Token Limit"
},
"ACCESS_CODE_LIST": {
"label": "Access Codes",
"description": "Comma-separated list. Users must enter one to chat. Empty = open access."
"description": "Separate several with commas; users must enter one to chat; empty means no limit"
},
"ENABLE_VLM_VALIDATION": {
"label": "VLM Diagram Validation",
"description": "Visually validate generated diagrams with a vision model."
"label": "Diagram Check",
"description": "After drawing, a model that can see images checks a screenshot"
},
"VALIDATION_MODEL": {
"label": "Validation Model",
"description": "Falls back to the default AI model when empty."
"label": "Check Model",
"description": "Empty means the default model"
},
"VALIDATION_TIMEOUT": {
"label": "Validation Timeout (ms)"
"label": "Check Timeout (ms)"
},
"ENABLE_HISTORY_XML_REPLACE": {
"label": "History XML Compression",
"description": "Replace old diagram XML in history with placeholders."
"label": "Trim Old Diagrams",
"description": "Old diagrams in the chat history are kept as a short marker"
},
"ALLOW_PRIVATE_URLS": {
"label": "Allow Private URLs",
"description": "Turn off to block requests to private IPs and internal hostnames (SSRF protection)."
"description": "When off, custom model URLs can't point to private networks; safer"
},
"LANGFUSE_PUBLIC_KEY": {
"label": "Langfuse Public Key"
@@ -598,29 +598,29 @@
"label": "Langfuse Secret Key"
},
"LANGFUSE_BASEURL": {
"label": "Langfuse Base URL"
"label": "Langfuse URL"
},
"DAILY_REQUEST_LIMIT": {
"label": "Daily Request Limit",
"description": "Per IP per day."
"label": "Daily Requests",
"description": "Requests per IP per day"
},
"DAILY_TOKEN_LIMIT": {
"label": "Daily Token Limit",
"description": "Per IP per day."
"label": "Daily Tokens",
"description": "Tokens per IP per day"
},
"TPM_LIMIT": {
"label": "Tokens Per Minute"
},
"DYNAMODB_QUOTA_TABLE": {
"label": "DynamoDB Table",
"description": "Quota enforcement is disabled when empty."
"label": "DynamoDB Table Name",
"description": "Empty means no usage limits"
},
"DYNAMODB_REGION": {
"label": "DynamoDB Region"
},
"QUOTA_TIMEZONE": {
"label": "Quota Timezone",
"description": "Timezone for the daily reset boundary."
"label": "Time Zone",
"description": "Daily usage resets at midnight in this time zone"
}
}
},
+62 -62
View File
@@ -118,7 +118,7 @@
"customSystemMessage": "カスタム指示",
"customSystemMessageDescription": "AIのシステムプロンプトに追加されるカスタム指示を入力します。",
"customSystemMessagePlaceholder": "例:ダイアグラムには常に青色のカラースキームを使用...",
"maxOutputTokens": "最大出力トークン数",
"maxOutputTokens": "出力トークン上限",
"maxOutputTokensDescription": "1回の応答の予算で、思考過程とダイアグラムの XML が共有します。AI が考え続けてダイアグラムが生成されない場合は大きくしてください。空欄ならデフォルト値を使います。",
"tabModels": "モデル",
"tabAbout": "このアプリについて",
@@ -482,114 +482,114 @@
},
"admin": {
"title": "管理者設定",
"loginPrompt": "サーバー設定を管理するには、管理者パスワード(ADMIN_PASSWORD 環境変数)を入力してください。",
"loginPrompt": "管理者パスワード(環境変数 ADMIN_PASSWORD)を入力してください",
"password": "パスワード",
"signIn": "ログイン",
"signingIn": "ログイン中…",
"loginFailed": "ログインに失敗しました",
"precedence": "ファイルが環境変数を上書き · 環境変数がデフォルトを上書き",
"notWritable": "このデプロイ環境では設定ファイルに書き込めません(サーバーレス環境には永続ディスクがありません)。設定は読み取り専用で表示されます——代わりに環境変数で構成してください。",
"settingGroups": "設定グループ",
"enabled": "有効",
"disabled": "無効",
"enableGroup": "{group} を有効化",
"precedence": "優先順位:このページ > 環境変数 > 既定値",
"notWritable": "この環境では設定を保存できず、表示のみです。環境変数で設定してください",
"settingGroups": "設定の分類",
"enabled": "オン",
"disabled": "オフ",
"enableGroup": "{group} をオンにする",
"unsavedChanges": "未保存の変更があります",
"saved": "設定を保存しました。変更は即座に反映されます。",
"saveFailed": "保存に失敗しました。接続を確認して再試行してください。",
"invalidSettings": "一部の設定が無効です。",
"discard": "破棄",
"saved": "保存しました。「再起動が必要」の項目以外はすぐに反映されます",
"saveFailed": "保存に失敗しました。接続を確認してもう一度お試しください",
"invalidSettings": "入力に誤りがある設定があります。項目の下の説明を確認してください",
"discard": "変更を破棄",
"saveChanges": "変更を保存",
"saving": "保存中…",
"sourceSaved": "保存済み",
"sourceSaved": "このページ",
"sourceEnv": "環境変数",
"sourceSavedTitle": "管理者設定ファイルで設定",
"sourceEnvTitle": "環境変数で設定",
"sourceSavedTitle": "このページで設定した値",
"sourceEnvTitle": "環境変数による設定",
"restartRequired": "再起動が必要",
"modified": "変更済み",
"notSet": "未設定",
"savedReplace": "保存済み({hint})——入力して置き換え",
"showValue": "値を表示",
"hideValue": "値を非表示",
"removeValue": "値を削除",
"removeValueTitle": "保存された値を削除",
"savedReplace": "保存済み({hint})。新しい値を入力すると置き換わります",
"showValue": "表示",
"hideValue": "非表示",
"removeValue": "クリア",
"removeValueTitle": "保存した値をクリア",
"resetToDefault": "デフォルトに戻す",
"models": "モデル",
"modelsDescription": "全ユーザーが利用できるサーバー側のプロバイダーとモデル——個人の API キーは不要です。ユーザーがモデルを選択しない場合、デフォルトプロバイダーの最初のモデルが使用されます。",
"addProviderHint": "プロバイダーを追加して、全ユーザーにサーバー側モデルを提供します。",
"selectProviderHint": "プロバイダーを選択または追加して、その資格情報とモデルを構成します。",
"addProviderToOfferModels": "ユーザーにこのプロバイダーを公開するには、モデルを少なくとも 1 つ追加してください。",
"managedViaEnv": "(環境変数で管理)",
"envReadOnly": "AI_MODELS_CONFIG / ai-models.json で定義——ここでは読み取り専用です。変更するには環境構成を編集してください。",
"modelsDescription": "全ユーザーが使えるモデル。個人の API キーは不要です",
"addProviderHint": "プロバイダーを追加して、全ユーザーにモデルを提供します",
"selectProviderHint": "プロバイダーを選択または追加し、キーとモデルを入力します",
"addProviderToOfferModels": "モデルを 1 つ以上追加すると、ユーザーにこのプロバイダーが表示されます",
"managedViaEnv": "(環境変数)",
"envReadOnly": "AI_MODELS_CONFIG / ai-models.json で定義されています。ここでは表示のみで、変更はそちらで行ってください",
"defaultModel": "デフォルトモデル",
"noModelsConfigured": "モデルが構成されていません",
"noModelsConfigured": "モデルはまだありません",
"modelCount": "{count} 個のモデル",
"modelCountPlural": "{count} 個のモデル",
"default": "デフォルト",
"setAsDefault": "デフォルトプロバイダーに設定",
"defaultProvider": "デフォルトプロバイダー",
"setAsDefault": "既定のプロバイダーにする",
"defaultProvider": "既定のプロバイダー",
"modelIdPlaceholder": "モデル ID…",
"addModel": "モデルを追加",
"suggested": "おすすめ",
"test": "テスト",
"testOk": "正常({ms}ms)",
"testOk": "成功({ms} ms)",
"testFailed": "失敗",
"removeModel": "{model} を削除",
"deleteProviderTitle": "{name} を削除しますか?",
"deleteProviderDesc": "保存後、その資格情報とモデルはサーバーから削除されます。",
"deleteProviderDesc": "保存すると、このプロバイダーのキーとモデルがサーバーから削除されます",
"cancel": "キャンセル",
"delete": "削除",
"groups": {
"generation": {
"title": "生成",
"description": "すべてのチャットリクエストに適用される出力パラメーター。"
"title": "応答の設定",
"description": "AI の応答のしかたを決めます。すべての会話に適用されます"
},
"access": {
"title": "アクセス制御",
"description": "このデプロイを使用できるユーザーを制限します。"
"title": "アクセス権",
"description": "このサイトを使える人を制限します"
},
"features": {
"title": "機能",
"description": "オプション機能とセキュリティの切り替え。"
"title": "オプション機能",
"description": "追加機能と安全のための設定"
},
"observability": {
"title": "オブザーバビリティ",
"description": "LLM 呼び出しの Langfuse トレース。"
"title": "呼び出し記録",
"description": "モデルの呼び出しを Langfuse に記録します"
},
"quota": {
"title": "クォータとレート制限",
"description": "IP ごとの使用制限。強制には DynamoDB テーブルが必要です。"
"title": "使用量の制限",
"description": "IP ごとの制限。DynamoDB テーブルがあるときだけ有効です"
}
},
"settings": {
"TEMPERATURE": {
"label": "温度",
"description": "温度を受け付けない推論モデルの場合は未設定のままにしてください。"
"description": "高いほどランダムになります。温度に対応しない推論モデルでは空にしてください"
},
"MAX_OUTPUT_TOKENS": {
"label": "最大出力トークン数"
"label": "出力トークン上限"
},
"ACCESS_CODE_LIST": {
"label": "アクセスコード",
"description": "カンマ区切りのリスト。チャットにはいずれかの入力が必要です。空 = オープンアクセス。"
"description": "複数はカンマ区切り。いずれかを入力しないと使えません。空なら制限なし"
},
"ENABLE_VLM_VALIDATION": {
"label": "VLM 図検証",
"description": "ビジョンモデルで生成された図を視覚的に検証します。"
"label": "図のチェック",
"description": "描画後、画像を読めるモデルがスクリーンショットをチェックします"
},
"VALIDATION_MODEL": {
"label": "検証モデル",
"description": "空の場合はデフォルトの AI モデルにフォールバックします。"
"label": "チェック用モデル",
"description": "空なら既定のモデルを使います"
},
"VALIDATION_TIMEOUT": {
"label": "検証タイムアウト(ms)"
"label": "チェックのタイムアウト(ms)"
},
"ENABLE_HISTORY_XML_REPLACE": {
"label": "履歴 XML 圧縮",
"description": "履歴内の古い図 XML をプレースホルダーで置き換えます。"
"label": "古い図を省略",
"description": "会話履歴の古い図は短い印だけ残します"
},
"ALLOW_PRIVATE_URLS": {
"label": "プライベート URL を許可",
"description": "オフにすると、プライベート IP や内部ホスト名へのリクエストをブロックします(SSRF 保護)。"
"label": "内部ネットワークへのアクセス",
"description": "オフにすると、ユーザーが指定するモデルの URL に内部アドレスを使えなくなり、安全です"
},
"LANGFUSE_PUBLIC_KEY": {
"label": "Langfuse Public Key"
@@ -598,29 +598,29 @@
"label": "Langfuse Secret Key"
},
"LANGFUSE_BASEURL": {
"label": "Langfuse Base URL"
"label": "Langfuse の URL"
},
"DAILY_REQUEST_LIMIT": {
"label": "1 日あたりのリクエスト上限",
"description": "IP ごと 1 日あたり。"
"label": "1 日のリクエスト数",
"description": "IP ごとの 1 日あたりのリクエスト数の上限"
},
"DAILY_TOKEN_LIMIT": {
"label": "1 日あたりのトークン上限",
"description": "IP ごと 1 日あたり。"
"label": "1 日のトークン数",
"description": "IP ごとの 1 日あたりのトークン使用量の上限"
},
"TPM_LIMIT": {
"label": "1 分あたりのトークン数"
},
"DYNAMODB_QUOTA_TABLE": {
"label": "DynamoDB テーブル",
"description": "空の場合、クォータの強制は無効になります。"
"label": "DynamoDB テーブル名",
"description": "空なら使用量を制限しません"
},
"DYNAMODB_REGION": {
"label": "DynamoDB リージョン"
},
"QUOTA_TIMEZONE": {
"label": "クォータタイムゾーン",
"description": "1 日のリセット境界に使用するタイムゾーン。"
"label": "タイムゾーン",
"description": "1 日の使用量はこのタイムゾーンの 0 時にリセットされます"
}
}
},
+76 -76
View File
@@ -82,9 +82,9 @@
"accessCode": "存取碼",
"accessCodePlaceholder": "輸入存取碼",
"accessCodeDescription": "使用此應用程式需要存取碼。",
"aiProvider": "AI 提供商設定",
"aiProvider": "AI 服務商設定",
"aiProviderDescription": "使用您自己的 API 金鑰來繞過使用限制。您的金鑰僅儲存在瀏覽器本機,不會儲存在伺服器上。",
"provider": "提供商",
"provider": "服務商",
"modelId": "模型 ID",
"apiKey": "API 金鑰",
"apiKeyPlaceholder": "您的 API 金鑰",
@@ -118,7 +118,7 @@
"customSystemMessage": "自訂指令",
"customSystemMessageDescription": "新增自訂指示,將附加到 AI 的系統提示末尾。",
"customSystemMessagePlaceholder": "例如:圖表始終使用藍色配色方案...",
"maxOutputTokens": "最大輸出 token 數",
"maxOutputTokens": "輸出 token 上限",
"maxOutputTokensDescription": "單次回覆的額度,思考過程與圖表 XML 共用。若 AI 一直在思考卻沒有產生圖表,請將它調大。留空則使用預設值。",
"tabModels": "模型",
"tabAbout": "關於",
@@ -207,10 +207,10 @@
},
"quota": {
"dailyLimit": "已達每日配額",
"tokenLimit": "已達每日令牌限制",
"tokenLimit": "已達每日 token 上限",
"tpmLimit": "速率限制",
"tpmMessage": "請求過多。請稍等片刻。",
"tpmMessageDetailed": "達到速率限制({limit} 令牌/分鐘)。請等待 {seconds} 秒後再傳送請求。",
"tpmMessageDetailed": "達到速率限制(每分鐘 {limit} token)。請等待 {seconds} 秒後再傳送請求。",
"messageApi": "看來您今天的體驗次數已達上限。很高興您玩得開心;為了讓大家都能公平使用,示範站對用量做了限制。",
"messageApiSelfHosted": null,
"messageToken": "看來您今天的 token 用量已達上限。很高興您玩得開心;為了讓大家都能公平使用,示範站對用量做了限制。",
@@ -481,146 +481,146 @@
"removeAccessKeys": "刪除存取金鑰"
},
"admin": {
"title": "管理員設定",
"loginPrompt": "輸入管理員密碼(即 ADMIN_PASSWORD 環境變數)以管理伺服器設定。",
"title": "後台設定",
"loginPrompt": "輸入管理員密碼(環境變數 ADMIN_PASSWORD)",
"password": "密碼",
"signIn": "登入",
"signingIn": "正在登入…",
"signingIn": "登入中…",
"loginFailed": "登入失敗",
"precedence": "檔案覆蓋環境變數 · 環境變數覆蓋預設值",
"notWritable": "此部署環境下設定檔不可寫入(無伺服器平台沒有持久化磁碟)。設定以唯讀方式顯示——請改用環境變數進行設定。",
"settingGroups": "設定分組",
"enabled": "已啟用",
"disabled": "已停用",
"enableGroup": "啟用 {group}",
"unsavedChanges": "有未儲存的變更",
"saved": "設定已儲存,變更立即生效。",
"saveFailed": "儲存失敗。請檢查網路連線後重試。",
"invalidSettings": "部分設定無效。",
"discard": "捨棄",
"saveChanges": "儲存變更",
"saving": "正在儲存…",
"sourceSaved": "已儲存",
"precedence": "優先順序:本頁設定 > 環境變數 > 預設值",
"notWritable": "這裡無法儲存設定,只能檢視;請改用環境變數",
"settingGroups": "設定分類",
"enabled": "已開啟",
"disabled": "已關閉",
"enableGroup": "開啟{group}",
"unsavedChanges": "有未儲存的修改",
"saved": "已儲存,立即生效(需重啟的項目除外)",
"saveFailed": "儲存失敗,請檢查網路後重試",
"invalidSettings": "有設定填寫有誤,請依提示修改",
"discard": "捨棄修改",
"saveChanges": "儲存修改",
"saving": "儲存中…",
"sourceSaved": "本頁設定",
"sourceEnv": "環境變數",
"sourceSavedTitle": "在管理員設定檔中設定",
"sourceEnvTitle": "透過環境變數設定",
"restartRequired": "需要重新啟動",
"sourceSavedTitle": "來自本頁設定",
"sourceEnvTitle": "來自環境變數",
"restartRequired": "需重啟",
"modified": "已修改",
"notSet": "未設定",
"savedReplace": "已儲存({hint})——輸入以取代",
"showValue": "顯示值",
"hideValue": "隱藏值",
"removeValue": "移除值",
"removeValueTitle": "移除已儲存的值",
"savedReplace": "已儲存({hint}),輸入新值即可取代",
"showValue": "顯示",
"hideValue": "隱藏",
"removeValue": "清除",
"removeValueTitle": "清除已儲存的值",
"resetToDefault": "重設為預設",
"models": "模型",
"modelsDescription": "面向所有使用者的伺服器端 provider 與模型——無需個人 API 金鑰。當使用者未選擇模型時,使用預設 provider 的第一個模型。",
"addProviderHint": "新增一個 provider,為所有使用者提供伺服器端模型。",
"selectProviderHint": "選擇或新增一個 provider 以設定其憑證和模型。",
"addProviderToOfferModels": "至少新增一個模型,才能向使用者開放此 provider。",
"managedViaEnv": "(透過環境變數管理)",
"envReadOnly": "在 AI_MODELS_CONFIG / ai-models.json 中定義——此處唯讀。請編輯環境設定以變更。",
"modelsDescription": "給所有使用者用的模型,無需自備金鑰",
"addProviderHint": "新增服務商,為所有使用者提供模型",
"selectProviderHint": "選擇或新增服務商,填寫金鑰和模型",
"addProviderToOfferModels": "至少新增一個模型,使用者才能看到這個服務商",
"managedViaEnv": "(環境變數)",
"envReadOnly": "由 AI_MODELS_CONFIG / ai-models.json 定義,這裡只能檢視,請到那裡修改",
"defaultModel": "預設模型",
"noModelsConfigured": "未設定模型",
"noModelsConfigured": "尚無模型",
"modelCount": "{count} 個模型",
"modelCountPlural": "{count} 個模型",
"default": "預設",
"setAsDefault": "設為預設 provider",
"defaultProvider": "預設 provider",
"setAsDefault": "設為預設服務商",
"defaultProvider": "預設服務商",
"modelIdPlaceholder": "模型 ID…",
"addModel": "新增模型",
"suggested": "推薦",
"test": "測試",
"testOk": "正常({ms} 毫秒)",
"testOk": "通過({ms} 毫秒)",
"testFailed": "失敗",
"removeModel": "移除 {model}",
"removeModel": "刪除 {model}",
"deleteProviderTitle": "刪除 {name}?",
"deleteProviderDesc": "儲存後,其憑證和模型將從伺服器上移除。",
"deleteProviderDesc": "儲存後,它的金鑰和模型會從伺服器刪除",
"cancel": "取消",
"delete": "刪除",
"groups": {
"generation": {
"title": "生成",
"description": "套用於所有聊天請求的輸出參數。"
"title": "回覆設定",
"description": "控制 AI 怎麼回覆,對所有對話生效"
},
"access": {
"title": "存取控制",
"description": "限制誰可以使用此部署。"
"title": "存取權限",
"description": "限制誰可以使用這個網站"
},
"features": {
"title": "功能",
"description": "選用功能和安全開關。"
"title": "選用功能",
"description": "附加功能和安全選項"
},
"observability": {
"title": "可觀測性",
"description": "對 LLM 呼叫進行 Langfuse 追蹤。"
"title": "呼叫記錄",
"description": "用 Langfuse 記錄模型呼叫"
},
"quota": {
"title": "配額與速率限制",
"description": "按 IP 的用量限制。強制執行需要 DynamoDB 表。"
"title": "用量限制",
"description": "按 IP 限制用量,需要 DynamoDB 資料表才生效"
}
},
"settings": {
"TEMPERATURE": {
"label": "溫度",
"description": "對於拒絕溫度參數的推理模型,請留空。"
"description": "越高越隨機;不支援溫度的推理模型請留空"
},
"MAX_OUTPUT_TOKENS": {
"label": "最大輸出 token 數"
"label": "輸出 token 上限"
},
"ACCESS_CODE_LIST": {
"label": "存取碼",
"description": "以逗號分隔的清單。使用者需輸入其中之一才能聊天。留空 = 開放存取。"
"description": "逗號分隔多個;輸入其中一個才能用;留空則不限"
},
"ENABLE_VLM_VALIDATION": {
"label": "VLM 圖表驗證",
"description": "使用視覺模型對產生的圖表進行視覺化驗證。"
"label": "圖表檢查",
"description": "畫完後讓能看圖的模型檢查截圖"
},
"VALIDATION_MODEL": {
"label": "驗證模型",
"description": "留空時回退到預設 AI 模型。"
"label": "檢查模型",
"description": "留空則用預設模型"
},
"VALIDATION_TIMEOUT": {
"label": "驗證逾時(毫秒)"
"label": "檢查逾時(毫秒)"
},
"ENABLE_HISTORY_XML_REPLACE": {
"label": "歷史 XML 壓縮",
"description": "用占位符取代歷史記錄中的舊圖表 XML。"
"label": "精簡舊圖",
"description": "對話歷史裡的舊圖表只留簡短標記"
},
"ALLOW_PRIVATE_URLS": {
"label": "允許私有 URL",
"description": "關閉以阻擋對私有 IP 和內部主機名的請求(SSRF 防護)。"
"label": "存取內網",
"description": "關閉後,自訂模型位址不能指向內網,更安全"
},
"LANGFUSE_PUBLIC_KEY": {
"label": "Langfuse Public Key"
"label": "Langfuse 公鑰"
},
"LANGFUSE_SECRET_KEY": {
"label": "Langfuse Secret Key"
"label": "Langfuse 金鑰"
},
"LANGFUSE_BASEURL": {
"label": "Langfuse Base URL"
"label": "Langfuse 位址"
},
"DAILY_REQUEST_LIMIT": {
"label": "每日請求上限",
"description": "每個 IP 每天。"
"label": "每日請求",
"description": "每個 IP 每天的請求次數上限"
},
"DAILY_TOKEN_LIMIT": {
"label": "每日 token 上限",
"description": "每個 IP 每天。"
"label": "每日 token",
"description": "每個 IP 每天的 token 用量上限"
},
"TPM_LIMIT": {
"label": "每分鐘 token 數"
"label": "每分鐘 token"
},
"DYNAMODB_QUOTA_TABLE": {
"label": "DynamoDB 表",
"description": "留空時配額強制執行被停用。"
"label": "DynamoDB 資料表名稱",
"description": "留空則不限制用量"
},
"DYNAMODB_REGION": {
"label": "DynamoDB 區域"
},
"QUOTA_TIMEZONE": {
"label": "配額時區",
"description": "每日重置邊界所用的時區。"
"label": "時區",
"description": "每日用量按這個時區的零點重置"
}
}
},
+76 -76
View File
@@ -82,9 +82,9 @@
"accessCode": "访问码",
"accessCodePlaceholder": "输入访问码",
"accessCodeDescription": "使用此应用程序需要访问码。",
"aiProvider": "AI 提供商设置",
"aiProvider": "AI 服务商设置",
"aiProviderDescription": "使用您自己的 API 密钥来绕过使用限制。您的密钥仅存储在浏览器本地,不会存储在服务器上。",
"provider": "提供商",
"provider": "服务商",
"modelId": "模型 ID",
"apiKey": "API 密钥",
"apiKeyPlaceholder": "您的 API 密钥",
@@ -118,7 +118,7 @@
"customSystemMessage": "自定义指令",
"customSystemMessageDescription": "添加自定义指令,将附加到 AI 的系统提示末尾。",
"customSystemMessagePlaceholder": "例如:图表始终使用蓝色配色方案...",
"maxOutputTokens": "最大输出 token 数",
"maxOutputTokens": "输出 token 上限",
"maxOutputTokensDescription": "单次回复的额度,思考过程和图表 XML 共用。如果 AI 一直在思考却没有生成图表,请把它调大。留空则使用默认值。",
"tabModels": "模型",
"tabAbout": "关于",
@@ -207,10 +207,10 @@
},
"quota": {
"dailyLimit": "已达每日配额",
"tokenLimit": "已达每日令牌限制",
"tokenLimit": "已达每日 token 上限",
"tpmLimit": "速率限制",
"tpmMessage": "请求过多。请稍等片刻。",
"tpmMessageDetailed": "达到速率限制({limit} 令牌/分钟)。请等待 {seconds} 秒后再发送请求。",
"tpmMessageDetailed": "达到速率限制(每分钟 {limit} token)。请等待 {seconds} 秒后再发送请求。",
"messageApi": "看来您今天的体验次数已达上限。很高兴您玩得开心;为了让大家都能公平使用,演示站对用量做了限制。",
"messageApiSelfHosted": null,
"messageToken": "看来您今天的 token 用量已达上限。很高兴您玩得开心;为了让大家都能公平使用,演示站对用量做了限制。",
@@ -481,146 +481,146 @@
"removeAccessKeys": "删除访问密钥"
},
"admin": {
"title": "管理员设置",
"loginPrompt": "输入管理员密码(即 ADMIN_PASSWORD 环境变量)以管理服务器设置。",
"title": "后台设置",
"loginPrompt": "输入管理员密码(环境变量 ADMIN_PASSWORD)",
"password": "密码",
"signIn": "登录",
"signingIn": "正在登录…",
"signingIn": "登录中…",
"loginFailed": "登录失败",
"precedence": "文件覆盖环境变量 · 环境变量覆盖默认值",
"notWritable": "此部署环境下设置文件不可写(无服务器平台没有持久化磁盘)。设置以只读方式显示——请改用环境变量进行配置。",
"settingGroups": "设置分组",
"enabled": "已启用",
"disabled": "已禁用",
"enableGroup": "启用 {group}",
"unsavedChanges": "有未保存的更改",
"saved": "设置已保存,更改立即生效。",
"saveFailed": "保存失败。请检查网络连接后重试。",
"invalidSettings": "部分设置无效。",
"discard": "放弃",
"saveChanges": "保存更改",
"saving": "正在保存…",
"sourceSaved": "已保存",
"precedence": "优先级:本页设置 > 环境变量 > 默认值",
"notWritable": "这里无法保存设置,只能查看;请改用环境变量",
"settingGroups": "设置分类",
"enabled": "已开启",
"disabled": "已关闭",
"enableGroup": "开启{group}",
"unsavedChanges": "有未保存的修改",
"saved": "已保存,立即生效(需重启的项除外)",
"saveFailed": "保存失败,请检查网络后重试",
"invalidSettings": "有设置填写有误,请按提示修改",
"discard": "放弃修改",
"saveChanges": "保存修改",
"saving": "保存中…",
"sourceSaved": "本页设置",
"sourceEnv": "环境变量",
"sourceSavedTitle": "在管理员设置文件中设置",
"sourceEnvTitle": "通过环境变量设置",
"restartRequired": "需要重启",
"sourceSavedTitle": "来自本页设置",
"sourceEnvTitle": "来自环境变量",
"restartRequired": "需重启",
"modified": "已修改",
"notSet": "未设置",
"savedReplace": "已保存({hint})——输入以替换",
"showValue": "显示值",
"hideValue": "隐藏值",
"removeValue": "移除值",
"removeValueTitle": "移除已保存的值",
"savedReplace": "已保存({hint}),输入新值可替换",
"showValue": "显示",
"hideValue": "隐藏",
"removeValue": "清除",
"removeValueTitle": "清除已保存的值",
"resetToDefault": "恢复默认",
"models": "模型",
"modelsDescription": "面向所有用户的服务端 provider 和模型——无需个人 API 密钥。当用户未选择模型时,使用默认 provider 的第一个模型。",
"addProviderHint": "添加一个 provider,为所有用户提供服务端模型。",
"selectProviderHint": "选择或添加一个 provider 以配置其凭证和模型。",
"addProviderToOfferModels": "至少添加一个模型,才能向用户开放此 provider。",
"managedViaEnv": "(通过环境变量管理)",
"envReadOnly": "在 AI_MODELS_CONFIG / ai-models.json 中定义——此处只读。请编辑环境配置以更改。",
"modelsDescription": "给所有用户用的模型,无需自带密钥",
"addProviderHint": "添加服务商,为所有用户提供模型",
"selectProviderHint": "选择或添加服务商,填写密钥和模型",
"addProviderToOfferModels": "至少添加一个模型,用户才能看到这个服务商",
"managedViaEnv": "(环境变量)",
"envReadOnly": "由 AI_MODELS_CONFIG / ai-models.json 定义,这里只能查看,请到那里修改",
"defaultModel": "默认模型",
"noModelsConfigured": "未配置模型",
"noModelsConfigured": "暂无模型",
"modelCount": "{count} 个模型",
"modelCountPlural": "{count} 个模型",
"default": "默认",
"setAsDefault": "设为默认 provider",
"defaultProvider": "默认 provider",
"setAsDefault": "设为默认服务商",
"defaultProvider": "默认服务商",
"modelIdPlaceholder": "模型 ID…",
"addModel": "添加模型",
"suggested": "推荐",
"test": "测试",
"testOk": "正常({ms} 毫秒)",
"testOk": "通过({ms} 毫秒)",
"testFailed": "失败",
"removeModel": "移除 {model}",
"removeModel": "删除 {model}",
"deleteProviderTitle": "删除 {name}?",
"deleteProviderDesc": "保存后,其凭证和模型将从服务器上移除。",
"deleteProviderDesc": "保存后,它的密钥和模型会从服务器删除",
"cancel": "取消",
"delete": "删除",
"groups": {
"generation": {
"title": "生成",
"description": "应用于所有聊天请求的输出参数。"
"title": "回复设置",
"description": "控制 AI 怎么回复,对所有对话生效"
},
"access": {
"title": "访问控制",
"description": "限制谁可以使用此部署。"
"title": "访问权限",
"description": "限制谁可以使用这个站点"
},
"features": {
"title": "功能",
"description": "可选功能和安全开关。"
"title": "可选功能",
"description": "附加功能和安全选项"
},
"observability": {
"title": "可观测性",
"description": "对 LLM 调用进行 Langfuse 追踪。"
"title": "调用记录",
"description": "用 Langfuse 记录模型调用"
},
"quota": {
"title": "配额与速率限制",
"description": "按 IP 的用量限制。强制执行需要 DynamoDB 表。"
"title": "用量限制",
"description": "按 IP 限制用量,需要 DynamoDB 表才生效"
}
},
"settings": {
"TEMPERATURE": {
"label": "温度",
"description": "对于拒绝温度参数的推理模型,请留空。"
"description": "越高越随机;不支持温度的推理模型请留空"
},
"MAX_OUTPUT_TOKENS": {
"label": "最大输出 token 数"
"label": "输出 token 上限"
},
"ACCESS_CODE_LIST": {
"label": "访问码",
"description": "以逗号分隔的列表。用户需输入其中之一才能聊天。留空 = 开放访问。"
"description": "逗号分隔多个;输入其中一个才能用;留空则不限"
},
"ENABLE_VLM_VALIDATION": {
"label": "VLM 图表验证",
"description": "使用视觉模型对生成的图表进行可视化验证。"
"label": "图表检查",
"description": "画完后让能看图的模型检查截图"
},
"VALIDATION_MODEL": {
"label": "验证模型",
"description": "留空时回退到默认 AI 模型。"
"label": "检查模型",
"description": "留空则用默认模型"
},
"VALIDATION_TIMEOUT": {
"label": "验证超时(毫秒)"
"label": "检查超时(毫秒)"
},
"ENABLE_HISTORY_XML_REPLACE": {
"label": "历史 XML 压缩",
"description": "用占位符替换历史记录中的旧图表 XML。"
"label": "精简旧图",
"description": "对话历史里的旧图表只留简短标记"
},
"ALLOW_PRIVATE_URLS": {
"label": "允许私有 URL",
"description": "关闭以阻止对私有 IP 和内部主机名的请求(SSRF 防护)。"
"label": "访问内网",
"description": "关闭后,自定义模型地址不能指向内网,更安全"
},
"LANGFUSE_PUBLIC_KEY": {
"label": "Langfuse Public Key"
"label": "Langfuse 公钥"
},
"LANGFUSE_SECRET_KEY": {
"label": "Langfuse Secret Key"
"label": "Langfuse 密钥"
},
"LANGFUSE_BASEURL": {
"label": "Langfuse Base URL"
"label": "Langfuse 地址"
},
"DAILY_REQUEST_LIMIT": {
"label": "每日请求上限",
"description": "每个 IP 每天。"
"label": "每日请求",
"description": "每个 IP 每天的请求次数上限"
},
"DAILY_TOKEN_LIMIT": {
"label": "每日 token 上限",
"description": "每个 IP 每天。"
"label": "每日 token",
"description": "每个 IP 每天的 token 用量上限"
},
"TPM_LIMIT": {
"label": "每分钟 token 数"
"label": "每分钟 token"
},
"DYNAMODB_QUOTA_TABLE": {
"label": "DynamoDB 表",
"description": "留空时配额强制执行被禁用。"
"label": "DynamoDB 表名",
"description": "留空则不限制用量"
},
"DYNAMODB_REGION": {
"label": "DynamoDB 区域"
},
"QUOTA_TIMEZONE": {
"label": "配额时区",
"description": "每日重置边界所用的时区。"
"label": "时区",
"description": "每日用量按这个时区的零点重置"
}
}
},
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "next-ai-draw-io",
"version": "0.4.16",
"version": "0.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "next-ai-draw-io",
"version": "0.4.16",
"version": "0.5.0",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/amazon-bedrock": "^4.0.191",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "next-ai-draw-io",
"version": "0.4.16",
"version": "0.5.0",
"license": "Apache-2.0",
"private": true,
"main": "dist-electron/main/index.js",
+1 -4
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB