Skip to content

Commit 3eef3a0

Browse files
authored
Merge pull request #31 from STRRL/tape-composer-2.0
fix(analyze): ACP-only path, tape, dedupe ChatModel callbacks
2 parents de47e64 + ae3d198 commit 3eef3a0

16 files changed

Lines changed: 736 additions & 645 deletions

go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/strrl/lapp
33
go 1.25.7
44

55
require (
6+
github.com/bytedance/sonic v1.15.0
67
github.com/cloudwego/eino v0.8.0
7-
github.com/cloudwego/eino-ext/adk/backend/local v0.1.2-0.20260306073537-008f82264d85
88
github.com/cloudwego/eino-ext/callbacks/langfuse v0.0.0-20260227151421-e109b4ff9563
99
github.com/cloudwego/eino-ext/components/model/openrouter v0.1.2
1010
github.com/duckdb/duckdb-go/v2 v2.5.5
@@ -24,10 +24,8 @@ require (
2424
require (
2525
github.com/apache/arrow-go/v18 v18.5.1 // indirect
2626
github.com/bahlo/generic-list-go v0.2.0 // indirect
27-
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
2827
github.com/buger/jsonparser v1.1.1 // indirect
2928
github.com/bytedance/gopkg v0.1.3 // indirect
30-
github.com/bytedance/sonic v1.15.0 // indirect
3129
github.com/bytedance/sonic/loader v0.5.0 // indirect
3230
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
3331
github.com/cenkalti/backoff/v5 v5.0.3 // indirect

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJe
88
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
99
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
1010
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
11-
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
12-
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
1311
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
1412
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
1513
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
@@ -34,8 +32,6 @@ github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI
3432
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
3533
github.com/cloudwego/eino v0.8.0 h1:DLbrgEAloA+l7aR2qim7qQocQB48DjPrb8LzG3PYMHY=
3634
github.com/cloudwego/eino v0.8.0/go.mod h1:+2N4nsMPxA6kGBHpH+75JuTfEcGprAMTdsZESrShKpU=
37-
github.com/cloudwego/eino-ext/adk/backend/local v0.1.2-0.20260306073537-008f82264d85 h1:mD47o0GKdeqMdGI5xEqnlO8ZtArvhalIorRtrCmLRkA=
38-
github.com/cloudwego/eino-ext/adk/backend/local v0.1.2-0.20260306073537-008f82264d85/go.mod h1:LfFk+VqZk0JOxIyl5RaerYqlFVLyXOCoSaqqak8hNls=
3935
github.com/cloudwego/eino-ext/callbacks/langfuse v0.0.0-20260227151421-e109b4ff9563 h1:DKTXDDw8ErC4RorZLfB2ZdHChjDKWIqOEO7VRSjjfbg=
4036
github.com/cloudwego/eino-ext/callbacks/langfuse v0.0.0-20260227151421-e109b4ff9563/go.mod h1:lrNKITZR4QUaYl9Rdz9W6qGOolHRy6mPamEZYA8uz7s=
4137
github.com/cloudwego/eino-ext/components/model/openrouter v0.1.2 h1:zDFteouktUsGk4I/7m1b7yT4e9qawy45gWtLoyeHwxI=

pkg/analyzer/acp_tool_model.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ package analyzer
33
import (
44
"context"
55

6+
"github.com/cloudwego/eino/components"
67
"github.com/cloudwego/eino/components/model"
78
"github.com/cloudwego/eino/schema"
89
einoacp "github.com/strrl/eino-acp"
910
)
1011

11-
var _ model.ToolCallingChatModel = (*acpToolCallingModel)(nil)
12+
var (
13+
_ model.ToolCallingChatModel = (*acpToolCallingModel)(nil)
14+
_ components.Checker = (*acpToolCallingModel)(nil)
15+
)
1216

1317
// acpToolCallingModel adapts eino-acp ChatModel to ToolCallingChatModel.
1418
// ACP agents manage tools in their own runtime, so WithTools is a no-op.
@@ -20,6 +24,10 @@ func newACPToolCallingModel(base *einoacp.ChatModel) model.ToolCallingChatModel
2024
return &acpToolCallingModel{base: base}
2125
}
2226

27+
func (m *acpToolCallingModel) IsCallbacksEnabled() bool {
28+
return true
29+
}
30+
2331
func (m *acpToolCallingModel) Generate(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.Message, error) {
2432
return m.base.Generate(ctx, input, opts...)
2533
}

pkg/analyzer/analyzer.go

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,30 @@ import (
77
"path/filepath"
88
"strings"
99

10-
"github.com/cloudwego/eino-ext/adk/backend/local"
1110
"github.com/cloudwego/eino/adk"
12-
fsmw "github.com/cloudwego/eino/adk/middlewares/filesystem"
1311
"github.com/go-errors/errors"
12+
"github.com/google/uuid"
1413
einoacp "github.com/strrl/eino-acp"
15-
"github.com/strrl/lapp/pkg/tape"
1614
"go.opentelemetry.io/otel"
1715
"go.opentelemetry.io/otel/attribute"
16+
17+
"github.com/strrl/lapp/pkg/tape"
18+
"github.com/strrl/lapp/pkg/tracing"
1819
)
1920

2021
func buildSystemPrompt(workDir string) string {
2122
return fmt.Sprintf(`You are a log analysis expert helping developers troubleshoot issues.
2223
23-
IMPORTANT: All file operations (read_file, grep, ls, glob, execute) MUST use paths under %s.
24-
Do NOT access files outside this workspace directory.
24+
IMPORTANT: Stay within the workspace directory %s for any file or shell work (your runtime provides the tools).
2525
2626
Your workspace contains pre-processed log data at %s:
2727
- %s/raw.log — the original log file
2828
- %s/summary.txt — log templates discovered by automated parsing, with occurrence counts and samples
2929
- %s/errors.txt — error and warning patterns extracted from logs
3030
3131
Start by reading %s/summary.txt and %s/errors.txt to understand the log patterns.
32-
Then use grep and read_file on %s/raw.log to investigate specific patterns in detail.
33-
You can also use the execute tool to run shell commands (e.g., awk, sort, wc) for deeper analysis.
32+
Then search and read %s/raw.log for specifics (grep, read, or equivalents your environment exposes).
33+
Use shell only when it helps (e.g. awk, sort, wc).
3434
3535
Provide:
3636
1. Key findings from the logs
@@ -46,16 +46,15 @@ Be concise and actionable. Focus on what matters.`,
4646
type Config struct {
4747
Provider string
4848
Model string
49-
// TapePath, when set, enables tape recording to this JSONL file.
49+
// TapePath overrides the default workspace tape file.
5050
TapePath string
5151
}
5252

5353
// BuildWorkspaceSystemPrompt builds a system prompt for the structured workspace layout.
5454
func BuildWorkspaceSystemPrompt(workDir string) string {
5555
return fmt.Sprintf(`You are a log analysis expert helping developers troubleshoot issues.
5656
57-
IMPORTANT: All file operations (read_file, grep, ls, glob, execute) MUST use paths under %s.
58-
Do NOT access files outside this workspace directory.
57+
IMPORTANT: Stay within the workspace directory %s for any file or shell work (your runtime provides the tools).
5958
6059
Your workspace at %s contains structured log data:
6160
- %s/logs/ — original log files
@@ -67,8 +66,8 @@ Your workspace at %s contains structured log data:
6766
6867
Start by reading %s/notes/summary.md and %s/notes/errors.md to understand the log patterns.
6968
Then drill into specific patterns under %s/patterns/ for details.
70-
Use grep on %s/logs/ to search for specific terms across all log files.
71-
You can also use the execute tool to run shell commands (e.g., awk, sort, wc) for deeper analysis.
69+
Search %s/logs/ for specific terms across log files.
70+
Use shell only when it helps (e.g., awk, sort, wc).
7271
7372
Provide:
7473
1. Key findings from the logs
@@ -117,30 +116,31 @@ func RunAgentWithPrompt(ctx context.Context, config Config, workDir, question, s
117116
return "", errors.Errorf("create chat model: %w", err)
118117
}
119118

120-
backend, err := local.NewBackend(ctx, &local.Config{})
121-
if err != nil {
122-
return "", errors.Errorf("create local backend: %w", err)
119+
if systemPrompt == "" {
120+
systemPrompt = buildSystemPrompt(absDir)
123121
}
124-
backendAdapter := newLocalBackendAdapter(backend)
125122

126-
fsHandler, err := fsmw.New(ctx, &fsmw.MiddlewareConfig{
127-
Backend: backendAdapter,
128-
StreamingShell: backendAdapter,
129-
})
123+
tapePath := config.TapePath
124+
if tapePath == "" {
125+
tapePath = filepath.Join(absDir, tape.FileName)
126+
}
127+
tapeStore, err := tape.OpenJSONL(tapePath)
130128
if err != nil {
131-
return "", errors.Errorf("create filesystem middleware: %w", err)
129+
return "", errors.Errorf("open tape store: %w", err)
132130
}
131+
defer func() { _ = tapeStore.Close() }()
133132

134-
if systemPrompt == "" {
135-
systemPrompt = buildSystemPrompt(absDir)
136-
}
133+
tapeHandler := tape.NewEinoHandler(tapeStore, tape.RunMeta{
134+
RunID: uuid.NewString(),
135+
Provider: provider,
136+
Model: config.Model,
137+
})
137138

138139
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
139140
Name: "log-analyzer",
140141
Description: "Analyzes log files to find root causes",
141142
Instruction: systemPrompt,
142143
Model: newACPToolCallingModel(chatModel),
143-
Handlers: []adk.ChatModelAgentMiddleware{fsHandler},
144144
MaxIterations: 15,
145145
})
146146
if err != nil {
@@ -153,13 +153,7 @@ func RunAgentWithPrompt(ctx context.Context, config Config, workDir, question, s
153153
}
154154

155155
runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: agent})
156-
var runOptions []adk.AgentRunOption
157-
if config.TapePath != "" {
158-
jsonlStore := tape.NewJSONLStore(config.TapePath)
159-
runOptions = append(runOptions, adk.WithCallbacks(tape.NewHandler(jsonlStore)))
160-
slog.Info("Tape recording enabled", "path", config.TapePath)
161-
}
162-
iter := runner.Query(ctx, userMessage, runOptions...)
156+
iter := runner.Query(ctx, userMessage, adk.WithCallbacks(tracing.NewSlogEinoHandler(nil), tapeHandler))
163157

164158
var result strings.Builder
165159
for {

pkg/analyzer/local_backend_adapter.go

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

0 commit comments

Comments
 (0)