Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_pi_helpers.go
ddcat666 a0f025aa96 feat(platform): 日志管理 - 统一日志出口并屏蔽查询与 Agent 流式事件日志
新增 internal/logging,slog、标准库 log、GORM 与 Gin 访问日志共用同一 Handler,由 CANVAS_LOG_LEVEL / CANVAS_LOG_FORMAT 控制。info 级别不再输出成功的查询类请求、成功 SQL、message_end 等 Agent 流式事件和常规画布自动保存;4xx/5xx、慢请求、慢 SQL、SQL 失败与 panic 仍会记录,SQL 不内联参数值。
2026-09-30 03:33:34 +08:00

287 lines
6.2 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 app
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log/slog"
"infinite-canvas/backend/internal/model"
)
// Skills Configuration Builders
// buildSkillPaths 构建 Skills 路径列表
func (s *Service) buildSkillPaths(skills []cloudAgentSkill) []string {
if len(skills) == 0 {
return []string{}
}
paths := make([]string, 0, len(skills))
for _, skill := range skills {
paths = append(paths, cloudAgentSkillPaths(skill)...)
}
return paths
}
// 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) map[string]any {
return map[string]any{
"enabled": true,
"strategy": "balanced", // balanced/aggressive/conservative
"reserveTokens": 2048,
"prioritizeRecent": true,
"keepSystemPrompt": true,
"keepToolResults": true,
}
}
// 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
}