Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_pi_helpers.go
T

281 lines
6.2 KiB
Go

package app
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log/slog"
"infinite-canvas/backend/internal/model"
)
// Skills Configuration Builders
// buildSkillManifests 构建 Skills 清单(传递给 Pi)
func (s *Service) buildSkillManifests(skills []cloudAgentSkill) []map[string]any {
if len(skills) == 0 {
return []map[string]any{}
}
manifests := make([]map[string]any, 0, len(skills))
for _, skill := range skills {
manifest := map[string]any{
"name": skill.Name,
"enabled": true,
"description": skill.Description,
"version": skill.Version,
"hash": skill.Hash,
"instruction": skill.Instruction,
"files": skill.Files,
}
manifests = append(manifests, manifest)
}
return manifests
}
// buildCompactionStrategy 构建压缩策略
func (s *Service) buildCompactionStrategy(userID, canvasID string, budgets ...cloudAgentContextBudget) map[string]any {
budget := defaultCloudAgentContextBudget()
if len(budgets) > 0 {
budget = budgets[0]
}
compactAt := budget.CompactAtTokens
if compactAt <= 0 || compactAt >= budget.ContextWindowTokens {
compactAt = budget.ContextWindowTokens * 80 / 100
}
keepRecentTokens := min(20_000, max(1_024, budget.ContextWindowTokens/6))
keepRecentTokens = min(keepRecentTokens, max(512, compactAt/2))
return map[string]any{
"enabled": true,
"reserveTokens": budget.ContextWindowTokens - compactAt,
"keepRecentTokens": keepRecentTokens,
}
}
// Utility Functions
// hashContentSHA256 使用 SHA256 计算内容哈希
func hashContentSHA256(content string) string {
hash := sha256.Sum256([]byte(content))
return hex.EncodeToString(hash[:])
}
// truncateString 截断字符串
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
// mergeMetadata 合并元数据
func mergeMetadata(base, override map[string]interface{}) map[string]interface{} {
if base == nil {
base = make(map[string]interface{})
}
if override == nil {
return base
}
result := make(map[string]interface{})
for k, v := range base {
result[k] = v
}
for k, v := range override {
result[k] = v
}
return result
}
// extractNodeIDs 从节点列表提取 ID
func extractNodeIDs(nodes []model.CanvasNode) []string {
ids := make([]string, 0, len(nodes))
for _, node := range nodes {
ids = append(ids, node.ID)
}
return ids
}
// filterNodesByType 按类型过滤节点
func filterNodesByType(nodes []model.CanvasNode, nodeType string) []model.CanvasNode {
filtered := make([]model.CanvasNode, 0)
for _, node := range nodes {
if node.Type == nodeType {
filtered = append(filtered, node)
}
}
return filtered
}
// findNodeByID 通过 ID 查找节点
func findNodeByID(nodes []model.CanvasNode, nodeID string) *model.CanvasNode {
for _, node := range nodes {
if node.ID == nodeID {
return &node
}
}
return nil
}
// Validation Functions
// validateNodeType 验证节点类型
func validateNodeType(nodeType string) bool {
validTypes := map[string]bool{
"text": true,
"markdown": true,
"code": true,
"image": true,
"video": true,
"audio": true,
"link": true,
"diagram": true,
"mindmap": true,
"flowchart": true,
"task": true,
"note": true,
}
return validTypes[nodeType]
}
// validateRelationshipType 验证关系类型
func validateRelationshipType(relType string) bool {
validTypes := map[string]bool{
"parent": true,
"reference": true,
"dependency": true,
"flow": true,
"semantic": true,
"custom": true,
}
return validTypes[relType]
}
// Format Functions
// formatBytes 格式化字节数
func formatBytes(bytes int64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
// formatDuration 格式化时长
func formatDuration(ms int64) string {
if ms < 1000 {
return fmt.Sprintf("%dms", ms)
}
if ms < 60000 {
return fmt.Sprintf("%.1fs", float64(ms)/1000)
}
return fmt.Sprintf("%.1fm", float64(ms)/60000)
}
// Debug and Logging Helpers
// logPiAgentStep 记录 Pi Agent 步骤
func logPiAgentStep(runID, step, message string) {
slog.Debug("agent step", "run", runID, "step", step, "message", message)
}
// logPiAgentError 记录 Pi Agent 错误
func logPiAgentError(runID string, err error) {
slog.Warn("agent error", "run", runID, "error", err)
}
// logPiAgentMetric 记录 Pi Agent 指标
func logPiAgentMetric(runID, metric string, value interface{}) {
slog.Debug("agent metric", "run", runID, "metric", metric, "value", value)
}
// Type Conversions
// toStringSlice 转换为字符串切片
func toStringSlice(input interface{}) []string {
if input == nil {
return []string{}
}
switch v := input.(type) {
case []string:
return v
case []interface{}:
result := make([]string, 0, len(v))
for _, item := range v {
if s, ok := item.(string); ok {
result = append(result, s)
}
}
return result
default:
return []string{}
}
}
// toMapStringAny 转换为 map[string]any
func toMapStringAny(input interface{}) map[string]any {
if input == nil {
return make(map[string]any)
}
if m, ok := input.(map[string]any); ok {
return m
}
if m, ok := input.(map[string]interface{}); ok {
return m
}
return make(map[string]any)
}
// Safe Accessors
// safeGetString 安全获取字符串
func safeGetString(m map[string]any, key string, defaultValue string) string {
if v, ok := m[key].(string); ok {
return v
}
return defaultValue
}
// safeGetInt 安全获取整数
func safeGetInt(m map[string]any, key string, defaultValue int) int {
if v, ok := m[key].(int); ok {
return v
}
if v, ok := m[key].(float64); ok {
return int(v)
}
return defaultValue
}
// safeGetBool 安全获取布尔值
func safeGetBool(m map[string]any, key string, defaultValue bool) bool {
if v, ok := m[key].(bool); ok {
return v
}
return defaultValue
}
// safeGetFloat 安全获取浮点数
func safeGetFloat(m map[string]any, key string, defaultValue float64) float64 {
if v, ok := m[key].(float64); ok {
return v
}
if v, ok := m[key].(int); ok {
return float64(v)
}
return defaultValue
}