39ef1a6b7f
Add POST /api/tenant/knowledge/items/retry/:id to re-queue failed parse tasks, plus a 20-rune cap on text item names with matching frontend validation. Wires the retry button into the knowledge table with a colored status tag for clearer state feedback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
221 lines
5.6 KiB
Go
221 lines
5.6 KiB
Go
package transport
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/bootstrap"
|
|
"github.com/geo-platform/tenant-api/internal/shared/response"
|
|
"github.com/geo-platform/tenant-api/internal/tenant/app"
|
|
)
|
|
|
|
type KnowledgeHandler struct {
|
|
svc *app.KnowledgeService
|
|
}
|
|
|
|
func NewKnowledgeHandler(a *bootstrap.App) *KnowledgeHandler {
|
|
return &KnowledgeHandler{
|
|
svc: app.NewKnowledgeService(
|
|
a.DB,
|
|
a.RetrievalProvider,
|
|
a.VectorStore,
|
|
a.ObjectStorage,
|
|
a.LLM,
|
|
a.Logger,
|
|
a.Config().Retrieval,
|
|
a.Config().LLM,
|
|
a.Config().Generation.QueueSize,
|
|
a.Config().Generation.WorkerConcurrency,
|
|
).WithConfigProvider(a.ConfigStore),
|
|
}
|
|
}
|
|
|
|
func (h *KnowledgeHandler) ListGroups(c *gin.Context) {
|
|
data, err := h.svc.ListGroups(c.Request.Context())
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) CreateGroup(c *gin.Context) {
|
|
var req app.KnowledgeGroupRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, err := h.svc.CreateGroup(c.Request.Context(), req)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.SuccessWithStatus(c, http.StatusCreated, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) UpdateGroup(c *gin.Context) {
|
|
groupID, err := strconv.ParseInt(c.Param("gid"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "knowledge group id must be a number"))
|
|
return
|
|
}
|
|
|
|
var req app.KnowledgeGroupRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
if err := h.svc.UpdateGroup(c.Request.Context(), groupID, req); err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, nil)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) DeleteGroup(c *gin.Context) {
|
|
groupID, err := strconv.ParseInt(c.Param("gid"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "knowledge group id must be a number"))
|
|
return
|
|
}
|
|
|
|
if err := h.svc.DeleteGroup(c.Request.Context(), groupID); err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, nil)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) ListItems(c *gin.Context) {
|
|
var groupID *int64
|
|
if raw := c.Query("group_id"); raw != "" {
|
|
parsed, err := strconv.ParseInt(raw, 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_group_id", "group id must be a number"))
|
|
return
|
|
}
|
|
groupID = &parsed
|
|
}
|
|
|
|
data, err := h.svc.ListItems(c.Request.Context(), groupID)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) GetItemDetail(c *gin.Context) {
|
|
itemID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "knowledge item id must be a number"))
|
|
return
|
|
}
|
|
|
|
data, err := h.svc.GetItemDetail(c.Request.Context(), itemID)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) CreateTextItem(c *gin.Context) {
|
|
var req app.KnowledgeTextItemRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, err := h.svc.CreateTextItem(c.Request.Context(), req)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.SuccessWithStatus(c, http.StatusCreated, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) CreateURLItem(c *gin.Context) {
|
|
var req app.KnowledgeURLItemRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, err := h.svc.CreateURLItem(c.Request.Context(), req)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.SuccessWithStatus(c, http.StatusCreated, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) CreateFileItem(c *gin.Context) {
|
|
groupID, err := strconv.ParseInt(c.PostForm("group_id"), 10, 64)
|
|
if err != nil || groupID <= 0 {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_group_id", "group id must be a number"))
|
|
return
|
|
}
|
|
|
|
name := c.PostForm("name")
|
|
fileHeader, err := c.FormFile("file")
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_file", "file is required"))
|
|
return
|
|
}
|
|
|
|
file, err := fileHeader.Open()
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_file", "file cannot be opened"))
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
content, err := io.ReadAll(file)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_file", "file cannot be read"))
|
|
return
|
|
}
|
|
|
|
data, err := h.svc.CreateFileItem(c.Request.Context(), groupID, name, fileHeader.Filename, content)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.SuccessWithStatus(c, http.StatusCreated, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) RetryItem(c *gin.Context) {
|
|
itemID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "knowledge item id must be a number"))
|
|
return
|
|
}
|
|
|
|
data, err := h.svc.RetryItem(c.Request.Context(), itemID)
|
|
if err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *KnowledgeHandler) DeleteItem(c *gin.Context) {
|
|
itemID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "knowledge item id must be a number"))
|
|
return
|
|
}
|
|
|
|
if err := h.svc.DeleteItem(c.Request.Context(), itemID); err != nil {
|
|
response.Error(c, err)
|
|
return
|
|
}
|
|
response.Success(c, nil)
|
|
}
|