Files
Agent-Native/runtime/session/microcompact.go
T
weichen 6ddd3da8df Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements.

Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate.

Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable.

Refs: Versioned dependencies U1/U3; feature/dependency-workspace
2026-10-13 15:36:10 +00:00

116 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package session
import (
"os"
"path/filepath"
"strings"
"git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/llm"
)
const (
// DefaultKeepToolResults 是运行内清理时保留的最近工具结果条数。
DefaultKeepToolResults = 5
// DefaultContextWindow 是未配置模型窗口时的保守字符数。
DefaultContextWindow = 32000
// DefaultOutputReserve 是留给模型输出的字符预留。
DefaultOutputReserve = 4096
// DefaultContextBuffer 是窗口估算的余量,避免贴着上限才清理。
DefaultContextBuffer = 1024
)
// EstimateChars 用字符数近似这次请求的体积。
func EstimateChars(msgs []llm.Message) int {
n := 0
for _, m := range msgs {
n += len([]rune(m.Content))
for _, c := range m.ToolCalls {
n += len([]rune(c.Name)) + len([]rune(c.Arguments))
}
}
return n
}
// NeedsClear 在估算超过「窗口 − 输出预留 − 缓冲」时返回真。
// window ≤ 0 时用保守默认值。
func NeedsClear(estimate, window, outputReserve, buffer int) bool {
if window <= 0 {
window = DefaultContextWindow
}
room := window - outputReserve - buffer
if room < 1 {
room = 1
}
return estimate > room
}
// ClearOldToolResults 把最近 keep 条之前的工具结果换成占位。
// 消息条数、顺序和 tool_call 配对都不变;占位写明完整结果仍在会话记录里。
// keep ≤ 0 时保留默认的 5 条。返回的切片不与输入共享被改写的消息。
func ClearOldToolResults(msgs []llm.Message, keep int) []llm.Message {
return ClearOldToolResultsTo(msgs, keep, "")
}
// ClearOldToolResultsTo 在 ClearOldToolResults 的基础上支持落盘:
// dir 非空时,把被清理结果的完整内容写进 <dir>/<toolCallID>.txt,
// 占位改为路径指引(ZCode 式「旧工具结果换成路径」);
// 写盘失败或调用 ID 为空时回落为转录指针占位,不阻断本轮。
func ClearOldToolResultsTo(msgs []llm.Message, keep int, dir string) []llm.Message {
if keep <= 0 {
keep = DefaultKeepToolResults
}
out := append([]llm.Message{}, msgs...)
var toolIdx []int
for i, m := range out {
if m.Role == llm.RoleTool {
toolIdx = append(toolIdx, i)
}
}
if len(toolIdx) <= keep {
return out
}
for _, i := range toolIdx[:len(toolIdx)-keep] {
name := out[i].Name
if name == "" {
name = "工具"
}
if dir != "" && out[i].ToolCallID != "" && out[i].Content != "" {
if path, ok := spillResult(dir, out[i].ToolCallID, out[i].Content); ok {
out[i].Content = "旧工具结果已清理。完整内容见 " + path + "(" + name + ")。"
continue
}
}
loc := "会话记录"
if out[i].ToolCallID != "" {
loc = "会话记录中的调用 " + out[i].ToolCallID
}
out[i].Content = "旧工具结果已清理。完整内容见" + loc + "(" + name + ")。"
}
return out
}
// spillResult 把一条旧工具结果写盘,返回路径。文件名只留安全字符。
func spillResult(dir, callID, content string) (string, bool) {
var sb strings.Builder
for _, r := range callID {
switch {
case r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' ||
r == '-' || r == '_' || r == '.':
sb.WriteRune(r)
default:
sb.WriteByte('_')
}
}
if sb.Len() == 0 {
return "", false
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", false
}
path := filepath.Join(dir, sb.String()+".txt")
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
return "", false
}
return path, true
}