feat: update chat, settings, prompts and system info modules

This commit is contained in:
2026-06-16 10:33:54 +08:00
parent b86c29c6a4
commit dd13d21c6a
18 changed files with 1362 additions and 43 deletions

View File

@@ -38,6 +38,12 @@ func handleChat(piClient *rpc.Client) gin.HandlerFunc {
}
}
// Intercept slash commands before forwarding to agent.
if dispatch := services.TrySlashDispatch(req.Message); dispatch.Handled {
dispatchSlashCommand(c, dispatch, piClient)
return
}
// normalize streamingBehavior: only "steer" / "followUp" are forwarded
if req.StreamingBehavior != "steer" && req.StreamingBehavior != "followUp" {
req.StreamingBehavior = ""
@@ -52,6 +58,95 @@ func handleChat(piClient *rpc.Client) gin.HandlerFunc {
}
}
// dispatchSlashCommand handles slash commands intercepted from /api/chat.
func dispatchSlashCommand(c *gin.Context, d services.SlashDispatchResult, piClient *rpc.Client) {
// Unsupported command (not in the whitelist)
if d.Message != "" {
c.JSON(http.StatusOK, gin.H{"ok": true, "slash": true, "message": d.Message})
return
}
switch d.Command {
case "new":
slashNew(c, piClient)
case "reload":
slashReload(c, piClient)
case "copy":
slashCopy(c, piClient)
default:
// Forward to agent as a prompt; SSE events carry the result back.
msg := "/" + d.Command
if d.Args != "" {
msg += " " + d.Args
}
if err := piClient.SubmitPrompt(models.ChatRequest{Message: msg}); err != nil {
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
return
}
c.JSON(http.StatusAccepted, gin.H{"ok": true, "slash": true, "accepted": true})
}
}
func slashNew(c *gin.Context, piClient *rpc.Client) {
resp, err := piClient.SendCmd(models.RPCCommand{Type: "new_session"})
if err != nil || !resp.Success {
msg := "新建会话失败"
if err != nil {
msg = err.Error()
} else if resp.Error != "" {
msg = resp.Error
}
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: msg})
return
}
out := gin.H{"ok": true, "slash": true, "action": "new_session"}
if state, serr := piClient.SendCmd(models.RPCCommand{Type: "get_state"}); serr == nil && state.Success {
var sd struct {
SessionFile string `json:"sessionFile"`
}
if json.Unmarshal(state.Data, &sd) == nil && sd.SessionFile != "" {
out["sessionFile"] = sd.SessionFile
}
}
c.JSON(http.StatusOK, out)
}
func slashReload(c *gin.Context, piClient *rpc.Client) {
reloadAgent(piClient)
c.JSON(http.StatusOK, gin.H{"ok": true, "slash": true, "action": "reload_sessions", "message": "已重新加载"})
}
func slashCopy(c *gin.Context, piClient *rpc.Client) {
resp, err := piClient.SendCmd(models.RPCCommand{Type: "get_messages"})
if err != nil || !resp.Success {
c.JSON(http.StatusOK, gin.H{"ok": true, "slash": true, "message": "获取消息失败"})
return
}
var msgs []struct {
Role string `json:"role"`
Content any `json:"content"`
}
if json.Unmarshal(resp.Data, &msgs) != nil {
c.JSON(http.StatusOK, gin.H{"ok": true, "slash": true, "message": "没有可复制的消息"})
return
}
for i := len(msgs) - 1; i >= 0; i-- {
if msgs[i].Role == "assistant" {
text := ""
switch v := msgs[i].Content.(type) {
case string:
text = v
default:
b, _ := json.Marshal(v)
text = string(b)
}
c.JSON(http.StatusOK, gin.H{"ok": true, "slash": true, "action": "copy", "message": text})
return
}
}
c.JSON(http.StatusOK, gin.H{"ok": true, "slash": true, "message": "没有助手消息可复制"})
}
func handleSteer(piClient *rpc.Client) gin.HandlerFunc {
return func(c *gin.Context) {
var req models.ChatRequest