Files
ragflow/internal/agent/runtime/state_clone_test.go
Hz_ 1b77e3ebcd fix(go-agent): preserve canvas system state across turns (#17010)
## Summary

- Preserve request-scoped system variables such as files and user IDs
during Canvas execution.
- Persist conversation history, turn counts, and tool memory in the
session DSL across turns.
- Parse agent uploads into `sys.files` and align system variable
rendering with Python.

## Testing

- `bash build.sh --test ./internal/agent/...`
- `bash build.sh --test ./internal/service/...`

<img width="1896" height="1232" alt="image"
src="https://github.com/user-attachments/assets/b420cd97-53c3-470f-a3e1-d39cea26a213"
/>
2026-07-17 15:53:58 +08:00

65 lines
2.3 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package runtime_test
import (
"testing"
"ragflow/internal/agent/component"
"ragflow/internal/agent/runtime"
)
func TestCanvasStateSnapshotClonesTypedJSONComposites(t *testing.T) {
t.Parallel()
var nilMap map[string]string
var nilDownloads []component.DownloadInfo
payload := map[string]any{
"typed_map": map[string]string{"status": "ready"},
"nested": []map[string][]string{{"tags": {"one", "two"}}},
"downloads": []component.DownloadInfo{{
DocID: "doc-1",
Filename: "report.pdf",
}},
"nil_map": nilMap,
"nil_downloads": nilDownloads,
}
state := runtime.NewCanvasState("run-copy", "task-copy")
state.AppendHistory("assistant", payload)
cloned := state.SnapshotHistory()[0]["payload"].(map[string]any)
cloned["typed_map"].(map[string]any)["status"] = "changed"
cloned["nested"].([]any)[0].(map[string]any)["tags"].([]any)[0] = "changed"
cloned["downloads"].([]any)[0].(map[string]any)["filename"] = "changed.pdf"
if cloned["nil_map"].(map[string]any) != nil {
t.Fatalf("nil typed map was not preserved: %#v", cloned["nil_map"])
}
if cloned["nil_downloads"].([]any) != nil {
t.Fatalf("nil typed slice was not preserved: %#v", cloned["nil_downloads"])
}
unchanged := state.SnapshotHistory()[0]["payload"].(map[string]any)
if got := unchanged["typed_map"].(map[string]any)["status"]; got != "ready" {
t.Fatalf("typed map mutation leaked into state: %v", got)
}
if got := unchanged["nested"].([]any)[0].(map[string]any)["tags"].([]any)[0]; got != "one" {
t.Fatalf("nested typed slice mutation leaked into state: %v", got)
}
if got := unchanged["downloads"].([]any)[0].(map[string]any)["filename"]; got != "report.pdf" {
t.Fatalf("typed struct slice mutation leaked into state: %v", got)
}
}