Skip to content

Commit 5654d44

Browse files
committed
snapshot
1 parent a5382ca commit 5654d44

File tree

2 files changed

+93
-44
lines changed

2 files changed

+93
-44
lines changed

summarize.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
tea "github.com/charmbracelet/bubbletea"
9+
openai "github.com/openai/openai-go"
10+
"github.com/openai/openai-go/option"
11+
)
12+
13+
// SummaryCommand represents a command's description and its captured output
14+
// used for generating an LLM summary.
15+
type SummaryCommand struct {
16+
Description string
17+
Output string
18+
}
19+
20+
// Summarizer encapsulates LLM client configuration used for summarization.
21+
type Summarizer struct {
22+
client openai.Client
23+
model string
24+
}
25+
26+
// NewSummarizer constructs a Summarizer with sensible defaults.
27+
func NewSummarizer() *Summarizer {
28+
return &Summarizer{
29+
client: openai.NewClient(option.WithBaseURL("https://openrouter.ai/api/v1")),
30+
model: "openai/gpt-oss-120b:nitro",
31+
}
32+
}
33+
34+
// Summarize generates a summary given a system prompt and a list of command
35+
// descriptions paired with their outputs. The systemPrompt is passed as a
36+
// system message, and the concatenated command outputs are passed as a user
37+
// message.
38+
func (s *Summarizer) Summarize(systemPrompt string, commands []SummaryCommand) (string, error) {
39+
ctx := context.Background()
40+
41+
var b strings.Builder
42+
for i, c := range commands {
43+
if strings.TrimSpace(c.Output) == "" {
44+
continue
45+
}
46+
b.WriteString(fmt.Sprintf("Command %d: %s\n", i+1, c.Description))
47+
b.WriteString(c.Output)
48+
b.WriteString("\n\n")
49+
}
50+
userContent := b.String()
51+
52+
resp, err := s.client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
53+
Model: s.model,
54+
Messages: []openai.ChatCompletionMessageParamUnion{
55+
openai.SystemMessage(systemPrompt),
56+
openai.UserMessage(userContent),
57+
},
58+
})
59+
if err != nil {
60+
return "", err
61+
}
62+
if len(resp.Choices) == 0 {
63+
return "", fmt.Errorf("no choices from LLM")
64+
}
65+
return resp.Choices[0].Message.Content, nil
66+
}
67+
68+
// summarizeCmd wraps the Summarizer.Summarize call into a Bubble Tea command
69+
// that returns an llmMsg for the UI state machine.
70+
func summarizeCmd(s *Summarizer, systemPrompt string, commands []SummaryCommand) tea.Cmd {
71+
return func() tea.Msg {
72+
summary, err := s.Summarize(systemPrompt, commands)
73+
if err != nil {
74+
return llmMsg{err: err}
75+
}
76+
return llmMsg{summary: summary}
77+
}
78+
}
79+
80+

ui.go

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"math"
76
"strings"
@@ -12,8 +11,6 @@ import (
1211
tea "github.com/charmbracelet/bubbletea"
1312
"github.com/charmbracelet/glamour"
1413
"github.com/charmbracelet/lipgloss"
15-
openai "github.com/openai/openai-go"
16-
"github.com/openai/openai-go/option"
1714
)
1815

1916
var (
@@ -80,6 +77,8 @@ type model struct {
8077
summaryErr error
8178

8279
done bool
80+
81+
summarizer *Summarizer
8382
}
8483

8584
// NewModel constructs a model initialised with all diagnostic commands in a
@@ -102,6 +101,7 @@ func NewModel(tb *Toolbox) *model {
102101
return s
103102
}(),
104103
startTime: time.Now(),
104+
summarizer: NewSummarizer(),
105105
}
106106
}
107107

@@ -118,46 +118,7 @@ func (m *model) Init() tea.Cmd {
118118
return tea.Batch(cmds...)
119119
}
120120

121-
// summarizeCmd calls OpenAI to generate summary.
122-
func summarizeCmd(outputs []string, errs []error) tea.Cmd {
123-
// Prepare input text
124-
var b strings.Builder
125-
for i, out := range outputs {
126-
if out == "" && errs[i] == nil {
127-
continue
128-
}
129-
b.WriteString(fmt.Sprintf("Command %d:\n", i+1))
130-
if out != "" {
131-
b.WriteString(out)
132-
b.WriteString("\n")
133-
}
134-
if errs[i] != nil {
135-
b.WriteString(fmt.Sprintf("ERROR: %v\n", errs[i]))
136-
}
137-
b.WriteString("\n")
138-
}
139-
inputText := b.String()
140-
141-
return func() tea.Msg {
142-
ctx := context.Background()
143-
client := openai.NewClient(option.WithBaseURL("https://openrouter.ai/api/v1"))
144-
content := "Please analyze the following Linux system diagnostic output. Focus on identifying any performance issues, errors, or notable system characteristics. Be concise but comprehensive. Start with 1-sentence overview and the key finding. Prefer bullet points. Your full output should be maximum 2-3 paragraphs (that includes bullet points - don't overdo them).\n\nDiagnostic output:\n\n" + inputText
145-
resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
146-
// Model: "openai/gpt-4.1",
147-
Model: "openai/gpt-oss-120b:nitro",
148-
Messages: []openai.ChatCompletionMessageParamUnion{
149-
openai.UserMessage(content),
150-
},
151-
})
152-
if err != nil {
153-
return llmMsg{err: err}
154-
}
155-
if len(resp.Choices) == 0 {
156-
return llmMsg{err: fmt.Errorf("no choices from LLM")}
157-
}
158-
return llmMsg{summary: resp.Choices[0].Message.Content}
159-
}
160-
}
121+
// summarizeCmd moved to summarize.go
161122

162123
// downloadToolboxCmd runs the toolbox download in a goroutine.
163124
func downloadToolboxCmd(tb *Toolbox) tea.Cmd {
@@ -224,7 +185,15 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
224185
if allDone {
225186
if !m.summarizing && m.summary == "" {
226187
m.summarizing = true
227-
return m, summarizeCmd(m.outputs, m.errors)
188+
var sc []SummaryCommand
189+
for i := range m.commands {
190+
sc = append(sc, SummaryCommand{
191+
Description: m.commands[i].Display,
192+
Output: m.outputs[i],
193+
})
194+
}
195+
systemPrompt := "Please analyze the following Linux system diagnostic output. Focus on identifying any performance issues, errors, or notable system characteristics. Be concise but comprehensive. Start with 1-sentence overview and the key finding. Prefer bullet points. Your full output should be maximum 2-3 paragraphs (that includes bullet points - don't overdo them)."
196+
return m, summarizeCmd(m.summarizer, systemPrompt, sc)
228197
}
229198
}
230199
// No follow-up commands here.

0 commit comments

Comments
 (0)