diff options
Diffstat (limited to 'internal/executor')
| -rw-r--r-- | internal/executor/gemini.go | 40 |
1 files changed, 39 insertions, 1 deletions
diff --git a/internal/executor/gemini.go b/internal/executor/gemini.go index 67ea7dd..bf284c6 100644 --- a/internal/executor/gemini.go +++ b/internal/executor/gemini.go @@ -3,6 +3,7 @@ package executor import ( "context" "fmt" + "io" "log/slog" "os" "os/exec" @@ -53,6 +54,7 @@ func (r *GeminiRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi if err := os.MkdirAll(logDir, 0700); err != nil { return fmt.Errorf("creating log dir: %w", err) } + if e.StdoutPath == "" { e.StdoutPath = filepath.Join(logDir, "stdout.log") e.StderrPath = filepath.Join(logDir, "stderr.log") @@ -137,7 +139,7 @@ func (r *GeminiRunner) execOnce(ctx context.Context, args []string, workingDir, go func() { defer wg.Done() // Reusing parseStream as the JSONL format should be compatible - costUSD, streamErr = parseStream(stdoutR, stdoutFile, r.Logger) + costUSD, streamErr = parseGeminiStream(stdoutR, stdoutFile, r.Logger) stdoutR.Close() }() @@ -164,6 +166,42 @@ func (r *GeminiRunner) execOnce(ctx context.Context, args []string, workingDir, return nil } +// parseGeminiStream reads streaming JSON from the gemini CLI, unwraps markdown +// code blocks, writes the inner JSON to w, and returns (costUSD, error). +// For now, it focuses on unwrapping and writing, not detailed parsing of cost/errors. +func parseGeminiStream(r io.Reader, w io.Writer, logger *slog.Logger) (float64, error) { + fullOutput, err := io.ReadAll(r) + if err != nil { + return 0, fmt.Errorf("reading full gemini output: %w", err) + } + + outputStr := strings.TrimSpace(string(fullOutput)) // Trim leading/trailing whitespace/newlines from the whole output + + jsonContent := outputStr // Default to raw output if no markdown block is found or malformed + jsonStartIdx := strings.Index(outputStr, "```json") + if jsonStartIdx != -1 { + // Found "```json", now look for the closing "```" + jsonEndIdx := strings.LastIndex(outputStr, "```") + if jsonEndIdx != -1 && jsonEndIdx > jsonStartIdx { + // Extract content between the markdown fences. + jsonContent = outputStr[jsonStartIdx+len("```json"):jsonEndIdx] + jsonContent = strings.TrimSpace(jsonContent) // Trim again after extraction, to remove potential inner newlines + } else { + logger.Warn("Malformed markdown JSON block from Gemini (missing closing ``` or invalid structure), falling back to raw output.", "outputLength", len(outputStr)) + } + } else { + logger.Warn("No markdown JSON block found from Gemini, falling back to raw output.", "outputLength", len(outputStr)) + } + + // Write the (possibly extracted and trimmed) JSON content to the writer. + _, writeErr := w.Write([]byte(jsonContent)) + if writeErr != nil { + return 0, fmt.Errorf("writing extracted gemini json: %w", writeErr) + } + + return 0, nil // For now, no cost/error parsing for Gemini stream +} + func (r *GeminiRunner) buildArgs(t *task.Task, e *storage.Execution, questionFile string) []string { // Gemini CLI uses a different command structure: gemini "instructions" [flags] |
