blob: f4bc6e02705a7e87522626a9f8cf72bfdd6a64f8 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package llmapp provides applications for LLM content generation
// to complete higher-level tasks.
package llmapp
import (
"context"
"embed"
_ "embed"
"errors"
"strings"
"text/template"
"golang.org/x/oscar/internal/llm"
"golang.org/x/oscar/internal/storage"
)
// A Doc is a document to provide to an LLM as part of a prompt.
type Doc struct {
// Freeform text describing the type of document.
// Used to help the LLM distinguish between kinds of
// documents, or understand their relative importance.
Type string `json:"type,omitempty"`
// The URL of the document, if one exists.
URL string `json:"url,omitempty"`
// The author of the document, if known.
Author string `json:"author,omitempty"`
// The title of the document, if known.
Title string `json:"title,omitempty"`
Text string `json:"text"` // required
}
// Overview returns an LLM-generated overview of the given documents,
// styled with markdown.
// The kind argument is a descriptor for the given documents, used to add
// additional context to the LLM prompt.
// Overview returns an error if no documents are provided or the LLM is unable
// to generate a response.
// TODO(tatianabradley): Chunk large prompts.
func Overview(ctx context.Context, g llm.TextGenerator, kind docsKind, docs ...*Doc) (string, error) {
if len(docs) == 0 {
return "", errors.New("llmapp.Overview: no documents")
}
return g.GenerateText(ctx, OverviewPrompt(kind, docs)...)
}
// OverviewPrompt converts the given docs into a slice of
// text prompts, followed by an instruction prompt based
// on the documents kind.
func OverviewPrompt(kind docsKind, docs []*Doc) []string {
var inputs = make([]string, len(docs))
for i, d := range docs {
inputs[i] = string(storage.JSON(d))
}
return append(inputs, kind.instructions())
}
// docsKind is a descriptor for a group of documents.
// unexported so that clients must use a predefined kind.
type docsKind struct{ name string }
var (
// Represents a group of documents of an unspecified kind.
Documents docsKind = docsKind{"documents"}
// The documents represent a post and comments/replies
// on that post. For example, a GitHub issue and its comments.
PostAndComments docsKind = docsKind{"post_and_comments"}
)
//go:embed prompts/*.tmpl
var promptFS embed.FS
var tmpls = template.Must(template.ParseFS(promptFS, "prompts/*.tmpl"))
// instructions returns the instruction prompt for the given
// document kind.
func (k docsKind) instructions() string {
w := &strings.Builder{}
err := tmpls.ExecuteTemplate(w, k.name, nil)
if err != nil {
// unreachable except bug in this package
panic(err)
}
return w.String()
}