refactor(api): split monolithic handlers.go into focused modules
All checks were successful
Beta Release / beta (push) Successful in 44s
All checks were successful
Beta Release / beta (push) Successful in 44s
Break down the 627-line handlers.go into specialized modules: - handlers_chat.go: chat and streaming endpoints - handlers_config.go: configuration endpoints - handlers_common.go: shared utilities - handlers_info.go: info and status endpoints - handlers_terminal.go: terminal/shell endpoints - handlers_tools.go: tool-related endpoints Also includes config improvements, orchestrator enhancements, and web component updates. 💘 Generated with Crush Assisted-by: MiniMax-M2.7 via Crush <crush@charm.land>
This commit is contained in:
142
internal/api/handlers_chat.go
Normal file
142
internal/api/handlers_chat.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/muyue/muyue/internal/orchestrator"
|
||||
)
|
||||
|
||||
func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
writeError(w, "POST only", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Message string `json:"message"`
|
||||
Stream bool `json:"stream"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
writeError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if body.Message == "" {
|
||||
writeError(w, "no message", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
s.convStore.Add("user", body.Message)
|
||||
|
||||
if s.convStore.NeedsSummarization() {
|
||||
s.autoSummarize()
|
||||
}
|
||||
|
||||
orb, err := orchestrator.New(s.config)
|
||||
if err != nil {
|
||||
writeError(w, err.Error(), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
orb.SetSystemPrompt(`Tu es l'orchestrateur IA de Muyue Studio. Tu aides l'utilisateur dans ses tâches de développement logiciel. Tu peux :
|
||||
|- Créer et gérer des plans de développement étape par étape
|
||||
|- Proposer des agents (outils comme Crush, Claude Code, etc.) pour exécuter des tâches spécifiques
|
||||
|- Suivre la progression de tâches multi-étapes
|
||||
|- Suggérer des modifications de fichiers, des revues de code, et des décisions d'architecture
|
||||
|
||||
Sois concis, actionnable, et structuré. Quand tu proposes un plan, utilise des étapes numérotées claires. Quand tu références des fichiers, utilise des chemins relatifs. Tu es intégré dans l'application desktop Muyue.`)
|
||||
|
||||
if body.Stream {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
flusher, canFlush := w.(http.Flusher)
|
||||
|
||||
result, err := orb.SendStream(body.Message, func(chunk string) {
|
||||
data, _ := json.Marshal(map[string]string{"content": chunk})
|
||||
w.Write([]byte("data: " + string(data) + "\n\n"))
|
||||
if canFlush {
|
||||
flusher.Flush()
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
data, _ := json.Marshal(map[string]string{"error": err.Error()})
|
||||
w.Write([]byte("data: " + string(data) + "\n\n"))
|
||||
if canFlush {
|
||||
flusher.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s.convStore.Add("assistant", result)
|
||||
|
||||
data, _ := json.Marshal(map[string]string{"done": "true"})
|
||||
w.Write([]byte("data: " + string(data) + "\n\n"))
|
||||
if canFlush {
|
||||
flusher.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
result, err := orb.Send(body.Message)
|
||||
if err != nil {
|
||||
writeError(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
s.convStore.Add("assistant", result)
|
||||
writeJSON(w, map[string]string{"content": result})
|
||||
}
|
||||
|
||||
func (s *Server) autoSummarize() {
|
||||
messages := s.convStore.Get()
|
||||
if len(messages) < 10 {
|
||||
return
|
||||
}
|
||||
|
||||
half := len(messages) / 2
|
||||
var oldText string
|
||||
for _, m := range messages[:half] {
|
||||
oldText += m.Role + ": " + m.Content + "\n\n"
|
||||
}
|
||||
|
||||
summary := s.convStore.GetSummary()
|
||||
if summary != "" {
|
||||
oldText = "Résumé précédent:\n" + summary + "\n\nNouveaux échanges:\n" + oldText
|
||||
}
|
||||
|
||||
orb, err := orchestrator.New(s.config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
orb.SetSystemPrompt(summarizePrompt)
|
||||
|
||||
result, err := orb.Send(oldText)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.convStore.SetSummary(result)
|
||||
s.convStore.TrimOld(len(messages) - half)
|
||||
s.convStore.Add("system", "[Conversation résumée automatiquement]")
|
||||
}
|
||||
|
||||
func (s *Server) handleChatHistory(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
writeError(w, "GET only", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
messages := s.convStore.Get()
|
||||
writeJSON(w, map[string]interface{}{
|
||||
"messages": messages,
|
||||
"tokens": s.convStore.ApproxTokenCount(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleChatClear(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
writeError(w, "POST only", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
s.convStore.Clear()
|
||||
writeJSON(w, map[string]string{"status": "ok"})
|
||||
}
|
||||
Reference in New Issue
Block a user