Skip to content

Commit 6656827

Browse files
feat(ragindex): expose DB tree builder
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 1e24175 commit 6656827

1 file changed

Lines changed: 43 additions & 17 deletions

File tree

internal/ragindex/builder.go

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -120,21 +120,23 @@ type nodeInfo struct {
120120
Kind model.NodeKind
121121
}
122122

123-
// Build는 DB에서 커뮤니티와 멤버 노드를 읽어 doc-index.json을 생성한다.
124-
// 반환값: (커뮤니티 수, 파일 수, 에러)
125-
// @intent 커뮤니티 구조와 문서 요약을 트리 형태 인덱스로 합성한다.
126-
// @sideEffect 데이터베이스를 읽고 doc-index.json 파일을 기록한다.
127-
// @ensures 성공 시 반환된 파일 수는 인덱스에 포함된 고유 파일 수와 같다.
128-
func (b *Builder) Build(ctx context.Context) (int, int, error) {
129-
slog.Debug("ragindex.Builder.Build 시작", "outDir", b.OutDir, "indexDir", b.IndexDir)
123+
// BuildTree는 DB에서 커뮤니티와 멤버 노드를 읽어 파일 쓰기 없이 RAG 탐색 트리를 합성한다.
124+
// 반환값: (루트 노드, 커뮤니티 수, 파일 수, 에러)
125+
// @intent 커뮤니티 구조와 문서 요약을 TreeNode JSON shape으로 합성해 파일 인덱스와 DB fallback이 같은 트리 의미를 공유한다.
126+
// @ensures 성공 시 반환된 파일 수는 트리에 포함된 고유 파일 수와 같다.
127+
func (b *Builder) BuildTree(ctx context.Context) (*TreeNode, int, int, error) {
128+
slog.Debug("ragindex.Builder.BuildTree 시작", "outDir", b.OutDir, "indexDir", b.IndexDir)
129+
if b.DB == nil {
130+
return nil, 0, 0, trace.New("DB not configured")
131+
}
130132

131133
ns := ctxns.FromContext(ctx)
132134

133135
// 1. 모든 커뮤니티와 멤버 로드
134136
var communities []model.Community
135-
q := b.DB.WithContext(ctx).Preload("Members").Where("namespace = ?", ns)
137+
q := b.DB.WithContext(ctx).Preload("Members").Where("namespace = ?", ns).Order("key ASC, id ASC")
136138
if err := q.Find(&communities).Error; err != nil {
137-
return 0, 0, trace.Wrap(err, "load communities")
139+
return nil, 0, 0, trace.Wrap(err, "load communities")
138140
}
139141
slog.Debug("커뮤니티 로드 완료", "count", len(communities))
140142

@@ -152,7 +154,7 @@ func (b *Builder) Build(ctx context.Context) (int, int, error) {
152154
var nodes []model.Node
153155
nq := b.DB.WithContext(ctx).Where("id IN ?", allNodeIDs).Where("namespace = ?", ns)
154156
if err := nq.Find(&nodes).Error; err != nil {
155-
return 0, 0, trace.Wrap(err, "load all nodes")
157+
return nil, 0, 0, trace.Wrap(err, "load all nodes")
156158
}
157159
for _, n := range nodes {
158160
nodeInfoMap[n.ID] = nodeInfo{
@@ -176,21 +178,22 @@ func (b *Builder) Build(ctx context.Context) (int, int, error) {
176178
for fp := range filePathSet {
177179
allFilePaths = append(allFilePaths, fp)
178180
}
181+
sort.Strings(allFilePaths)
179182

180183
// 3. 배치 fileSummary 조회
181184
summaries, err := b.batchFileSummaries(ctx, allFilePaths)
182185
if err != nil {
183-
return 0, 0, trace.Wrap(err, "batchFileSummaries")
186+
return nil, 0, 0, trace.Wrap(err, "batchFileSummaries")
184187
}
185188
fileSearchTexts, fileFieldTexts, err := b.batchFileSearchTexts(ctx, allFilePaths)
186189
if err != nil {
187-
return 0, 0, trace.Wrap(err, "batchFileSearchTexts")
190+
return nil, 0, 0, trace.Wrap(err, "batchFileSearchTexts")
188191
}
189192

190193
// 4. annotation text를 가진 symbol 노드 배치 조회
191194
symbolsByFile, err := b.batchSymbolNodes(ctx, allNodeIDs)
192195
if err != nil {
193-
return 0, 0, trace.Wrap(err, "batchSymbolNodes")
196+
return nil, 0, 0, trace.Wrap(err, "batchSymbolNodes")
194197
}
195198

196199
root := &TreeNode{
@@ -229,7 +232,13 @@ func (b *Builder) Build(ctx context.Context) (int, int, error) {
229232
slog.Debug("파일 경로 그룹 완료", "community", comm.Key, "files", len(commFilePathSet))
230233

231234
// 각 파일 경로별 TreeNode 생성
235+
commFilePaths := make([]string, 0, len(commFilePathSet))
232236
for filePath := range commFilePathSet {
237+
commFilePaths = append(commFilePaths, filePath)
238+
}
239+
sort.Strings(commFilePaths)
240+
241+
for _, filePath := range commFilePaths {
233242
summary := summaries[filePath]
234243
fileNode := &TreeNode{
235244
ID: fmt.Sprintf("file:%s", filePath),
@@ -249,20 +258,37 @@ func (b *Builder) Build(ctx context.Context) (int, int, error) {
249258
root.Children = append(root.Children, commNode)
250259
}
251260

252-
// 6. Index 구조체 구성
261+
slog.Debug("ragindex.Builder.BuildTree 완료", "communities", len(communities), "files", len(uniqueFiles))
262+
return root, len(communities), len(uniqueFiles), nil
263+
}
264+
265+
// Build는 DB에서 커뮤니티와 멤버 노드를 읽어 doc-index.json을 생성한다.
266+
// 반환값: (커뮤니티 수, 파일 수, 에러)
267+
// @intent 커뮤니티 구조와 문서 요약을 트리 형태 인덱스로 합성한다.
268+
// @sideEffect 데이터베이스를 읽고 doc-index.json 파일을 기록한다.
269+
// @ensures 성공 시 반환된 파일 수는 인덱스에 포함된 고유 파일 수와 같다.
270+
func (b *Builder) Build(ctx context.Context) (int, int, error) {
271+
slog.Debug("ragindex.Builder.Build 시작", "outDir", b.OutDir, "indexDir", b.IndexDir)
272+
273+
root, communities, files, err := b.BuildTree(ctx)
274+
if err != nil {
275+
return 0, 0, err
276+
}
277+
278+
// Index 구조체 구성
253279
idx := &Index{
254280
Version: 1,
255281
BuiltAt: time.Now().UTC(),
256282
Root: root,
257283
}
258284

259-
// 7. doc-index.json 파일 기록 (원자적 쓰기)
285+
// doc-index.json 파일 기록 (원자적 쓰기)
260286
if err := b.writeIndex(idx); err != nil {
261287
return 0, 0, trace.Wrap(err, "writeIndex")
262288
}
263289

264-
slog.Debug("ragindex.Builder.Build 완료", "communities", len(communities), "files", len(uniqueFiles))
265-
return len(communities), len(uniqueFiles), nil
290+
slog.Debug("ragindex.Builder.Build 완료", "communities", communities, "files", files)
291+
return communities, files, nil
266292
}
267293

268294
// batchFileSummaries는 주어진 파일 경로 목록에 대해 filePath → summary 맵을 한 번에 반환한다.

0 commit comments

Comments
 (0)