first commit
This commit is contained in:
@@ -31,7 +31,6 @@ func handleChat(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate images
|
||||
if len(req.Images) > 0 {
|
||||
if err := services.ValidateImages(req.Images); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
|
||||
@@ -39,51 +38,40 @@ func handleChat(piClient *rpc.Client) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// Try slash command dispatch
|
||||
result := services.TrySlashDispatch(req.Message)
|
||||
if result.Handled {
|
||||
if result.Message != "" {
|
||||
// unsupported command – return friendly message
|
||||
c.JSON(http.StatusOK, gin.H{"handled": true, "message": result.Message})
|
||||
return
|
||||
}
|
||||
// supported built-in command – forward to pi CLI as a command type
|
||||
resp, err := piClient.SendCmd(models.RPCCommand{
|
||||
Type: result.Command,
|
||||
Args: result.Args,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"handled": true, "result": resp.Result})
|
||||
return
|
||||
// normalize streamingBehavior: only "steer" / "followUp" are forwarded
|
||||
if req.StreamingBehavior != "steer" && req.StreamingBehavior != "followUp" {
|
||||
req.StreamingBehavior = ""
|
||||
}
|
||||
|
||||
// Regular prompt
|
||||
// Submit prompt; agent output streams back over SSE.
|
||||
if err := piClient.SubmitPrompt(req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, models.OKResponse{OK: true})
|
||||
c.JSON(http.StatusAccepted, gin.H{"ok": true, "accepted": true})
|
||||
}
|
||||
}
|
||||
|
||||
func handleSteer(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var req models.SteerRequest
|
||||
var req models.ChatRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
err := piClient.SubmitPrompt(models.ChatRequest{
|
||||
Message: req.Message,
|
||||
StreamingBehavior: "steer",
|
||||
resp, err := piClient.SendCmd(models.RPCCommand{
|
||||
Type: "steer",
|
||||
Message: req.Message,
|
||||
Images: req.Images,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: orDefault(resp.Error, "steer 失败")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, models.OKResponse{OK: true})
|
||||
}
|
||||
}
|
||||
@@ -95,11 +83,19 @@ func handleFollowUp(piClient *rpc.Client) gin.HandlerFunc {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
req.StreamingBehavior = "follow_up"
|
||||
if err := piClient.SubmitPrompt(req); err != nil {
|
||||
resp, err := piClient.SendCmd(models.RPCCommand{
|
||||
Type: "follow_up",
|
||||
Message: req.Message,
|
||||
Images: req.Images,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: orDefault(resp.Error, "follow_up 失败")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, models.OKResponse{OK: true})
|
||||
}
|
||||
}
|
||||
@@ -112,21 +108,24 @@ func handleBash(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
resp, err := piClient.SendCmd(models.RPCCommand{
|
||||
Type: "bash",
|
||||
Args: req.Command,
|
||||
Type: "bash",
|
||||
Command: req.Command,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp.Result)
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: orDefault(resp.Error, "命令执行失败")})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, jsonRaw(resp.Data))
|
||||
}
|
||||
}
|
||||
|
||||
func handleAbort(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
_, err := piClient.SendCmd(models.RPCCommand{Type: "abort"})
|
||||
if err != nil {
|
||||
if _, err := piClient.SendCmd(models.RPCCommand{Type: "abort"}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
@@ -136,8 +135,7 @@ func handleAbort(piClient *rpc.Client) gin.HandlerFunc {
|
||||
|
||||
func handleAbortRetry(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
_, err := piClient.SendCmd(models.RPCCommand{Type: "abort_retry"})
|
||||
if err != nil {
|
||||
if _, err := piClient.SendCmd(models.RPCCommand{Type: "abort_retry"}); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
@@ -147,16 +145,16 @@ func handleAbortRetry(piClient *rpc.Client) gin.HandlerFunc {
|
||||
|
||||
func handleMessages(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var req models.MessagesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{Error: err.Error()})
|
||||
resp, err := piClient.SendCmd(models.RPCCommand{Type: "get_messages"})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
snap := piClient.GetSnapshot()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"sessionFile": snap.SessionFile,
|
||||
"events": snap.ReplayEvents,
|
||||
})
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusInternalServerError, models.ErrorResponse{Error: resp.Error})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, jsonRaw(resp.Data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +171,7 @@ func handleSSE(piClient *rpc.Client) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
ch := make(chan []byte, 64)
|
||||
ch := make(chan []byte, 256)
|
||||
client := &rpc.SSEClient{
|
||||
Ch: ch,
|
||||
Done: c.Request.Context().Done(),
|
||||
@@ -181,19 +179,19 @@ func handleSSE(piClient *rpc.Client) gin.HandlerFunc {
|
||||
piClient.RegisterSSEClient(client)
|
||||
defer piClient.UnregisterSSEClient(client)
|
||||
|
||||
// send initial keepalive
|
||||
_, _ = io.WriteString(c.Writer, ": keepalive\n\n")
|
||||
// initial comment to open the stream
|
||||
_, _ = io.WriteString(c.Writer, ": connected\n\n")
|
||||
flusher.Flush()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.Request.Context().Done():
|
||||
return
|
||||
case raw, ok := <-ch:
|
||||
case frame, ok := <-ch:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
frame := rpc.FormatSSEEvent(raw)
|
||||
// frame is already a fully-formatted "data: ...\n\n" SSE frame
|
||||
if _, err := c.Writer.Write(frame); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -203,12 +201,21 @@ func handleSSE(piClient *rpc.Client) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// jsonRaw is a helper to return pre-serialized JSON.
|
||||
// jsonRaw decodes pre-serialized JSON for re-emission via gin.
|
||||
func jsonRaw(raw json.RawMessage) any {
|
||||
if raw == nil {
|
||||
return nil
|
||||
if len(raw) == 0 {
|
||||
return gin.H{}
|
||||
}
|
||||
var v any
|
||||
_ = json.Unmarshal(raw, &v)
|
||||
if err := json.Unmarshal(raw, &v); err != nil {
|
||||
return gin.H{}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func orDefault(s, fallback string) string {
|
||||
if s == "" {
|
||||
return fallback
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user