Skip to content

Commit

Permalink
[WIP] Integrate OpenAI
Browse files Browse the repository at this point in the history
  • Loading branch information
mput committed Jun 23, 2024
1 parent a67bbd2 commit 847a19f
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 57 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export URL="YOUR_URL"
export GIT_URL="YOUR_GIT_URL"
## Fine-grained personal access tokens for repo with RW Contents scope
export GIT_ACCESS_TOKEN="YOUR_GIT"
export OPENAI_TOKEN="YOUR_OPEN_AI_TOKEN"
24 changes: 19 additions & 5 deletions app/bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ type Opts struct {
MainLedgerFile string `long:"main-ledger-file" env:"MAIN_LEDGER_FILE" required:"true" description:"main ledger file path from the repo root"`
} `group:"github" namespace:"github" env-namespace:"GITHUB"`

OpenAI struct {
Token string `long:"token" env:"TOKEN" required:"true" description:"openai api token"`
} `group:"openai" namespace:"openai" env-namespace:"OPENAI"`

URL string `long:"url" env:"URL" required:"true" description:"bot url"`
Debug bool `long:"debug" env:"DEBUG" description:"debug mode"`
Version string
Expand All @@ -44,7 +48,11 @@ func NewBot(opts *Opts) (*Bot, error) {
}

rs := repo.NewInMemoryRepo(opts.Github.URL, opts.Github.Token)
ldgr := ledger.NewLedger(rs, nil, opts.Github.MainLedgerFile, true)
llmGenerator := ledger.OpenAITransactionGenerator{
Token: opts.OpenAI.Token,
}

ldgr := ledger.NewLedger(rs, llmGenerator, opts.Github.MainLedgerFile, true)
tel := teledger.NewTeledger(ldgr)

return &Bot{
Expand Down Expand Up @@ -82,7 +90,7 @@ func (bot *Bot) Start() error {
dispatcher.AddHandler(handlers.NewCommand("version", wrapUserResponse(bot.vesrion, "version")))
dispatcher.AddHandler(handlers.NewCommand("/", wrapUserResponse(bot.comment, "comment")))
dispatcher.AddHandler(handlers.NewCommand("balance", wrapUserResponse(bot.bal, "balance")))
dispatcher.AddHandler(handlers.NewMessage(nil, wrapUserResponse(echo, "echo")))
dispatcher.AddHandler(handlers.NewMessage(nil, wrapUserResponse(bot.proposeTransaction, "propose-transaction")))



Expand Down Expand Up @@ -182,8 +190,14 @@ func (bot *Bot) comment(ctx *ext.Context) (string, *gotgbot.SendMessageOpts, err
return fmt.Sprintf("```\n%s\n```", comment), &gotgbot.SendMessageOpts{ParseMode: "MarkdownV2"}, nil
}


func echo(ctx *ext.Context) (string, *gotgbot.SendMessageOpts, error) {
func (bot *Bot) proposeTransaction(ctx *ext.Context) (string, *gotgbot.SendMessageOpts, error) {
msg := ctx.EffectiveMessage
return fmt.Sprintf("Message received! (%s)", msg.Text), nil, nil

transaction, err := bot.teledger.ProposeTransaction(msg.Text)

if err != nil {
return fmt.Sprintf("Error: %v", err), nil, nil
}

return fmt.Sprintf("```\n%s\n```", transaction), &gotgbot.SendMessageOpts{ParseMode: "MarkdownV2"}, nil
}
54 changes: 13 additions & 41 deletions app/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/mput/teledger/app/repo"
"github.com/mput/teledger/app/utils"
_ "embed"
)

// Ledger is a wrapper around the ledger command line tool
Expand All @@ -33,6 +34,11 @@ func NewLedger(rs repo.Service,gen TransactionGenerator , mainFile string, stric

const ledgerBinary = "ledger"


//go:embed templates/default_prompt.txt
var defaultPrompt string


func resolveIncludesReader(rs repo.Service, file string) (io.ReadCloser, error) {
ledgerFile, err := rs.Open(file)
if err != nil {
Expand Down Expand Up @@ -191,7 +197,7 @@ func (l *Ledger) validate() error {
}

func (l *Ledger) validateWith(addition string) error {
_, err := l.execute("balance")
_, err := l.executeWith(addition, "balance")
return err
}

Expand Down Expand Up @@ -250,7 +256,7 @@ type Transaction struct {
Comment string
}

func (t *Transaction) toString() string {
func (t *Transaction) ToString() string {
var res strings.Builder
if t.Comment != "" {
res.WriteString(fmt.Sprintf(";; %s: %s\n",t.Date.Format("2006-01-02 15:04:05 Monday"), t.Comment))
Expand Down Expand Up @@ -279,7 +285,7 @@ type TransactionGenerator interface {
}

type OpenAITransactionGenerator struct {
token string
Token string
}

func (b OpenAITransactionGenerator) GenerateTransaction(promptCtx PromptCtx) (Transaction, error) {
Expand Down Expand Up @@ -394,7 +400,7 @@ func (l *Ledger) proposeTransaction(userInput string) (Transaction, error) {
return Transaction{}, fmt.Errorf("unable to generate transaction: %v", err)
}

_, err = l.executeWith(trx.toString(), "balance")
err = l.validateWith(trx.ToString())
if err != nil {
return Transaction{}, fmt.Errorf("unable to validate transaction: %v", err)
}
Expand All @@ -403,8 +409,8 @@ func (l *Ledger) proposeTransaction(userInput string) (Transaction, error) {

}

func (l *Ledger) ProposeTransaction(userInput string, times int) (tr Transaction,err error) {
if times <= 0 {
func (l *Ledger) ProposeTransaction(userInput string, attempts int) (tr Transaction,err error) {
if attempts <= 0 {
panic("times should be greater than 0")
}
err = l.repo.Init()
Expand All @@ -414,7 +420,7 @@ func (l *Ledger) ProposeTransaction(userInput string, times int) (tr Transaction
return tr, err
}

for i := 0; i < times; i++ {
for i := 0; i < attempts; i++ {
if i > 0 {
slog.Warn("retrying transaction generation", "attempt", i)
}
Expand All @@ -435,37 +441,3 @@ type PromptCtx struct {
}


const template = `
Your goal is to propose a transaction in the ledger cli format.
Your responses MUST be in JSON and adhere to the Transaction struct ONLY with no additional narrative or markup, backquotes or anything.
Bellow is the list of accounts you MUST use in your transaction:
{{range .Accounts}}
"{{.}}"
{{end}}
Use "EUR" as the default currency if nothing else is specified in user request.
Another possible currency is "USD", "RUB".
All descriptions should be in English.
// Transaction represents a single transaction in a ledger.
type Transaction struct {
Date time.Time json:"date" // The date of the transaction
Description string json:"description" // A description of the transaction
Postings []Posting json:"postings" // A slice of postings that belong to this transaction
}
// Posting represents a single posting in a transaction, linking an account with an amount and currency.
type Posting struct {
Account string json:"account" // The name of the account
Amount float64 json:"amount" // The amount posted to the account
Currency string json:"currency" // The currency of the amount
}
Use Assets:Cards:Wise-EUR as default account if nothing else is specified in user request
Bellow is the message from the USER for which you need to propose a transaction:
{{.UserInput}}
`
2 changes: 1 addition & 1 deletion app/ledger/ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ account Equity
Assets:Cash -30.43 EUR
Food 30.43 EUR
`,
tr.toString(),
tr.ToString(),
)
})

Expand Down
32 changes: 32 additions & 0 deletions app/ledger/templates/default_prompt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Your goal is to propose a transaction in the ledger cli format.
Your responses MUST be in JSON and adhere to the Transaction struct ONLY with no additional narrative or markup, backquotes or anything.

Bellow is the list of accounts you MUST use in your transaction:
{{range .Accounts}}
"{{.}}"
{{end}}


Use "EUR" as the default currency if nothing else is specified in user request.
Another possible currency is "USD", "RUB".
All descriptions should be in English.

// Transaction represents a single transaction in a ledger.
type Transaction struct {
Date time.Time json:"date" // The date of the transaction
Description string json:"description" // A description of the transaction
Postings []Posting json:"postings" // A slice of postings that belong to this transaction
}

// Posting represents a single posting in a transaction, linking an account with an amount and currency.
type Posting struct {
Account string json:"account" // The name of the account
Amount float64 json:"amount" // The amount posted to the account
Currency string json:"currency" // The currency of the amount
}

Use Assets:Cards:Wise-EUR as default account if nothing else is specified in user request

Bellow is the message from the USER for which you need to propose a transaction:

{{.UserInput}}
23 changes: 13 additions & 10 deletions app/teledger/teledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,17 @@ func (tel *Teledger) Balance() (string, error) {
}


// // Receive a short free-text description of a transaction
// // and propose a formatted transaction validated with the
// // ledger file.
// // Store the transaction in a state, so the user can confirm
// // or reject it.
// func (tel *Teledger) proposeTransaction(desc string) (string, error) {
// prompt := fmt.Sprintf("Proposed transaction:\n```\n%s\n```", desc)
// structuredTrx, err := tel.openai.GetStructuredResponse(desc, &transaction{})
// trx := structuredTrx.toString()
// Receive a short free-text description of a transaction
// and propose a formatted transaction validated with the
// ledger file.
// Store the transaction in a state, so the user can confirm
// or reject it.
func (tel *Teledger) ProposeTransaction(desc string) (string, error) {

// }
tr, err := tel.ledger.ProposeTransaction(desc, 2)
if err != nil {
return "", err
}

return tr.ToString(), nil
}

0 comments on commit 847a19f

Please sign in to comment.