2026-04-26 06:44:47 +08:00
|
|
|
|
// Package internal 包含 hxclaw 的内部工具模块
|
|
|
|
|
|
// 提供配置管理、Markdown 渲染、输入读取等功能
|
2026-04-15 06:13:52 +08:00
|
|
|
|
package internal
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// Config 是项目配置结构体,包含流式输出、Markdown 渲染和 UI 相关配置
|
|
|
|
|
|
type Config struct {
|
|
|
|
|
|
// Streaming 流式输出配置
|
2026-04-15 06:13:52 +08:00
|
|
|
|
Streaming StreamingConfig `yaml:"streaming"`
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// Markdown Markdown 渲染配置
|
|
|
|
|
|
Markdown MarkdownConfig `yaml:"markdown"`
|
|
|
|
|
|
// UI UI 显示配置
|
|
|
|
|
|
UI UIConfig `yaml:"ui"`
|
|
|
|
|
|
// TTS TTS 语音配置
|
|
|
|
|
|
TTS TTSConfig `yaml:"tts"`
|
2026-04-27 06:16:19 +08:00
|
|
|
|
// Memory 聊天记忆体配置
|
|
|
|
|
|
Memory MemoryConfig `yaml:"memory"`
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// StreamingConfig 流式输出配置,控制模拟打字效果的延迟时间
|
2026-04-15 06:13:52 +08:00
|
|
|
|
type StreamingConfig struct {
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// LineDelayMs 每行输出延迟(毫秒)
|
|
|
|
|
|
LineDelayMs int `yaml:"line_delay_ms"`
|
|
|
|
|
|
// LastLineDelayMs 最后一行输出延迟(毫秒)
|
2026-04-15 06:13:52 +08:00
|
|
|
|
LastLineDelayMs int `yaml:"last_line_delay_ms"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// MarkdownConfig Markdown 渲染配置,控制渲染主题和换行行为
|
2026-04-15 06:13:52 +08:00
|
|
|
|
type MarkdownConfig struct {
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// Theme 渲染主题,支持 dark、light、dracula、tokyo-night 等
|
|
|
|
|
|
Theme string `yaml:"theme"`
|
|
|
|
|
|
// LineWidth 自动换行宽度(0=自动,>0=固定宽度,-1=禁用)
|
|
|
|
|
|
LineWidth int `yaml:"line_width"`
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// UIConfig UI 显示配置,控制 Logo 和用户提示符
|
2026-04-15 06:13:52 +08:00
|
|
|
|
type UIConfig struct {
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// Logo 应用 Logo 字符
|
|
|
|
|
|
Logo string `yaml:"logo"`
|
|
|
|
|
|
// UserIcon 用户输入提示符
|
|
|
|
|
|
UserIcon string `yaml:"user_icon"`
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// TTSConfig TTS 语音配置
|
2026-04-26 03:01:28 +08:00
|
|
|
|
type TTSConfig struct {
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// Enabled 全局开关
|
2026-04-26 03:01:28 +08:00
|
|
|
|
Enabled bool `yaml:"enabled"`
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// Port 端口
|
|
|
|
|
|
Port int `yaml:"port"`
|
|
|
|
|
|
// Auto AI 回复后自动朗读
|
|
|
|
|
|
Auto bool `yaml:"auto"`
|
2026-04-26 03:01:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 06:16:19 +08:00
|
|
|
|
// MemoryConfig 聊天记忆体配置
|
|
|
|
|
|
type MemoryConfig struct {
|
|
|
|
|
|
// Enabled 启用开关
|
|
|
|
|
|
Enabled bool `yaml:"enabled"`
|
|
|
|
|
|
// DBPath 数据库路径
|
|
|
|
|
|
DBPath string `yaml:"db_path"`
|
|
|
|
|
|
// AutoSession 自动创建 Session
|
|
|
|
|
|
AutoSession bool `yaml:"auto_session"`
|
|
|
|
|
|
// AutoExport 退出时自动导出
|
|
|
|
|
|
AutoExport bool `yaml:"auto_export"`
|
2026-04-27 08:58:08 +08:00
|
|
|
|
// SummaryTimeout 摘要生成超时时间(秒)
|
|
|
|
|
|
SummaryTimeout int `yaml:"summary_timeout"`
|
2026-04-27 06:16:19 +08:00
|
|
|
|
// Vector 向量服务配置
|
|
|
|
|
|
Vector VectorConfig `yaml:"vector"`
|
|
|
|
|
|
// Recall 检索配置
|
|
|
|
|
|
Recall RecallConfig `yaml:"recall"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VectorConfig 向量服务配置
|
|
|
|
|
|
type VectorConfig struct {
|
|
|
|
|
|
// APIKey 硅基流动 API Key
|
|
|
|
|
|
APIKey string `yaml:"api_key"`
|
|
|
|
|
|
// BaseURL API 地址
|
|
|
|
|
|
BaseURL string `yaml:"base_url"`
|
|
|
|
|
|
// Model 向量模型
|
|
|
|
|
|
Model string `yaml:"model"`
|
|
|
|
|
|
// Dimension <20><>量维度
|
|
|
|
|
|
Dimension int `yaml:"dimension"`
|
|
|
|
|
|
// MaxSearchResults 最大检索结果数
|
|
|
|
|
|
MaxSearchResults int `yaml:"max_search_results"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RecallConfig 检索配置
|
|
|
|
|
|
type RecallConfig struct {
|
|
|
|
|
|
// Keywords 触发关键词列表
|
|
|
|
|
|
Keywords []string `yaml:"keywords"`
|
|
|
|
|
|
// AutoRecall 是否自动检测相似度
|
|
|
|
|
|
AutoRecall bool `yaml:"auto_recall"`
|
|
|
|
|
|
// SimilarityThreshold 相似度阈值
|
|
|
|
|
|
SimilarityThreshold float64 `yaml:"similarity_threshold"`
|
|
|
|
|
|
// MaxResults 最大检索结果数
|
|
|
|
|
|
MaxResults int `yaml:"max_results"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 06:13:52 +08:00
|
|
|
|
var (
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// defaultCfg 默认配置值,当配置文件不存在或字段为空时使用
|
|
|
|
|
|
defaultCfg = Config{
|
2026-04-15 06:13:52 +08:00
|
|
|
|
Streaming: StreamingConfig{
|
|
|
|
|
|
LineDelayMs: 1000,
|
|
|
|
|
|
LastLineDelayMs: 600,
|
|
|
|
|
|
},
|
|
|
|
|
|
Markdown: MarkdownConfig{
|
2026-04-26 06:44:47 +08:00
|
|
|
|
Theme: "dark",
|
|
|
|
|
|
LineWidth: -1,
|
2026-04-15 06:13:52 +08:00
|
|
|
|
},
|
|
|
|
|
|
UI: UIConfig{
|
2026-04-26 06:44:47 +08:00
|
|
|
|
Logo: "🦐",
|
|
|
|
|
|
UserIcon: "👀 ",
|
2026-04-15 06:13:52 +08:00
|
|
|
|
},
|
2026-04-26 03:01:28 +08:00
|
|
|
|
TTS: TTSConfig{
|
|
|
|
|
|
Enabled: false,
|
|
|
|
|
|
Port: 9876,
|
|
|
|
|
|
Auto: true,
|
|
|
|
|
|
},
|
2026-04-27 06:16:19 +08:00
|
|
|
|
Memory: MemoryConfig{
|
2026-04-27 08:58:08 +08:00
|
|
|
|
Enabled: true,
|
|
|
|
|
|
DBPath: "",
|
|
|
|
|
|
AutoSession: true,
|
|
|
|
|
|
AutoExport: true,
|
|
|
|
|
|
SummaryTimeout: 20,
|
2026-04-27 06:16:19 +08:00
|
|
|
|
Vector: VectorConfig{
|
|
|
|
|
|
APIKey: "",
|
|
|
|
|
|
BaseURL: "https://api.siliconflow.cn/v1",
|
|
|
|
|
|
Model: "BAAI/bge-m3",
|
|
|
|
|
|
Dimension: 1024,
|
|
|
|
|
|
MaxSearchResults: 10,
|
|
|
|
|
|
},
|
|
|
|
|
|
Recall: RecallConfig{
|
|
|
|
|
|
Keywords: []string{"之前", "聊过", "记得", "找找", "曾经", "谈论过", "提过"},
|
|
|
|
|
|
AutoRecall: true,
|
|
|
|
|
|
SimilarityThreshold: 0.7,
|
|
|
|
|
|
MaxResults: 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
cfg *Config // 已合并的配置(用户配置 + 项目配置)
|
|
|
|
|
|
cfgLock sync.RWMutex // 配置读写锁
|
2026-04-15 06:13:52 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// 用户配置文件路径常量
|
|
|
|
|
|
const (
|
2026-04-27 06:16:19 +08:00
|
|
|
|
userConfigFile = "config.yml" // 用户配置文件名
|
2026-04-26 06:44:47 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// LoadProjectConfig 加载并合并项目配置
|
|
|
|
|
|
// 加载顺序:用户配置(~/.config/hxclaw/config.yml) > 项目配置(project.config.yml)
|
|
|
|
|
|
// 最终配置由两者合并得出,用户配置优先于项目配置
|
2026-04-15 06:13:52 +08:00
|
|
|
|
func LoadProjectConfig() error {
|
2026-04-26 06:44:47 +08:00
|
|
|
|
cfgLock.Lock()
|
|
|
|
|
|
defer cfgLock.Unlock()
|
2026-04-15 06:13:52 +08:00
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
userCfg := loadUserConfig()
|
|
|
|
|
|
projCfg := loadProjectConfig()
|
|
|
|
|
|
|
|
|
|
|
|
merged := mergeConfig(userCfg, projCfg)
|
|
|
|
|
|
cfg = merged
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// loadUserConfig 加载用户配置文件
|
|
|
|
|
|
// 路径:~/.config/hxclaw/config.yml
|
|
|
|
|
|
// 如果文件不存在,则自动创建默认配置文件
|
|
|
|
|
|
func loadUserConfig() *Config {
|
|
|
|
|
|
userPath := getUserConfigPath()
|
|
|
|
|
|
if userPath == "" {
|
2026-04-15 06:13:52 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
data, err := os.ReadFile(userPath)
|
2026-04-15 06:13:52 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
if os.IsNotExist(err) {
|
2026-04-26 06:44:47 +08:00
|
|
|
|
createUserConfig(userPath)
|
2026-04-15 06:13:52 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
return nil
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
var userCfg Config
|
|
|
|
|
|
if err := yaml.Unmarshal(data, &userCfg); err != nil {
|
|
|
|
|
|
return nil
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
return &userCfg
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// createUserConfig 创建默认的用户配置文件
|
|
|
|
|
|
func createUserConfig(userPath string) error {
|
|
|
|
|
|
dir := filepath.Dir(userPath)
|
|
|
|
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
|
|
|
|
return err
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
|
|
|
|
|
|
defaultContent := `# hxclaw 用户配置文件
|
|
|
|
|
|
# 此文件位于 ~/.config/hxclaw/config.yml
|
|
|
|
|
|
# 用户配置优先于项目配置
|
|
|
|
|
|
|
|
|
|
|
|
# Markdown 渲染配置
|
|
|
|
|
|
markdown:
|
2026-04-27 06:16:19 +08:00
|
|
|
|
theme: dark
|
|
|
|
|
|
line_width: 0
|
2026-04-26 06:44:47 +08:00
|
|
|
|
|
|
|
|
|
|
# UI 配置
|
|
|
|
|
|
ui:
|
|
|
|
|
|
logo: "🦐"
|
|
|
|
|
|
user_icon: "👀 "
|
|
|
|
|
|
|
|
|
|
|
|
# TTS 语音配置
|
|
|
|
|
|
tts:
|
2026-04-27 06:16:19 +08:00
|
|
|
|
enabled: false
|
|
|
|
|
|
auto: true
|
|
|
|
|
|
|
|
|
|
|
|
# 聊天记忆体配置
|
|
|
|
|
|
memory:
|
|
|
|
|
|
enabled: true
|
|
|
|
|
|
auto_session: true
|
|
|
|
|
|
auto_export: true
|
2026-04-27 08:58:08 +08:00
|
|
|
|
summary_timeout: 30
|
2026-04-27 06:16:19 +08:00
|
|
|
|
vector:
|
|
|
|
|
|
api_key: ""
|
|
|
|
|
|
base_url: "https://api.siliconflow.cn/v1"
|
|
|
|
|
|
model: "BAAI/bge-m3"
|
|
|
|
|
|
dimension: 1024
|
|
|
|
|
|
max_search_results: 10
|
|
|
|
|
|
recall:
|
|
|
|
|
|
keywords: ["之前", "聊过", "记得", "找找", "曾经", "谈论过", "提过"]
|
|
|
|
|
|
auto_recall: true
|
|
|
|
|
|
similarity_threshold: 0.7
|
|
|
|
|
|
max_results: 5
|
2026-04-26 06:44:47 +08:00
|
|
|
|
`
|
|
|
|
|
|
return os.WriteFile(userPath, []byte(defaultContent), 0644)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// loadProjectConfig 加载项目级配置文件
|
|
|
|
|
|
// 路径优先级:环境变量 HXCLAW_CONFIG 指定路径 > ./project.config.yml
|
|
|
|
|
|
func loadProjectConfig() *Config {
|
2026-04-27 06:16:19 +08:00
|
|
|
|
cfgPath := GetProjectConfigPath()
|
2026-04-26 06:44:47 +08:00
|
|
|
|
if cfgPath == "" {
|
|
|
|
|
|
return &defaultCfg
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
|
|
|
|
|
|
data, err := os.ReadFile(cfgPath)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
|
return &defaultCfg
|
|
|
|
|
|
}
|
|
|
|
|
|
return &defaultCfg
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
|
|
|
|
|
|
var projCfg Config
|
|
|
|
|
|
if err := yaml.Unmarshal(data, &projCfg); err != nil {
|
|
|
|
|
|
return &defaultCfg
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
|
|
|
|
|
|
return &projCfg
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mergeConfig 合并用户配置和项目配置
|
|
|
|
|
|
// 合并规则:用户配置优先于项目配置,项目配置优先于默认配置
|
|
|
|
|
|
func mergeConfig(userCfg, projCfg *Config) *Config {
|
|
|
|
|
|
result := defaultCfg
|
|
|
|
|
|
|
|
|
|
|
|
// 先应用项目配置
|
|
|
|
|
|
if projCfg != nil {
|
|
|
|
|
|
if projCfg.Streaming.LineDelayMs > 0 {
|
|
|
|
|
|
result.Streaming.LineDelayMs = projCfg.Streaming.LineDelayMs
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Streaming.LastLineDelayMs > 0 {
|
|
|
|
|
|
result.Streaming.LastLineDelayMs = projCfg.Streaming.LastLineDelayMs
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Markdown.Theme != "" {
|
|
|
|
|
|
result.Markdown.Theme = projCfg.Markdown.Theme
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Markdown.LineWidth != 0 {
|
|
|
|
|
|
result.Markdown.LineWidth = projCfg.Markdown.LineWidth
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.UI.Logo != "" {
|
|
|
|
|
|
result.UI.Logo = projCfg.UI.Logo
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.UI.UserIcon != "" {
|
|
|
|
|
|
result.UI.UserIcon = projCfg.UI.UserIcon
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.TTS.Port > 0 {
|
|
|
|
|
|
result.TTS.Port = projCfg.TTS.Port
|
|
|
|
|
|
}
|
2026-04-27 06:16:19 +08:00
|
|
|
|
// Memory 配置
|
|
|
|
|
|
if projCfg.Memory.Enabled {
|
|
|
|
|
|
result.Memory.Enabled = projCfg.Memory.Enabled
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.DBPath != "" {
|
|
|
|
|
|
result.Memory.DBPath = projCfg.Memory.DBPath
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.AutoSession {
|
|
|
|
|
|
result.Memory.AutoSession = projCfg.Memory.AutoSession
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.AutoExport {
|
|
|
|
|
|
result.Memory.AutoExport = projCfg.Memory.AutoExport
|
|
|
|
|
|
}
|
2026-04-27 08:58:08 +08:00
|
|
|
|
if projCfg.Memory.SummaryTimeout > 0 {
|
|
|
|
|
|
result.Memory.SummaryTimeout = projCfg.Memory.SummaryTimeout
|
|
|
|
|
|
}
|
2026-04-27 06:16:19 +08:00
|
|
|
|
if projCfg.Memory.Vector.APIKey != "" {
|
|
|
|
|
|
result.Memory.Vector.APIKey = projCfg.Memory.Vector.APIKey
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Vector.BaseURL != "" {
|
|
|
|
|
|
result.Memory.Vector.BaseURL = projCfg.Memory.Vector.BaseURL
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Vector.Model != "" {
|
|
|
|
|
|
result.Memory.Vector.Model = projCfg.Memory.Vector.Model
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Vector.Dimension > 0 {
|
|
|
|
|
|
result.Memory.Vector.Dimension = projCfg.Memory.Vector.Dimension
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Vector.MaxSearchResults > 0 {
|
|
|
|
|
|
result.Memory.Vector.MaxSearchResults = projCfg.Memory.Vector.MaxSearchResults
|
|
|
|
|
|
}
|
|
|
|
|
|
// Recall 配置
|
|
|
|
|
|
if len(projCfg.Memory.Recall.Keywords) > 0 {
|
|
|
|
|
|
result.Memory.Recall.Keywords = projCfg.Memory.Recall.Keywords
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Recall.AutoRecall {
|
|
|
|
|
|
result.Memory.Recall.AutoRecall = projCfg.Memory.Recall.AutoRecall
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Recall.SimilarityThreshold > 0 {
|
|
|
|
|
|
result.Memory.Recall.SimilarityThreshold = projCfg.Memory.Recall.SimilarityThreshold
|
|
|
|
|
|
}
|
|
|
|
|
|
if projCfg.Memory.Recall.MaxResults > 0 {
|
|
|
|
|
|
result.Memory.Recall.MaxResults = projCfg.Memory.Recall.MaxResults
|
|
|
|
|
|
}
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 再应用用户配置(覆盖项目配置)
|
|
|
|
|
|
if userCfg != nil {
|
|
|
|
|
|
// Markdown
|
|
|
|
|
|
if userCfg.Markdown.Theme != "" {
|
|
|
|
|
|
result.Markdown.Theme = userCfg.Markdown.Theme
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Markdown.LineWidth != 0 {
|
|
|
|
|
|
result.Markdown.LineWidth = userCfg.Markdown.LineWidth
|
|
|
|
|
|
}
|
|
|
|
|
|
// UI
|
|
|
|
|
|
if userCfg.UI.Logo != "" {
|
|
|
|
|
|
result.UI.Logo = userCfg.UI.Logo
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.UI.UserIcon != "" {
|
|
|
|
|
|
result.UI.UserIcon = userCfg.UI.UserIcon
|
|
|
|
|
|
}
|
|
|
|
|
|
// TTS(用户配置可以覆盖 enabled 和 auto)
|
|
|
|
|
|
if userCfg.TTS.Enabled {
|
|
|
|
|
|
result.TTS.Enabled = userCfg.TTS.Enabled
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.TTS.Port > 0 {
|
|
|
|
|
|
result.TTS.Port = userCfg.TTS.Port
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.TTS.Auto {
|
|
|
|
|
|
result.TTS.Auto = userCfg.TTS.Auto
|
|
|
|
|
|
}
|
2026-04-27 06:16:19 +08:00
|
|
|
|
// Memory 配置(用户配置优先)
|
|
|
|
|
|
if userCfg.Memory.Enabled {
|
|
|
|
|
|
result.Memory.Enabled = userCfg.Memory.Enabled
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.DBPath != "" {
|
|
|
|
|
|
result.Memory.DBPath = userCfg.Memory.DBPath
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.AutoSession {
|
|
|
|
|
|
result.Memory.AutoSession = userCfg.Memory.AutoSession
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.AutoExport {
|
|
|
|
|
|
result.Memory.AutoExport = userCfg.Memory.AutoExport
|
|
|
|
|
|
}
|
|
|
|
|
|
// 向量 API Key 只能在用户配置中指定
|
|
|
|
|
|
if userCfg.Memory.Vector.APIKey != "" {
|
|
|
|
|
|
result.Memory.Vector.APIKey = userCfg.Memory.Vector.APIKey
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Vector.BaseURL != "" {
|
|
|
|
|
|
result.Memory.Vector.BaseURL = userCfg.Memory.Vector.BaseURL
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Vector.Model != "" {
|
|
|
|
|
|
result.Memory.Vector.Model = userCfg.Memory.Vector.Model
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Vector.Dimension > 0 {
|
|
|
|
|
|
result.Memory.Vector.Dimension = userCfg.Memory.Vector.Dimension
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Vector.MaxSearchResults > 0 {
|
|
|
|
|
|
result.Memory.Vector.MaxSearchResults = userCfg.Memory.Vector.MaxSearchResults
|
|
|
|
|
|
}
|
|
|
|
|
|
// Recall 配置
|
|
|
|
|
|
if len(userCfg.Memory.Recall.Keywords) > 0 {
|
|
|
|
|
|
result.Memory.Recall.Keywords = userCfg.Memory.Recall.Keywords
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Recall.AutoRecall {
|
|
|
|
|
|
result.Memory.Recall.AutoRecall = userCfg.Memory.Recall.AutoRecall
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Recall.SimilarityThreshold > 0 {
|
|
|
|
|
|
result.Memory.Recall.SimilarityThreshold = userCfg.Memory.Recall.SimilarityThreshold
|
|
|
|
|
|
}
|
|
|
|
|
|
if userCfg.Memory.Recall.MaxResults > 0 {
|
|
|
|
|
|
result.Memory.Recall.MaxResults = userCfg.Memory.Recall.MaxResults
|
|
|
|
|
|
}
|
2026-04-26 03:01:28 +08:00
|
|
|
|
}
|
2026-04-15 06:13:52 +08:00
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
return &result
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// GetProjectConfig 获取当前生效的配置(线程安全)
|
|
|
|
|
|
// 如果配置未加载,返回默认配置
|
|
|
|
|
|
func GetProjectConfig() *Config {
|
|
|
|
|
|
cfgLock.RLock()
|
|
|
|
|
|
defer cfgLock.RUnlock()
|
|
|
|
|
|
if cfg == nil {
|
2026-04-15 06:13:52 +08:00
|
|
|
|
return &defaultCfg
|
|
|
|
|
|
}
|
2026-04-26 06:44:47 +08:00
|
|
|
|
return cfg
|
2026-04-15 06:13:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-26 06:44:47 +08:00
|
|
|
|
// getUserConfigPath 获取用户配置文件路径
|
|
|
|
|
|
// 路径格式:~/.config/hxclaw/config.yml
|
|
|
|
|
|
func getUserConfigPath() string {
|
2026-04-27 06:16:19 +08:00
|
|
|
|
return GetConfigFile()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetUserConfigDir 获取用户配置目录
|
|
|
|
|
|
func GetUserConfigDir() string {
|
|
|
|
|
|
return GetConfigDir()
|
2026-04-26 06:44:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 06:16:19 +08:00
|
|
|
|
func GetProjectConfigPath() string {
|
2026-04-15 06:13:52 +08:00
|
|
|
|
if path := os.Getenv("HXCLAW_CONFIG"); path != "" {
|
|
|
|
|
|
return path
|
|
|
|
|
|
}
|
|
|
|
|
|
return filepath.Join(".", "project.config.yml")
|
2026-04-26 06:44:47 +08:00
|
|
|
|
}
|