From d1c436b804e83e425420c78c580eb5252884dc62 Mon Sep 17 00:00:00 2001 From: Hz_ Date: Tue, 9 Jun 2026 17:03:42 +0800 Subject: [PATCH] feat(api): implement `GET /api/v1/agents/prompts` endpoint in Go (#15748) ### Description This PR ports the `GET /api/v1/agents/prompts` endpoint from the Python backend to the Go backend. ### Changes Made - **Handler**: Added `GetPrompts` method to `internal/handler/agent.go`. - **Router**: Registered the `agents.GET("/prompts")` route in `internal/router/router.go`. - **Logic**: Leveraged the existing `service.LoadPrompt` utility to read `analyze_task_system`, `analyze_task_user`, `next_step`, `reflect`, and `citation_prompt` templates directly from the `rag/prompts` directory. - **Unit Test**: Added `TestGetPrompts_Success` to `internal/handler/agent_test.go` to mock the HTTP context and validate the JSON response structure. ### Motivation This is part of the ongoing effort to port the Agent API surface to Go. Since this specific endpoint only serves static prompt templates and does not require the complex DAG/Canvas execution engine, it can be seamlessly and safely handled by the Go backend. ### Testing - [x] Added automated unit test `TestGetPrompts_Success` (verified passing). - [x] Tested locally via `curl` against the Go server (port 9380) and Python server (port 9384). - [x] Verified that the Go JSON response structure and loaded prompt text are logically 100% identical to the Python implementation. --- internal/handler/agent.go | 52 ++++++++++++++++++++++++++++++++++ internal/handler/agent_test.go | 35 +++++++++++++++++++++++ internal/router/router.go | 1 + 3 files changed, 88 insertions(+) diff --git a/internal/handler/agent.go b/internal/handler/agent.go index 63a9f12055..97c7a8651b 100644 --- a/internal/handler/agent.go +++ b/internal/handler/agent.go @@ -570,3 +570,55 @@ func (h *AgentHandler) UpdateAgentTags(c *gin.Context) { "message": "success", }) } + +// GetPrompts returns the default prompts used by the agent. +// @Summary Get Agent Prompts +// @Description Returns the default prompts used by the agent, such as task analysis, plan generation, reflection, and citation guidelines. +// @Tags agents +// @Produce json +// @Security ApiKeyAuth +// @Success 200 {object} map[string]interface{} +// @Router /api/v1/agents/prompts [get] +func (h *AgentHandler) GetPrompts(c *gin.Context) { + if _, errorCode, errorMessage := GetUser(c); errorCode != common.CodeSuccess { + jsonError(c, errorCode, errorMessage) + return + } + + taskAnalysisSys, err := service.LoadPrompt("analyze_task_system") + if err != nil { + jsonError(c, common.CodeServerError, err.Error()) + return + } + taskAnalysisUser, err := service.LoadPrompt("analyze_task_user") + if err != nil { + jsonError(c, common.CodeServerError, err.Error()) + return + } + planGeneration, err := service.LoadPrompt("next_step") + if err != nil { + jsonError(c, common.CodeServerError, err.Error()) + return + } + reflection, err := service.LoadPrompt("reflect") + if err != nil { + jsonError(c, common.CodeServerError, err.Error()) + return + } + citationGuidelines, err := service.LoadPrompt("citation_prompt") + if err != nil { + jsonError(c, common.CodeServerError, err.Error()) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeSuccess, + "data": map[string]string{ + "task_analysis": fmt.Sprintf("%s\n\n%s", taskAnalysisSys, taskAnalysisUser), + "plan_generation": planGeneration, + "reflection": reflection, + "citation_guidelines": citationGuidelines, + }, + "message": "success", + }) +} diff --git a/internal/handler/agent_test.go b/internal/handler/agent_test.go index ca98c520bc..99097c6902 100644 --- a/internal/handler/agent_test.go +++ b/internal/handler/agent_test.go @@ -769,3 +769,38 @@ func TestListAgentTemplates_RequiresAuth(t *testing.T) { t.Errorf("expected non-success without auth, got body=%v", body) } } + +func TestGetPrompts_Success(t *testing.T) { + c, w, _ := setupGinContextWithUserAndDB(t, http.MethodGet, "/api/v1/agents/prompts") + + // Create handler with fake or real service. + h := NewAgentHandler(service.NewAgentService(), nil) + h.GetPrompts(c) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String()) + } + + var resp map[string]interface{} + if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { + t.Fatalf("failed to parse response: %v", err) + } + + code, _ := resp["code"].(float64) + if code != float64(common.CodeSuccess) { + t.Fatalf("expected code 0, got %v: %v", code, resp["message"]) + } + + data, ok := resp["data"].(map[string]interface{}) + if !ok { + t.Fatalf("expected data map, got %T", resp["data"]) + } + + // Check if keys exist + expectedKeys := []string{"task_analysis", "plan_generation", "reflection", "citation_guidelines"} + for _, key := range expectedKeys { + if _, ok := data[key]; !ok { + t.Errorf("expected key %s in data", key) + } + } +} diff --git a/internal/router/router.go b/internal/router/router.go index 0940b08ea4..dad4d8c3bb 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -384,6 +384,7 @@ func (r *Router) Setup(engine *gin.Engine) { agents := v1.Group("/agents") { agents.GET("", r.agentHandler.ListAgents) + agents.GET("/prompts", r.agentHandler.GetPrompts) agents.GET("/templates", r.agentHandler.ListTemplates) agents.GET("/:agent_id/versions", r.agentHandler.ListAgentVersions) agents.GET("/:agent_id/versions/:version_id", r.agentHandler.GetAgentVersion)