Skip to content

Commit 7813528

Browse files
committed
dashboard: remember ai job correctness review history
On the bug's page, display the history of job result changes.
1 parent fa7953d commit 7813528

File tree

7 files changed

+127
-0
lines changed

7 files changed

+127
-0
lines changed

dashboard/app/ai.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ type uiAIJobPage struct {
3939
Jobs []*uiAIJob
4040
CrashReport template.HTML
4141
Trajectory []*uiAITrajectorySpan
42+
History []*uiJobReviewHistory
43+
}
44+
45+
type uiJobReviewHistory struct {
46+
Date time.Time
47+
User string
48+
Correct string
4249
}
4350

4451
type uiAIJob struct {
@@ -141,6 +148,18 @@ func handleAIJobPage(ctx context.Context, w http.ResponseWriter, r *http.Request
141148
default:
142149
job.Correct = spanner.NullBool{}
143150
}
151+
userEmail := ""
152+
if user := currentUser(ctx); user != nil {
153+
userEmail = user.Email
154+
}
155+
if err := aidb.AddJobReviewHistory(ctx, &aidb.JobReviewHistory{
156+
JobID: job.ID,
157+
Date: timeNow(ctx),
158+
User: userEmail,
159+
Correct: job.Correct,
160+
}); err != nil {
161+
return err
162+
}
144163
if err := aiJobUpdate(ctx, job); err != nil {
145164
return err
146165
}
@@ -149,6 +168,10 @@ func handleAIJobPage(ctx context.Context, w http.ResponseWriter, r *http.Request
149168
if err != nil {
150169
return err
151170
}
171+
history, err := aidb.LoadJobReviewHistory(ctx, job.ID)
172+
if err != nil {
173+
return err
174+
}
152175
hdr, err := commonHeader(ctx, r, w, job.Namespace)
153176
if err != nil {
154177
return err
@@ -172,6 +195,7 @@ func handleAIJobPage(ctx context.Context, w http.ResponseWriter, r *http.Request
172195
Jobs: []*uiAIJob{uiJob},
173196
CrashReport: crashReport,
174197
Trajectory: makeUIAITrajectory(trajectory),
198+
History: makeUIJobReviewHistory(history),
175199
}
176200
return serveTemplate(w, "ai_job.html", page)
177201
}
@@ -258,6 +282,26 @@ func makeUIAITrajectory(trajetory []*aidb.TrajectorySpan) []*uiAITrajectorySpan
258282
return res
259283
}
260284

285+
func makeUIJobReviewHistory(history []*aidb.JobReviewHistory) []*uiJobReviewHistory {
286+
var res []*uiJobReviewHistory
287+
for _, h := range history {
288+
val := aiCorrectnessUnset
289+
if h.Correct.Valid {
290+
if h.Correct.Bool {
291+
val = aiCorrectnessCorrect
292+
} else {
293+
val = aiCorrectnessIncorrect
294+
}
295+
}
296+
res = append(res, &uiJobReviewHistory{
297+
Date: h.Date,
298+
User: h.User,
299+
Correct: val,
300+
})
301+
}
302+
return res
303+
}
304+
261305
func apiAIJobPoll(ctx context.Context, req *dashapi.AIJobPollReq) (any, error) {
262306
if len(req.Workflows) == 0 || req.CodeRevision == "" {
263307
return nil, fmt.Errorf("invalid request")

dashboard/app/ai_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99
"time"
1010

11+
"github.com/google/syzkaller/dashboard/app/aidb"
1112
"github.com/google/syzkaller/dashboard/dashapi"
1213
"github.com/google/syzkaller/pkg/aflow/ai"
1314
"github.com/google/syzkaller/pkg/aflow/trajectory"
@@ -236,15 +237,29 @@ func TestAIAssessmentKCSAN(t *testing.T) {
236237
_, err = c.GET(fmt.Sprintf("/ai_job?id=%v&correct=%v", resp.ID, aiCorrectnessCorrect))
237238
require.NoError(t, err)
238239

240+
history, err := aidb.LoadJobReviewHistory(c.ctx, resp.ID)
241+
require.NoError(t, err)
242+
require.Len(t, history, 1)
243+
require.True(t, history[0].Correct.Bool)
244+
require.NotEmpty(t, history[0].User)
245+
239246
bug, _, _ := c.loadBug(extID)
240247
labels := bug.LabelValues(RaceLabel)
241248
require.Len(t, labels, 1)
242249
require.Equal(t, labels[0].Value, BenignRace)
243250

251+
c.advanceTime(time.Second)
252+
244253
// Re-mark the result as incorrect, this should remove the label.
245254
_, err = c.GET(fmt.Sprintf("/ai_job?id=%v&correct=%v", resp.ID, aiCorrectnessIncorrect))
246255
require.NoError(t, err)
247256

257+
history, err = aidb.LoadJobReviewHistory(c.ctx, resp.ID)
258+
require.NoError(t, err)
259+
require.Len(t, history, 2)
260+
require.False(t, history[0].Correct.Bool)
261+
require.True(t, history[1].Correct.Bool)
262+
248263
bug, _, _ = c.loadBug(extID)
249264
labels = bug.LabelValues(RaceLabel)
250265
require.Len(t, labels, 0)

dashboard/app/aidb/crud.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,34 @@ func selectTrajectorySpans() string {
265265
return selectAllFrom[TrajectorySpan]("TrajectorySpans")
266266
}
267267

268+
func selectJobReviewHistory() string {
269+
return selectAllFrom[JobReviewHistory]("JobReviewHistory")
270+
}
271+
272+
func AddJobReviewHistory(ctx context.Context, history *JobReviewHistory) error {
273+
history.ID = uuid.NewString()
274+
client, err := dbClient(ctx)
275+
if err != nil {
276+
return err
277+
}
278+
defer client.Close()
279+
mut, err := spanner.InsertStruct("JobReviewHistory", history)
280+
if err != nil {
281+
return err
282+
}
283+
_, err = client.Apply(ctx, []*spanner.Mutation{mut})
284+
return err
285+
}
286+
287+
func LoadJobReviewHistory(ctx context.Context, jobID string) ([]*JobReviewHistory, error) {
288+
return selectAll[JobReviewHistory](ctx, spanner.Statement{
289+
SQL: selectJobReviewHistory() + `WHERE JobID = @jobID ORDER BY Date DESC`,
290+
Params: map[string]any{
291+
"jobID": jobID,
292+
},
293+
})
294+
}
295+
268296
func selectAllFrom[T any](table string) string {
269297
var fields []string
270298
for _, field := range reflect.VisibleFields(reflect.TypeFor[T]()) {

dashboard/app/aidb/entities.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,11 @@ type TrajectorySpan struct {
5656
OutputTokens spanner.NullInt64
5757
OutputThoughtsTokens spanner.NullInt64
5858
}
59+
60+
type JobReviewHistory struct {
61+
ID string
62+
JobID string
63+
Date time.Time
64+
User string
65+
Correct spanner.NullBool
66+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP TABLE JobReviewHistory;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CREATE TABLE JobReviewHistory (
2+
ID STRING(36) NOT NULL,
3+
JobID STRING(36) NOT NULL,
4+
Date TIMESTAMP NOT NULL,
5+
User STRING(1000),
6+
Correct BOOL,
7+
CONSTRAINT FK_JobReviewHistory_Job FOREIGN KEY (JobID) REFERENCES Jobs (ID),
8+
) PRIMARY KEY (ID);

dashboard/app/templates/ai_job.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,29 @@
3131
<br>
3232
{{end}}
3333

34+
{{if .History}}
35+
<table class="list_table">
36+
<caption>Decision History:</caption>
37+
<thead>
38+
<tr>
39+
<th>Time</th>
40+
<th>User</th>
41+
<th>Decision</th>
42+
</tr>
43+
</thead>
44+
<tbody>
45+
{{range .History}}
46+
<tr>
47+
<td>{{formatTime .Date}}</td>
48+
<td>{{.User}}</td>
49+
<td>{{.Correct}}</td>
50+
</tr>
51+
{{end}}
52+
</tbody>
53+
</table>
54+
<br>
55+
{{end}}
56+
3457
{{range $res := .Job.Results}}
3558
{{if $res.IsBool}}
3659
<b>{{$res.Name}}:</b>

0 commit comments

Comments
 (0)