Skip to content

Commit bb8b0d1

Browse files
committed
Put back the sessions json. It was useful after all
Signed-off-by: David Gageot <david.gageot@docker.com>
1 parent 4e6da79 commit bb8b0d1

3 files changed

Lines changed: 114 additions & 1 deletion

File tree

cmd/root/eval.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,15 @@ func (f *evalFlags) runEvalCommand(cmd *cobra.Command, args []string) error {
124124
if err != nil {
125125
slog.Error("Failed to save sessions database", "error", err)
126126
} else {
127-
fmt.Fprintf(teeOut, "\nSessions: %s\n", dbPath)
127+
fmt.Fprintf(teeOut, "\nSessions DB: %s\n", dbPath)
128+
}
129+
130+
// Save sessions to JSON file (same format as /eval produces)
131+
sessionsPath, err := evaluation.SaveRunSessionsJSON(run, outputDir)
132+
if err != nil {
133+
slog.Error("Failed to save sessions JSON", "error", err)
134+
} else {
135+
fmt.Fprintf(teeOut, "Sessions JSON: %s\n", sessionsPath)
128136
}
129137

130138
fmt.Fprintf(teeOut, "Log: %s\n", logPath)

pkg/evaluation/save.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,23 @@ func SaveRunJSON(run *EvalRun, outputDir string) (string, error) {
307307
return saveJSON(run, filepath.Join(outputDir, run.Name+".json"))
308308
}
309309

310+
// SaveRunSessionsJSON saves all eval sessions to a single JSON file.
311+
// Each session is saved in the same format as /eval produces (session.Session).
312+
// This complements SaveRunSessions which saves to SQLite, providing a
313+
// human-readable format for inspection.
314+
func SaveRunSessionsJSON(run *EvalRun, outputDir string) (string, error) {
315+
// Collect all sessions from results
316+
var sessions []*session.Session
317+
for i := range run.Results {
318+
if run.Results[i].Session != nil {
319+
sessions = append(sessions, run.Results[i].Session)
320+
}
321+
}
322+
323+
outputPath := filepath.Join(outputDir, run.Name+".json")
324+
return saveJSON(sessions, outputPath)
325+
}
326+
310327
func Save(sess *session.Session, filename string) (string, error) {
311328
baseName := cmp.Or(filename, sess.ID)
312329

pkg/evaluation/save_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package evaluation
22

33
import (
4+
"encoding/json"
5+
"os"
46
"path/filepath"
57
"testing"
68
"time"
@@ -107,6 +109,92 @@ func TestSaveRunSessions(t *testing.T) {
107109
assert.True(t, titles["eval-test-2"], "should have eval-test-2")
108110
}
109111

112+
func TestSaveRunSessionsJSON(t *testing.T) {
113+
t.Parallel()
114+
115+
outputDir := t.TempDir()
116+
117+
// Create sessions with different content
118+
sess1 := session.New(
119+
session.WithTitle("eval-json-1"),
120+
session.WithUserMessage("What is the capital of France?"),
121+
)
122+
sess1.InputTokens = 100
123+
sess1.OutputTokens = 50
124+
sess1.Cost = 0.01
125+
126+
sess2 := session.New(
127+
session.WithTitle("eval-json-2"),
128+
session.WithUserMessage("What is 2+2?"),
129+
)
130+
sess2.InputTokens = 80
131+
sess2.OutputTokens = 30
132+
sess2.Cost = 0.005
133+
134+
// Create an eval run with sessions
135+
run := &EvalRun{
136+
Name: "test-json-001",
137+
Timestamp: time.Now(),
138+
Results: []Result{
139+
{
140+
Title: "eval-json-1",
141+
Question: "What is the capital of France?",
142+
Response: "Paris is the capital of France.",
143+
Session: sess1,
144+
},
145+
{
146+
Title: "eval-json-2",
147+
Question: "What is 2+2?",
148+
Response: "4",
149+
Session: sess2,
150+
},
151+
{
152+
// Result without a session (error case)
153+
Title: "eval-json-3",
154+
Error: "container failed",
155+
Session: nil,
156+
},
157+
},
158+
}
159+
160+
// Save sessions to JSON
161+
sessionsPath, err := SaveRunSessionsJSON(run, outputDir)
162+
require.NoError(t, err)
163+
assert.Equal(t, filepath.Join(outputDir, "test-json-001.json"), sessionsPath)
164+
assert.FileExists(t, sessionsPath)
165+
166+
// Read and parse the JSON file
167+
data, err := os.ReadFile(sessionsPath)
168+
require.NoError(t, err)
169+
170+
var loadedSessions []*session.Session
171+
err = json.Unmarshal(data, &loadedSessions)
172+
require.NoError(t, err)
173+
174+
// Should have 2 sessions (excluding the error case)
175+
assert.Len(t, loadedSessions, 2)
176+
177+
// Verify session content
178+
titles := make(map[string]*session.Session)
179+
for _, sess := range loadedSessions {
180+
titles[sess.Title] = sess
181+
}
182+
183+
assert.Contains(t, titles, "eval-json-1")
184+
assert.Contains(t, titles, "eval-json-2")
185+
186+
// Verify cost and token data is preserved
187+
sess1Loaded := titles["eval-json-1"]
188+
assert.Equal(t, int64(100), sess1Loaded.InputTokens)
189+
assert.Equal(t, int64(50), sess1Loaded.OutputTokens)
190+
assert.InDelta(t, 0.01, sess1Loaded.Cost, 0.0001)
191+
192+
sess2Loaded := titles["eval-json-2"]
193+
assert.Equal(t, int64(80), sess2Loaded.InputTokens)
194+
assert.Equal(t, int64(30), sess2Loaded.OutputTokens)
195+
assert.InDelta(t, 0.005, sess2Loaded.Cost, 0.0001)
196+
}
197+
110198
func TestSaveRunSessionsWithCost(t *testing.T) {
111199
t.Parallel()
112200

0 commit comments

Comments
 (0)