feat: ByteClaw 0.2.0 – Provider-Wahl ab erster Nachricht, Markdown/Code-Blöcke mit Kopieren, Nachrichten-Aktionen, Spracheingabe, Session-Suche/Umbenennen, Verbindungsstatus, Benachrichtigungen
This commit is contained in:
parent
52b6b68117
commit
ec797335a6
26 changed files with 560 additions and 30 deletions
|
|
@ -178,3 +178,45 @@ func TestManualSummarize(t *testing.T) {
|
|||
t.Fatal(r.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebSocketFirstMessageUsesSessionProvider(t *testing.T) {
|
||||
ts, st := setupFull(t, echo{})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
c, _, err := websocket.Dial(ctx, "ws"+strings.TrimPrefix(ts.URL, "http")+"/api/ws", &websocket.DialOptions{HTTPHeader: map[string][]string{"Authorization": {"Bearer " + tok}}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.CloseNow()
|
||||
wsjson.Write(ctx, c, chatReq{Type: "message", Text: "hi", Provider: "dahl", Model: "zai-org/GLM-5.3-Flash"})
|
||||
var sid string
|
||||
for {
|
||||
var e agent.Event
|
||||
if err := wsjson.Read(ctx, c, &e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e.Type == "session" {
|
||||
sid = e.Session
|
||||
got, _ := st.GetSession(ctx, sid)
|
||||
if got.Provider != "dahl" || got.Model != "zai-org/GLM-5.3-Flash" {
|
||||
t.Fatalf("provider not set before first run: %+v", got)
|
||||
}
|
||||
}
|
||||
if e.Type == "done" {
|
||||
break
|
||||
}
|
||||
if e.Type == "error" {
|
||||
t.Fatalf("%+v", e)
|
||||
}
|
||||
}
|
||||
// unconfigured provider is rejected without creating a session
|
||||
wsjson.Write(ctx, c, chatReq{Type: "message", Text: "x", Provider: "openrouter"})
|
||||
var e agent.Event
|
||||
if err := wsjson.Read(ctx, c, &e); err != nil || e.Type != "error" || !strings.HasPrefix(e.Text, "provider:") {
|
||||
t.Fatalf("%+v %v", e, err)
|
||||
}
|
||||
list, _ := st.ListSessions(ctx, 10)
|
||||
if len(list) != 1 {
|
||||
t.Fatalf("expected 1 session, got %d", len(list))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,6 +230,27 @@ type chatReq struct {
|
|||
Type string `json:"type,omitempty"`
|
||||
SessionID string `json:"session_id"`
|
||||
Text string `json:"text"`
|
||||
// Optional provider/model for a NEW session, so the very first answer
|
||||
// already uses the per-session choice. Ignored for existing sessions.
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
}
|
||||
|
||||
// startSession is ensureSession plus applying an optional per-session provider
|
||||
// to a freshly created session before the first agent run.
|
||||
func (s *Server) startSession(ctx context.Context, req chatReq) (string, error) {
|
||||
var a providers.Active
|
||||
if req.SessionID == "" && req.Provider != "" && s.Providers != nil {
|
||||
var err error
|
||||
if a, err = s.Providers.Validate(req.Provider, req.Model); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
id, err := s.ensureSession(ctx, req.SessionID, req.Text)
|
||||
if err != nil || a.Provider == "" {
|
||||
return id, err
|
||||
}
|
||||
return id, s.Store.SetSessionProvider(ctx, id, a.Provider, a.Model)
|
||||
}
|
||||
|
||||
// ensureSession returns an existing session id or creates one titled after the message.
|
||||
|
|
@ -255,8 +276,12 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) {
|
|||
writeErr(w, http.StatusBadRequest, "text required")
|
||||
return
|
||||
}
|
||||
id, err := s.ensureSession(r.Context(), req.SessionID, req.Text)
|
||||
id, err := s.startSession(r.Context(), req)
|
||||
if err != nil {
|
||||
if req.SessionID == "" && req.Provider != "" {
|
||||
providerErr(w, err)
|
||||
return
|
||||
}
|
||||
s.storeErr(w, err)
|
||||
return
|
||||
}
|
||||
|
|
@ -327,10 +352,14 @@ func (s *Server) ws(w http.ResponseWriter, r *http.Request) {
|
|||
send(agent.Event{Type: "error", Text: "an answer is still streaming; cancel it first"})
|
||||
continue
|
||||
}
|
||||
id, err := s.ensureSession(ctx, req.SessionID, req.Text)
|
||||
id, err := s.startSession(ctx, req)
|
||||
if err != nil {
|
||||
<-busy
|
||||
send(agent.Event{Type: "error", Text: "unknown session"})
|
||||
msg := "unknown session"
|
||||
if req.SessionID == "" && req.Provider != "" {
|
||||
msg = "provider: " + err.Error()
|
||||
}
|
||||
send(agent.Event{Type: "error", Text: msg})
|
||||
continue
|
||||
}
|
||||
send(agent.Event{Type: "session", Session: id})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue