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.
This commit is contained in:
Hz_
2026-06-09 17:03:42 +08:00
committed by GitHub
parent 01a2a44766
commit d1c436b804
3 changed files with 88 additions and 0 deletions

View File

@@ -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",
})
}

View File

@@ -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)
}
}
}

View File

@@ -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)