Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evaluation Results Data Explorer #96

Merged
merged 24 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
95f71c6
Start defining a service for evaluation results. Use connect-go to ge…
jlewi May 6, 2024
c17ed4a
The go_package option was missing a directory. Not sure why it wasn't…
jlewi May 6, 2024
cbd4715
This is working in terms of setting up a connect protocol server usin…
jlewi May 7, 2024
5fb957d
Add the api prefix to the route.
jlewi May 7, 2024
db4ccd2
Implemented the EvalServiceList request.
jlewi May 7, 2024
ecb167e
Add a navigation bar for different pages.
jlewi May 7, 2024
29d8431
Rename viewer to app to be the main application.
jlewi May 7, 2024
434c357
Add a left hand navigation bar to navigate between different pages.
jlewi May 7, 2024
19c3a3b
Start displaying the eval viewer.
jlewi May 7, 2024
42596ac
Add a status bar to display the version. Try to update Version to for…
jlewi May 7, 2024
b561fce
We have rudimentary client app working; can display a table and click…
jlewi May 7, 2024
e8e19b6
Add a text box to specify the data
jlewi May 7, 2024
bc9515a
Fix styling of the table for the eval results.
jlewi May 7, 2024
885aaf5
Can render the view although it isn't displayed well.
jlewi May 7, 2024
369e0db
Implement all the different viewer options.
jlewi May 7, 2024
e196d32
Tidy
jlewi May 7, 2024
589cd54
Fix lint.
jlewi May 7, 2024
dbe20c2
Tidy
jlewi May 7, 2024
c2a2d39
Rename mainWindow to BlockLogView
jlewi May 7, 2024
fa25204
Move the blockLogView into the block_viewer.go
jlewi May 7, 2024
76a2ff9
Fix compile errors.
jlewi May 7, 2024
27cc3fe
More cleanup.
jlewi May 7, 2024
b6eb7f4
Fix imports.
jlewi May 7, 2024
17e8029
Fix lint.
jlewi May 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions app/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.1
replace github.com/jlewi/foyle/protos/go => ../protos/go

require (
connectrpc.com/connect v1.16.1
github.com/Kunde21/markdownfmt/v3 v3.1.0
github.com/agnivade/levenshtein v1.1.1
github.com/cockroachdb/pebble v1.1.0
Expand Down Expand Up @@ -37,9 +38,12 @@ require (
go.opentelemetry.io/otel/trace v1.25.0
go.uber.org/zap v1.27.0
gonum.org/v1/gonum v0.15.0
google.golang.org/api v0.162.0
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.27.3
sigs.k8s.io/kustomize/kyaml v0.13.9
)

require (
Expand Down Expand Up @@ -189,7 +193,6 @@ require (
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.15.0 // indirect
google.golang.org/api v0.162.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect
Expand All @@ -198,13 +201,11 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apimachinery v0.27.3 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
4 changes: 2 additions & 2 deletions app/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8=
cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
connectrpc.com/connect v1.16.1 h1:rOdrK/RTI/7TVnn3JsVxt3n028MlTRwmK5Q4heSpjis=
connectrpc.com/connect v1.16.1/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
Expand Down Expand Up @@ -348,8 +350,6 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jlewi/hydros v0.0.6 h1:gylMzSH5VvO+uTtz20JUtJPPr3ZLAQxvH3j/0FHK4FE=
github.com/jlewi/hydros v0.0.6/go.mod h1:4fV+JUCnexPY2ZbKzdfV/RsyrfralN832MsUSq/7FqE=
github.com/jlewi/hydros v0.0.7-0.20240503183011-8f99ead373fb h1:2G2k606S3Qcg40czr7gnkeIG5KgQ2wXJ1BMxAuC+P3I=
github.com/jlewi/hydros v0.0.7-0.20240503183011-8f99ead373fb/go.mod h1:4fV+JUCnexPY2ZbKzdfV/RsyrfralN832MsUSq/7FqE=
github.com/jlewi/monogo v0.0.0-20240123191147-401afe194d74 h1:pbOw/rOMs0AZ494bGnI6DieGKwqoJQEjHWaJZrvxsJo=
Expand Down
2 changes: 2 additions & 0 deletions app/pkg/eval/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ func (e *Evaluator) loadFoyleFiles(ctx context.Context, db *pebble.DB, files []s
continue
}

// TODO(https://github.com/jlewi/foyle/issues/95): We should assign an ID to each example that is stable
// across experiments.
id := uuid.NewString()
example := &v1alpha1.Example{
Id: id,
Expand Down
71 changes: 71 additions & 0 deletions app/pkg/eval/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package eval

import (
"context"

"connectrpc.com/connect"
"github.com/cockroachdb/pebble"
"github.com/jlewi/foyle/app/pkg/logs"
"github.com/jlewi/foyle/protos/go/foyle/v1alpha1"
"github.com/jlewi/monogo/helpers"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)

// EvalServer is the server that implements the Eval service interface.
// This is used to make results available to the frontend.
type EvalServer struct{}

func (s *EvalServer) List(
ctx context.Context,
req *connect.Request[v1alpha1.EvalResultListRequest],
) (*connect.Response[v1alpha1.EvalResultListResponse], error) {
log := logs.FromContext(ctx)

if req.Msg.GetDatabase() == "" {
err := connect.NewError(connect.CodeInvalidArgument, errors.New("Request is missing database"))
log.Error(err, "Invalid EvalResultListRequest")
return nil, err
}

db, err := pebble.Open(req.Msg.GetDatabase(), &pebble.Options{})
if err != nil {
log.Error(err, "Failed to open database")
return nil, connect.NewError(connect.CodeInternal, err)
}
defer helpers.DeferIgnoreError(db.Close)

iter, err := db.NewIterWithContext(ctx, nil)
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
defer iter.Close()

results := &v1alpha1.EvalResultListResponse{
Items: make([]*v1alpha1.EvalResult, 0, 100),
}

for iter.First(); iter.Valid(); iter.Next() {
key := iter.Key()
if key == nil {
break
}

value, err := iter.ValueAndErr()
if err != nil {
log.Error(err, "Failed to read value for key", "key", string(key))
continue
}

result := &v1alpha1.EvalResult{}
if err := proto.Unmarshal(value, result); err != nil {
log.Error(err, "Failed to unmarshal value for", "key", string(key))
continue
}
results.Items = append(results.Items, result)
}

res := connect.NewResponse(results)
res.Header().Set("Eval-Version", "v1alpha1")
return res, nil
}
99 changes: 99 additions & 0 deletions app/pkg/logsviewer/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package logsviewer

import (
"github.com/go-logr/zapr"
"github.com/maxence-charriere/go-app/v9/pkg/app"
"github.com/pkg/errors"
"go.uber.org/zap"
)

type page string
type view string

const (
getAction = "/get"
setPage = "/setPage"

errorView view = "error"
generatedBlockView view = "generatedBlock"
executedBlockView view = "executedBlock"
rawView view = "raw"

blockLogsView page = "blockLogs"
evalsView page = "evals"

getErrorState = "/getError"
blockLogState = "/blocklog"
)

// MainApp is the main window of the application.
//
// The main application consists of a left hand navigation bar and a right hand side component that is the page
// to display. When you click on one of the left hand navigation buttons it fires of an action setPage to change the
// view. The handler for this action loads the appropriate page and sets MainApp.page to the component for that
// page.
type MainApp struct {
app.Compo
// Page keeps track of the page to display in the right hand side.
page app.UI
}

func (m *MainApp) Render() app.UI {
if m.page == nil {
// TODO(jeremy): Could we keep track of the last view so if we refresh we show the same data?
// One way to do that is to update the URL with query arguments containing the relevant state information.
// Then when we click refresh we could get the information directly from the URL
m.page = &BlockViewer{}
}
return app.Div().Class("main-layout").Body(
app.Div().Class("content").Body(
app.Div().Class("sidebar").Body(
&navigationBar{},
),
app.Div().Class("page-window").Body(
m.page,
),
), &StatusBar{},
)
}

func (m *MainApp) OnMount(ctx app.Context) {
// register to handle the setPage action
ctx.Handle(setPage, m.handleSetPage)
}

// handleSetPage handles the setPage action. The event will tell us which view to display.
func (m *MainApp) handleSetPage(ctx app.Context, action app.Action) {
log := zapr.NewLogger(zap.L())
pageValue, ok := action.Value.(page)
if !ok {
log.Error(errors.New("No page provided"), "Invalid action")
return
}
log.Info("Handling set page action", "page", pageValue)
switch pageValue {
case blockLogsView:
if _, ok := m.page.(*BlockViewer); !ok {
log.Info("Setting page to BlockViewer")
m.page = &BlockViewer{}
}
case evalsView:
if _, ok := m.page.(*EvalViewer); !ok {
log.Info("Setting page to EvalViewer")
m.page = &EvalViewer{}
}
}
// We need to call update to trigger a re-render of the component.
m.Update()
}

// StatusBar at the bottom of the page. Inspired by the vscode/intellij status bar.
// We use this to show useful information like the version number.
type StatusBar struct {
app.Compo
}

func (s *StatusBar) Render() app.UI {
version := app.Getenv("GOAPP_VERSION")
return app.Div().Class("status-bar").Text("goapp version: " + version)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,85 @@ import (
"go.uber.org/zap"
)

// mainWindow is the main window of the application.
// BlockViewer is the page that displays the block logs.
//
// How it works:
// Clicking load fetches the blocklog from the server.
// The log is then stored in the application context (https://go-app.dev/states)
// this allows other components to use it. Load then fires off an UpdateView event to trigger
// the blockLogView to update its content.
// The UpdateView event takes a string argument which is what view should be rendered.
// There is a left hand navigation bar with buttons to display different views of the current log.
// Changing the view is achieved by sending UpdateView events to change the view
type BlockViewer struct {
app.Compo
main *blockLogView
}

func (c *BlockViewer) Render() app.UI {
if c.main == nil {
c.main = &blockLogView{}
}
return app.Div().Class("main-layout").Body(
app.Div().Class("header").Body(
&blockSelector{},
),
app.Div().Class("content").Body(
app.Div().Class("sidebar").Body(
&sideBar{},
),
app.Div().Class("main-window").Body(
c.main,
),
),
)
}

// sideBar adds a navigation bar between the views to the left side.
type sideBar struct {
app.Compo
}

func (s *sideBar) Render() app.UI {
return app.Div().Body(
// Each button needs to be enclosed in a div. Otherwise events get triggered for all the buttons.
app.Div().Body(
app.Button().Text("Generated Block").OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue(getAction, generatedBlockView)
}),
),
app.Div().Body(
app.Button().Text("Executed Block")).OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue(getAction, executedBlockView)
}),
app.Div().Body(
app.Button().Text("Raw")).OnClick(func(ctx app.Context, e app.Event) {
ctx.NewActionWithValue(getAction, rawView)
}))
}

// blockLogView is the main window of the application.
// What it displays will change depending on the view selected.
// The content of the main window is HTML which gets set by the action handler for different events.
//
// The main window registers a handler for the getAction event. The getAction event is triggered when ever
// a blockLog is loaded. The handler for the getAction event will set the HTML content of the main windowß
type mainWindow struct {
type blockLogView struct {
app.Compo
HTMLContent string
}

func (m *mainWindow) Render() app.UI {
func (m *blockLogView) Render() app.UI {
// Raw requires the value to have a single root element. So we enclose the HTML content in a div to ensure
// that is all ways true.
return app.Raw("<div>" + m.HTMLContent + "</div>")
}

func (m *mainWindow) OnMount(ctx app.Context) {
func (m *blockLogView) OnMount(ctx app.Context) {
ctx.Handle(getAction, m.handleGetAction)
}

func (m *mainWindow) handleGetAction(ctx app.Context, action app.Action) {
func (m *blockLogView) handleGetAction(ctx app.Context, action app.Action) {
log := zapr.NewLogger(zap.L())
viewValue, ok := action.Value.(view) // Checks if a name was given.
if !ok {
Expand Down
Loading
Loading