Steve Dudenhoeffer
0993a8e865
Modify `FunctionCall` struct to handle arguments as strings. Add debugging logs to facilitate error tracing and improve JSON unmarshalling in various functions.
93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
package go_llm
|
|
|
|
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"
|
|
)
|
|
|
|
type Function struct {
|
|
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
|
|
p := reflect.New(f.paramType)
|
|
fmt.Println("Function.Execute", f.Name, "input:", input)
|
|
//m := map[string]any{}
|
|
err := json.Unmarshal([]byte(input), p.Interface())
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to unmarshal input: %w (input: %s)", err, input)
|
|
}
|
|
|
|
// now we can call the function
|
|
exec := func(ctx context.Context) (string, error) {
|
|
out := f.fn.Call([]reflect.Value{reflect.ValueOf(ctx), p.Elem()})
|
|
|
|
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
|
|
}
|
|
|
|
type FunctionCall struct {
|
|
Name string `json:"name,omitempty"`
|
|
Arguments string `json:"arguments,omitempty"`
|
|
}
|