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

feat(process): config func, process history #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 2 additions & 6 deletions agency.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (

// Operation is basic building block.
type Operation struct {
handler OperationHandler
config *OperationConfig
handler OperationHandler // handler must never be nil
config *OperationConfig // config is a pointer because it must be possible to modify it, but it must never be nil
}

// OperationHandler is a function that implements logic.
Expand All @@ -22,10 +22,6 @@ type OperationConfig struct {
Messages []Message
}

func (p *Operation) Config() *OperationConfig {
return p.config
}

// NewOperation allows to create an operation from a function.
func NewOperation(handler OperationHandler) *Operation {
return &Operation{
Expand Down
85 changes: 85 additions & 0 deletions examples/agent_swarm/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import (
"context"
"fmt"
"os"

_ "github.com/joho/godotenv/autoload"

"github.com/neurocult/agency"
"github.com/neurocult/agency/providers/openai"
)

func main() {
provider := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})
params := openai.TextToTextParams{
Model: "gpt-3.5-turbo",
Temperature: openai.Temperature(0),
MaxTokens: 100,
}

var (
msg agency.Message = agency.UserMessage("Flying cars") // this is what we start with
chatHistory []agency.Message // we gonna use this to accumulate history between iterations
)

// FIXME currently this example does not work
// maybe we need to be able to set initial history for the process to implement this easily
// current approach cannot be working because on iteration 2 steps 2 and 3 are missing the history of the previous iterations
// also could be related to the fact that history lacks initial message
for i := 0; i < 3; i++ {
output, curHistory, err := agency.NewProcess( // on each iteration we create new process with its own execution context
// writer
agency.ProcessStep{
ConfigFunc: injectHistory,
Operation: provider.
TextToText(params).
SetPrompt("Create slogan for a given context. A short but catchy phrase").
SetMessages(chatHistory), // start with the history from the previous iteration
},
// critic
agency.ProcessStep{
ConfigFunc: injectHistory, // use history from the previous iteration plus previous step
Operation: provider.
TextToText(params).
SetPrompt("Criticize the given slogan. Find its weaknesses and suggest improvements"),
},
// censor
agency.ProcessStep{
ConfigFunc: injectHistory, // use history from the previous iteration plus two previous steps
Operation: provider.
TextToText(params).
SetPrompt(
"You are a safe guard. Text must not contain expectations about future. If you see anything happy, point to it so it can be removed.",
),
},
).Execute(
context.Background(),
msg,
logStep,
)

if err != nil {
panic(err)
}

chatHistory = curHistory.All()
chatHistory = chatHistory[0 : len(chatHistory)-1] // remove last output to avoid duplication, we will have it as the input
msg = output
}

fmt.Printf("RESULT: %v\n\n", msg)
}

// injectHistory uses history passed in by the process
// and injects in into the configuration of the operation so operation handler has access to history.
func injectHistory(history agency.ProcessHistory, cfg *agency.OperationConfig) error {
cfg.Messages = append(cfg.Messages, history.All()...)
return nil
}

// logStep simply prints data related to each step execution. It implements interceptor interface.
func logStep(in, out agency.Message, cfg agency.OperationConfig, stepIndex uint) {
fmt.Printf("---\n\nSTEP %d executed\n\nINPUT: %v\n\nCONFIG: %v\n\nOUTPUT: %v\n\n", stepIndex, in, cfg, out)
}
2 changes: 1 addition & 1 deletion examples/custom_operation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
func main() {
increment := agency.NewOperation(incrementFunc)

msg, err := agency.NewProcess(
msg, _, err := agency.ProcessFromOperations(
increment, increment, increment,
).Execute(context.Background(), agency.UserMessage("0"))

Expand Down
4 changes: 2 additions & 2 deletions examples/logging/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func main() {
factory := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})
params := openai.TextToTextParams{Model: "gpt-3.5-turbo"}

_, err := agency.NewProcess(
_, _, err := agency.ProcessFromOperations(
factory.TextToText(params).SetPrompt("explain what that means"),
factory.TextToText(params).SetPrompt("translate to russian"),
factory.TextToText(params).SetPrompt("replace all spaces with '_'"),
Expand All @@ -31,6 +31,6 @@ func main() {
}
}

func Logger(input, output agency.Message, cfg *agency.OperationConfig) {
func Logger(input, output agency.Message, cfg agency.OperationConfig, _ uint) {
fmt.Printf("in: %v\nprompt: %v\nout: %v\n\n", input, cfg.Prompt, output)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,31 @@ import (
"github.com/neurocult/agency/providers/openai"
)

type Saver []agency.Message

func (s *Saver) Save(input, output agency.Message, _ *agency.OperationConfig) {
*s = append(*s, output)
}

func main() {
factory := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})
provider := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})

// step 1
hear := factory.
hear := provider.
SpeechToText(openai.SpeechToTextParams{
Model: goopenai.Whisper1,
})

// step2
translate := factory.
translate := provider.
TextToText(openai.TextToTextParams{
Model: "gpt-3.5-turbo",
Temperature: openai.Temperature(0.5),
}).
SetPrompt("translate to russian")

// step 3
uppercase := factory.
uppercase := provider.
TextToText(openai.TextToTextParams{
Model: "gpt-3.5-turbo",
Temperature: openai.Temperature(1),
}).
SetPrompt("uppercase every letter of the text")

saver := Saver{}

sound, err := os.ReadFile("speech.mp3")
if err != nil {
panic(err)
Expand All @@ -54,16 +46,16 @@ func main() {
ctx := context.Background()
speechMsg := agency.Message{Content: sound}

_, err = agency.NewProcess(
_, history, err := agency.ProcessFromOperations(
hear,
translate,
uppercase,
).Execute(ctx, speechMsg, saver.Save)
).Execute(ctx, speechMsg)
if err != nil {
panic(err)
}

for _, msg := range saver {
for _, msg := range history.All() {
fmt.Println(msg.String())
}
}
57 changes: 57 additions & 0 deletions examples/process_with_config_func/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"context"
"fmt"
"os"

_ "github.com/joho/godotenv/autoload"

"github.com/neurocult/agency"
"github.com/neurocult/agency/providers/openai"
)

// In this example we demonstrate how we can use config func to build a process where 3 step uses the result of the 1 step
func main() {
provider := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})
params := openai.TextToTextParams{
Model: "gpt-3.5-turbo",
Temperature: openai.Temperature(0),
MaxTokens: 100,
}

result, _, err := agency.NewProcess(
agency.ProcessStep{
Operation: provider.
TextToText(params).
SetPrompt("Increase the number by adding 1 to it. Answer only in numbers, without text"),
},
agency.ProcessStep{
Operation: provider.
TextToText(params).
SetPrompt("Double the number. Answer only in numbers, without text"),
},
agency.ProcessStep{
ConfigFunc: func(history agency.ProcessHistory, cfg *agency.OperationConfig) error {
firstStepResult, _ := history.Get(0) // we ignore error because it's obvious first step exist at the time third executed
cfg.Prompt = fmt.Sprintf("Add %s", firstStepResult) // we override the prompt with the result of the first step
return nil
},
Operation: provider.TextToText(params), // Note that we don't use SetPrompt because we already set prompt in config func
},
).Execute(
context.Background(),
agency.UserMessage("5"),
logStep,
)

if err != nil {
panic(err)
}

fmt.Println(result)
}

func logStep(in, out agency.Message, cfg agency.OperationConfig, stepIndex uint) {
fmt.Printf("---\n\nSTEP %d executed\n\nINPUT: %v\n\nCONFIG: %v\n\nOUTPUT: %v\n\n", stepIndex, in, cfg, out)
}
2 changes: 1 addition & 1 deletion examples/rag_vector_database/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func main() {
Model: "tts-1", ResponseFormat: "mp3", Speed: 1, Voice: "onyx",
})

result, err := agency.NewProcess(
result, _, err := agency.ProcessFromOperations(
retrieve,
summarize,
voice,
Expand Down
4 changes: 2 additions & 2 deletions examples/speech_to_text/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import (
)

func main() {
factory := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})
provider := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})

data, err := os.ReadFile("speech.mp3")
if err != nil {
panic(err)
}

result, err := factory.SpeechToText(openai.SpeechToTextParams{
result, err := provider.SpeechToText(openai.SpeechToTextParams{
Model: goopenai.Whisper1,
}).Execute(
context.Background(),
Expand Down
2 changes: 1 addition & 1 deletion examples/speech_to_text_to_image/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {
panic(err)
}

msg, err := agency.NewProcess(
msg, _, err := agency.ProcessFromOperations(
factory.SpeechToText(openai.SpeechToTextParams{Model: goopenai.Whisper1}),
factory.TextToImage(openai.TextToImageParams{
Model: goopenai.CreateImageModelDallE2,
Expand Down
4 changes: 2 additions & 2 deletions examples/translate_text/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import (
)

func main() {
factory := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})
provider := openai.New(openai.Params{Key: os.Getenv("OPENAI_API_KEY")})

result, err := factory.
result, err := provider.
TextToText(openai.TextToTextParams{Model: goopenai.GPT3Dot5Turbo}).
SetPrompt("You are a helpful assistant that translates English to French").
Execute(context.Background(), agency.UserMessage("I love programming."))
Expand Down
2 changes: 1 addition & 1 deletion messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package agency
import "fmt"

type Message struct {
Role Role
Role Role // TODO refine the model. Role only has something to do with the chat
Content []byte
}

Expand Down