package client import ( "context" "fmt" "net/http" "net/url" "strconv" "time" ) // Tag represents a knowledge base tag. type Tag struct { ID string `json:"id"` TenantID uint64 `json:"tenant_id"` KnowledgeBaseID string `json:"knowledge_base_id"` Name string `json:"name"` Color string `json:"color"` SortOrder int `json:"sort_order"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // TagWithStats represents tag information along with usage statistics. type TagWithStats struct { Tag KnowledgeCount int64 `json:"knowledge_count"` ChunkCount int64 `json:"chunk_count"` } // CreateTagPayload is used to create a new tag. type CreateTagPayload struct { Name string `json:"name"` Color string `json:"color,omitempty"` SortOrder int `json:"sort_order,omitempty"` } // UpdateTagPayload is used to update an existing tag. type UpdateTagPayload struct { Name *string `json:"name,omitempty"` Color *string `json:"color,omitempty"` SortOrder *int `json:"sort_order,omitempty"` } // TagsPage contains paginated tag results. type TagsPage struct { Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"page_size"` Tags []TagWithStats `json:"data"` } // TagsResponse wraps the paginated tags response. type TagsResponse struct { Success bool `json:"success"` Data *TagsPage `json:"data"` Message string `json:"message,omitempty"` Code string `json:"code,omitempty"` } // TagResponse wraps a single tag response. type TagResponse struct { Success bool `json:"success"` Data *Tag `json:"data"` Message string `json:"message,omitempty"` Code string `json:"code,omitempty"` } type tagSimpleResponse struct { Success bool `json:"success"` Message string `json:"message,omitempty"` Code string `json:"code,omitempty"` } // ListTags returns paginated tags under a knowledge base. func (c *Client) ListTags(ctx context.Context, knowledgeBaseID string, page, pageSize int, keyword string, ) (*TagsPage, error) { path := fmt.Sprintf("/api/v1/knowledge-bases/%s/tags", knowledgeBaseID) query := url.Values{} if page > 0 { query.Add("page", strconv.Itoa(page)) } if pageSize > 0 { query.Add("page_size", strconv.Itoa(pageSize)) } if keyword != "" { query.Add("keyword", keyword) } resp, err := c.doRequest(ctx, http.MethodGet, path, nil, query) if err != nil { return nil, err } var response TagsResponse if err := parseResponse(resp, &response); err != nil { return nil, err } if response.Data == nil { return &TagsPage{}, nil } return response.Data, nil } // CreateTag creates a new tag under a knowledge base. func (c *Client) CreateTag(ctx context.Context, knowledgeBaseID string, payload *CreateTagPayload, ) (*Tag, error) { path := fmt.Sprintf("/api/v1/knowledge-bases/%s/tags", knowledgeBaseID) resp, err := c.doRequest(ctx, http.MethodPost, path, payload, nil) if err != nil { return nil, err } var response TagResponse if err := parseResponse(resp, &response); err != nil { return nil, err } return response.Data, nil } // UpdateTag updates an existing tag. func (c *Client) UpdateTag(ctx context.Context, knowledgeBaseID, tagID string, payload *UpdateTagPayload, ) (*Tag, error) { path := fmt.Sprintf("/api/v1/knowledge-bases/%s/tags/%s", knowledgeBaseID, tagID) resp, err := c.doRequest(ctx, http.MethodPut, path, payload, nil) if err != nil { return nil, err } var response TagResponse if err := parseResponse(resp, &response); err != nil { return nil, err } return response.Data, nil } // DeleteTag deletes a tag. Set force to true to delete even if the tag is referenced. func (c *Client) DeleteTag(ctx context.Context, knowledgeBaseID, tagID string, force bool, ) error { path := fmt.Sprintf("/api/v1/knowledge-bases/%s/tags/%s", knowledgeBaseID, tagID) query := url.Values{} if force { query.Add("force", "true") } resp, err := c.doRequest(ctx, http.MethodDelete, path, nil, query) if err != nil { return err } var response tagSimpleResponse return parseResponse(resp, &response) }