mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-06 02:34:09 +08:00
fix(gemini): filter noisy stderr output from gemini backend (#83)
* fix(gemini): filter noisy stderr output from gemini backend - Add filteringWriter to filter [STARTUP], Warning, Session cleanup etc. - Apply filter only for gemini backend stderr output - Add unit tests for filtering logic * fix: use defer for stderrFilter.Flush to cover all return paths Address review feedback: ensure filter is flushed on failure paths
This commit is contained in:
@@ -683,8 +683,17 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
|
||||
if stderrLogger != nil {
|
||||
stderrWriters = append(stderrWriters, stderrLogger)
|
||||
}
|
||||
|
||||
// For gemini backend, filter noisy stderr output
|
||||
var stderrFilter *filteringWriter
|
||||
if !silent {
|
||||
stderrWriters = append([]io.Writer{os.Stderr}, stderrWriters...)
|
||||
stderrOut := io.Writer(os.Stderr)
|
||||
if cfg.Backend == "gemini" {
|
||||
stderrFilter = newFilteringWriter(os.Stderr, geminiNoisePatterns)
|
||||
stderrOut = stderrFilter
|
||||
defer stderrFilter.Flush()
|
||||
}
|
||||
stderrWriters = append([]io.Writer{stderrOut}, stderrWriters...)
|
||||
}
|
||||
if len(stderrWriters) == 1 {
|
||||
cmd.SetStderr(stderrWriters[0])
|
||||
|
||||
66
codeagent-wrapper/filter.go
Normal file
66
codeagent-wrapper/filter.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// geminiNoisePatterns contains stderr patterns to filter for gemini backend
|
||||
var geminiNoisePatterns = []string{
|
||||
"[STARTUP]",
|
||||
"Session cleanup disabled",
|
||||
"Warning:",
|
||||
"(node:",
|
||||
"(Use `node --trace-warnings",
|
||||
"Loaded cached credentials",
|
||||
"Loading extension:",
|
||||
"YOLO mode is enabled",
|
||||
}
|
||||
|
||||
// filteringWriter wraps an io.Writer and filters out lines matching patterns
|
||||
type filteringWriter struct {
|
||||
w io.Writer
|
||||
patterns []string
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func newFilteringWriter(w io.Writer, patterns []string) *filteringWriter {
|
||||
return &filteringWriter{w: w, patterns: patterns}
|
||||
}
|
||||
|
||||
func (f *filteringWriter) Write(p []byte) (n int, err error) {
|
||||
f.buf.Write(p)
|
||||
for {
|
||||
line, err := f.buf.ReadString('\n')
|
||||
if err != nil {
|
||||
// incomplete line, put it back
|
||||
f.buf.WriteString(line)
|
||||
break
|
||||
}
|
||||
if !f.shouldFilter(line) {
|
||||
f.w.Write([]byte(line))
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (f *filteringWriter) shouldFilter(line string) bool {
|
||||
for _, pattern := range f.patterns {
|
||||
if strings.Contains(line, pattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Flush writes any remaining buffered content
|
||||
func (f *filteringWriter) Flush() {
|
||||
if f.buf.Len() > 0 {
|
||||
remaining := f.buf.String()
|
||||
if !f.shouldFilter(remaining) {
|
||||
f.w.Write([]byte(remaining))
|
||||
}
|
||||
f.buf.Reset()
|
||||
}
|
||||
}
|
||||
73
codeagent-wrapper/filter_test.go
Normal file
73
codeagent-wrapper/filter_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFilteringWriter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
patterns []string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "filter STARTUP lines",
|
||||
patterns: geminiNoisePatterns,
|
||||
input: "[STARTUP] Recording metric\nHello World\n[STARTUP] Another line\n",
|
||||
want: "Hello World\n",
|
||||
},
|
||||
{
|
||||
name: "filter Warning lines",
|
||||
patterns: geminiNoisePatterns,
|
||||
input: "Warning: something bad\nActual output\n",
|
||||
want: "Actual output\n",
|
||||
},
|
||||
{
|
||||
name: "filter multiple patterns",
|
||||
patterns: geminiNoisePatterns,
|
||||
input: "YOLO mode is enabled\nSession cleanup disabled\nReal content\nLoading extension: foo\n",
|
||||
want: "Real content\n",
|
||||
},
|
||||
{
|
||||
name: "no filtering needed",
|
||||
patterns: geminiNoisePatterns,
|
||||
input: "Line 1\nLine 2\nLine 3\n",
|
||||
want: "Line 1\nLine 2\nLine 3\n",
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
patterns: geminiNoisePatterns,
|
||||
input: "",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
fw := newFilteringWriter(&buf, tt.patterns)
|
||||
fw.Write([]byte(tt.input))
|
||||
fw.Flush()
|
||||
|
||||
if got := buf.String(); got != tt.want {
|
||||
t.Errorf("got %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteringWriterPartialLines(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
fw := newFilteringWriter(&buf, geminiNoisePatterns)
|
||||
|
||||
// Write partial line
|
||||
fw.Write([]byte("Hello "))
|
||||
fw.Write([]byte("World\n"))
|
||||
fw.Flush()
|
||||
|
||||
if got := buf.String(); got != "Hello World\n" {
|
||||
t.Errorf("got %q, want %q", got, "Hello World\n")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user