feat: the assistant's talk is kept in the database per user and the model sees a trimmed window of it; clear is a broom; the ball steps aside for the chat and comes back where the chat was
This commit is contained in:
1 parent
92a90646a5
commit
00617451aa
6 files changed
+286
-63
No files matched your search
@@ -130,7 +130,7 @@ func TestAIChatNeedsUserOn(t *testing.T) {
|
||||
_ = s.db.SetConfig(cfgAIType, "openai")
|
||||
_ = s.db.SetConfig(cfgAIURL, srv.URL)
|
||||
post := func() *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/panel/ai/chat", strings.NewReader(`{"messages":[{"role":"user","content":"hi"}],"context":"x"}`))
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/panel/ai/chat", strings.NewReader(`{"message":"hi","context":"x"}`))
|
||||
req.AddCookie(c)
|
||||
w := httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(w, req)
|
||||
@@ -146,6 +146,10 @@ func TestAIChatNeedsUserOn(t *testing.T) {
|
||||
if w.Code != 200 || string(b) != "ok" {
|
||||
t.Fatalf("on: %d %q", w.Code, b)
|
||||
}
|
||||
// The talk is kept: the question and the answer.
|
||||
if h := s.aiHistory(a.ID, 10); len(h) != 2 || h[0].Content != "hi" || h[1].Content != "ok" {
|
||||
t.Fatalf("history: %+v", h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionAtLeast(t *testing.T) {
|
||||
@@ -158,3 +162,41 @@ func TestVersionAtLeast(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The window the model sees: newest first within the budget, old results
|
||||
// and answers cut down, results as the user's words, same roles merged.
|
||||
func TestAIWindow(t *testing.T) {
|
||||
big := strings.Repeat("x", 5000)
|
||||
rows := []aiMsg{
|
||||
{Role: "user", Content: "старое"},
|
||||
{Role: "assistant", Content: big},
|
||||
{Role: "result", Content: big},
|
||||
{Role: "user", Content: "дальше"},
|
||||
{Role: "assistant", Content: "ок"},
|
||||
{Role: "result", Content: "вывод"},
|
||||
}
|
||||
w := aiWindow(rows, 100000)
|
||||
if len(w) != 5 || w[0].Role != "user" || w[len(w)-1].Role != "user" {
|
||||
t.Fatalf("roles: %+v", w)
|
||||
}
|
||||
if len(w[1].Content) > 3200 || len(w[2].Content) > 1700+len("дальше")+40 {
|
||||
t.Fatalf("old not cut: %d %d", len(w[1].Content), len(w[2].Content))
|
||||
}
|
||||
if !strings.Contains(w[4].Content, "вывод") || !strings.HasPrefix(w[4].Content, "Результат") {
|
||||
t.Fatalf("last result: %q", w[4].Content)
|
||||
}
|
||||
// A small budget keeps the newest, starting with the user.
|
||||
w = aiWindow(rows, 60)
|
||||
if len(w) == 0 || w[0].Role != "user" || !strings.Contains(w[len(w)-1].Content, "вывод") {
|
||||
t.Fatalf("small: %+v", w)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripThink(t *testing.T) {
|
||||
if got := stripThink("::думаю::\nхм\n::ответ::\n391"); got != "391" {
|
||||
t.Fatalf("%q", got)
|
||||
}
|
||||
if got := stripThink("просто"); got != "просто" {
|
||||
t.Fatalf("%q", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user