fix(api): add POST for hybrid-search while keeping GET compatibility (#1727)

GET with a JSON body was non-standard and confused integrators. Register
POST as the preferred method and keep GET for existing clients; update SDK,
MCP server, and API docs accordingly.

Refs: https://github.com/Tencent/WeKnora/issues/1727
This commit is contained in:
wizardchen
2026-06-22 17:20:59 +08:00
committed by lyingbug
parent 632cd55018
commit abf6a99b90
8 changed files with 1153 additions and 189 deletions
+2 -4
View File
@@ -371,13 +371,11 @@ type SearchParams struct {
DisableVectorMatch bool `json:"disable_vector_match"`
}
// HybridSearch performs hybrid search
// Note: The backend route is GET but expects JSON body, which is non-standard.
// This client uses POST with JSON body for better compatibility.
// HybridSearch performs hybrid search.
func (c *Client) HybridSearch(ctx context.Context, knowledgeBaseID string, params *SearchParams) ([]*SearchResult, error) {
path := fmt.Sprintf("/api/v1/knowledge-bases/%s/hybrid-search", knowledgeBaseID)
resp, err := c.doRequest(ctx, http.MethodGet, path, params, nil)
resp, err := c.doRequest(ctx, http.MethodPost, path, params, nil)
if err != nil {
return nil, err
}
+6 -5
View File
@@ -18,7 +18,8 @@
| PUT | `/knowledge-bases/:id` | 更新知识库 |
| DELETE | `/knowledge-bases/:id` | 删除知识库 |
| PUT | `/knowledge-bases/:id/pin` | 置顶/取消置顶知识库 |
| GET | `/knowledge-bases/:id/hybrid-search` | 混合搜索(向量+关键词) |
| POST | `/knowledge-bases/:id/hybrid-search` | 混合搜索(向量+关键词,推荐) |
| GET | `/knowledge-bases/:id/hybrid-search` | 混合搜索(兼容旧客户端,需 JSON 请求体) |
| POST | `/knowledge-bases/copy` | 拷贝知识库(异步任务) |
| GET | `/knowledge-bases/copy/progress/:task_id` | 获取拷贝进度 |
| GET | `/knowledge-bases/:id/move-targets` | 获取可迁移目标知识库列表 |
@@ -358,11 +359,11 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0
**响应**: 字段结构同 `POST /knowledge-bases` 响应(包含 Phase 2 的 `vector_store_*` 元数据字段),本接口操作后 `is_pinned` 翻转、`pinned_at` 同步更新。
## GET `/knowledge-bases/:id/hybrid-search` - 混合搜索
## POST `/knowledge-bases/:id/hybrid-search` - 混合搜索
在指定知识库内执行向量召回 + 关键词召回的混合检索。
在指定知识库内执行向量召回 + 关键词召回的混合检索。请求参数通过 JSON 请求体传递(`SearchParams`)。
**注意**:此接口使用 `GET` 方法但 **需要 JSON 请求体**(`SearchParams`),并非通过 query string 传参。
> **兼容说明**:`GET` 方法同样可用(需携带 JSON 请求体),供旧版客户端兼容;新集成请使用 `POST`。
**路径参数**:
@@ -389,7 +390,7 @@ curl --location --request PUT 'http://localhost:8080/api/v1/knowledge-bases/kb-0
**请求**:
```curl
curl --location --request GET 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/hybrid-search' \
curl --location --request POST 'http://localhost:8080/api/v1/knowledge-bases/kb-00000001/hybrid-search' \
--header 'X-API-Key: sk-xxxxx' \
--header 'Content-Type: application/json' \
--data '{
+426 -65
View File
@@ -4524,7 +4524,61 @@ const docTemplate = `{
"ApiKeyAuth": []
}
],
"description": "在知识库中执行向量和关键词混合搜索",
"description": "在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"知识库"
],
"summary": "混合搜索",
"parameters": [
{
"type": "string",
"description": "知识库ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "搜索参数",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.SearchParams"
}
}
],
"responses": {
"200": {
"description": "搜索结果",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "请求参数错误",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
},
"post": {
"security": [
{
"Bearer": []
},
{
"ApiKeyAuth": []
}
],
"description": "在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。",
"consumes": [
"application/json"
],
@@ -6350,21 +6404,21 @@ const docTemplate = `{
}
}
},
"/knowledgebase/{kb_id}/wiki/categories": {
"/knowledgebase/{kb_id}/wiki/folders": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "Retrieve direct child directories for a wiki page type and optional parent_path",
"description": "Retrieve the direct child folders of a parent folder (parent_id empty = root level), each with its page count and a has-children flag for the directory tree.",
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "List wiki category paths",
"summary": "List wiki folders",
"parameters": [
{
"type": "string",
@@ -6375,27 +6429,8 @@ const docTemplate = `{
},
{
"type": "string",
"description": "Wiki page type; comma-separated for multiple (e.g. entity,concept)",
"name": "page_type",
"in": "query",
"required": true
},
{
"type": "string",
"description": "Parent category path, slash-separated",
"name": "parent_path",
"in": "query"
},
{
"type": "integer",
"description": "Page number (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Page size",
"name": "page_size",
"description": "Parent folder id (empty = root)",
"name": "parent_id",
"in": "query"
}
],
@@ -6403,7 +6438,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiCategoryPathListResponse"
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderListResponse"
}
},
"400": {
@@ -6413,6 +6448,178 @@ const docTemplate = `{
}
}
}
},
"post": {
"security": [
{
"Bearer": []
}
],
"description": "Create a new (initially empty) directory node under parent_id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "Create a wiki folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"description": "Folder data",
"name": "folder",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolder"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
}
},
"/knowledgebase/{kb_id}/wiki/folders/{folder_id}": {
"put": {
"security": [
{
"Bearer": []
}
],
"description": "Rename and/or reparent a folder; the whole subtree's paths and the affected pages' cached paths are recomputed",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "Rename or move a wiki folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Folder ID",
"name": "folder_id",
"in": "path",
"required": true
},
{
"description": "Folder update",
"name": "folder",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolder"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
},
"delete": {
"security": [
{
"Bearer": []
}
],
"description": "Delete a folder that has no pages and no child folders",
"tags": [
"Wiki"
],
"summary": "Delete an empty wiki folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Folder ID",
"name": "folder_id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
}
},
"/knowledgebase/{kb_id}/wiki/graph": {
@@ -6722,6 +6929,58 @@ const docTemplate = `{
}
}
},
"/knowledgebase/{kb_id}/wiki/move-page": {
"put": {
"security": [
{
"Bearer": []
}
],
"description": "Relocate a page (identified by slug in the body) into a folder (folder_id empty = root); the page's cached category path is recomputed",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "Move a wiki page into a folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"description": "Move target",
"name": "move",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiPageMoveRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiPage"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
}
},
"/knowledgebase/{kb_id}/wiki/pages": {
"get": {
"security": [
@@ -17163,43 +17422,6 @@ const docTemplate = `{
"WebSearchProviderTypeSearxng"
]
},
"github_com_Tencent_WeKnora_internal_types.WikiCategoryPath": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"path": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiCategoryPathListResponse": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"paths": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiCategoryPath"
}
},
"total": {
"type": "integer"
},
"total_pages": {
"type": "integer"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiConfig": {
"type": "object",
"properties": {
@@ -17246,6 +17468,127 @@ const docTemplate = `{
"WikiExtractionExhaustive"
]
},
"github_com_Tencent_WeKnora_internal_types.WikiFolder": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"deleted_at": {
"$ref": "#/definitions/gorm.DeletedAt"
},
"depth": {
"type": "integer"
},
"id": {
"type": "string"
},
"knowledge_base_id": {
"type": "string"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "string"
},
"path": {
"type": "string"
},
"sort_order": {
"type": "integer"
},
"tenant_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderCreateRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"parent_id": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderListResponse": {
"type": "object",
"properties": {
"folders": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderNode"
}
},
"parent_id": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderNode": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"deleted_at": {
"$ref": "#/definitions/gorm.DeletedAt"
},
"depth": {
"type": "integer"
},
"has_children": {
"type": "boolean"
},
"id": {
"type": "string"
},
"knowledge_base_id": {
"type": "string"
},
"name": {
"type": "string"
},
"page_count": {
"type": "integer"
},
"parent_id": {
"type": "string"
},
"path": {
"type": "string"
},
"sort_order": {
"type": "integer"
},
"tenant_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderUpdateRequest": {
"type": "object",
"properties": {
"move_parent": {
"type": "boolean"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiGraphData": {
"type": "object",
"properties": {
@@ -17474,7 +17817,7 @@ const docTemplate = `{
}
},
"category_path": {
"description": "CategoryPath is the directory breadcrumb that groups this page in the\nwiki browser, e.g. [\"AI\", \"LLM 应用\", \"RAG\"]. It is intentionally\nlabel-based so intermediate directory nodes do not need to be real pages.",
"description": "CategoryPath is the directory breadcrumb that groups this page in the\nwiki browser, e.g. [\"AI\", \"LLM 应用\", \"RAG\"]. Derived cache of the\nfolder chain identified by FolderID.",
"type": "array",
"items": {
"type": "string"
@@ -17507,6 +17850,10 @@ const docTemplate = `{
"description": "Depth is len(CategoryPath), cached for filtering / display.",
"type": "integer"
},
"folder_id": {
"description": "FolderID is the single source of truth for where this page sits in the\ndirectory tree — a reference to wiki_folders.id (\"\" = wiki root). The\nCategoryPath / WikiPath / Depth fields below are denormalized caches\nrecomputed from this folder's chain on every write so list/index/search\nqueries don't have to join wiki_folders.",
"type": "string"
},
"id": {
"description": "Unique identifier (UUID)",
"type": "string"
@@ -17541,7 +17888,7 @@ const docTemplate = `{
"type": "string"
},
"parent_slug": {
"description": "ParentSlug optionally points at the wiki page that should act as this\npage's semantic parent in the directory tree. The parent may be empty\nwhen the page is grouped only by CategoryPath.",
"description": "ParentSlug optionally points at the wiki page that should act as this\npage's semantic parent in the directory tree. The parent may be empty\nwhen the page is grouped only by FolderID.",
"type": "string"
},
"slug": {
@@ -17656,6 +18003,20 @@ const docTemplate = `{
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiPageMoveRequest": {
"type": "object",
"required": [
"slug"
],
"properties": {
"folder_id": {
"type": "string"
},
"slug": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiStats": {
"type": "object",
"properties": {
+426 -65
View File
@@ -4517,7 +4517,61 @@
"ApiKeyAuth": []
}
],
"description": "在知识库中执行向量和关键词混合搜索",
"description": "在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"知识库"
],
"summary": "混合搜索",
"parameters": [
{
"type": "string",
"description": "知识库ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "搜索参数",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.SearchParams"
}
}
],
"responses": {
"200": {
"description": "搜索结果",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "请求参数错误",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
},
"post": {
"security": [
{
"Bearer": []
},
{
"ApiKeyAuth": []
}
],
"description": "在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。",
"consumes": [
"application/json"
],
@@ -6343,21 +6397,21 @@
}
}
},
"/knowledgebase/{kb_id}/wiki/categories": {
"/knowledgebase/{kb_id}/wiki/folders": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "Retrieve direct child directories for a wiki page type and optional parent_path",
"description": "Retrieve the direct child folders of a parent folder (parent_id empty = root level), each with its page count and a has-children flag for the directory tree.",
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "List wiki category paths",
"summary": "List wiki folders",
"parameters": [
{
"type": "string",
@@ -6368,27 +6422,8 @@
},
{
"type": "string",
"description": "Wiki page type; comma-separated for multiple (e.g. entity,concept)",
"name": "page_type",
"in": "query",
"required": true
},
{
"type": "string",
"description": "Parent category path, slash-separated",
"name": "parent_path",
"in": "query"
},
{
"type": "integer",
"description": "Page number (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Page size",
"name": "page_size",
"description": "Parent folder id (empty = root)",
"name": "parent_id",
"in": "query"
}
],
@@ -6396,7 +6431,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiCategoryPathListResponse"
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderListResponse"
}
},
"400": {
@@ -6406,6 +6441,178 @@
}
}
}
},
"post": {
"security": [
{
"Bearer": []
}
],
"description": "Create a new (initially empty) directory node under parent_id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "Create a wiki folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"description": "Folder data",
"name": "folder",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolder"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
}
},
"/knowledgebase/{kb_id}/wiki/folders/{folder_id}": {
"put": {
"security": [
{
"Bearer": []
}
],
"description": "Rename and/or reparent a folder; the whole subtree's paths and the affected pages' cached paths are recomputed",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "Rename or move a wiki folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Folder ID",
"name": "folder_id",
"in": "path",
"required": true
},
{
"description": "Folder update",
"name": "folder",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolder"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
},
"delete": {
"security": [
{
"Bearer": []
}
],
"description": "Delete a folder that has no pages and no child folders",
"tags": [
"Wiki"
],
"summary": "Delete an empty wiki folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Folder ID",
"name": "folder_id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
}
},
"/knowledgebase/{kb_id}/wiki/graph": {
@@ -6715,6 +6922,58 @@
}
}
},
"/knowledgebase/{kb_id}/wiki/move-page": {
"put": {
"security": [
{
"Bearer": []
}
],
"description": "Relocate a page (identified by slug in the body) into a folder (folder_id empty = root); the page's cached category path is recomputed",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Wiki"
],
"summary": "Move a wiki page into a folder",
"parameters": [
{
"type": "string",
"description": "Knowledge base ID",
"name": "kb_id",
"in": "path",
"required": true
},
{
"description": "Move target",
"name": "move",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiPageMoveRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiPage"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError"
}
}
}
}
},
"/knowledgebase/{kb_id}/wiki/pages": {
"get": {
"security": [
@@ -17156,43 +17415,6 @@
"WebSearchProviderTypeSearxng"
]
},
"github_com_Tencent_WeKnora_internal_types.WikiCategoryPath": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"path": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiCategoryPathListResponse": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"paths": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiCategoryPath"
}
},
"total": {
"type": "integer"
},
"total_pages": {
"type": "integer"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiConfig": {
"type": "object",
"properties": {
@@ -17239,6 +17461,127 @@
"WikiExtractionExhaustive"
]
},
"github_com_Tencent_WeKnora_internal_types.WikiFolder": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"deleted_at": {
"$ref": "#/definitions/gorm.DeletedAt"
},
"depth": {
"type": "integer"
},
"id": {
"type": "string"
},
"knowledge_base_id": {
"type": "string"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "string"
},
"path": {
"type": "string"
},
"sort_order": {
"type": "integer"
},
"tenant_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderCreateRequest": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"parent_id": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderListResponse": {
"type": "object",
"properties": {
"folders": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderNode"
}
},
"parent_id": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderNode": {
"type": "object",
"properties": {
"created_at": {
"type": "string"
},
"deleted_at": {
"$ref": "#/definitions/gorm.DeletedAt"
},
"depth": {
"type": "integer"
},
"has_children": {
"type": "boolean"
},
"id": {
"type": "string"
},
"knowledge_base_id": {
"type": "string"
},
"name": {
"type": "string"
},
"page_count": {
"type": "integer"
},
"parent_id": {
"type": "string"
},
"path": {
"type": "string"
},
"sort_order": {
"type": "integer"
},
"tenant_id": {
"type": "integer"
},
"updated_at": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiFolderUpdateRequest": {
"type": "object",
"properties": {
"move_parent": {
"type": "boolean"
},
"name": {
"type": "string"
},
"parent_id": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiGraphData": {
"type": "object",
"properties": {
@@ -17467,7 +17810,7 @@
}
},
"category_path": {
"description": "CategoryPath is the directory breadcrumb that groups this page in the\nwiki browser, e.g. [\"AI\", \"LLM 应用\", \"RAG\"]. It is intentionally\nlabel-based so intermediate directory nodes do not need to be real pages.",
"description": "CategoryPath is the directory breadcrumb that groups this page in the\nwiki browser, e.g. [\"AI\", \"LLM 应用\", \"RAG\"]. Derived cache of the\nfolder chain identified by FolderID.",
"type": "array",
"items": {
"type": "string"
@@ -17500,6 +17843,10 @@
"description": "Depth is len(CategoryPath), cached for filtering / display.",
"type": "integer"
},
"folder_id": {
"description": "FolderID is the single source of truth for where this page sits in the\ndirectory tree — a reference to wiki_folders.id (\"\" = wiki root). The\nCategoryPath / WikiPath / Depth fields below are denormalized caches\nrecomputed from this folder's chain on every write so list/index/search\nqueries don't have to join wiki_folders.",
"type": "string"
},
"id": {
"description": "Unique identifier (UUID)",
"type": "string"
@@ -17534,7 +17881,7 @@
"type": "string"
},
"parent_slug": {
"description": "ParentSlug optionally points at the wiki page that should act as this\npage's semantic parent in the directory tree. The parent may be empty\nwhen the page is grouped only by CategoryPath.",
"description": "ParentSlug optionally points at the wiki page that should act as this\npage's semantic parent in the directory tree. The parent may be empty\nwhen the page is grouped only by FolderID.",
"type": "string"
},
"slug": {
@@ -17649,6 +17996,20 @@
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiPageMoveRequest": {
"type": "object",
"required": [
"slug"
],
"properties": {
"folder_id": {
"type": "string"
},
"slug": {
"type": "string"
}
}
},
"github_com_Tencent_WeKnora_internal_types.WikiStats": {
"type": "object",
"properties": {
+288 -48
View File
@@ -3347,30 +3347,6 @@ definitions:
- WebSearchProviderTypeOllama
- WebSearchProviderTypeBaidu
- WebSearchProviderTypeSearxng
github_com_Tencent_WeKnora_internal_types.WikiCategoryPath:
properties:
count:
type: integer
path:
items:
type: string
type: array
type: object
github_com_Tencent_WeKnora_internal_types.WikiCategoryPathListResponse:
properties:
page:
type: integer
page_size:
type: integer
paths:
items:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiCategoryPath'
type: array
total:
type: integer
total_pages:
type: integer
type: object
github_com_Tencent_WeKnora_internal_types.WikiConfig:
properties:
extraction_granularity:
@@ -3420,6 +3396,85 @@ definitions:
- WikiExtractionFocused
- WikiExtractionStandard
- WikiExtractionExhaustive
github_com_Tencent_WeKnora_internal_types.WikiFolder:
properties:
created_at:
type: string
deleted_at:
$ref: '#/definitions/gorm.DeletedAt'
depth:
type: integer
id:
type: string
knowledge_base_id:
type: string
name:
type: string
parent_id:
type: string
path:
type: string
sort_order:
type: integer
tenant_id:
type: integer
updated_at:
type: string
type: object
github_com_Tencent_WeKnora_internal_types.WikiFolderCreateRequest:
properties:
name:
type: string
parent_id:
type: string
type: object
github_com_Tencent_WeKnora_internal_types.WikiFolderListResponse:
properties:
folders:
items:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderNode'
type: array
parent_id:
type: string
type: object
github_com_Tencent_WeKnora_internal_types.WikiFolderNode:
properties:
created_at:
type: string
deleted_at:
$ref: '#/definitions/gorm.DeletedAt'
depth:
type: integer
has_children:
type: boolean
id:
type: string
knowledge_base_id:
type: string
name:
type: string
page_count:
type: integer
parent_id:
type: string
path:
type: string
sort_order:
type: integer
tenant_id:
type: integer
updated_at:
type: string
type: object
github_com_Tencent_WeKnora_internal_types.WikiFolderUpdateRequest:
properties:
move_parent:
type: boolean
name:
type: string
parent_id:
type: string
type: object
github_com_Tencent_WeKnora_internal_types.WikiGraphData:
properties:
edges:
@@ -3590,8 +3645,8 @@ definitions:
category_path:
description: |-
CategoryPath is the directory breadcrumb that groups this page in the
wiki browser, e.g. ["AI", "LLM 应用", "RAG"]. It is intentionally
label-based so intermediate directory nodes do not need to be real pages.
wiki browser, e.g. ["AI", "LLM 应用", "RAG"]. Derived cache of the
folder chain identified by FolderID.
items:
type: string
type: array
@@ -3620,6 +3675,14 @@ definitions:
depth:
description: Depth is len(CategoryPath), cached for filtering / display.
type: integer
folder_id:
description: |-
FolderID is the single source of truth for where this page sits in the
directory tree — a reference to wiki_folders.id ("" = wiki root). The
CategoryPath / WikiPath / Depth fields below are denormalized caches
recomputed from this folder's chain on every write so list/index/search
queries don't have to join wiki_folders.
type: string
id:
description: Unique identifier (UUID)
type: string
@@ -3649,7 +3712,7 @@ definitions:
description: |-
ParentSlug optionally points at the wiki page that should act as this
page's semantic parent in the directory tree. The parent may be empty
when the page is grouped only by CategoryPath.
when the page is grouped only by FolderID.
type: string
slug:
description: |-
@@ -3743,6 +3806,15 @@ definitions:
total_pages:
type: integer
type: object
github_com_Tencent_WeKnora_internal_types.WikiPageMoveRequest:
properties:
folder_id:
type: string
slug:
type: string
required:
- slug
type: object
github_com_Tencent_WeKnora_internal_types.WikiStats:
properties:
is_active:
@@ -7687,7 +7759,41 @@ paths:
get:
consumes:
- application/json
description: 在知识库中执行向量和关键词混合搜索
description: 在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。
parameters:
- description: 知识库ID
in: path
name: id
required: true
type: string
- description: 搜索参数
in: body
name: request
required: true
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.SearchParams'
produces:
- application/json
responses:
"200":
description: 搜索结果
schema:
additionalProperties: true
type: object
"400":
description: 请求参数错误
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
security:
- Bearer: []
- ApiKeyAuth: []
summary: 混合搜索
tags:
- 知识库
post:
consumes:
- application/json
description: 在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。
parameters:
- description: 知识库ID
in: path
@@ -8903,47 +9009,147 @@ paths:
summary: Auto-fix wiki issues
tags:
- Wiki
/knowledgebase/{kb_id}/wiki/categories:
/knowledgebase/{kb_id}/wiki/folders:
get:
description: Retrieve direct child directories for a wiki page type and optional
parent_path
description: Retrieve the direct child folders of a parent folder (parent_id
empty = root level), each with its page count and a has-children flag for
the directory tree.
parameters:
- description: Knowledge base ID
in: path
name: kb_id
required: true
type: string
- description: Wiki page type; comma-separated for multiple (e.g. entity,concept)
- description: Parent folder id (empty = root)
in: query
name: page_type
required: true
name: parent_id
type: string
- description: Parent category path, slash-separated
in: query
name: parent_path
type: string
- description: Page number (1-based)
in: query
name: page
type: integer
- description: Page size
in: query
name: page_size
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiCategoryPathListResponse'
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderListResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
security:
- Bearer: []
summary: List wiki category paths
summary: List wiki folders
tags:
- Wiki
post:
consumes:
- application/json
description: Create a new (initially empty) directory node under parent_id
parameters:
- description: Knowledge base ID
in: path
name: kb_id
required: true
type: string
- description: Folder data
in: body
name: folder
required: true
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderCreateRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolder'
"400":
description: Bad Request
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
"409":
description: Conflict
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
security:
- Bearer: []
summary: Create a wiki folder
tags:
- Wiki
/knowledgebase/{kb_id}/wiki/folders/{folder_id}:
delete:
description: Delete a folder that has no pages and no child folders
parameters:
- description: Knowledge base ID
in: path
name: kb_id
required: true
type: string
- description: Folder ID
in: path
name: folder_id
required: true
type: string
responses:
"204":
description: No Content
"400":
description: Bad Request
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
"404":
description: Not Found
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
security:
- Bearer: []
summary: Delete an empty wiki folder
tags:
- Wiki
put:
consumes:
- application/json
description: Rename and/or reparent a folder; the whole subtree's paths and
the affected pages' cached paths are recomputed
parameters:
- description: Knowledge base ID
in: path
name: kb_id
required: true
type: string
- description: Folder ID
in: path
name: folder_id
required: true
type: string
- description: Folder update
in: body
name: folder
required: true
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolderUpdateRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiFolder'
"400":
description: Bad Request
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
"404":
description: Not Found
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
"409":
description: Conflict
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
security:
- Bearer: []
summary: Rename or move a wiki folder
tags:
- Wiki
/knowledgebase/{kb_id}/wiki/graph:
@@ -9153,6 +9359,40 @@ paths:
summary: Get wiki operation log
tags:
- Wiki
/knowledgebase/{kb_id}/wiki/move-page:
put:
consumes:
- application/json
description: Relocate a page (identified by slug in the body) into a folder
(folder_id empty = root); the page's cached category path is recomputed
parameters:
- description: Knowledge base ID
in: path
name: kb_id
required: true
type: string
- description: Move target
in: body
name: move
required: true
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiPageMoveRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_types.WikiPage'
"404":
description: Not Found
schema:
$ref: '#/definitions/github_com_Tencent_WeKnora_internal_errors.AppError'
security:
- Bearer: []
summary: Move a wiki page into a folder
tags:
- Wiki
/knowledgebase/{kb_id}/wiki/pages:
get:
description: List wiki pages with optional filtering and pagination
+2 -1
View File
@@ -271,7 +271,7 @@ func (h *KnowledgeBaseHandler) resolveKBStoreView(
// HybridSearch godoc
// @Summary 混合搜索
// @Description 在知识库中执行向量和关键词混合搜索
// @Description 在知识库中执行向量和关键词混合搜索。推荐使用 POST;GET 携带 JSON 请求体仍受支持(兼容旧客户端)。
// @Tags 知识库
// @Accept json
// @Produce json
@@ -281,6 +281,7 @@ func (h *KnowledgeBaseHandler) resolveKBStoreView(
// @Failure 400 {object} errors.AppError "请求参数错误"
// @Security Bearer
// @Security ApiKeyAuth
// @Router /knowledge-bases/{id}/hybrid-search [post]
// @Router /knowledge-bases/{id}/hybrid-search [get]
func (h *KnowledgeBaseHandler) HybridSearch(c *gin.Context) {
ctx := c.Request.Context()
+2
View File
@@ -393,6 +393,8 @@ func RegisterKnowledgeBaseRoutes(r *gin.RouterGroup, handler *handler.KnowledgeB
// so callers can't poke at KBs they can't see.
kb.PUT("/:id/pin", g.Viewer(), g.KBAccessRead("id"), handler.TogglePinKnowledgeBase)
// 混合搜索 — Viewer+ 且对 KB 有 read 权限 (read-only)
// POST is preferred; GET with JSON body is kept for backward compatibility (#1727).
kb.POST("/:id/hybrid-search", g.Viewer(), g.KBAccessRead("id"), handler.HybridSearch)
kb.GET("/:id/hybrid-search", g.Viewer(), g.KBAccessRead("id"), handler.HybridSearch)
// 拷贝知识库 — Contributor+ (副本归调用者所有;不需要原 KB 的所有权)
kb.POST("/copy", g.Contributor(), handler.CopyKnowledgeBase)
+1 -1
View File
@@ -193,7 +193,7 @@ class WeKnoraClient:
**config, # Include thresholds and match count
}
return self._request(
"GET", f"/knowledge-bases/{kb_id}/hybrid-search", json=data
"POST", f"/knowledge-bases/{kb_id}/hybrid-search", json=data
)
# Knowledge Management - Methods for creating and managing knowledge entries