Skip to content

Commit

Permalink
Render OpenAI request/response as HTML (#265)
Browse files Browse the repository at this point in the history
This PR adds support for rendering of OpenAI requests/responses as HTML
(fix #259). This functionality was already supported for Anthropic.

I added an LLM sub-command to support rendering the data as HTML. This
makes it reusable and not closely tied to the GetLLMLogs RPC.

Fix HTML rendering of our prompts to properly handle the XML tags in our
prompt. We need to escape them so they aren't treated as raw HTML tags
which don't render.

Remove the obsolete logs subcommand.

Fix #259
  • Loading branch information
jlewi authored Sep 30, 2024
1 parent f4cdbd6 commit f22b0ba
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 118 deletions.
127 changes: 127 additions & 0 deletions app/cmd/llms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package cmd

import (
"fmt"
"os"

"github.com/jlewi/monogo/helpers"

"github.com/jlewi/foyle/app/api"
"github.com/jlewi/foyle/app/pkg/analyze"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

// NewLLMsCmd returns a command to work with llms
func NewLLMsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "llms",
}

cmd.AddCommand(NewLLMsRenderCmd())

return cmd
}

func NewLLMsRenderCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "render",
}

cmd.AddCommand(NewLLMsRenderRequestCmd())
cmd.AddCommand(NewLLMsRenderResponseCmd())

return cmd
}

func NewLLMsRenderRequestCmd() *cobra.Command {
var provider string
var inputFile string
var outputFile string
cmd := &cobra.Command{
Use: "request",
Run: func(cmd *cobra.Command, args []string) {
err := func() error {
p := api.ModelProvider(provider)

data, err := os.ReadFile(inputFile)
if err != nil {
return errors.Wrapf(err, "Failed to read file %s", inputFile)
}

htmlData, err := analyze.RenderRequestHTML(string(data), p)
if err != nil {
return err
}

if outputFile != "" {
if err := os.WriteFile(outputFile, []byte(htmlData), 0644); err != nil {
return errors.Wrapf(err, "Failed to write file %s", outputFile)
}
} else {
fmt.Fprint(os.Stdout, htmlData)
}

return nil
}()

if err != nil {
fmt.Printf("Error rendering request;\n %+v\n", err)
os.Exit(1)
}
},
}

cmd.Flags().StringVarP(&provider, "provider", "p", string(api.ModelProviderOpenAI), "The model provider for the request.")
cmd.Flags().StringVarP(&inputFile, "input", "i", "", "The file containing the JSON representation of the request.")
cmd.Flags().StringVarP(&inputFile, "output", "o", "", "The file to write the output to. If blank output to stdout.")
helpers.IgnoreError(cmd.MarkFlagRequired("input"))

return cmd
}

func NewLLMsRenderResponseCmd() *cobra.Command {
var provider string
var inputFile string
var outputFile string
cmd := &cobra.Command{
Use: "response",
Run: func(cmd *cobra.Command, args []string) {
err := func() error {
p := api.ModelProvider(provider)

data, err := os.ReadFile(inputFile)
if err != nil {
return errors.Wrapf(err, "Failed to read file %s", inputFile)
}

htmlData, err := analyze.RenderResponseHTML(string(data), p)
if err != nil {
return err
}

if outputFile != "" {
if err := os.WriteFile(outputFile, []byte(htmlData), 0644); err != nil {
return errors.Wrapf(err, "Failed to write file %s", outputFile)
}
} else {
fmt.Fprint(os.Stdout, htmlData)
}

return nil
}()

if err != nil {
fmt.Printf("Error rendering response;\n %+v\n", err)
os.Exit(1)
}
},
}

cmd.Flags().StringVarP(&provider, "provider", "p", string(api.ModelProviderOpenAI), "The model provider for the request.")
cmd.Flags().StringVarP(&inputFile, "input", "i", "", "The file containing the JSON representation of the request.")
cmd.Flags().StringVarP(&inputFile, "output", "o", "", "The file to write the output to. If blank output to stdout.")
helpers.IgnoreError(cmd.MarkFlagRequired("input"))

return cmd
}
61 changes: 0 additions & 61 deletions app/cmd/logs.go

This file was deleted.

2 changes: 1 addition & 1 deletion app/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func NewRootCmd() *cobra.Command {
rootCmd.AddCommand(NewVersionCmd(appName, os.Stdout))
rootCmd.AddCommand(NewServeCmd())
rootCmd.AddCommand(NewConfigCmd())
rootCmd.AddCommand(NewLogsCmd())
rootCmd.AddCommand(NewLLMsCmd())
rootCmd.AddCommand(NewApplyCmd())
rootCmd.AddCommand(NewProtoToJsonCmd())
return rootCmd
Expand Down
18 changes: 2 additions & 16 deletions app/pkg/analyze/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,8 @@ func readLLMLog(ctx context.Context, traceId string, logFile string) (*logspb.Ge
}
}

if provider == api.ModelProviderAnthropic && resp.ResponseJson != "" {
html, err := renderAnthropicRequestJson(resp.RequestJson)
if err != nil {
log.Error(err, "Failed to render request")

} else {
resp.RequestHtml = html
}

htmlResp, err := renderAnthropicResponseJson(resp.ResponseJson)
if err != nil {
log.Error(err, "Failed to render response")

} else {
resp.ResponseHtml = htmlResp
}
if err := renderHTML(resp, provider); err != nil {
log.Error(err, "Failed to render HTML")
}
return resp, nil
}
Loading

0 comments on commit f22b0ba

Please sign in to comment.