更新代码
This commit is contained in:
1 parent
eda7e8bc96
commit
6ff632e0b0
19 files changed
+2033
-197
No files matched your search
+38
-13
@@ -41,13 +41,14 @@ func NewProfile(name, format string) Profile {
|
||||
|
||||
// Capture 是抓屏与触发策略参数。
|
||||
type Capture struct {
|
||||
FPS float64 `json:"fps"` // 监听帧率,1~5
|
||||
MaxEdge int `json:"maxEdge"` // 发给模型的图片长边上限
|
||||
JPEGQuality int `json:"jpegQuality"` // 1~100
|
||||
DiffThreshold int `json:"diffThreshold"` // 感知哈希汉明距离阈值
|
||||
CooldownMs int `json:"cooldownMs"` // 两次自动提问的最小间隔
|
||||
StableFrames int `json:"stableFrames"` // 连续稳定帧数(去抖)
|
||||
HistoryTurns int `json:"historyTurns"` // 带上的历史文本轮数
|
||||
FPS float64 `json:"fps"` // 监听帧率,1~5
|
||||
MaxEdge int `json:"maxEdge"` // 发给模型的图片长边上限
|
||||
JPEGQuality int `json:"jpegQuality"` // 1~100
|
||||
DiffThreshold int `json:"diffThreshold"` // 感知哈希汉明距离阈值
|
||||
DiffSensitivity float64 `json:"diffSensitivity"` // 细节网格归一化平均差阈值(越小越灵敏)
|
||||
CooldownMs int `json:"cooldownMs"` // 两次自动提问的最小间隔
|
||||
StableFrames int `json:"stableFrames"` // 连续稳定帧数(去抖)
|
||||
HistoryTurns int `json:"historyTurns"` // 带上的历史文本轮数
|
||||
}
|
||||
|
||||
// Hotkeys 保存全局热键描述,语法如 "Ctrl+Alt+Q"。
|
||||
@@ -140,7 +141,7 @@ func Default() *Config {
|
||||
return &Config{
|
||||
Capture: Capture{
|
||||
FPS: 2, MaxEdge: 1280, JPEGQuality: 75,
|
||||
DiffThreshold: 5, CooldownMs: 3000, StableFrames: 2, HistoryTurns: 6,
|
||||
DiffThreshold: 5, DiffSensitivity: 0.01, CooldownMs: 3000, StableFrames: 2, HistoryTurns: 6,
|
||||
},
|
||||
Hotkeys: Hotkeys{
|
||||
SelectRegion: "Ctrl+Alt+X",
|
||||
@@ -246,11 +247,24 @@ func (c *Config) Template(key string) Template {
|
||||
return Template{Key: "chat", Name: "自由对话", System: "你是桌面视觉助手。"}
|
||||
}
|
||||
|
||||
// ensureModel 在没有任何接入点时补一个空白项。
|
||||
//
|
||||
// 只用于首次运行(还没有配置文件)时给用户一个可以填的入口;
|
||||
// 用户主动删光之后不该被自动塞回来。
|
||||
func (c *Config) ensureModel() {
|
||||
if len(c.Models) > 0 {
|
||||
return
|
||||
}
|
||||
c.Models = []Profile{NewProfile("接入点 1", llm.FormatOpenAI)}
|
||||
c.Active = c.Models[0].Name
|
||||
}
|
||||
|
||||
// Clone 返回一份深拷贝,避免设置窗口与引擎共享同一份可变配置。
|
||||
func (c *Config) Clone() *Config {
|
||||
if c == nil {
|
||||
cfg := Default()
|
||||
cfg.Normalize()
|
||||
cfg.ensureModel()
|
||||
return cfg
|
||||
}
|
||||
out := *c
|
||||
@@ -270,10 +284,10 @@ func (c *Config) Clone() *Config {
|
||||
}
|
||||
|
||||
// Normalize 补全缺失字段,保证运行期不会出现零值。
|
||||
//
|
||||
// 注意这里**不再**自动补接入点:用户主动删光之后不该又被塞回来,
|
||||
// 否则"删除"看起来像没生效。首次运行的空白接入点由 ensureModel 负责。
|
||||
func (c *Config) Normalize() {
|
||||
if len(c.Models) == 0 {
|
||||
c.Models = []Profile{NewProfile("接入点 1", llm.FormatOpenAI)}
|
||||
}
|
||||
for i := range c.Models {
|
||||
m := &c.Models[i]
|
||||
m.Name = strings.TrimSpace(m.Name)
|
||||
@@ -303,8 +317,13 @@ func (c *Config) Normalize() {
|
||||
c.Models[i].Name = name
|
||||
used[name] = true
|
||||
}
|
||||
if _, ok := c.ActiveModel(); !ok {
|
||||
c.Active = c.Models[0].Name
|
||||
// 一个接入点都没有时,Active 留空即可。
|
||||
if len(c.Models) > 0 {
|
||||
if _, ok := c.ActiveModel(); !ok {
|
||||
c.Active = c.Models[0].Name
|
||||
}
|
||||
} else {
|
||||
c.Active = ""
|
||||
}
|
||||
|
||||
if len(c.Templates) == 0 {
|
||||
@@ -322,6 +341,9 @@ func (c *Config) Normalize() {
|
||||
if c.Capture.DiffThreshold <= 0 {
|
||||
c.Capture.DiffThreshold = 5
|
||||
}
|
||||
if c.Capture.DiffSensitivity <= 0 {
|
||||
c.Capture.DiffSensitivity = 0.01
|
||||
}
|
||||
if c.Capture.CooldownMs <= 0 {
|
||||
c.Capture.CooldownMs = 3000
|
||||
}
|
||||
@@ -377,6 +399,8 @@ func (s *Store) Load() *Config {
|
||||
if err != nil {
|
||||
cfg := Default()
|
||||
cfg.Normalize()
|
||||
// 首次运行:给一个空白接入点作为填写入口。
|
||||
cfg.ensureModel()
|
||||
return cfg
|
||||
}
|
||||
cfg := Default()
|
||||
@@ -384,6 +408,7 @@ func (s *Store) Load() *Config {
|
||||
_ = os.WriteFile(s.path+".broken", raw, 0o644)
|
||||
fresh := Default()
|
||||
fresh.Normalize()
|
||||
fresh.ensureModel()
|
||||
return fresh
|
||||
}
|
||||
var legacy legacyConfig
|
||||
|
||||
@@ -12,9 +12,10 @@ import (
|
||||
func TestDefaultIsUsable(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.Normalize()
|
||||
cfg.ensureModel()
|
||||
|
||||
if len(cfg.Models) == 0 {
|
||||
t.Fatal("Normalize 后应至少有一份接入点")
|
||||
t.Fatal("首次运行应有一份空白接入点")
|
||||
}
|
||||
if _, ok := cfg.ActiveModel(); !ok {
|
||||
t.Fatal("Normalize 后应有生效接入点")
|
||||
@@ -33,9 +34,10 @@ func TestDefaultIsUsable(t *testing.T) {
|
||||
func TestNormalizeFillsZeroValues(t *testing.T) {
|
||||
cfg := &Config{}
|
||||
cfg.Normalize()
|
||||
cfg.ensureModel()
|
||||
|
||||
if len(cfg.Models) == 0 {
|
||||
t.Fatal("Normalize 应补全接入点列表")
|
||||
t.Fatal("首次运行应补上一份接入点")
|
||||
}
|
||||
if cfg.Models[0].Format != llm.FormatOpenAI {
|
||||
t.Fatalf("默认接入点应为 OpenAI 兼容格式,实际 %s", cfg.Models[0].Format)
|
||||
@@ -105,6 +107,7 @@ func TestNormalizeTheme(t *testing.T) {
|
||||
func TestCloneIsIndependent(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.Normalize()
|
||||
cfg.ensureModel()
|
||||
|
||||
cp := cfg.Clone()
|
||||
cp.Models[0].Model = "changed"
|
||||
@@ -173,6 +176,53 @@ func TestRemoveModelClearsActive(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveModelStaysRemoved(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.Models = []Profile{
|
||||
{Name: "a", Model: "m-a"},
|
||||
{Name: "b", Model: "m-b"},
|
||||
{Name: "c", Model: "m-c"},
|
||||
}
|
||||
cfg.Active = "b"
|
||||
cfg.Normalize()
|
||||
|
||||
if !cfg.RemoveModel("b") {
|
||||
t.Fatal("应能删除接入点")
|
||||
}
|
||||
// 再归一化一次,模拟落盘后重新加载:被删的接入点不该复活。
|
||||
cfg.Normalize()
|
||||
|
||||
if len(cfg.Models) != 2 {
|
||||
t.Fatalf("删除后应只剩 2 个接入点,实际 %d: %+v", len(cfg.Models), cfg.Models)
|
||||
}
|
||||
for _, m := range cfg.Models {
|
||||
if m.Name == "b" {
|
||||
t.Fatalf("被删的接入点不该被补回来: %+v", cfg.Models)
|
||||
}
|
||||
}
|
||||
active, ok := cfg.ActiveModel()
|
||||
if !ok {
|
||||
t.Fatal("应自动选中一个剩余接入点")
|
||||
}
|
||||
if active.Name != "a" && active.Name != "c" {
|
||||
t.Fatalf("生效项应指向剩余的接入点,实际 %s", active.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeKeepsEmptyModelList(t *testing.T) {
|
||||
// 用户把接入点删光后,Normalize 不该再偷偷补一个回来,
|
||||
// 否则"删除"看起来完全没生效。
|
||||
cfg := Default()
|
||||
cfg.Normalize()
|
||||
|
||||
if len(cfg.Models) != 0 {
|
||||
t.Fatalf("Normalize 不应凭空补接入点: %+v", cfg.Models)
|
||||
}
|
||||
if cfg.Active != "" {
|
||||
t.Fatalf("没有接入点时 Active 应为空,实际 %s", cfg.Active)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateLegacyProfiles(t *testing.T) {
|
||||
cfg := Default()
|
||||
cfg.migrateLegacy(legacyConfig{
|
||||
|
||||
Reference in new issue
Block a user