prompt

package
v0.2.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2024 License: MIT Imports: 10 Imported by: 0

README

Butterfish Prompt Library

A goal of Butterfish is to make prompts transparent and easily editable. Butterfish will write a prompt library to ~/.config/butterfish/prompts.yaml and load this every time it runs. You can edit prompts in that file to tweak them. If you edit a prompt then set OkToReplace: false, which prevents overwriting.

This is the golang module for managing a library of LLM prompts in a YAML file on disk, then loading and interpolating the prompts at runtime. This module can be used separately from Butterfish itself.

Why do we have the OkToReplace field to prevent overwriting specific prompts on disk? Because we want a library of default prompts (found at ./defaults.go in this directory) and we want to be able to update the defaults when a new version of Butterfish is released, but prevent overwritting prompts that the user has customized.

A prompt consists of a string name, the prompt itself, and a field indicating whether or not a prompt can be overwritten. When written to the YAML file, these look like the following.

> head -n 8 ~/.config/butterfish/prompts.yaml
- name: watch_shell_output
  prompt: The following is output from a user inside a "{shell_name}" shell, the user
    ran the command "{command}", if the output contains an error then print the specific
    segment that is an error and explain briefly how to solve the error, otherwise
    respond with only "NOOP". "{output}"
  oktoreplace: true

To fetch and interpolate that prompt at runtime (after initializing the prompt library) you would make a call like:

prompt, err := this.PromptLibrary.GetPrompt("watch_shell_output",
  "shell_name", openCmd,
  "command", lastCmd,
  "output", string(output))

The GetPrompt() method will throw an error if the expected fields are missing.

Here's a more full lifecycle example that demonstrates creating/initializing the prompt library.

import "fmt"
import "os"
import "github.com/bakks/butterfish/prompt"


func NewDiskPromptLibrary(path string, verbose bool, writer io.Writer) (*prompt.DiskPromptLibrary, error) {
	promptLibrary := prompt.NewPromptLibrary(path, verbose, writer)
	loaded := false

	if promptLibrary.LibraryFileExists() {
		err := promptLibrary.Load()
		if err != nil {
			return nil, err
		}
		loaded = true
	}
	promptLibrary.ReplacePrompts(prompt.DefaultPrompts)
	promptLibrary.Save()

	if !loaded {
		fmt.Fprintf(writer, "Wrote prompt library at %s\n", path)
	}

	return promptLibrary, nil
}

func main() {
  library, err := NewDiskPromptLibrary("./prompts.yaml", true, os.Stdout)
  if err != nil {
    panic(err)
  }

  prompt, err := this.PromptLibrary.GetPrompt("watch_shell_output",
    "shell_name", openCmd,
    "command", lastCmd,
    "output", string(output))

  if err != nil {
    panic(err)
  }

  fmt.Printf("%s\n", prompt)
}

Documentation

Index

Constants

View Source
const (
	PromptFixCommand           = "fix_command"
	PromptSummarize            = "summarize"
	PromptSummarizeFacts       = "summarize_facts"
	PromptSummarizeListOfFacts = "summarize_list_of_facts"
	PromptGenerateCommand      = "generate_command"
	PromptQuestion             = "question"
	PromptSystemMessage        = "prompt_system_message"
	ShellAutosuggestCommand    = "shell_autocomplete_command"
	ShellAutosuggestNewCommand = "shell_autocomplete_new_command"
	ShellAutosuggestPrompt     = "shell_autocomplete_prompt"
	ShellSystemMessage         = "shell_system_message"
	GoalModeSystemMessage      = "goal_mode_system_message"
)

Variables

View Source
var DefaultPrompts []Prompt = []Prompt{

	{
		Name:        PromptSystemMessage,
		Prompt:      "You are an assistant that helps the user in a Unix shell. Make your answers technical but succinct.",
		OkToReplace: true,
	},

	{
		Name:        ShellSystemMessage,
		Prompt:      "You are an assistant that helps the user with a Unix shell. Give advice about commands that can be run and examples but keep your answers succinct. You don't need to tell the user how to install commands that you mention. It is ok if the user asks questions not directly related to the unix shell. Here is system info about the local machine: '{sysinfo}'",
		OkToReplace: true,
	},

	{
		Name:        GoalModeSystemMessage,
		Prompt:      "You are an agent helping me achieve the following goal: '{goal}'. You will execute unix commands to achieve the goal. To execute a command, call the command function. Only run one command at a time. I will give you the results of the command. If the command fails, try to edit it or try another command to do the same thing. If we haven't reached our goal, you will then continue execute commands. If there is significant ambiguity then ask me questions. You must verify that the goal is achieved. You must call one of the functions in your response but state your reasoning before calling the function. Here is system info about the local machine: '{sysinfo}'",
		OkToReplace: true,
	},

	{
		Name:        ShellAutosuggestCommand,
		OkToReplace: true,
		Prompt: `You are a unix shell command autocompleter. I will give you the user's history, predict the full command they will type. You will find good suggestions in the user's history. You must suggest a command longer than has been typed thus far.

Here are examples of prompts and predictions:

prompt: > tel
prediction: telnet

prompt: > l
prediction: ls

prompt: > git a
prediction: git add *

prompt: How do I do a recursive find? """ find . -name "*.go" """ > fin
prediction: find . -name "*.go"

prompt: How do I do a recursive find? """ find . -name "*.go" """ > find .
prediction: find . -name "*.go"

I will give you the user's shell history including assistant messages. Respond with only the prediction, no quotes. This is the start of shell history:
-------------
{history}
> {command}`,
	},

	{
		Name:        ShellAutosuggestNewCommand,
		OkToReplace: true,
		Prompt: `You are a unix shell command predictor. I will give you the user's history, predict a new command they might run. You will find good suggestions in the user's history. The user might have asked a question and you might have suggested a command, if that is recent then suggest that command. Only predict a unix shell command, do not predict output. Provide a single line of text for the response.

Examples of good predictions:
- git status
- ls -l

Start of history:
-------------
{history}
-------------
Predicted command:
`,
	},

	{
		Name:        ShellAutosuggestPrompt,
		OkToReplace: true,
		Prompt: `You are a unix shell question autocompleter. The user has started asking a natural language question, predict the rest of the question. Do not predict an answer to that question. Include the start of the question in your answer.

This is the start of shell history:
-------------
{history}

predicted question:
{command}`,
	},

	{
		Name:        PromptFixCommand,
		OkToReplace: true,
		Prompt: `The user ran the command "{command}", which failed with exit code {status}. The output from the command is below.
		'''
		{output}
		'''
		We want to do several things:
		1. Explain to the user why the command probably failed. If unsure, explain that you do not know.
		2. Edit the command to fix the problem, don't use placeholders. If unsure, explain that you do not know. If sure, then a new line beginning with '>' and then have the updated command. The final line of your response should only have the updated command.`,
	},

	{
		Name:        PromptSummarize,
		OkToReplace: true,
		Prompt: `The following is a raw text file, summarize the file contents, the file's purpose, and write a list of the file's key elements:
'''
{content}
'''

Summary:`,
	},

	{
		Name:        PromptSummarizeFacts,
		OkToReplace: true,
		Prompt: `The following is a raw text file, write a bullet-point list of facts from the document starting with the most important.
'''
{content}
'''

Summary:`,
	},

	{
		Name:        PromptSummarizeListOfFacts,
		OkToReplace: true,
		Prompt: `The following is a list of facts, write a general description of the document and summarize its important facts in a bulleted list.
'''
{content}
'''

Description and Important Facts:`,
	},

	{
		Name:        PromptGenerateCommand,
		OkToReplace: true,
		Prompt: `Write a shell command that accomplishes the following goal. Respond with only the shell command.
'''
{content}
'''

Shell command:`,
	},

	{
		Name:        PromptQuestion,
		OkToReplace: true,
		Prompt: `Answer this question about files stored on disk. Here are some snippets from the file separated by '---'.
'''
{snippets}
'''
{question}:`,
	},
}

Functions

func Interpolate added in v0.1.10

func Interpolate(p string, args ...string) (string, error)

Types

type DiskPromptLibrary

type DiskPromptLibrary struct {
	Path          string
	Prompts       []Prompt
	Verbose       bool
	VerboseWriter io.Writer
}

DiskPromptLibrary struct which includes a Path string and a Prompts instance This implements the PromptLibrary interface.

func NewPromptLibrary

func NewPromptLibrary(path string, verbose bool, verboseWriter io.Writer) *DiskPromptLibrary

NewPromptLibrary function to make a NewPromptLibrary which takes a path argument

func (*DiskPromptLibrary) ContainsPromptNamed

func (this *DiskPromptLibrary) ContainsPromptNamed(name string) int

Checks for an exact string match between the of a prompt and the internal prompt array of the DiskPromptLibrary, returns the index of the prompt if found, otherwise returns -1

func (*DiskPromptLibrary) GetPrompt

func (this *DiskPromptLibrary) GetPrompt(name string, args ...string) (string, error)

Fetch a prompt with a given name, interpolating the fields into the prompt string. Throws an error if fields are missing. The argument pattern is first the field name, then the value, for example:

GetPrompt("my_prompt", "name", "John", "age", "30")

func (*DiskPromptLibrary) GetUninterpolatedPrompt added in v0.1.10

func (this *DiskPromptLibrary) GetUninterpolatedPrompt(name string) (string, error)

Fetch a prompt with a given name, interpolating later

func (*DiskPromptLibrary) InterpolatePrompt added in v0.1.10

func (this *DiskPromptLibrary) InterpolatePrompt(prompt string, args ...string) (string, error)

func (*DiskPromptLibrary) LibraryFileExists

func (this *DiskPromptLibrary) LibraryFileExists() bool

Check if the library file exists, should be called before Load()

func (*DiskPromptLibrary) Load

func (this *DiskPromptLibrary) Load() error

Load a yaml file at the path with a contents marshalled into Prompts

func (*DiskPromptLibrary) ReplacePrompts

func (this *DiskPromptLibrary) ReplacePrompts(newPrompts []Prompt)

Given an array of Prompt objects, replace prompts in the prompt library based on name, only if OkToReplace is true on the Prompt already in the library

func (*DiskPromptLibrary) Save

func (this *DiskPromptLibrary) Save() error

Write a yaml file at the path with the contents marshalled from Prompts

type Prompt

type Prompt struct {
	Name        string
	Prompt      string
	OkToReplace bool
}

Prompt struct with fields Name, Prompt string, OkToReplace bool

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL