From 35a2843ac23850d5b73179b739b861ff1eacd086 Mon Sep 17 00:00:00 2001 From: Hyu Date: Sat, 25 Jul 2026 14:40:37 +0800 Subject: [PATCH] fix(seo): improve docs indexing and localization (#103) Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com> --- docs.json | 285 ++++ openapi/service-api-en.json | 360 ++--- package.json | 1 + robots.txt | 6 + scripts/generate-alternate-sitemap.mjs | 75 + sitemap-alternates.xml | 1746 ++++++++++++++++++++++++ tests/seo.test.mjs | 88 ++ 7 files changed, 2381 insertions(+), 180 deletions(-) create mode 100644 robots.txt create mode 100644 scripts/generate-alternate-sitemap.mjs create mode 100644 sitemap-alternates.xml diff --git a/docs.json b/docs.json index c69efa06d..063d57376 100644 --- a/docs.json +++ b/docs.json @@ -228,6 +228,291 @@ { "source": "/app", "destination": "/en/insight/guide" + }, + { + "source": "/en/api-reference/system/获取系统信息", + "destination": "/en/api-reference/system/get-system-information", + "permanent": true + }, + { + "source": "/en/api-reference/model-providers/获取所有供应商", + "destination": "/en/api-reference/model-providers/list-all-model-providers", + "permanent": true + }, + { + "source": "/en/api-reference/model-providers/创建供应商", + "destination": "/en/api-reference/model-providers/create-a-model-provider", + "permanent": true + }, + { + "source": "/en/api-reference/model-providers/获取单个供应商", + "destination": "/en/api-reference/model-providers/get-a-model-provider", + "permanent": true + }, + { + "source": "/en/api-reference/model-providers/更新供应商", + "destination": "/en/api-reference/model-providers/update-a-model-provider", + "permanent": true + }, + { + "source": "/en/api-reference/model-providers/删除供应商", + "destination": "/en/api-reference/model-providers/delete-a-model-provider", + "permanent": true + }, + { + "source": "/en/api-reference/llm-models/获取所有-llm-模型", + "destination": "/en/api-reference/llm-models/list-all-llm-models", + "permanent": true + }, + { + "source": "/en/api-reference/llm-models/创建-llm-模型", + "destination": "/en/api-reference/llm-models/create-an-llm-model", + "permanent": true + }, + { + "source": "/en/api-reference/llm-models/获取单个-llm-模型", + "destination": "/en/api-reference/llm-models/get-an-llm-model", + "permanent": true + }, + { + "source": "/en/api-reference/llm-models/更新-llm-模型", + "destination": "/en/api-reference/llm-models/update-an-llm-model", + "permanent": true + }, + { + "source": "/en/api-reference/llm-models/删除-llm-模型", + "destination": "/en/api-reference/llm-models/delete-an-llm-model", + "permanent": true + }, + { + "source": "/en/api-reference/llm-models/测试-llm-模型", + "destination": "/en/api-reference/llm-models/test-an-llm-model", + "permanent": true + }, + { + "source": "/en/api-reference/embedding-models/获取所有向量模型", + "destination": "/en/api-reference/embedding-models/list-all-embedding-models", + "permanent": true + }, + { + "source": "/en/api-reference/embedding-models/创建向量模型", + "destination": "/en/api-reference/embedding-models/create-an-embedding-model", + "permanent": true + }, + { + "source": "/en/api-reference/embedding-models/获取单个向量模型", + "destination": "/en/api-reference/embedding-models/get-an-embedding-model", + "permanent": true + }, + { + "source": "/en/api-reference/embedding-models/更新向量模型", + "destination": "/en/api-reference/embedding-models/update-an-embedding-model", + "permanent": true + }, + { + "source": "/en/api-reference/embedding-models/删除向量模型", + "destination": "/en/api-reference/embedding-models/delete-an-embedding-model", + "permanent": true + }, + { + "source": "/en/api-reference/embedding-models/测试向量模型", + "destination": "/en/api-reference/embedding-models/test-an-embedding-model", + "permanent": true + }, + { + "source": "/en/api-reference/bots/获取所有机器人", + "destination": "/en/api-reference/bots/list-all-bots", + "permanent": true + }, + { + "source": "/en/api-reference/bots/创建机器人", + "destination": "/en/api-reference/bots/create-a-bot", + "permanent": true + }, + { + "source": "/en/api-reference/bots/获取单个机器人", + "destination": "/en/api-reference/bots/get-a-bot", + "permanent": true + }, + { + "source": "/en/api-reference/bots/更新机器人", + "destination": "/en/api-reference/bots/update-a-bot", + "permanent": true + }, + { + "source": "/en/api-reference/bots/删除机器人", + "destination": "/en/api-reference/bots/delete-a-bot", + "permanent": true + }, + { + "source": "/en/api-reference/bots/获取机器人事件日志", + "destination": "/en/api-reference/bots/get-bot-event-logs", + "permanent": true + }, + { + "source": "/en/api-reference/bots/通过机器人发送消息", + "destination": "/en/api-reference/bots/send-a-message-through-a-bot", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/获取所有流水线", + "destination": "/en/api-reference/pipelines/list-all-pipelines", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/创建流水线", + "destination": "/en/api-reference/pipelines/create-a-pipeline", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/获取流水线元数据", + "destination": "/en/api-reference/pipelines/get-pipeline-metadata", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/获取单个流水线", + "destination": "/en/api-reference/pipelines/get-a-pipeline", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/更新流水线", + "destination": "/en/api-reference/pipelines/update-a-pipeline", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/删除流水线", + "destination": "/en/api-reference/pipelines/delete-a-pipeline", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/复制流水线", + "destination": "/en/api-reference/pipelines/copy-a-pipeline", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/获取流水线扩展配置", + "destination": "/en/api-reference/pipelines/get-pipeline-extension-configuration", + "permanent": true + }, + { + "source": "/en/api-reference/pipelines/更新流水线扩展配置", + "destination": "/en/api-reference/pipelines/update-pipeline-extension-configuration", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/获取所有知识库", + "destination": "/en/api-reference/knowledge-bases/list-all-knowledge-bases", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/创建知识库", + "destination": "/en/api-reference/knowledge-bases/create-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/获取单个知识库", + "destination": "/en/api-reference/knowledge-bases/get-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/更新知识库", + "destination": "/en/api-reference/knowledge-bases/update-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/删除知识库", + "destination": "/en/api-reference/knowledge-bases/delete-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/获取知识库文件列表", + "destination": "/en/api-reference/knowledge-bases/list-knowledge-base-files", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/将文件存入知识库", + "destination": "/en/api-reference/knowledge-bases/add-a-file-to-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/从知识库删除文件", + "destination": "/en/api-reference/knowledge-bases/remove-a-file-from-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-bases/检索知识库", + "destination": "/en/api-reference/knowledge-bases/retrieve-from-a-knowledge-base", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-engines/获取可用知识引擎列表", + "destination": "/en/api-reference/knowledge-engines/list-available-knowledge-engines", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-engines/获取知识引擎创建配置模式", + "destination": "/en/api-reference/knowledge-engines/get-the-knowledge-engine-creation-schema", + "permanent": true + }, + { + "source": "/en/api-reference/knowledge-engines/获取知识引擎检索配置模式", + "destination": "/en/api-reference/knowledge-engines/get-the-knowledge-engine-retrieval-schema", + "permanent": true + }, + { + "source": "/en/api-reference/parsers/获取可用解析器列表", + "destination": "/en/api-reference/parsers/list-available-parsers", + "permanent": true + }, + { + "source": "/en/api-reference/files/上传图片", + "destination": "/en/api-reference/files/upload-an-image", + "permanent": true + }, + { + "source": "/en/api-reference/files/上传文档", + "destination": "/en/api-reference/files/upload-a-document", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/获取已安装插件列表", + "destination": "/en/api-reference/plugins/list-installed-plugins", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/获取插件详情", + "destination": "/en/api-reference/plugins/get-plugin-details", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/删除插件", + "destination": "/en/api-reference/plugins/delete-a-plugin", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/升级插件", + "destination": "/en/api-reference/plugins/upgrade-a-plugin", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/获取插件配置", + "destination": "/en/api-reference/plugins/get-plugin-configuration", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/更新插件配置", + "destination": "/en/api-reference/plugins/update-plugin-configuration", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/从-marketplace-安装插件", + "destination": "/en/api-reference/plugins/install-a-plugin-from-marketplace", + "permanent": true + }, + { + "source": "/en/api-reference/plugins/从-github-安装插件", + "destination": "/en/api-reference/plugins/install-a-plugin-from-github", + "permanent": true } ], "navigation": { diff --git a/openapi/service-api-en.json b/openapi/service-api-en.json index e775076a4..8bc4dd634 100644 --- a/openapi/service-api-en.json +++ b/openapi/service-api-en.json @@ -79,12 +79,12 @@ "tags": [ "System" ], - "summary": "获取系统信息", - "description": "获取 LangBot 实例的版本、版本类型等基本信息。此接口无需认证。", + "summary": "Get system information", + "description": "Get basic information about the LangBot instance, including its version and release type. This endpoint does not require authentication.", "security": [], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -150,11 +150,11 @@ "tags": [ "Model Providers" ], - "summary": "获取所有供应商", - "description": "获取所有模型供应商列表,包含每个供应商关联的 LLM 和 Embedding 模型数量。", + "summary": "List all model providers", + "description": "List all model providers, including the number of LLM and embedding models associated with each provider.", "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -186,8 +186,8 @@ "tags": [ "Model Providers" ], - "summary": "创建供应商", - "description": "创建一个新的模型供应商。", + "summary": "Create a model provider", + "description": "Create a new model provider.", "requestBody": { "required": true, "content": { @@ -200,7 +200,7 @@ }, "responses": { "200": { - "description": "创建成功", + "description": "Created successfully", "content": { "application/json": { "schema": { @@ -232,7 +232,7 @@ "tags": [ "Model Providers" ], - "summary": "获取单个供应商", + "summary": "Get a model provider", "parameters": [ { "name": "provider_uuid", @@ -246,7 +246,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -278,7 +278,7 @@ "tags": [ "Model Providers" ], - "summary": "更新供应商", + "summary": "Update a model provider", "parameters": [ { "name": "provider_uuid", @@ -310,8 +310,8 @@ "tags": [ "Model Providers" ], - "summary": "删除供应商", - "description": "删除供应商。如果还有模型引用该供应商,则无法删除。", + "summary": "Delete a model provider", + "description": "Delete a model provider. A provider cannot be deleted while it is still referenced by a model.", "parameters": [ { "name": "provider_uuid", @@ -328,7 +328,7 @@ "$ref": "#/components/responses/Success" }, "400": { - "description": "供应商仍有模型引用,无法删除", + "description": "The provider is still referenced by one or more models and cannot be deleted", "content": { "application/json": { "schema": { @@ -345,8 +345,8 @@ "tags": [ "LLM Models" ], - "summary": "获取所有 LLM 模型", - "description": "获取所有大语言模型列表。可通过 provider_uuid 过滤特定供应商的模型。", + "summary": "List all LLM models", + "description": "List all large language models. Use provider_uuid to filter models by provider.", "parameters": [ { "name": "provider_uuid", @@ -356,12 +356,12 @@ "type": "string", "format": "uuid" }, - "description": "按供应商 UUID 过滤" + "description": "Filter by provider UUID" } ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -393,8 +393,8 @@ "tags": [ "LLM Models" ], - "summary": "创建 LLM 模型", - "description": "创建一个新的大语言模型。模型必须关联到一个已存在的供应商。", + "summary": "Create an LLM model", + "description": "Create a new large language model. The model must be associated with an existing provider.", "requestBody": { "required": true, "content": { @@ -407,7 +407,7 @@ }, "responses": { "200": { - "description": "创建成功", + "description": "Created successfully", "content": { "application/json": { "schema": { @@ -439,7 +439,7 @@ "tags": [ "LLM Models" ], - "summary": "获取单个 LLM 模型", + "summary": "Get an LLM model", "parameters": [ { "name": "model_uuid", @@ -453,7 +453,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -485,7 +485,7 @@ "tags": [ "LLM Models" ], - "summary": "更新 LLM 模型", + "summary": "Update an LLM model", "parameters": [ { "name": "model_uuid", @@ -517,7 +517,7 @@ "tags": [ "LLM Models" ], - "summary": "删除 LLM 模型", + "summary": "Delete an LLM model", "parameters": [ { "name": "model_uuid", @@ -541,8 +541,8 @@ "tags": [ "LLM Models" ], - "summary": "测试 LLM 模型", - "description": "向指定的 LLM 模型发送测试请求,验证模型是否可用。", + "summary": "Test an LLM model", + "description": "Send a test request to the specified LLM model to verify that it is available.", "parameters": [ { "name": "model_uuid", @@ -563,7 +563,7 @@ "properties": { "prompt": { "type": "string", - "description": "测试用的提示文本", + "description": "Prompt text to use for the test", "example": "Hello" } } @@ -583,8 +583,8 @@ "tags": [ "Embedding Models" ], - "summary": "获取所有向量模型", - "description": "获取所有向量模型列表。可通过 provider_uuid 过滤特定供应商的模型。", + "summary": "List all embedding models", + "description": "List all embedding models. Use provider_uuid to filter models by provider.", "parameters": [ { "name": "provider_uuid", @@ -594,12 +594,12 @@ "type": "string", "format": "uuid" }, - "description": "按供应商 UUID 过滤" + "description": "Filter by provider UUID" } ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -631,8 +631,8 @@ "tags": [ "Embedding Models" ], - "summary": "创建向量模型", - "description": "创建一个新的向量模型。模型必须关联到一个已存在的供应商。", + "summary": "Create an embedding model", + "description": "Create a new embedding model. The model must be associated with an existing provider.", "requestBody": { "required": true, "content": { @@ -645,7 +645,7 @@ }, "responses": { "200": { - "description": "创建成功", + "description": "Created successfully", "content": { "application/json": { "schema": { @@ -677,7 +677,7 @@ "tags": [ "Embedding Models" ], - "summary": "获取单个向量模型", + "summary": "Get an embedding model", "parameters": [ { "name": "model_uuid", @@ -691,7 +691,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -723,7 +723,7 @@ "tags": [ "Embedding Models" ], - "summary": "更新向量模型", + "summary": "Update an embedding model", "parameters": [ { "name": "model_uuid", @@ -755,7 +755,7 @@ "tags": [ "Embedding Models" ], - "summary": "删除向量模型", + "summary": "Delete an embedding model", "parameters": [ { "name": "model_uuid", @@ -779,8 +779,8 @@ "tags": [ "Embedding Models" ], - "summary": "测试向量模型", - "description": "向指定的向量模型发送测试请求,验证模型是否可用。", + "summary": "Test an embedding model", + "description": "Send a test request to the specified embedding model to verify that it is available.", "parameters": [ { "name": "model_uuid", @@ -801,7 +801,7 @@ "properties": { "text": { "type": "string", - "description": "测试用的文本", + "description": "Text to use for the test", "example": "Hello" } } @@ -821,10 +821,10 @@ "tags": [ "Bots" ], - "summary": "获取所有机器人", + "summary": "List all bots", "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -856,7 +856,7 @@ "tags": [ "Bots" ], - "summary": "创建机器人", + "summary": "Create a bot", "requestBody": { "required": true, "content": { @@ -869,7 +869,7 @@ }, "responses": { "200": { - "description": "创建成功", + "description": "Created successfully", "content": { "application/json": { "schema": { @@ -901,8 +901,8 @@ "tags": [ "Bots" ], - "summary": "获取单个机器人", - "description": "获取机器人信息,包括运行时信息(如 Webhook 地址等)。", + "summary": "Get a bot", + "description": "Get bot details, including runtime information such as the webhook URL.", "parameters": [ { "name": "bot_uuid", @@ -916,7 +916,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -948,7 +948,7 @@ "tags": [ "Bots" ], - "summary": "更新机器人", + "summary": "Update a bot", "parameters": [ { "name": "bot_uuid", @@ -980,7 +980,7 @@ "tags": [ "Bots" ], - "summary": "删除机器人", + "summary": "Delete a bot", "parameters": [ { "name": "bot_uuid", @@ -1004,8 +1004,8 @@ "tags": [ "Bots" ], - "summary": "获取机器人事件日志", - "description": "获取指定机器人的事件日志记录。", + "summary": "Get bot event logs", + "description": "Get event log entries for the specified bot.", "parameters": [ { "name": "bot_uuid", @@ -1026,12 +1026,12 @@ "properties": { "from_index": { "type": "integer", - "description": "起始索引(-1 表示从最新开始)", + "description": "Starting index (-1 starts from the most recent entry)", "default": -1 }, "max_count": { "type": "integer", - "description": "最大返回数量", + "description": "Maximum number of entries to return", "default": 10 } } @@ -1041,7 +1041,7 @@ }, "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1078,8 +1078,8 @@ "tags": [ "Bots" ], - "summary": "通过机器人发送消息", - "description": "通过指定的机器人向目标发送消息。仅支持 API Key 认证。", + "summary": "Send a message through a bot", + "description": "Send a message to a target through the specified bot. This endpoint supports API Key authentication only.", "security": [ { "ApiKeyAuth": [] @@ -1114,15 +1114,15 @@ "person", "group" ], - "description": "目标类型" + "description": "Target type" }, "target_id": { "type": "string", - "description": "目标 ID(用户 ID 或群组 ID)" + "description": "Target ID (user ID or group ID)" }, "message_chain": { "type": "array", - "description": "消息链", + "description": "Message chain", "items": { "type": "object", "properties": { @@ -1134,19 +1134,19 @@ "At", "Voice" ], - "description": "消息元素类型" + "description": "Message element type" }, "text": { "type": "string", - "description": "文本内容(Plain 类型)" + "description": "Text content (Plain type)" }, "url": { "type": "string", - "description": "图片 URL(Image 类型)" + "description": "Image URL (Image type)" }, "target": { "type": "string", - "description": "@ 的目标 ID(At 类型)" + "description": "Mention target ID (At type)" } } }, @@ -1164,7 +1164,7 @@ }, "responses": { "200": { - "description": "发送成功", + "description": "Message sent successfully", "content": { "application/json": { "schema": { @@ -1189,7 +1189,7 @@ } }, "400": { - "description": "参数错误", + "description": "Invalid parameters", "content": { "application/json": { "schema": { @@ -1206,7 +1206,7 @@ "tags": [ "Pipelines" ], - "summary": "获取所有流水线", + "summary": "List all pipelines", "parameters": [ { "name": "sort_by", @@ -1233,7 +1233,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1265,7 +1265,7 @@ "tags": [ "Pipelines" ], - "summary": "创建流水线", + "summary": "Create a pipeline", "requestBody": { "required": true, "content": { @@ -1278,7 +1278,7 @@ }, "responses": { "200": { - "description": "创建成功", + "description": "Created successfully", "content": { "application/json": { "schema": { @@ -1310,11 +1310,11 @@ "tags": [ "Pipelines" ], - "summary": "获取流水线元数据", - "description": "获取流水线配置的元数据描述,包括可配置的字段和类型信息。", + "summary": "Get pipeline metadata", + "description": "Get metadata describing the pipeline configuration, including configurable fields and their types.", "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1345,7 +1345,7 @@ "tags": [ "Pipelines" ], - "summary": "获取单个流水线", + "summary": "Get a pipeline", "parameters": [ { "name": "pipeline_uuid", @@ -1359,7 +1359,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1391,7 +1391,7 @@ "tags": [ "Pipelines" ], - "summary": "更新流水线", + "summary": "Update a pipeline", "parameters": [ { "name": "pipeline_uuid", @@ -1423,7 +1423,7 @@ "tags": [ "Pipelines" ], - "summary": "删除流水线", + "summary": "Delete a pipeline", "parameters": [ { "name": "pipeline_uuid", @@ -1447,8 +1447,8 @@ "tags": [ "Pipelines" ], - "summary": "复制流水线", - "description": "复制一个已有的流水线,生成一个新的副本。", + "summary": "Copy a pipeline", + "description": "Copy an existing pipeline to create a new one.", "parameters": [ { "name": "pipeline_uuid", @@ -1462,7 +1462,7 @@ ], "responses": { "200": { - "description": "复制成功", + "description": "Copied successfully", "content": { "application/json": { "schema": { @@ -1497,8 +1497,8 @@ "tags": [ "Pipelines" ], - "summary": "获取流水线扩展配置", - "description": "获取流水线绑定的插件和 MCP 服务器配置。", + "summary": "Get pipeline extension configuration", + "description": "Get the plugins and MCP server configuration associated with a pipeline.", "parameters": [ { "name": "pipeline_uuid", @@ -1512,7 +1512,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1571,8 +1571,8 @@ "tags": [ "Pipelines" ], - "summary": "更新流水线扩展配置", - "description": "更新流水线绑定的插件和 MCP 服务器。", + "summary": "Update pipeline extension configuration", + "description": "Update the plugins and MCP servers associated with a pipeline.", "parameters": [ { "name": "pipeline_uuid", @@ -1628,10 +1628,10 @@ "tags": [ "Knowledge Bases" ], - "summary": "获取所有知识库", + "summary": "List all knowledge bases", "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1663,7 +1663,7 @@ "tags": [ "Knowledge Bases" ], - "summary": "创建知识库", + "summary": "Create a knowledge base", "requestBody": { "required": true, "content": { @@ -1682,11 +1682,11 @@ }, "engine_plugin_id": { "type": "string", - "description": "知识引擎插件 ID(author/name 格式)" + "description": "Knowledge engine plugin ID (author/name format)" }, "creation_settings": { "type": "object", - "description": "引擎创建参数" + "description": "Engine creation parameters" } } } @@ -1695,7 +1695,7 @@ }, "responses": { "200": { - "description": "创建成功", + "description": "Created successfully", "content": { "application/json": { "schema": { @@ -1727,7 +1727,7 @@ "tags": [ "Knowledge Bases" ], - "summary": "获取单个知识库", + "summary": "Get a knowledge base", "parameters": [ { "name": "kb_uuid", @@ -1741,7 +1741,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1773,7 +1773,7 @@ "tags": [ "Knowledge Bases" ], - "summary": "更新知识库", + "summary": "Update a knowledge base", "parameters": [ { "name": "kb_uuid", @@ -1813,7 +1813,7 @@ "tags": [ "Knowledge Bases" ], - "summary": "删除知识库", + "summary": "Delete a knowledge base", "parameters": [ { "name": "kb_uuid", @@ -1837,7 +1837,7 @@ "tags": [ "Knowledge Bases" ], - "summary": "获取知识库文件列表", + "summary": "List knowledge base files", "parameters": [ { "name": "kb_uuid", @@ -1851,7 +1851,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1883,8 +1883,8 @@ "tags": [ "Knowledge Bases" ], - "summary": "将文件存入知识库", - "description": "将已上传的文件关联到知识库并开始解析。先通过 /api/v1/files/documents 上传文件获取 file_id。", + "summary": "Add a file to a knowledge base", + "description": "Associate an uploaded file with a knowledge base and begin parsing it. First upload the file through /api/v1/files/documents to obtain a file_id.", "parameters": [ { "name": "kb_uuid", @@ -1908,11 +1908,11 @@ "properties": { "file_id": { "type": "string", - "description": "已上传的文件 ID" + "description": "Uploaded file ID" }, "parser_plugin_id": { "type": "string", - "description": "指定的解析器插件 ID(可选)" + "description": "Parser plugin ID to use (optional)" } } } @@ -1921,7 +1921,7 @@ }, "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -1952,7 +1952,7 @@ "tags": [ "Knowledge Bases" ], - "summary": "从知识库删除文件", + "summary": "Remove a file from a knowledge base", "parameters": [ { "name": "kb_uuid", @@ -1984,8 +1984,8 @@ "tags": [ "Knowledge Bases" ], - "summary": "检索知识库", - "description": "对知识库进行语义检索,返回相关文档片段。", + "summary": "Retrieve from a knowledge base", + "description": "Perform semantic retrieval against a knowledge base and return relevant document chunks.", "parameters": [ { "name": "kb_uuid", @@ -2009,11 +2009,11 @@ "properties": { "query": { "type": "string", - "description": "检索查询文本" + "description": "Retrieval query text" }, "retrieval_settings": { "type": "object", - "description": "检索参数(如 top_k 等)", + "description": "Retrieval parameters (such as top_k)", "properties": { "top_k": { "type": "integer" @@ -2027,7 +2027,7 @@ }, "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2061,11 +2061,11 @@ "tags": [ "Knowledge Engines" ], - "summary": "获取可用知识引擎列表", - "description": "列出所有由插件提供的知识引擎,包含其能力和配置模式。", + "summary": "List available knowledge engines", + "description": "List all plugin-provided knowledge engines, including their capabilities and configuration schemas.", "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2099,8 +2099,8 @@ "tags": [ "Knowledge Engines" ], - "summary": "获取知识引擎创建配置模式", - "description": "获取指定知识引擎的创建参数 JSON Schema。", + "summary": "Get the knowledge engine creation schema", + "description": "Get the JSON Schema for the specified knowledge engine's creation parameters.", "parameters": [ { "name": "plugin_id", @@ -2109,12 +2109,12 @@ "schema": { "type": "string" }, - "description": "插件 ID(author/name 格式)" + "description": "Plugin ID (author/name format)" } ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2145,8 +2145,8 @@ "tags": [ "Knowledge Engines" ], - "summary": "获取知识引擎检索配置模式", - "description": "获取指定知识引擎的检索参数 JSON Schema。", + "summary": "Get the knowledge engine retrieval schema", + "description": "Get the JSON Schema for the specified knowledge engine's retrieval parameters.", "parameters": [ { "name": "plugin_id", @@ -2155,12 +2155,12 @@ "schema": { "type": "string" }, - "description": "插件 ID(author/name 格式)" + "description": "Plugin ID (author/name format)" } ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2191,8 +2191,8 @@ "tags": [ "Parsers" ], - "summary": "获取可用解析器列表", - "description": "列出所有由插件提供的文件解析器。可通过 mime_type 过滤。", + "summary": "List available parsers", + "description": "List all plugin-provided file parsers. Use mime_type to filter the results.", "parameters": [ { "name": "mime_type", @@ -2201,12 +2201,12 @@ "schema": { "type": "string" }, - "description": "按 MIME 类型过滤解析器" + "description": "Filter parsers by MIME type" } ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2240,8 +2240,8 @@ "tags": [ "Files" ], - "summary": "上传图片", - "description": "上传图片文件,支持 jpg、jpeg、png、gif、webp 格式,大小限制 10MB。", + "summary": "Upload an image", + "description": "Upload an image file. Supported formats are jpg, jpeg, png, gif, and webp. The maximum file size is 10 MB.", "requestBody": { "required": true, "content": { @@ -2255,7 +2255,7 @@ "file": { "type": "string", "format": "binary", - "description": "图片文件" + "description": "Image file" } } } @@ -2264,7 +2264,7 @@ }, "responses": { "200": { - "description": "上传成功", + "description": "Uploaded successfully", "content": { "application/json": { "schema": { @@ -2279,7 +2279,7 @@ "properties": { "file_key": { "type": "string", - "description": "文件标识符,可用于引用图片" + "description": "File identifier that can be used to reference the image" } } } @@ -2296,8 +2296,8 @@ "tags": [ "Files" ], - "summary": "上传文档", - "description": "上传文档文件,大小限制 10MB。上传后可将 file_id 用于知识库关联。", + "summary": "Upload a document", + "description": "Upload a document file up to 10 MB. After the upload, use the file_id to associate the document with a knowledge base.", "requestBody": { "required": true, "content": { @@ -2311,7 +2311,7 @@ "file": { "type": "string", "format": "binary", - "description": "文档文件" + "description": "Document file" } } } @@ -2320,7 +2320,7 @@ }, "responses": { "200": { - "description": "上传成功", + "description": "Uploaded successfully", "content": { "application/json": { "schema": { @@ -2335,7 +2335,7 @@ "properties": { "file_id": { "type": "string", - "description": "文件 ID,可用于知识库关联" + "description": "File ID that can be used to associate the document with a knowledge base" } } } @@ -2352,10 +2352,10 @@ "tags": [ "Plugins" ], - "summary": "获取已安装插件列表", + "summary": "List installed plugins", "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2389,7 +2389,7 @@ "tags": [ "Plugins" ], - "summary": "获取插件详情", + "summary": "Get plugin details", "parameters": [ { "name": "author", @@ -2410,7 +2410,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2442,8 +2442,8 @@ "tags": [ "Plugins" ], - "summary": "删除插件", - "description": "异步删除插件,返回任务 ID。", + "summary": "Delete a plugin", + "description": "Delete a plugin asynchronously and return the task ID.", "parameters": [ { "name": "author", @@ -2469,12 +2469,12 @@ "type": "boolean", "default": false }, - "description": "是否同时删除插件数据" + "description": "Whether to delete the plugin data as well" } ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2505,8 +2505,8 @@ "tags": [ "Plugins" ], - "summary": "升级插件", - "description": "异步升级插件到最新版本,返回任务 ID。", + "summary": "Upgrade a plugin", + "description": "Upgrade a plugin to the latest version asynchronously and return the task ID.", "parameters": [ { "name": "author", @@ -2527,7 +2527,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2558,8 +2558,8 @@ "tags": [ "Plugins" ], - "summary": "获取插件配置", - "description": "获取插件的当前配置值和配置模式。", + "summary": "Get plugin configuration", + "description": "Get the plugin's current configuration values and configuration schema.", "parameters": [ { "name": "author", @@ -2580,7 +2580,7 @@ ], "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2604,7 +2604,7 @@ "tags": [ "Plugins" ], - "summary": "更新插件配置", + "summary": "Update plugin configuration", "parameters": [ { "name": "author", @@ -2629,7 +2629,7 @@ "application/json": { "schema": { "type": "object", - "description": "插件配置键值对" + "description": "Plugin configuration key-value pairs" } } } @@ -2646,8 +2646,8 @@ "tags": [ "Plugins" ], - "summary": "从 Marketplace 安装插件", - "description": "从 LangBot Marketplace 安装插件。异步操作,返回任务 ID。", + "summary": "Install a plugin from Marketplace", + "description": "Install a plugin from LangBot Marketplace asynchronously and return the task ID.", "requestBody": { "required": true, "content": { @@ -2660,7 +2660,7 @@ "properties": { "plugin_id": { "type": "string", - "description": "Marketplace 插件 ID(author/name 格式)", + "description": "Marketplace plugin ID (author/name format)", "example": "kellz-dev/disaster-alert" } } @@ -2670,7 +2670,7 @@ }, "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2701,8 +2701,8 @@ "tags": [ "Plugins" ], - "summary": "从 GitHub 安装插件", - "description": "从 GitHub 仓库安装插件。异步操作,返回任务 ID。", + "summary": "Install a plugin from GitHub", + "description": "Install a plugin from a GitHub repository asynchronously and return the task ID.", "requestBody": { "required": true, "content": { @@ -2715,7 +2715,7 @@ "properties": { "url": { "type": "string", - "description": "GitHub 仓库 URL", + "description": "GitHub repository URL", "example": "https://github.com/rocksclawbot/langbot-community-plugins/tree/main/disaster-alert" } } @@ -2725,7 +2725,7 @@ }, "responses": { "200": { - "description": "成功", + "description": "Successful response", "content": { "application/json": { "schema": { @@ -2874,7 +2874,7 @@ "schemas": { "ModelProvider": { "type": "object", - "description": "模型供应商。定义了 API 地址和密钥,模型通过 provider_uuid 关联。", + "description": "Model provider. Defines the API endpoint and keys; models are associated through provider_uuid.", "properties": { "uuid": { "type": "string", @@ -2886,28 +2886,28 @@ }, "requester": { "type": "string", - "description": "请求器类型", + "description": "Requester type", "example": "openai-chat-completions" }, "base_url": { "type": "string", - "description": "API 基础地址", + "description": "API base URL", "example": "https://api.openai.com/v1" }, "api_keys": { "type": "array", - "description": "API 密钥列表", + "description": "List of API keys", "items": { "type": "string" } }, "llm_count": { "type": "integer", - "description": "关联的 LLM 模型数量" + "description": "Number of associated LLM models" }, "embedding_count": { "type": "integer", - "description": "关联的 Embedding 模型数量" + "description": "Number of associated embedding models" }, "created_at": { "type": "string", @@ -2933,7 +2933,7 @@ }, "requester": { "type": "string", - "description": "请求器类型。可通过 GET /api/v1/provider/requesters 获取可用列表。", + "description": "Requester type. Use GET /api/v1/provider/requesters to list available requesters.", "example": "openai-chat-completions" }, "base_url": { @@ -2973,7 +2973,7 @@ }, "LLMModel": { "type": "object", - "description": "大语言模型。通过 provider_uuid 关联到供应商,继承供应商的 API 地址和密钥。", + "description": "Large language model. Associated with a provider through provider_uuid and inherits the provider's API endpoint and keys.", "properties": { "uuid": { "type": "string", @@ -2981,20 +2981,20 @@ }, "name": { "type": "string", - "description": "模型名称(即模型 ID,发送到 API 的 model 参数值)", + "description": "Model name (the model ID sent as the API model parameter)", "example": "gpt-4o" }, "provider_uuid": { "type": "string", "format": "uuid", - "description": "关联的供应商 UUID" + "description": "Associated provider UUID" }, "abilities": { "type": "array", "items": { "type": "string" }, - "description": "模型能力标签", + "description": "Model capability tags", "example": [ "chat", "vision", @@ -3003,11 +3003,11 @@ }, "extra_args": { "type": "object", - "description": "额外参数(如 temperature、max_tokens 等)" + "description": "Additional parameters (such as temperature and max_tokens)" }, "prefered_ranking": { "type": "integer", - "description": "优先级排序(越大越优先)", + "description": "Priority ranking (higher values take precedence)", "default": 0 }, "created_at": { @@ -3029,13 +3029,13 @@ "properties": { "name": { "type": "string", - "description": "模型名称(即模型 ID)", + "description": "Model name (model ID)", "example": "gpt-4o" }, "provider_uuid": { "type": "string", "format": "uuid", - "description": "关联的供应商 UUID" + "description": "Associated provider UUID" }, "abilities": { "type": "array", @@ -3082,7 +3082,7 @@ }, "EmbeddingModel": { "type": "object", - "description": "向量模型。通过 provider_uuid 关联到供应商。", + "description": "Embedding model. Associated with a provider through provider_uuid.", "properties": { "uuid": { "type": "string", @@ -3090,13 +3090,13 @@ }, "name": { "type": "string", - "description": "模型名称(即模型 ID)", + "description": "Model name (model ID)", "example": "text-embedding-3-small" }, "provider_uuid": { "type": "string", "format": "uuid", - "description": "关联的供应商 UUID" + "description": "Associated provider UUID" }, "extra_args": { "type": "object" @@ -3326,7 +3326,7 @@ }, "responses": { "Success": { - "description": "操作成功", + "description": "Operation completed successfully", "content": { "application/json": { "schema": { @@ -3342,7 +3342,7 @@ } }, "NotFound": { - "description": "资源未找到", + "description": "Resource not found", "content": { "application/json": { "schema": { @@ -3353,4 +3353,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 96928f6d1..bdbd358c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "scripts": { "dev": "npx mintlify dev", + "generate:sitemap-alternates": "node scripts/generate-alternate-sitemap.mjs", "test": "node --test tests/*.test.mjs" }, "dependencies": { diff --git a/robots.txt b/robots.txt new file mode 100644 index 000000000..b2f247c19 --- /dev/null +++ b/robots.txt @@ -0,0 +1,6 @@ +User-agent: * +Allow: / +Allow: /_next/ + +Sitemap: https://docs.langbot.app/sitemap.xml +Sitemap: https://docs.langbot.app/sitemap-alternates.xml diff --git a/scripts/generate-alternate-sitemap.mjs b/scripts/generate-alternate-sitemap.mjs new file mode 100644 index 000000000..e8d1760d7 --- /dev/null +++ b/scripts/generate-alternate-sitemap.mjs @@ -0,0 +1,75 @@ +#!/usr/bin/env node + +import { writeFile } from "node:fs/promises"; + +const base = "https://docs.langbot.app"; +const source = process.env.SITEMAP_SOURCE ?? `${base}/sitemap.xml`; +const output = new URL("../sitemap-alternates.xml", import.meta.url); +const locales = ["en", "zh", "ja"]; + +function escapeXml(value) { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +const response = await fetch(source, { + headers: { "user-agent": "LangBot docs alternate-sitemap generator" }, +}); +if (!response.ok) { + throw new Error(`Unable to fetch ${source}: ${response.status}`); +} + +const sitemap = await response.text(); +const urls = [...sitemap.matchAll(/([^<]+)<\/loc>/g)].map((match) => + match[1].replaceAll("&", "&"), +); +const groups = new Map(); + +for (const url of urls) { + for (const locale of locales) { + const prefix = `${base}/${locale}/`; + if (!url.startsWith(prefix)) continue; + const suffix = url.slice(prefix.length); + const group = groups.get(suffix) ?? {}; + group[locale] = url; + groups.set(suffix, group); + break; + } +} + +const translatedGroups = [...groups.values()] + .filter((group) => group.en && group.zh) + .sort((left, right) => left.en.localeCompare(right.en)); +const lines = [ + '', + '', +]; + +for (const group of translatedGroups) { + const alternates = [ + ["en", group.en], + ["zh-CN", group.zh], + ...(group.ja ? [["ja", group.ja]] : []), + ["x-default", group.zh], + ]; + for (const locale of locales) { + if (!group[locale]) continue; + lines.push(" ", ` ${escapeXml(group[locale])}`); + for (const [language, href] of alternates) { + lines.push( + ` `, + ); + } + lines.push(" "); + } +} +lines.push(""); + +await writeFile(output, `${lines.join("\n")}\n`); +console.log( + `Wrote ${translatedGroups.length} reciprocal language groups to ${output.pathname}`, +); diff --git a/sitemap-alternates.xml b/sitemap-alternates.xml new file mode 100644 index 000000000..0cb18acc7 --- /dev/null +++ b/sitemap-alternates.xml @@ -0,0 +1,1746 @@ + + + + https://docs.langbot.app/en/deploy/command + + + + + + + https://docs.langbot.app/zh/deploy/command + + + + + + + https://docs.langbot.app/ja/deploy/command + + + + + + + https://docs.langbot.app/en/deploy/langbot/docker + + + + + + + https://docs.langbot.app/zh/deploy/langbot/docker + + + + + + + https://docs.langbot.app/ja/deploy/langbot/docker + + + + + + + https://docs.langbot.app/en/deploy/langbot/kubernetes + + + + + + + https://docs.langbot.app/zh/deploy/langbot/kubernetes + + + + + + + https://docs.langbot.app/ja/deploy/langbot/kubernetes + + + + + + + https://docs.langbot.app/en/deploy/langbot/manual + + + + + + + https://docs.langbot.app/zh/deploy/langbot/manual + + + + + + + https://docs.langbot.app/ja/deploy/langbot/manual + + + + + + + https://docs.langbot.app/en/deploy/langbot/one-click/1panel + + + + + + + https://docs.langbot.app/zh/deploy/langbot/one-click/1panel + + + + + + + https://docs.langbot.app/ja/deploy/langbot/one-click/1panel + + + + + + + https://docs.langbot.app/en/deploy/langbot/one-click/bt + + + + + + + https://docs.langbot.app/zh/deploy/langbot/one-click/bt + + + + + + + https://docs.langbot.app/ja/deploy/langbot/one-click/bt + + + + + + + https://docs.langbot.app/en/deploy/langbot/package + + + + + + + https://docs.langbot.app/zh/deploy/langbot/package + + + + + + + https://docs.langbot.app/ja/deploy/langbot/package + + + + + + + https://docs.langbot.app/en/deploy/settings + + + + + + + https://docs.langbot.app/zh/deploy/settings + + + + + + + https://docs.langbot.app/ja/deploy/settings + + + + + + + https://docs.langbot.app/en/deploy/update + + + + + + + https://docs.langbot.app/zh/deploy/update + + + + + + + https://docs.langbot.app/ja/deploy/update + + + + + + + https://docs.langbot.app/en/develop/agent-context + + + + + + + https://docs.langbot.app/zh/develop/agent-context + + + + + + + https://docs.langbot.app/ja/develop/agent-context + + + + + + + https://docs.langbot.app/en/develop/comp-arch + + + + + + + https://docs.langbot.app/zh/develop/comp-arch + + + + + + + https://docs.langbot.app/ja/develop/comp-arch + + + + + + + https://docs.langbot.app/en/develop/dev-config + + + + + + + https://docs.langbot.app/zh/develop/dev-config + + + + + + + https://docs.langbot.app/ja/develop/dev-config + + + + + + + https://docs.langbot.app/en/develop/plugin-runtime + + + + + + + https://docs.langbot.app/zh/develop/plugin-runtime + + + + + + + https://docs.langbot.app/ja/develop/plugin-runtime + + + + + + + https://docs.langbot.app/en/insight/community + + + + + + + https://docs.langbot.app/zh/insight/community + + + + + + + https://docs.langbot.app/ja/insight/community + + + + + + + https://docs.langbot.app/en/insight/data-collection-policy + + + + + + + https://docs.langbot.app/zh/insight/data-collection-policy + + + + + + + https://docs.langbot.app/ja/insight/data-collection-policy + + + + + + + https://docs.langbot.app/en/insight/features + + + + + + + https://docs.langbot.app/zh/insight/features + + + + + + + https://docs.langbot.app/ja/insight/features + + + + + + + https://docs.langbot.app/en/insight/guide + + + + + + + https://docs.langbot.app/zh/insight/guide + + + + + + + https://docs.langbot.app/ja/insight/guide + + + + + + + https://docs.langbot.app/en/insight/platform-features + + + + + + + https://docs.langbot.app/zh/insight/platform-features + + + + + + + https://docs.langbot.app/ja/insight/platform-features + + + + + + + https://docs.langbot.app/en/insight/troubleshooting + + + + + + + https://docs.langbot.app/zh/insight/troubleshooting + + + + + + + https://docs.langbot.app/ja/insight/troubleshooting + + + + + + + https://docs.langbot.app/en/plugin/compatibility + + + + + + + https://docs.langbot.app/zh/plugin/compatibility + + + + + + + https://docs.langbot.app/ja/plugin/compatibility + + + + + + + https://docs.langbot.app/en/plugin/dev/apis/common + + + + + + + https://docs.langbot.app/zh/plugin/dev/apis/common + + + + + + + https://docs.langbot.app/ja/plugin/dev/apis/common + + + + + + + https://docs.langbot.app/en/plugin/dev/apis/messages + + + + + + + https://docs.langbot.app/zh/plugin/dev/apis/messages + + + + + + + https://docs.langbot.app/ja/plugin/dev/apis/messages + + + + + + + https://docs.langbot.app/en/plugin/dev/apis/pipeline-events + + + + + + + https://docs.langbot.app/zh/plugin/dev/apis/pipeline-events + + + + + + + https://docs.langbot.app/ja/plugin/dev/apis/pipeline-events + + + + + + + https://docs.langbot.app/en/plugin/dev/apis/tech-details + + + + + + + https://docs.langbot.app/zh/plugin/dev/apis/tech-details + + + + + + + https://docs.langbot.app/ja/plugin/dev/apis/tech-details + + + + + + + https://docs.langbot.app/en/plugin/dev/basic-info + + + + + + + https://docs.langbot.app/zh/plugin/dev/basic-info + + + + + + + https://docs.langbot.app/ja/plugin/dev/basic-info + + + + + + + https://docs.langbot.app/en/plugin/dev/components/add + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/add + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/add + + + + + + + https://docs.langbot.app/en/plugin/dev/components/command + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/command + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/command + + + + + + + https://docs.langbot.app/en/plugin/dev/components/event-listener + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/event-listener + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/event-listener + + + + + + + https://docs.langbot.app/en/plugin/dev/components/knowledge-engine + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/knowledge-engine + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/knowledge-engine + + + + + + + https://docs.langbot.app/en/plugin/dev/components/knowledge-retriever + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/knowledge-retriever + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/knowledge-retriever + + + + + + + https://docs.langbot.app/en/plugin/dev/components/page + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/page + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/page + + + + + + + https://docs.langbot.app/en/plugin/dev/components/parser + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/parser + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/parser + + + + + + + https://docs.langbot.app/en/plugin/dev/components/tool + + + + + + + https://docs.langbot.app/zh/plugin/dev/components/tool + + + + + + + https://docs.langbot.app/ja/plugin/dev/components/tool + + + + + + + https://docs.langbot.app/en/plugin/dev/directory-structure + + + + + + + https://docs.langbot.app/zh/plugin/dev/directory-structure + + + + + + + https://docs.langbot.app/ja/plugin/dev/directory-structure + + + + + + + https://docs.langbot.app/en/plugin/dev/migration + + + + + + + https://docs.langbot.app/zh/plugin/dev/migration + + + + + + + https://docs.langbot.app/ja/plugin/dev/migration + + + + + + + https://docs.langbot.app/en/plugin/dev/publish/github + + + + + + + https://docs.langbot.app/zh/plugin/dev/publish/github + + + + + + + https://docs.langbot.app/ja/plugin/dev/publish/github + + + + + + + https://docs.langbot.app/en/plugin/dev/publish/market + + + + + + + https://docs.langbot.app/zh/plugin/dev/publish/market + + + + + + + https://docs.langbot.app/ja/plugin/dev/publish/market + + + + + + + https://docs.langbot.app/en/plugin/dev/style + + + + + + + https://docs.langbot.app/zh/plugin/dev/style + + + + + + + https://docs.langbot.app/ja/plugin/dev/style + + + + + + + https://docs.langbot.app/en/plugin/dev/tutor + + + + + + + https://docs.langbot.app/zh/plugin/dev/tutor + + + + + + + https://docs.langbot.app/ja/plugin/dev/tutor + + + + + + + https://docs.langbot.app/en/plugin/plugin-intro + + + + + + + https://docs.langbot.app/zh/plugin/plugin-intro + + + + + + + https://docs.langbot.app/ja/plugin/plugin-intro + + + + + + + https://docs.langbot.app/en/tags/readme + + + + + + + https://docs.langbot.app/zh/tags/readme + + + + + + + https://docs.langbot.app/ja/tags/readme + + + + + + + https://docs.langbot.app/en/tags/webhook + + + + + + + https://docs.langbot.app/zh/tags/webhook + + + + + + + https://docs.langbot.app/ja/tags/webhook + + + + + + + https://docs.langbot.app/en/usage/knowledge/readme + + + + + + + https://docs.langbot.app/zh/usage/knowledge/readme + + + + + + + https://docs.langbot.app/ja/usage/knowledge/readme + + + + + + + https://docs.langbot.app/en/usage/mcp/readme + + + + + + + https://docs.langbot.app/zh/usage/mcp/readme + + + + + + + https://docs.langbot.app/ja/usage/mcp/readme + + + + + + + https://docs.langbot.app/en/usage/models/readme + + + + + + + https://docs.langbot.app/zh/usage/models/readme + + + + + + + https://docs.langbot.app/ja/usage/models/readme + + + + + + + https://docs.langbot.app/en/usage/pipelines/coze + + + + + + + https://docs.langbot.app/zh/usage/pipelines/coze + + + + + + + https://docs.langbot.app/ja/usage/pipelines/coze + + + + + + + https://docs.langbot.app/en/usage/pipelines/deerflow + + + + + + + https://docs.langbot.app/zh/usage/pipelines/deerflow + + + + + + + https://docs.langbot.app/ja/usage/pipelines/deerflow + + + + + + + https://docs.langbot.app/en/usage/pipelines/dify + + + + + + + https://docs.langbot.app/zh/usage/pipelines/dify + + + + + + + https://docs.langbot.app/ja/usage/pipelines/dify + + + + + + + https://docs.langbot.app/en/usage/pipelines/langflow + + + + + + + https://docs.langbot.app/zh/usage/pipelines/langflow + + + + + + + https://docs.langbot.app/ja/usage/pipelines/langflow + + + + + + + https://docs.langbot.app/en/usage/pipelines/n8n + + + + + + + https://docs.langbot.app/zh/usage/pipelines/n8n + + + + + + + https://docs.langbot.app/ja/usage/pipelines/n8n + + + + + + + https://docs.langbot.app/en/usage/pipelines/readme + + + + + + + https://docs.langbot.app/zh/usage/pipelines/readme + + + + + + + https://docs.langbot.app/ja/usage/pipelines/readme + + + + + + + https://docs.langbot.app/en/usage/pipelines/weknora + + + + + + + https://docs.langbot.app/zh/usage/pipelines/weknora + + + + + + + https://docs.langbot.app/ja/usage/pipelines/weknora + + + + + + + https://docs.langbot.app/en/usage/platforms/dingtalk + + + + + + + https://docs.langbot.app/zh/usage/platforms/dingtalk + + + + + + + https://docs.langbot.app/ja/usage/platforms/dingtalk + + + + + + + https://docs.langbot.app/en/usage/platforms/discord + + + + + + + https://docs.langbot.app/zh/usage/platforms/discord + + + + + + + https://docs.langbot.app/ja/usage/platforms/discord + + + + + + + https://docs.langbot.app/en/usage/platforms/http-bot + + + + + + + https://docs.langbot.app/zh/usage/platforms/http-bot + + + + + + + https://docs.langbot.app/ja/usage/platforms/http-bot + + + + + + + https://docs.langbot.app/en/usage/platforms/kook + + + + + + + https://docs.langbot.app/zh/usage/platforms/kook + + + + + + + https://docs.langbot.app/ja/usage/platforms/kook + + + + + + + https://docs.langbot.app/en/usage/platforms/lark + + + + + + + https://docs.langbot.app/zh/usage/platforms/lark + + + + + + + https://docs.langbot.app/ja/usage/platforms/lark + + + + + + + https://docs.langbot.app/en/usage/platforms/line + + + + + + + https://docs.langbot.app/zh/usage/platforms/line + + + + + + + https://docs.langbot.app/ja/usage/platforms/line + + + + + + + https://docs.langbot.app/en/usage/platforms/qq/aiocqhttp/lagrange + + + + + + + https://docs.langbot.app/zh/usage/platforms/qq/aiocqhttp/lagrange + + + + + + + https://docs.langbot.app/ja/usage/platforms/qq/aiocqhttp/lagrange + + + + + + + https://docs.langbot.app/en/usage/platforms/qq/aiocqhttp/llonebot + + + + + + + https://docs.langbot.app/zh/usage/platforms/qq/aiocqhttp/llonebot + + + + + + + https://docs.langbot.app/ja/usage/platforms/qq/aiocqhttp/llonebot + + + + + + + https://docs.langbot.app/en/usage/platforms/qq/aiocqhttp/napcat + + + + + + + https://docs.langbot.app/zh/usage/platforms/qq/aiocqhttp/napcat + + + + + + + https://docs.langbot.app/ja/usage/platforms/qq/aiocqhttp/napcat + + + + + + + https://docs.langbot.app/en/usage/platforms/qq/official_webhook + + + + + + + https://docs.langbot.app/zh/usage/platforms/qq/official_webhook + + + + + + + https://docs.langbot.app/ja/usage/platforms/qq/official_webhook + + + + + + + https://docs.langbot.app/en/usage/platforms/readme + + + + + + + https://docs.langbot.app/zh/usage/platforms/readme + + + + + + + https://docs.langbot.app/ja/usage/platforms/readme + + + + + + + https://docs.langbot.app/en/usage/platforms/satori/llonebot + + + + + + + https://docs.langbot.app/zh/usage/platforms/satori/llonebot + + + + + + + https://docs.langbot.app/ja/usage/platforms/satori/llonebot + + + + + + + https://docs.langbot.app/en/usage/platforms/slack + + + + + + + https://docs.langbot.app/zh/usage/platforms/slack + + + + + + + https://docs.langbot.app/ja/usage/platforms/slack + + + + + + + https://docs.langbot.app/en/usage/platforms/telegram + + + + + + + https://docs.langbot.app/zh/usage/platforms/telegram + + + + + + + https://docs.langbot.app/ja/usage/platforms/telegram + + + + + + + https://docs.langbot.app/en/usage/platforms/webpage + + + + + + + https://docs.langbot.app/zh/usage/platforms/webpage + + + + + + + https://docs.langbot.app/ja/usage/platforms/webpage + + + + + + + https://docs.langbot.app/en/usage/platforms/wechat/weixin + + + + + + + https://docs.langbot.app/zh/usage/platforms/wechat/weixin + + + + + + + https://docs.langbot.app/ja/usage/platforms/wechat/weixin + + + + + + + https://docs.langbot.app/en/usage/platforms/wecom/wecom + + + + + + + https://docs.langbot.app/zh/usage/platforms/wecom/wecom + + + + + + + https://docs.langbot.app/ja/usage/platforms/wecom/wecom + + + + + + + https://docs.langbot.app/en/usage/platforms/wecom/wecombot + + + + + + + https://docs.langbot.app/zh/usage/platforms/wecom/wecombot + + + + + + + https://docs.langbot.app/ja/usage/platforms/wecom/wecombot + + + + + + + https://docs.langbot.app/en/usage/platforms/wecom/wecomcs + + + + + + + https://docs.langbot.app/zh/usage/platforms/wecom/wecomcs + + + + + + + https://docs.langbot.app/ja/usage/platforms/wecom/wecomcs + + + + + + + https://docs.langbot.app/en/usage/platforms/wxoa + + + + + + + https://docs.langbot.app/zh/usage/platforms/wxoa + + + + + + + https://docs.langbot.app/ja/usage/platforms/wxoa + + + + + + + https://docs.langbot.app/en/usage/sandbox/config + + + + + + + https://docs.langbot.app/zh/usage/sandbox/config + + + + + + + https://docs.langbot.app/ja/usage/sandbox/config + + + + + + + https://docs.langbot.app/en/usage/sandbox/readme + + + + + + + https://docs.langbot.app/zh/usage/sandbox/readme + + + + + + + https://docs.langbot.app/ja/usage/sandbox/readme + + + + + + + https://docs.langbot.app/en/usage/sandbox/runtime-relationships + + + + + + + https://docs.langbot.app/zh/usage/sandbox/runtime-relationships + + + + + + + https://docs.langbot.app/ja/usage/sandbox/runtime-relationships + + + + + + + https://docs.langbot.app/en/usage/skills/readme + + + + + + + https://docs.langbot.app/zh/usage/skills/readme + + + + + + + https://docs.langbot.app/ja/usage/skills/readme + + + + + + + https://docs.langbot.app/en/workshop/302ai-integration + + + + + + + https://docs.langbot.app/zh/workshop/302ai-integration + + + + + + + https://docs.langbot.app/ja/workshop/302ai-integration + + + + + + + https://docs.langbot.app/en/workshop/impl-platform-adapter + + + + + + + https://docs.langbot.app/zh/workshop/impl-platform-adapter + + + + + + + https://docs.langbot.app/ja/workshop/impl-platform-adapter + + + + + + + https://docs.langbot.app/en/workshop/mcp-details + + + + + + + https://docs.langbot.app/zh/workshop/mcp-details + + + + + + + https://docs.langbot.app/ja/workshop/mcp-details + + + + + + + https://docs.langbot.app/en/workshop/network-details + + + + + + + https://docs.langbot.app/zh/workshop/network-details + + + + + + + https://docs.langbot.app/ja/workshop/network-details + + + + + + + https://docs.langbot.app/en/workshop/newapi-integration + + + + + + + https://docs.langbot.app/zh/workshop/newapi-integration + + + + + + + https://docs.langbot.app/ja/workshop/newapi-integration + + + + + + + https://docs.langbot.app/en/workshop/ppio-integration + + + + + + + https://docs.langbot.app/zh/workshop/ppio-integration + + + + + + + https://docs.langbot.app/ja/workshop/ppio-integration + + + + + + + https://docs.langbot.app/en/workshop/production/proxy-and-ssl + + + + + + + https://docs.langbot.app/zh/workshop/production/proxy-and-ssl + + + + + + + https://docs.langbot.app/ja/workshop/production/proxy-and-ssl + + + + + + diff --git a/tests/seo.test.mjs b/tests/seo.test.mjs index a9ebcedae..c7e5c729e 100644 --- a/tests/seo.test.mjs +++ b/tests/seo.test.mjs @@ -3,6 +3,7 @@ import { readFile } from "node:fs/promises"; import test from "node:test"; const repoRoot = new URL("../", import.meta.url); +const cjk = /[\u3400-\u4DBF\u4E00-\u9FFF\u3040-\u30FF\uAC00-\uD7AF]/u; test("repository-only English README is excluded from Mintlify publishing", async () => { const mintignore = await readFile(new URL(".mintignore", repoRoot), "utf8"); @@ -30,3 +31,90 @@ test("the historical README_EN URL keeps its permanent canonical redirect", asyn assert.equal(redirects[0].destination, "/en/insight/guide"); assert.notEqual(redirects[0].permanent, false); }); + +test("custom robots policy is served from the Mintlify project root", async () => { + const robots = await readFile(new URL("robots.txt", repoRoot), "utf8"); + assert.match(robots, /^User-agent: \*$/m); + assert.match(robots, /^Allow: \/_next\/$/m); + assert.match( + robots, + /^Sitemap: https:\/\/docs\.langbot\.app\/sitemap\.xml$/m, + ); + assert.match( + robots, + /^Sitemap: https:\/\/docs\.langbot\.app\/sitemap-alternates\.xml$/m, + ); +}); + +test("alternate sitemap declares reciprocal en, zh-CN, ja and x-default links", async () => { + const sitemap = await readFile( + new URL("sitemap-alternates.xml", repoRoot), + "utf8", + ); + const blocks = [...sitemap.matchAll(/([\s\S]*?)<\/url>/g)].map( + (match) => match[1], + ); + assert.ok(blocks.length > 0); + + const groups = new Map(); + for (const block of blocks) { + const loc = block.match(/([^<]+)<\/loc>/)?.[1]; + assert.ok(loc); + const links = Object.fromEntries( + [...block.matchAll(/hreflang="([^"]+)" href="([^"]+)"/g)].map( + (match) => [match[1], match[2]], + ), + ); + assert.ok(links.en); + assert.ok(links["zh-CN"]); + assert.equal(links["x-default"], links["zh-CN"]); + const reciprocalUrls = [links.en, links["zh-CN"], links.ja].filter(Boolean); + const key = reciprocalUrls.join("\n"); + groups.set(key, [...(groups.get(key) ?? []), loc]); + } + + assert.ok(groups.size > 0); + for (const [key, locations] of groups) { + assert.deepEqual(locations.sort(), key.split("\n").sort()); + } +}); + +test("English Service API specification contains no CJK copy", async () => { + const source = await readFile( + new URL("openapi/service-api-en.json", repoRoot), + "utf8", + ); + assert.doesNotThrow(() => JSON.parse(source)); + const matches = [...source.matchAll(new RegExp(cjk.source, "gu"))]; + assert.equal(matches.length, 0, `found ${matches.length} CJK code points`); +}); + +test("translated English API routes preserve every historical Chinese URL", async () => { + const docs = JSON.parse(await readFile(new URL("docs.json", repoRoot), "utf8")); + const redirects = docs.redirects.filter((item) => + item.source.startsWith("/en/api-reference/") && cjk.test(item.source), + ); + cjk.lastIndex = 0; + + assert.equal(redirects.length, 57); + assert.equal(new Set(redirects.map((item) => item.source)).size, 57); + for (const redirect of redirects) { + assert.equal(redirect.permanent, true); + assert.match(redirect.destination, /^\/en\/api-reference\//); + assert.doesNotMatch(redirect.destination, cjk); + cjk.lastIndex = 0; + } +}); + +test("every Markdown image in the English guide has descriptive alt text", async () => { + const guide = await readFile( + new URL("en/insight/guide.mdx", repoRoot), + "utf8", + ); + const images = [...guide.matchAll(/!\[([^\]]*)\]\(([^)]+)\)/g)]; + assert.ok(images.length > 0, "expected at least one Markdown image"); + + for (const [, alt, src] of images) { + assert.ok(alt.trim().length >= 8, `${src} is missing descriptive alt text`); + } +});