2024-10-06 21:02:26 -04:00
|
|
|
package go_llm
|
2024-10-06 20:01:01 -04:00
|
|
|
|
2024-11-08 20:53:12 -05:00
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/go-llm/schema"
|
|
|
|
"github.com/sashabaranov/go-openai"
|
|
|
|
"github.com/sashabaranov/go-openai/jsonschema"
|
|
|
|
"reflect"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2024-10-06 20:01:01 -04:00
|
|
|
type Function struct {
|
2024-11-08 20:53:12 -05:00
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
Strict bool `json:"strict,omitempty"`
|
|
|
|
Parameters schema.Type `json:"parameters"`
|
|
|
|
|
|
|
|
Forced bool `json:"forced,omitempty"`
|
|
|
|
|
|
|
|
// Timeout is the maximum time to wait for the function to complete
|
|
|
|
Timeout time.Duration `json:"-"`
|
|
|
|
|
|
|
|
// fn is the function to call, only set if this is constructed with NewFunction
|
|
|
|
fn reflect.Value
|
|
|
|
|
|
|
|
paramType reflect.Type
|
|
|
|
|
|
|
|
// definition is a cache of the openaiImpl jsonschema definition
|
|
|
|
definition *jsonschema.Definition
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Function) Execute(ctx context.Context, input string) (string, error) {
|
|
|
|
if !f.fn.IsValid() {
|
|
|
|
return "", fmt.Errorf("function %s is not implemented", f.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// first, we need to parse the input into the struct
|
2024-11-11 00:23:01 -05:00
|
|
|
p := reflect.New(f.paramType)
|
|
|
|
fmt.Println("Function.Execute", f.Name, "input:", input)
|
2024-11-08 20:53:12 -05:00
|
|
|
//m := map[string]any{}
|
2024-11-11 00:23:01 -05:00
|
|
|
err := json.Unmarshal([]byte(input), p.Interface())
|
2024-11-08 20:53:12 -05:00
|
|
|
if err != nil {
|
2024-11-11 00:23:01 -05:00
|
|
|
return "", fmt.Errorf("failed to unmarshal input: %w (input: %s)", err, input)
|
2024-11-08 20:53:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// now we can call the function
|
|
|
|
exec := func(ctx context.Context) (string, error) {
|
2024-11-11 00:23:01 -05:00
|
|
|
out := f.fn.Call([]reflect.Value{reflect.ValueOf(ctx), p.Elem()})
|
2024-11-08 20:53:12 -05:00
|
|
|
|
|
|
|
if len(out) != 2 {
|
|
|
|
return "", fmt.Errorf("function %s must return two values, got %d", f.Name, len(out))
|
|
|
|
}
|
|
|
|
|
|
|
|
if out[1].IsNil() {
|
|
|
|
return out[0].String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", out[1].Interface().(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
var cancel context.CancelFunc
|
|
|
|
if f.Timeout > 0 {
|
|
|
|
ctx, cancel = context.WithTimeout(ctx, f.Timeout)
|
|
|
|
defer cancel()
|
|
|
|
}
|
|
|
|
|
|
|
|
return exec(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Function) toOpenAIFunction() *openai.FunctionDefinition {
|
|
|
|
return &openai.FunctionDefinition{
|
|
|
|
Name: f.Name,
|
|
|
|
Description: f.Description,
|
|
|
|
Strict: f.Strict,
|
|
|
|
Parameters: f.Parameters,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (f *Function) toOpenAIDefinition() jsonschema.Definition {
|
|
|
|
if f.definition == nil {
|
|
|
|
def := f.Parameters.Definition()
|
|
|
|
f.definition = &def
|
|
|
|
}
|
|
|
|
|
|
|
|
return *f.definition
|
2024-10-06 20:01:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type FunctionCall struct {
|
|
|
|
Name string `json:"name,omitempty"`
|
2024-11-11 00:23:01 -05:00
|
|
|
Arguments string `json:"arguments,omitempty"`
|
2024-10-06 20:01:01 -04:00
|
|
|
}
|