Skip to content

Commit b029a1c

Browse files
committed
refactor: 完成RPC服务端与客户端的标准化重构
本次提交进行了多项核心重构与功能完善: 1. 移除冗余代码与废弃文件:删除pkg/indexing/service.go多余空行、ISSUES.md和TODO.md文档 2. 标准化RPC结构体:将内部结果体迁移到pkg/rpc包统一管理,包括MemoryCountResult、FSReadResult、Optimize相关结构体等 3. 重构RPC注册逻辑:将handler注册改为使用map集中管理,提升可维护性 4. 完善空指针保护:为kvstore操作、事件发送等场景增加nil检查 5. 增加panic捕获:为多个后台goroutine增加崩溃日志捕获,提升服务稳定性 6. 优化日志格式:将fmt格式化日志改为结构化日志参数形式 7. 新增RPC客户端测试套件:覆盖全部RPC方法的单元测试 8. 重构文件删除逻辑:新增递归删除和强制删除参数,优化fs.rm接口行为 9. 简化模型参数处理:移除内部重复的modelGetParams结构体,复用rpc包中定义的结构
1 parent a1b4de8 commit b029a1c

24 files changed

Lines changed: 1577 additions & 282 deletions

ISSUES.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

TODO.md

Lines changed: 0 additions & 61 deletions
This file was deleted.

internal/core/app.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func DefaultApp(mindxConfig *MindxConfig) (*App, error) {
9494
var err error
9595
err = godotenv.Load()
9696
if err != nil {
97-
logger.Warn("WARNING: failed to load .env file: %v", err)
97+
logger.Warn("WARNING: failed to load .env file", "error", err)
9898
}
9999

100100
constants.SYSTEM_INFO_NAME = "MindX"
@@ -167,7 +167,7 @@ func DefaultApp(mindxConfig *MindxConfig) (*App, error) {
167167
var embErr error
168168
emb, embErr = memory.NewEmbedderFromConfig(modelPath)
169169
if embErr != nil {
170-
logger.Warn("Failed to create embedder, memory disabled: %v", embErr)
170+
logger.Warn("Failed to create embedder, memory disabled", "error", embErr)
171171
}
172172
}
173173

@@ -204,7 +204,7 @@ func resolveCurrentAgentName(cfg *MindxConfig, agents *config.AgentRegistry, log
204204
if agents.Get(cfg.LastAgent) != nil {
205205
return cfg.LastAgent
206206
}
207-
logger.Warn("last_agent %q not found in registry, will use fallback", cfg.LastAgent)
207+
logger.Warn("last_agent not found in registry, will use fallback", "agent", cfg.LastAgent)
208208
}
209209

210210
if list := agents.List(); len(list) > 0 {
@@ -562,7 +562,7 @@ func (a *App) createRuntime(agentName string) (*agents.Runtime, error) {
562562
Logger: a.logger,
563563
})
564564
if ltErr != nil {
565-
a.logger.Warn("Failed to create long-term memory for agent %q: %v", agent.Name, ltErr)
565+
a.logger.Warn("Failed to create long-term memory", "agent", agent.Name, "error", ltErr)
566566
} else {
567567
opts = append(opts, agents.WithMemory(ltMem))
568568
a.logger.Info("createRuntime: long-term memory OK", "agent", agentName)

internal/svc/daemon.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -675,23 +675,27 @@ func (d *Daemon) autoUpdateLoop(ctx context.Context) {
675675
"latest", info.LatestVersion,
676676
)
677677
// 通知客户端更新即将开始
678-
d.gw.BroadcastNotification("update_started", map[string]interface{}{
679-
"type": "update_started",
680-
"data": map[string]string{
681-
"version": info.LatestVersion,
682-
},
683-
})
678+
if d.gw != nil {
679+
d.gw.BroadcastNotification("update_started", map[string]interface{}{
680+
"type": "update_started",
681+
"data": map[string]string{
682+
"version": info.LatestVersion,
683+
},
684+
})
685+
}
684686
// 自动下载并安装新二进制(但不要重启,只记录日志通知用户)
685687
if err := d.updater.DownloadAndInstall(ctx); err != nil {
686688
d.logger.Warn("auto-update: download and install failed", "error", err)
687689
} else {
688690
d.logger.Info("auto-update: update installed. User should restart the daemon.")
689-
d.gw.BroadcastNotification("update_installed", map[string]interface{}{
690-
"type": "update_installed",
691-
"data": map[string]string{
692-
"version": info.LatestVersion,
693-
},
694-
})
691+
if d.gw != nil {
692+
d.gw.BroadcastNotification("update_installed", map[string]interface{}{
693+
"type": "update_installed",
694+
"data": map[string]string{
695+
"version": info.LatestVersion,
696+
},
697+
})
698+
}
695699
}
696700
} else {
697701
d.logger.Info("auto-update: already up-to-date", "version", info.CurrentVersion)
@@ -985,6 +989,11 @@ func (d *Daemon) Start(ctx context.Context) error {
985989
// ── Hot-reload: watch agents/skills directories for file changes ──
986990
d.hotReload = NewHotReloadWatcher(d.app, d.logger)
987991
go func() {
992+
defer func() {
993+
if r := recover(); r != nil && d.logger != nil {
994+
d.logger.Error("hot-reload watcher: goroutine panic", fmt.Errorf("%v", r))
995+
}
996+
}()
988997
if err := d.hotReload.Start(ctx); err != nil && d.logger != nil {
989998
d.logger.Warn("hot-reload watcher exited with error", "error", err)
990999
}
@@ -999,6 +1008,11 @@ func (d *Daemon) Start(ctx context.Context) error {
9991008
ctx, cancel := context.WithCancel(context.Background())
10001009
d.watchCancel = cancel
10011010
go func() {
1011+
defer func() {
1012+
if r := recover(); r != nil {
1013+
d.logger.Error("auto-restore filewatch: goroutine panic", fmt.Errorf("%v", r))
1014+
}
1015+
}()
10021016
if err := d.kbWatch.Start(ctx); err != nil {
10031017
d.logger.Warn("auto-restore: filewatch exited with error", "error", err)
10041018
}

internal/svc/event_dispatcher.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import (
1010
)
1111

1212
func (d *Daemon) sendEvent(clientID, sessionID string, respType gateway.ResponseType, title string, data string, opts ...gateway.ResponseOption) {
13+
if d.gw == nil {
14+
return
15+
}
1316
allOpts := append([]gateway.ResponseOption{gateway.WithSessionID(sessionID)}, opts...)
1417
_ = d.gw.SendResponse(clientID, respType, title, data, allOpts...)
1518
}
@@ -28,6 +31,9 @@ func (d *Daemon) broadcastScheduleEvent(sessionID, agent, eventType string, data
2831
}
2932

3033
func (d *Daemon) sendExecutionSummary(clientID, sessionID string, summary goharnessevents.ExecutionSummaryData, agentName string) {
34+
if d.gw == nil {
35+
return
36+
}
3137
d.logger.Info("[SSE-TRACE L5] sendExecutionSummary: total_tokens=" + fmt.Sprint(summary.TokensUsed.TotalTokens) +
3238
" input=" + fmt.Sprint(summary.TokensUsed.InputTokens) +
3339
" output=" + fmt.Sprint(summary.TokensUsed.OutputTokens))

internal/svc/handler_fs.go

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,6 @@ func (d *Daemon) handleFSHome(_ context.Context, _ json.RawMessage) (any, error)
9898
return map[string]string{"path": defaultFSHome()}, nil
9999
}
100100

101-
type fsReadResult struct {
102-
Content string `json:"content"`
103-
}
104-
105101
func (d *Daemon) handleFSRead(_ context.Context, params json.RawMessage) (any, error) {
106102
var p rpc.FSReadParams
107103
if err := unmarshalParams(params, &p); err != nil {
@@ -126,7 +122,7 @@ func (d *Daemon) handleFSRead(_ context.Context, params json.RawMessage) (any, e
126122
if err != nil {
127123
return nil, fmt.Errorf("cannot read file: %w", err)
128124
}
129-
return fsReadResult{Content: string(data)}, nil
125+
return rpc.FSReadResult{Content: string(data)}, nil
130126
}
131127

132128
func (d *Daemon) handleFSWrite(_ context.Context, params json.RawMessage) (any, error) {
@@ -187,16 +183,28 @@ func (d *Daemon) handleFSRm(_ context.Context, params json.RawMessage) (any, err
187183
return nil, fmt.Errorf("cannot access path: %w", err)
188184
}
189185
if info.IsDir() {
190-
// Only remove empty directories to avoid accidental data loss
191-
entries, err := os.ReadDir(absPath)
192-
if err != nil {
193-
return nil, fmt.Errorf("cannot read directory: %w", err)
194-
}
195-
if len(entries) > 0 {
196-
return nil, fmt.Errorf("directory not empty: %s", p.Path)
197-
}
198-
if err := os.Remove(absPath); err != nil {
199-
return nil, fmt.Errorf("cannot remove directory: %w", err)
186+
if p.Recurse {
187+
if err := os.RemoveAll(absPath); err != nil {
188+
return nil, fmt.Errorf("cannot remove directory tree: %w", err)
189+
}
190+
} else {
191+
entries, err := os.ReadDir(absPath)
192+
if err != nil {
193+
return nil, fmt.Errorf("cannot read directory: %w", err)
194+
}
195+
if len(entries) > 0 {
196+
if p.Force {
197+
if err := os.RemoveAll(absPath); err != nil {
198+
return nil, fmt.Errorf("cannot force remove directory: %w", err)
199+
}
200+
} else {
201+
return nil, fmt.Errorf("directory not empty: %s", p.Path)
202+
}
203+
} else {
204+
if err := os.Remove(absPath); err != nil {
205+
return nil, fmt.Errorf("cannot remove directory: %w", err)
206+
}
207+
}
200208
}
201209
} else {
202210
if err := os.Remove(absPath); err != nil {
@@ -234,10 +242,6 @@ func (d *Daemon) handleFSMv(_ context.Context, params json.RawMessage) (any, err
234242

235243
// ── 新增:reveal ──
236244

237-
type fsRevealResult struct {
238-
Status string `json:"status"`
239-
}
240-
241245
// handleFSReveal opens the file's parent directory in the native file manager,
242246
// and on macOS also highlights/selects the file.
243247
func (d *Daemon) handleFSReveal(_ context.Context, params json.RawMessage) (any, error) {
@@ -276,7 +280,7 @@ func (d *Daemon) handleFSReveal(_ context.Context, params json.RawMessage) (any,
276280
}
277281
}
278282

279-
return fsRevealResult{Status: "ok"}, nil
283+
return rpc.FSRevealResult{Status: "ok"}, nil
280284
}
281285

282286
// ── HTTP download ──

internal/svc/handler_kb.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,6 @@ func (d *Daemon) handleKBSyncProject(_ context.Context, params json.RawMessage)
125125
// kb.stats — 获取知识库文件索引进度统计
126126
// ---------------------------------------------------------------------------
127127

128-
type kbStatsResult struct {
129-
TotalFiles int `json:"total_files"`
130-
IndexedFiles int `json:"indexed_files"`
131-
TotalChunks int `json:"total_chunks"`
132-
}
133-
134128
func (d *Daemon) handleKBStats(_ context.Context, params json.RawMessage) (any, error) {
135129
var p struct {
136130
ProjectDir string `json:"project_dir"`
@@ -170,7 +164,7 @@ func (d *Daemon) handleKBStats(_ context.Context, params json.RawMessage) (any,
170164
"total_chunks", totalChunks,
171165
)
172166

173-
return kbStatsResult{
167+
return rpc.KBStatsResult{
174168
TotalFiles: totalFiles,
175169
IndexedFiles: indexedFiles,
176170
TotalChunks: totalChunks,

internal/svc/handler_kvstore.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ func (d *Daemon) handleKVDelete(_ context.Context, params json.RawMessage) (any,
139139

140140
// kvDeleteInternal deletes a key from the default bucket.
141141
func (d *Daemon) kvDeleteInternal(key string) error {
142+
if d.kvStore == nil {
143+
return fmt.Errorf("kvstore not available")
144+
}
142145
return d.kvStore.Update(func(tx *bbolt.Tx) error {
143146
b := tx.Bucket([]byte(kvStoreBucket))
144147
if b == nil {

internal/svc/handler_memory.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,9 @@ func (d *Daemon) handleMemoryGetChunks(_ context.Context, params json.RawMessage
237237
d.logger.Info("memory.get_chunks called", "doc_id", p.DocID, "returned", len(items))
238238

239239
return struct {
240-
DocID string `json:"doc_id"`
240+
DocID string `json:"doc_id"`
241241
Chunks []rpc.ChunkItem `json:"chunks"`
242-
Count int `json:"count"`
242+
Count int `json:"count"`
243243
}{
244244
DocID: p.DocID,
245245
Chunks: items,
@@ -251,10 +251,6 @@ func (d *Daemon) handleMemoryGetChunks(_ context.Context, params json.RawMessage
251251
// memory.count — 获取 RAG 索引中的分块总数
252252
// ---------------------------------------------------------------------------
253253

254-
type memoryCountResult struct {
255-
Count int `json:"count"`
256-
}
257-
258254
func (d *Daemon) handleMemoryCount(_ context.Context, _ json.RawMessage) (any, error) {
259255
mem := d.sharedMemory
260256
if mem == nil {
@@ -273,7 +269,7 @@ func (d *Daemon) handleMemoryCount(_ context.Context, _ json.RawMessage) (any, e
273269

274270
d.logger.Info("memory.count called", "count", count)
275271

276-
return memoryCountResult{Count: count}, nil
272+
return rpc.MemoryCountResult{Count: count}, nil
277273
}
278274

279275
// ---------------------------------------------------------------------------
@@ -310,6 +306,11 @@ func (d *Daemon) handleFilewatchStart(_ context.Context, params json.RawMessage)
310306
d.logger.Info("filewatch.start: starting filewatch service")
311307

312308
go func() {
309+
defer func() {
310+
if r := recover(); r != nil {
311+
d.logger.Error("filewatch.start: goroutine panic", fmt.Errorf("%v", r))
312+
}
313+
}()
313314
if err := d.kbWatch.Start(ctx); err != nil {
314315
d.logger.Warn("filewatch.start: service exited with error", "error", err)
315316
}

internal/svc/handler_model.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,8 @@ func (d *Daemon) handleProviderList(_ context.Context, _ json.RawMessage) (any,
4949
return result, nil
5050
}
5151

52-
type modelGetParams struct {
53-
Name string `json:"name"`
54-
}
55-
5652
func (d *Daemon) handleModelGet(_ context.Context, params json.RawMessage) (any, error) {
57-
var p modelGetParams
53+
var p rpc.ModelGetParams
5854
if err := unmarshalParams(params, &p); err != nil {
5955
return nil, err
6056
}

0 commit comments

Comments
 (0)