Compare commits
4 Commits
0b06fd965e
...
function
Author | SHA1 | Date | |
---|---|---|---|
f4e9082ce4 | |||
0993a8e865 | |||
cd4ad59a38 | |||
37939088ed |
77
anthropic.go
77
anthropic.go
@@ -2,9 +2,13 @@ package go_llm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
anth "github.com/liushuangls/go-anthropic/v2"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
anth "github.com/liushuangls/go-anthropic/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type anthropic struct {
|
type anthropic struct {
|
||||||
@@ -60,17 +64,47 @@ func (a anthropic) requestToAnthropicRequest(req Request) anth.MessagesRequest {
|
|||||||
|
|
||||||
for _, img := range msg.Images {
|
for _, img := range msg.Images {
|
||||||
if img.Base64 != "" {
|
if img.Base64 != "" {
|
||||||
m.Content = append(m.Content, anth.NewImageMessageContent(anth.MessageContentImageSource{
|
m.Content = append(m.Content, anth.NewImageMessageContent(
|
||||||
Type: "base64",
|
anth.NewMessageContentSource(
|
||||||
MediaType: img.ContentType,
|
anth.MessagesContentSourceTypeBase64,
|
||||||
Data: img.Base64,
|
img.ContentType,
|
||||||
}))
|
img.Base64,
|
||||||
|
)))
|
||||||
} else if img.Url != "" {
|
} else if img.Url != "" {
|
||||||
m.Content = append(m.Content, anth.NewImageMessageContent(anth.MessageContentImageSource{
|
|
||||||
Type: "url",
|
// download the image
|
||||||
MediaType: img.ContentType,
|
cl, err := http.NewRequest(http.MethodGet, img.Url, nil)
|
||||||
Data: img.Url,
|
if err != nil {
|
||||||
}))
|
log.Println("failed to create request", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(cl)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to download image", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
img.ContentType = resp.Header.Get("Content-Type")
|
||||||
|
|
||||||
|
// read the image
|
||||||
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to read image", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// base64 encode the image
|
||||||
|
img.Base64 = string(b)
|
||||||
|
|
||||||
|
m.Content = append(m.Content, anth.NewImageMessageContent(
|
||||||
|
anth.NewMessageContentSource(
|
||||||
|
anth.MessagesContentSourceTypeBase64,
|
||||||
|
img.ContentType,
|
||||||
|
img.Base64,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +121,7 @@ func (a anthropic) requestToAnthropicRequest(req Request) anth.MessagesRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tool := range req.Toolbox {
|
for _, tool := range req.Toolbox.funcs {
|
||||||
res.Tools = append(res.Tools, anth.ToolDefinition{
|
res.Tools = append(res.Tools, anth.ToolDefinition{
|
||||||
Name: tool.Name,
|
Name: tool.Name,
|
||||||
Description: tool.Description,
|
Description: tool.Description,
|
||||||
@@ -120,13 +154,18 @@ func (a anthropic) responseToLLMResponse(in anth.MessagesResponse) Response {
|
|||||||
|
|
||||||
case anth.MessagesContentTypeToolUse:
|
case anth.MessagesContentTypeToolUse:
|
||||||
if msg.MessageContentToolUse != nil {
|
if msg.MessageContentToolUse != nil {
|
||||||
choice.Calls = append(choice.Calls, ToolCall{
|
b, e := json.Marshal(msg.MessageContentToolUse.Input)
|
||||||
ID: msg.MessageContentToolUse.ID,
|
if e != nil {
|
||||||
FunctionCall: FunctionCall{
|
log.Println("failed to marshal input", e)
|
||||||
Name: msg.MessageContentToolUse.Name,
|
} else {
|
||||||
Arguments: msg.MessageContentToolUse.Input,
|
choice.Calls = append(choice.Calls, ToolCall{
|
||||||
},
|
ID: msg.MessageContentToolUse.ID,
|
||||||
})
|
FunctionCall: FunctionCall{
|
||||||
|
Name: msg.MessageContentToolUse.Name,
|
||||||
|
Arguments: string(b),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
error.go
Normal file
21
error.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package go_llm
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Error is essentially just an error, but it is used to differentiate between a normal error and a fatal error.
|
||||||
|
type Error struct {
|
||||||
|
error
|
||||||
|
|
||||||
|
Source error
|
||||||
|
Parameter error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newError(parent error, err error) Error {
|
||||||
|
e := fmt.Errorf("%w: %w", parent, err)
|
||||||
|
return Error{
|
||||||
|
error: e,
|
||||||
|
|
||||||
|
Source: parent,
|
||||||
|
Parameter: err,
|
||||||
|
}
|
||||||
|
}
|
89
function.go
89
function.go
@@ -1,13 +1,92 @@
|
|||||||
package go_llm
|
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 {
|
type Function struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Strict bool `json:"strict,omitempty"`
|
Strict bool `json:"strict,omitempty"`
|
||||||
Parameters any `json:"parameters"`
|
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 {
|
type FunctionCall struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Arguments any `json:"arguments,omitempty"`
|
Arguments string `json:"arguments,omitempty"`
|
||||||
}
|
}
|
||||||
|
35
functions.go
Normal file
35
functions.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package go_llm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"gitea.stevedudenhoeffer.com/steve/go-llm/schema"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse takes a function pointer and returns a function object.
|
||||||
|
// fn must be a pointer to a function that takes a context.Context as its first argument, and then a struct that contains
|
||||||
|
// the parameters for the function. The struct must contain only the types: string, int, float64, bool, and pointers to
|
||||||
|
// those types.
|
||||||
|
// The struct parameters can have the following tags:
|
||||||
|
// - Description: a string that describes the parameter, passed to openaiImpl to tell it what the parameter is for
|
||||||
|
|
||||||
|
func NewFunction[T any](name string, description string, fn func(context.Context, T) (string, error)) *Function {
|
||||||
|
var o T
|
||||||
|
|
||||||
|
res := Function{
|
||||||
|
Name: name,
|
||||||
|
Description: description,
|
||||||
|
Parameters: schema.GetType(o),
|
||||||
|
fn: reflect.ValueOf(fn),
|
||||||
|
paramType: reflect.TypeOf(o),
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.fn.Kind() != reflect.Func {
|
||||||
|
panic("fn must be a function")
|
||||||
|
}
|
||||||
|
if res.paramType.Kind() != reflect.Struct {
|
||||||
|
panic("function parameter must be a struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res
|
||||||
|
}
|
65
go.mod
65
go.mod
@@ -3,43 +3,42 @@ module gitea.stevedudenhoeffer.com/steve/go-llm
|
|||||||
go 1.23.1
|
go 1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/liushuangls/go-anthropic/v2 v2.8.0
|
github.com/google/generative-ai-go v0.19.0
|
||||||
github.com/sashabaranov/go-openai v1.31.0
|
github.com/liushuangls/go-anthropic/v2 v2.13.0
|
||||||
|
github.com/sashabaranov/go-openai v1.36.0
|
||||||
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||||
|
google.golang.org/api v0.214.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.115.0 // indirect
|
cloud.google.com/go v0.117.0 // indirect
|
||||||
cloud.google.com/go/ai v0.8.0 // indirect
|
cloud.google.com/go/ai v0.9.0 // indirect
|
||||||
cloud.google.com/go/auth v0.6.0 // indirect
|
cloud.google.com/go/auth v0.13.0 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||||
cloud.google.com/go/longrunning v0.5.7 // indirect
|
cloud.google.com/go/longrunning v0.6.3 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/google/s2a-go v0.1.8 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/google/generative-ai-go v0.18.0 // indirect
|
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.26.0 // indirect
|
go.opentelemetry.io/otel v1.33.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.26.0 // indirect
|
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.26.0 // indirect
|
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.8.0 // indirect
|
||||||
google.golang.org/api v0.186.0 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect
|
google.golang.org/grpc v1.69.2 // indirect
|
||||||
google.golang.org/grpc v1.64.1 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
|
||||||
)
|
)
|
||||||
|
226
go.sum
226
go.sum
@@ -1,163 +1,87 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.117.0 h1:Z5TNFfQxj7WG2FgOGX1ekC5RiXrYgms6QscOm32M/4s=
|
||||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
cloud.google.com/go v0.117.0/go.mod h1:ZbwhVTb1DBGt2Iwb3tNO6SEK4q+cplHZmLWH+DelYYc=
|
||||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
cloud.google.com/go/ai v0.9.0 h1:r1Ig8O8+Qr3Ia3WfoO+gokD0fxB2Rk4quppuKjmGMsY=
|
||||||
cloud.google.com/go/ai v0.8.0 h1:rXUEz8Wp2OlrM8r1bfmpF2+VKqc1VJpafE3HgzRnD/w=
|
cloud.google.com/go/ai v0.9.0/go.mod h1:28bKM/oxmRgxmRgI1GLumFv+NSkt+DscAg/gF+54zzY=
|
||||||
cloud.google.com/go/ai v0.8.0/go.mod h1:t3Dfk4cM61sytiggo2UyGsDVW3RF1qGZaUKDrZFyqkE=
|
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
|
||||||
cloud.google.com/go/auth v0.6.0 h1:5x+d6b5zdezZ7gmLWD1m/xNjnaQ2YDhmIz/HH3doy1g=
|
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
|
||||||
cloud.google.com/go/auth v0.6.0/go.mod h1:b4acV+jLQDyjwm4OXHYjNvRi4jvGBzHWJRtJcy+2P4g=
|
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng=
|
||||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/generative-ai-go v0.18.0 h1:6ybg9vOCLcI/UpBBYXOTVgvKmcUKFRNj+2Cj3GnebSo=
|
github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaHSYgSpUgdg=
|
||||||
github.com/google/generative-ai-go v0.18.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
|
github.com/google/generative-ai-go v0.19.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||||
github.com/liushuangls/go-anthropic/v2 v2.8.0 h1:0zH2jDNycbrlszxnLrG+Gx8vVT0yJAPWU4s3ZTkWzgI=
|
github.com/liushuangls/go-anthropic/v2 v2.13.0 h1:f7KJ54IHxIpHPPhrCzs3SrdP2PfErXiJcJn7DUVstSA=
|
||||||
github.com/liushuangls/go-anthropic/v2 v2.8.0/go.mod h1:8BKv/fkeTaL5R9R9bGkaknYBueyw2WxY20o7bImbOek=
|
github.com/liushuangls/go-anthropic/v2 v2.13.0/go.mod h1:5ZwRLF5TQ+y5s/MC9Z1IJYx9WUFgQCKfqFM2xreIQLk=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/sashabaranov/go-openai v1.36.0 h1:fcSrn8uGuorzPWCBp8L0aCR95Zjb/Dd+ZSML0YZy9EI=
|
||||||
github.com/sashabaranov/go-openai v1.31.0 h1:rGe77x7zUeCjtS2IS7NCY6Tp4bQviXNMhkQM6hz/UC4=
|
github.com/sashabaranov/go-openai v1.36.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||||
github.com/sashabaranov/go-openai v1.31.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0=
|
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU=
|
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI=
|
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc=
|
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||||
go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
|
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||||
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
|
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||||
go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
|
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||||
go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
|
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||||
go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
|
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||||
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.186.0 h1:n2OPp+PPXX0Axh4GuSsL5QL8xQCTb2oDwyzPnQvqUug=
|
|
||||||
google.golang.org/api v0.186.0/go.mod h1:hvRbBmgoje49RV3xqVXrmP6w93n6ehGgIVPYrGtBFFc=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 h1:MuYw1wJzT+ZkybKfaOXKp5hJiZDn2iHaXRw0mRYdHSc=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 h1:Di6ANFilr+S60a4S61ZM00vLdw0IrQOSMS2/6mrnOU0=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
|
||||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
|
||||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
|
13
google.go
13
google.go
@@ -2,6 +2,7 @@ package go_llm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/generative-ai-go/genai"
|
"github.com/google/generative-ai-go/genai"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
@@ -30,8 +31,8 @@ func (g google) requestToGoogleRequest(in Request, model *genai.GenerativeModel)
|
|||||||
res = append(res, genai.Text(c.Text))
|
res = append(res, genai.Text(c.Text))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tool := range in.Toolbox {
|
for _, tool := range in.Toolbox.funcs {
|
||||||
panic("google toolbox is todo" + tool.Name)
|
panic("google ToolBox is todo" + tool.Name)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
t := genai.Tool{}
|
t := genai.Tool{}
|
||||||
@@ -63,11 +64,17 @@ func (g google) responseToLLMResponse(in *genai.GenerateContentResponse) (Respon
|
|||||||
choice := ResponseChoice{}
|
choice := ResponseChoice{}
|
||||||
|
|
||||||
choice.Content = v.Name
|
choice.Content = v.Name
|
||||||
|
b, e := json.Marshal(v.Args)
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
return Response{}, fmt.Errorf("error marshalling args: %w", e)
|
||||||
|
}
|
||||||
|
|
||||||
call := ToolCall{
|
call := ToolCall{
|
||||||
ID: v.Name,
|
ID: v.Name,
|
||||||
FunctionCall: FunctionCall{
|
FunctionCall: FunctionCall{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
Arguments: v.Args,
|
Arguments: string(b),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
llm.go
4
llm.go
@@ -27,7 +27,7 @@ type Message struct {
|
|||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Messages []Message
|
Messages []Message
|
||||||
Toolbox []Function
|
Toolbox *ToolBox
|
||||||
Temperature *float32
|
Temperature *float32
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ type LLM interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func OpenAI(key string) LLM {
|
func OpenAI(key string) LLM {
|
||||||
return openai{key: key}
|
return openaiImpl{key: key}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Anthropic(key string) LLM {
|
func Anthropic(key string) LLM {
|
||||||
|
30
openai.go
30
openai.go
@@ -7,14 +7,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type openai struct {
|
type openaiImpl struct {
|
||||||
key string
|
key string
|
||||||
model string
|
model string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ LLM = openai{}
|
var _ LLM = openaiImpl{}
|
||||||
|
|
||||||
func (o openai) requestToOpenAIRequest(request Request) oai.ChatCompletionRequest {
|
func (o openaiImpl) requestToOpenAIRequest(request Request) oai.ChatCompletionRequest {
|
||||||
res := oai.ChatCompletionRequest{
|
res := oai.ChatCompletionRequest{
|
||||||
Model: o.model,
|
Model: o.model,
|
||||||
}
|
}
|
||||||
@@ -57,16 +57,18 @@ func (o openai) requestToOpenAIRequest(request Request) oai.ChatCompletionReques
|
|||||||
res.Messages = append(res.Messages, m)
|
res.Messages = append(res.Messages, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tool := range request.Toolbox {
|
for _, tool := range request.Toolbox.funcs {
|
||||||
res.Tools = append(res.Tools, oai.Tool{
|
res.Tools = append(res.Tools, oai.Tool{
|
||||||
Type: "function",
|
Type: "function",
|
||||||
Function: &oai.FunctionDefinition{
|
Function: &oai.FunctionDefinition{
|
||||||
Name: tool.Name,
|
Name: tool.Name,
|
||||||
Description: tool.Description,
|
Description: tool.Description,
|
||||||
Strict: tool.Strict,
|
Strict: tool.Strict,
|
||||||
Parameters: tool.Parameters,
|
Parameters: tool.Parameters.Definition(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fmt.Println("tool:", tool.Name, tool.Description, tool.Strict, tool.Parameters.Definition())
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Temperature != nil {
|
if request.Temperature != nil {
|
||||||
@@ -90,12 +92,13 @@ func (o openai) requestToOpenAIRequest(request Request) oai.ChatCompletionReques
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o openai) responseToLLMResponse(response oai.ChatCompletionResponse) Response {
|
func (o openaiImpl) responseToLLMResponse(response oai.ChatCompletionResponse) Response {
|
||||||
res := Response{}
|
res := Response{}
|
||||||
|
|
||||||
for _, choice := range response.Choices {
|
for _, choice := range response.Choices {
|
||||||
var tools []ToolCall
|
var toolCalls []ToolCall
|
||||||
for _, call := range choice.Message.ToolCalls {
|
for _, call := range choice.Message.ToolCalls {
|
||||||
|
fmt.Println("responseToLLMResponse: call:", call.Function.Arguments)
|
||||||
toolCall := ToolCall{
|
toolCall := ToolCall{
|
||||||
ID: call.ID,
|
ID: call.ID,
|
||||||
FunctionCall: FunctionCall{
|
FunctionCall: FunctionCall{
|
||||||
@@ -104,7 +107,9 @@ func (o openai) responseToLLMResponse(response oai.ChatCompletionResponse) Respo
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tools = append(tools, toolCall)
|
fmt.Println("toolCall.FunctionCall.Arguments:", toolCall.FunctionCall.Arguments)
|
||||||
|
|
||||||
|
toolCalls = append(toolCalls, toolCall)
|
||||||
|
|
||||||
}
|
}
|
||||||
res.Choices = append(res.Choices, ResponseChoice{
|
res.Choices = append(res.Choices, ResponseChoice{
|
||||||
@@ -112,13 +117,14 @@ func (o openai) responseToLLMResponse(response oai.ChatCompletionResponse) Respo
|
|||||||
Role: Role(choice.Message.Role),
|
Role: Role(choice.Message.Role),
|
||||||
Name: choice.Message.Name,
|
Name: choice.Message.Name,
|
||||||
Refusal: choice.Message.Refusal,
|
Refusal: choice.Message.Refusal,
|
||||||
|
Calls: toolCalls,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o openai) ChatComplete(ctx context.Context, request Request) (Response, error) {
|
func (o openaiImpl) ChatComplete(ctx context.Context, request Request) (Response, error) {
|
||||||
cl := oai.NewClient(o.key)
|
cl := oai.NewClient(o.key)
|
||||||
|
|
||||||
req := o.requestToOpenAIRequest(request)
|
req := o.requestToOpenAIRequest(request)
|
||||||
@@ -128,14 +134,14 @@ func (o openai) ChatComplete(ctx context.Context, request Request) (Response, er
|
|||||||
fmt.Println("resp:", fmt.Sprintf("%#v", resp))
|
fmt.Println("resp:", fmt.Sprintf("%#v", resp))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Response{}, fmt.Errorf("unhandled openai error: %w", err)
|
return Response{}, fmt.Errorf("unhandled openaiImpl error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return o.responseToLLMResponse(resp), nil
|
return o.responseToLLMResponse(resp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o openai) ModelVersion(modelVersion string) (ChatCompletion, error) {
|
func (o openaiImpl) ModelVersion(modelVersion string) (ChatCompletion, error) {
|
||||||
return openai{
|
return openaiImpl{
|
||||||
key: o.key,
|
key: o.key,
|
||||||
model: modelVersion,
|
model: modelVersion,
|
||||||
}, nil
|
}, nil
|
||||||
|
125
schema/GetType.go
Normal file
125
schema/GetType.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetType will, given an interface{} that is a struct (NOT a pointer to a struct), return the Type of the struct that
|
||||||
|
// can be used to generate a json schema and build an object from a parsed json object.
|
||||||
|
func GetType(a any) Type {
|
||||||
|
t := reflect.TypeOf(a)
|
||||||
|
|
||||||
|
if t.Kind() != reflect.Struct {
|
||||||
|
panic("GetType expects a struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return getObject(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFromType(t reflect.Type, b basic) Type {
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
b.required = false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
b.DataType = jsonschema.String
|
||||||
|
return b
|
||||||
|
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
b.DataType = jsonschema.Integer
|
||||||
|
return b
|
||||||
|
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
b.DataType = jsonschema.Integer
|
||||||
|
return b
|
||||||
|
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
b.DataType = jsonschema.Number
|
||||||
|
return b
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
b.DataType = jsonschema.Boolean
|
||||||
|
return b
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
o := getObject(t)
|
||||||
|
|
||||||
|
o.basic.required = b.required
|
||||||
|
o.basic.index = b.index
|
||||||
|
o.basic.description = b.description
|
||||||
|
|
||||||
|
return o
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
return getArray(t)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("unhandled default case for " + t.Kind().String() + " in getFromType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getField(f reflect.StructField, index int) Type {
|
||||||
|
b := basic{
|
||||||
|
index: index,
|
||||||
|
required: true,
|
||||||
|
description: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
t := f.Type
|
||||||
|
|
||||||
|
// if the tag "description" is set, use that as the description
|
||||||
|
if desc, ok := f.Tag.Lookup("description"); ok {
|
||||||
|
b.description = desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// now if the tag "enum" is set, we need to create an enum type
|
||||||
|
if v, ok := f.Tag.Lookup("enum"); ok {
|
||||||
|
vals := strings.Split(v, ",")
|
||||||
|
|
||||||
|
for i := 0; i < len(vals); i++ {
|
||||||
|
vals[i] = strings.TrimSpace(vals[i])
|
||||||
|
|
||||||
|
if vals[i] == "" {
|
||||||
|
vals = append(vals[:i], vals[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return enum{
|
||||||
|
basic: b,
|
||||||
|
values: vals,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFromType(t, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getObject(t reflect.Type) object {
|
||||||
|
fields := make(map[string]Type, t.NumField())
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
fields[field.Name] = getField(field, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return object{
|
||||||
|
basic: basic{DataType: jsonschema.Object},
|
||||||
|
fields: fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getArray(t reflect.Type) array {
|
||||||
|
res := array{
|
||||||
|
basic: basic{
|
||||||
|
DataType: jsonschema.Array,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res.items = getFromType(t.Elem(), basic{})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
65
schema/array.go
Normal file
65
schema/array.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type array struct {
|
||||||
|
basic
|
||||||
|
|
||||||
|
// items is the schema of the items in the array
|
||||||
|
items Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a array) SchemaType() jsonschema.DataType {
|
||||||
|
return jsonschema.Array
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a array) Definition() jsonschema.Definition {
|
||||||
|
def := a.basic.Definition()
|
||||||
|
def.Type = jsonschema.Array
|
||||||
|
i := a.items.Definition()
|
||||||
|
def.Items = &i
|
||||||
|
def.AdditionalProperties = false
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a array) FromAny(val any) (reflect.Value, error) {
|
||||||
|
v := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
// first realize we may have a pointer to a slice if this type is not required
|
||||||
|
if !a.required && v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Slice {
|
||||||
|
return reflect.Value{}, errors.New("expected slice, got " + v.Kind().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the slice is nil, we can just return it
|
||||||
|
if v.IsNil() {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the slice is not nil, we need to convert each item
|
||||||
|
items := make([]reflect.Value, v.Len())
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
item, err := a.items.FromAny(v.Index(i).Interface())
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, err
|
||||||
|
}
|
||||||
|
items[i] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.ValueOf(items), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a array) SetValue(obj reflect.Value, val reflect.Value) {
|
||||||
|
if !a.required {
|
||||||
|
val = val.Addr()
|
||||||
|
}
|
||||||
|
obj.Field(a.index).Set(val)
|
||||||
|
}
|
105
schema/basic.go
Normal file
105
schema/basic.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// just enforcing that basic implements Type
|
||||||
|
var _ Type = basic{}
|
||||||
|
|
||||||
|
type basic struct {
|
||||||
|
jsonschema.DataType
|
||||||
|
|
||||||
|
// index is the position of the parameter in the StructField of the function's parameter struct
|
||||||
|
index int
|
||||||
|
|
||||||
|
// required is a flag that indicates whether the parameter is required in the function's parameter struct.
|
||||||
|
// this is inferred by if the parameter is a pointer type or not.
|
||||||
|
required bool
|
||||||
|
|
||||||
|
// description is a llm-readable description of the parameter passed to openai
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b basic) SchemaType() jsonschema.DataType {
|
||||||
|
return b.DataType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b basic) Definition() jsonschema.Definition {
|
||||||
|
return jsonschema.Definition{
|
||||||
|
Type: b.DataType,
|
||||||
|
Description: b.description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b basic) Required() bool {
|
||||||
|
return b.required
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b basic) Description() string {
|
||||||
|
return b.description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b basic) FromAny(val any) (reflect.Value, error) {
|
||||||
|
v := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
switch b.DataType {
|
||||||
|
case jsonschema.String:
|
||||||
|
var val = v.String()
|
||||||
|
|
||||||
|
return reflect.ValueOf(val), nil
|
||||||
|
|
||||||
|
case jsonschema.Integer:
|
||||||
|
if v.Kind() == reflect.Float64 {
|
||||||
|
return v.Convert(reflect.TypeOf(int(0))), nil
|
||||||
|
} else if v.Kind() != reflect.Int {
|
||||||
|
return reflect.Value{}, errors.New("expected int, got " + v.Kind().String())
|
||||||
|
} else {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case jsonschema.Number:
|
||||||
|
if v.Kind() == reflect.Float64 {
|
||||||
|
return v.Convert(reflect.TypeOf(float64(0))), nil
|
||||||
|
} else if v.Kind() != reflect.Float64 {
|
||||||
|
return reflect.Value{}, errors.New("expected float64, got " + v.Kind().String())
|
||||||
|
} else {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case jsonschema.Boolean:
|
||||||
|
if v.Kind() == reflect.Bool {
|
||||||
|
return v, nil
|
||||||
|
} else if v.Kind() == reflect.String {
|
||||||
|
b, err := strconv.ParseBool(v.String())
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, errors.New("expected bool, got " + v.Kind().String())
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(b), nil
|
||||||
|
} else {
|
||||||
|
return reflect.Value{}, errors.New("expected bool, got " + v.Kind().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return reflect.Value{}, errors.New("unknown type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b basic) SetValueOnField(obj reflect.Value, val reflect.Value) {
|
||||||
|
// if this basic type is not required that means it's a pointer type
|
||||||
|
// so we need to create a new value of the type of the pointer
|
||||||
|
if !b.required {
|
||||||
|
vv := reflect.New(obj.Field(b.index).Type().Elem())
|
||||||
|
|
||||||
|
// and then set the value of the pointer to the new value
|
||||||
|
vv.Elem().Set(val)
|
||||||
|
|
||||||
|
obj.Field(b.index).Set(vv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.Field(b.index).Set(val)
|
||||||
|
}
|
47
schema/enum.go
Normal file
47
schema/enum.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type enum struct {
|
||||||
|
basic
|
||||||
|
|
||||||
|
values []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e enum) SchemaType() jsonschema.DataType {
|
||||||
|
return jsonschema.String
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e enum) Definition() jsonschema.Definition {
|
||||||
|
def := e.basic.Definition()
|
||||||
|
def.Enum = e.values
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e enum) FromAny(val any) (reflect.Value, error) {
|
||||||
|
v := reflect.ValueOf(val)
|
||||||
|
if v.Kind() != reflect.String {
|
||||||
|
return reflect.Value{}, errors.New("expected string, got " + v.Kind().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
s := v.String()
|
||||||
|
if !slices.Contains(e.values, s) {
|
||||||
|
return reflect.Value{}, errors.New("value " + s + " not in enum")
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e enum) SetValueOnField(obj reflect.Value, val reflect.Value) {
|
||||||
|
if !e.required {
|
||||||
|
val = val.Addr()
|
||||||
|
}
|
||||||
|
obj.Field(e.index).Set(val)
|
||||||
|
}
|
78
schema/object.go
Normal file
78
schema/object.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type object struct {
|
||||||
|
basic
|
||||||
|
|
||||||
|
ref reflect.Type
|
||||||
|
|
||||||
|
fields map[string]Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o object) SchemaType() jsonschema.DataType {
|
||||||
|
return jsonschema.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o object) Definition() jsonschema.Definition {
|
||||||
|
def := o.basic.Definition()
|
||||||
|
def.Type = jsonschema.Object
|
||||||
|
def.Properties = make(map[string]jsonschema.Definition)
|
||||||
|
for k, v := range o.fields {
|
||||||
|
def.Properties[k] = v.Definition()
|
||||||
|
}
|
||||||
|
|
||||||
|
def.AdditionalProperties = false
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o object) FromAny(val any) (reflect.Value, error) {
|
||||||
|
// if the value is nil, we can't do anything
|
||||||
|
if val == nil {
|
||||||
|
return reflect.Value{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// now make a new object of the type we're trying to parse
|
||||||
|
obj := reflect.New(o.ref).Elem()
|
||||||
|
|
||||||
|
// now we need to iterate over the fields and set the values
|
||||||
|
for k, v := range o.fields {
|
||||||
|
// get the field by name
|
||||||
|
field := obj.FieldByName(k)
|
||||||
|
if !field.IsValid() {
|
||||||
|
return reflect.Value{}, errors.New("field " + k + " not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the value from the map
|
||||||
|
val2, ok := val.(map[string]interface{})[k]
|
||||||
|
if !ok {
|
||||||
|
return reflect.Value{}, errors.New("field " + k + " not found in map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we need to convert the value to the correct type
|
||||||
|
val3, err := v.FromAny(val2)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we need to set the value on the field
|
||||||
|
v.SetValueOnField(field, val3)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o object) SetValueOnField(obj reflect.Value, val reflect.Value) {
|
||||||
|
// if this basic type is not required that means it's a pointer type so we need to set the value to the address of the value
|
||||||
|
if !o.required {
|
||||||
|
val = val.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.Field(o.index).Set(val)
|
||||||
|
}
|
18
schema/type.go
Normal file
18
schema/type.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/sashabaranov/go-openai/jsonschema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Type interface {
|
||||||
|
SchemaType() jsonschema.DataType
|
||||||
|
Definition() jsonschema.Definition
|
||||||
|
|
||||||
|
Required() bool
|
||||||
|
Description() string
|
||||||
|
|
||||||
|
FromAny(any) (reflect.Value, error)
|
||||||
|
SetValueOnField(obj reflect.Value, val reflect.Value)
|
||||||
|
}
|
79
toolbox.go
Normal file
79
toolbox.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package go_llm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/sashabaranov/go-openai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToolBox is a collection of tools that OpenAI can use to execute functions.
|
||||||
|
// It is a wrapper around a collection of functions, and provides a way to automatically call the correct function with
|
||||||
|
// the correct parameters.
|
||||||
|
type ToolBox struct {
|
||||||
|
funcs []Function
|
||||||
|
names map[string]Function
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewToolBox(fns ...*Function) *ToolBox {
|
||||||
|
res := ToolBox{
|
||||||
|
funcs: []Function{},
|
||||||
|
names: map[string]Function{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fns {
|
||||||
|
o := *f
|
||||||
|
res.names[o.Name] = o
|
||||||
|
res.funcs = append(res.funcs, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ToolBox) WithFunction(f Function) *ToolBox {
|
||||||
|
t2 := *t
|
||||||
|
t2.names[f.Name] = f
|
||||||
|
t2.funcs = append(t2.funcs, f)
|
||||||
|
|
||||||
|
return &t2
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToOpenAI will convert the current ToolBox to a slice of openai.Tool, which can be used to send to the OpenAI API.
|
||||||
|
func (t *ToolBox) toOpenAI() []openai.Tool {
|
||||||
|
var res []openai.Tool
|
||||||
|
|
||||||
|
for _, f := range t.funcs {
|
||||||
|
res = append(res, openai.Tool{
|
||||||
|
Type: "function",
|
||||||
|
Function: f.toOpenAIFunction(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ToolBox) ToToolChoice() any {
|
||||||
|
if len(t.funcs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "required"
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFunctionNotFound = errors.New("function not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *ToolBox) ExecuteFunction(ctx context.Context, functionName string, params string) (string, error) {
|
||||||
|
f, ok := t.names[functionName]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return "", newError(ErrFunctionNotFound, fmt.Errorf("function \"%s\" not found", functionName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Execute(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ToolBox) Execute(ctx context.Context, toolCall ToolCall) (string, error) {
|
||||||
|
return t.ExecuteFunction(ctx, toolCall.FunctionCall.Name, toolCall.FunctionCall.Arguments)
|
||||||
|
}
|
Reference in New Issue
Block a user