diff --git a/broker/http_test.go b/broker/http_test.go index e5e701b2..cfd3089e 100644 --- a/broker/http_test.go +++ b/broker/http_test.go @@ -362,7 +362,6 @@ func BenchmarkSub32(b *testing.B) { sub(b, 32) } - func BenchmarkPub1(b *testing.B) { pub(b, 1) } diff --git a/broker/rabbitmq/rabbitmq.go b/broker/rabbitmq/rabbitmq.go index e35d1110..127661fa 100644 --- a/broker/rabbitmq/rabbitmq.go +++ b/broker/rabbitmq/rabbitmq.go @@ -45,8 +45,6 @@ type publication struct { err error } - - func (p *publication) Ack() error { return p.d.Ack(false) } diff --git a/cmd/cmd.go b/cmd/cmd.go index e10333d9..dace8684 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -4,17 +4,12 @@ package cmd import ( "fmt" "math/rand" - "os" "sort" + "os" "strings" "time" "github.com/urfave/cli/v2" - "go-micro.dev/v5/auth" - nbroker "go-micro.dev/v5/broker/nats" - rabbit "go-micro.dev/v5/broker/rabbitmq" - - "go-micro.dev/v5/broker" "go-micro.dev/v5/cache" "go-micro.dev/v5/cache/redis" "go-micro.dev/v5/client" @@ -26,6 +21,13 @@ import ( "go-micro.dev/v5/events" "go-micro.dev/v5/logger" mprofile "go-micro.dev/v5/profile" + "go-micro.dev/v5/auth" + "go-micro.dev/v5/broker" + nbroker "go-micro.dev/v5/broker/nats" + rabbit "go-micro.dev/v5/broker/rabbitmq" + "go-micro.dev/v5/genai" + "go-micro.dev/v5/genai/gemini" + "go-micro.dev/v5/genai/openai" "go-micro.dev/v5/registry" "go-micro.dev/v5/registry/consul" "go-micro.dev/v5/registry/etcd" @@ -246,6 +248,21 @@ var ( EnvVars: []string{"MICRO_CONFIG"}, Usage: "The source of the config to be used to get configuration", }, + &cli.StringFlag{ + Name: "genai", + EnvVars: []string{"MICRO_GENAI"}, + Usage: "GenAI provider to use (e.g. openai, gemini, noop)", + }, + &cli.StringFlag{ + Name: "genai_key", + EnvVars: []string{"MICRO_GENAI_KEY"}, + Usage: "GenAI API key", + }, + &cli.StringFlag{ + Name: "genai_model", + EnvVars: []string{"MICRO_GENAI_MODEL"}, + Usage: "GenAI model to use (optional)", + }, } DefaultBrokers = map[string]func(...broker.Option) broker.Broker{ @@ -295,6 +312,11 @@ var ( "redis": redis.NewRedisCache, } DefaultStreams = map[string]func(...events.Option) (events.Stream, error){} + + DefaultGenAI = map[string]func(...genai.Option) genai.GenAI{ + "openai": openai.New, + "gemini": gemini.New, + } ) func init() { @@ -367,6 +389,8 @@ func (c *cmd) Options() Options { } func (c *cmd) Before(ctx *cli.Context) error { + // Set GenAI provider from flags/env + setGenAIFromFlags(ctx) // If flags are set then use them otherwise do nothing var serverOpts []server.Option var clientOpts []client.Option @@ -799,3 +823,24 @@ func Register(cmds ...*cli.Command) { return app.Commands[i].Name < app.Commands[j].Name }) } + +func setGenAIFromFlags(ctx *cli.Context) { + provider := ctx.String("genai") + key := ctx.String("genai_key") + model := ctx.String("genai_model") + + switch provider { + case "openai": + if key == "" { + key = os.Getenv("OPENAI_API_KEY") + } + genai.DefaultGenAI = openai.New(genai.WithAPIKey(key), genai.WithModel(model)) + case "gemini": + if key == "" { + key = os.Getenv("GEMINI_API_KEY") + } + genai.DefaultGenAI = gemini.New(genai.WithAPIKey(key), genai.WithModel(model)) + default: + genai.DefaultGenAI = genai.Default + } +} diff --git a/events/natsjs/nats_test.go b/events/natsjs/nats_test.go index cff42adc..ea89a594 100644 --- a/events/natsjs/nats_test.go +++ b/events/natsjs/nats_test.go @@ -8,11 +8,11 @@ import ( "testing" "time" - "go-micro.dev/v5/events/natsjs" nserver "github.com/nats-io/nats-server/v2/server" "github.com/stretchr/testify/assert" "github.com/test-go/testify/require" "go-micro.dev/v5/events" + "go-micro.dev/v5/events/natsjs" ) type Payload struct { diff --git a/genai/default.go b/genai/default.go new file mode 100644 index 00000000..dffa7420 --- /dev/null +++ b/genai/default.go @@ -0,0 +1,16 @@ +package genai + +import ( + "sync" +) + +var ( + DefaultGenAI GenAI = &noopGenAI{} + defaultOnce sync.Once +) + +func SetDefault(g GenAI) { + defaultOnce.Do(func() { + DefaultGenAI = g + }) +} diff --git a/genai/gemini/gemini.go b/genai/gemini/gemini.go new file mode 100644 index 00000000..4e1c5cfc --- /dev/null +++ b/genai/gemini/gemini.go @@ -0,0 +1,161 @@ +package gemini + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "os" + + "go-micro.dev/v5/genai" +) + +// gemini implements the GenAI interface using Google Gemini 2.5 API. +type gemini struct { + options genai.Options +} + +func New(opts ...genai.Option) genai.GenAI { + var options genai.Options + for _, o := range opts { + o(&options) + } + if options.APIKey == "" { + options.APIKey = os.Getenv("GEMINI_API_KEY") + } + return &gemini{options: options} +} + +func (g *gemini) Generate(prompt string, opts ...genai.Option) (*genai.Result, error) { + options := g.options + for _, o := range opts { + o(&options) + } + ctx := context.Background() + + res := &genai.Result{Prompt: prompt, Type: options.Type} + + endpoint := options.Endpoint + if endpoint == "" { + endpoint = "https://generativelanguage.googleapis.com/v1beta/models/" + } + + var url string + var body map[string]interface{} + + // Determine model to use + var model string + switch options.Type { + case "image": + if options.Model != "" { + model = options.Model + } else { + model = "gemini-2.5-pro-vision" + } + url = endpoint + model + ":generateContent?key=" + options.APIKey + body = map[string]interface{}{ + "contents": []map[string]interface{}{ + {"parts": []map[string]string{{"text": prompt}}}, + }, + } + case "audio": + if options.Model != "" { + model = options.Model + } else { + model = "gemini-2.5-pro" + } + url = endpoint + model + ":generateContent?key=" + options.APIKey + body = map[string]interface{}{ + "contents": []map[string]interface{}{ + {"parts": []map[string]string{{"text": prompt}}}, + }, + "response_mime_type": "audio/wav", + } + case "text": + fallthrough + default: + if options.Model != "" { + model = options.Model + } else { + model = "gemini-2.5-pro" + } + url = endpoint + model + ":generateContent?key=" + options.APIKey + body = map[string]interface{}{ + "contents": []map[string]interface{}{ + {"parts": []map[string]string{{"text": prompt}}}, + }, + } + } + + b, _ := json.Marshal(body) + req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(b)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if options.Type == "audio" { + var result struct { + Candidates []struct { + Content struct { + Parts []struct { + InlineData struct { + Data []byte `json:"data"` + } `json:"inline_data"` + } `json:"parts"` + } `json:"content"` + } `json:"candidates"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + if len(result.Candidates) == 0 || len(result.Candidates[0].Content.Parts) == 0 { + return nil, fmt.Errorf("no audio returned") + } + res.Data = result.Candidates[0].Content.Parts[0].InlineData.Data + return res, nil + } + + var result struct { + Candidates []struct { + Content struct { + Parts []struct { + Text string `json:"text"` + } `json:"parts"` + } `json:"content"` + } `json:"candidates"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + if len(result.Candidates) == 0 || len(result.Candidates[0].Content.Parts) == 0 { + return nil, fmt.Errorf("no candidates returned") + } + res.Text = result.Candidates[0].Content.Parts[0].Text + return res, nil +} + +func (g *gemini) Stream(prompt string, opts ...genai.Option) (*genai.Stream, error) { + results := make(chan *genai.Result) + go func() { + defer close(results) + res, err := g.Generate(prompt, opts...) + if err != nil { + // Send error via Stream.Err, not channel + return + } + results <- res + }() + return &genai.Stream{Results: results}, nil +} + +func init() { + genai.Register("gemini", New()) +} diff --git a/genai/genai.go b/genai/genai.go new file mode 100644 index 00000000..58978ff5 --- /dev/null +++ b/genai/genai.go @@ -0,0 +1,53 @@ +// Package genai provides a generic interface for generative AI providers. +package genai + +// Result is the unified response from GenAI providers. +type Result struct { + Prompt string + Type string + Data []byte // for audio/image binary data + Text string // for text or image URL +} + +// Stream represents a streaming response from a GenAI provider. +type Stream struct { + Results <-chan *Result + Err error + // You can add fields for cancellation, errors, etc. if needed +} + +// GenAI is the generic interface for generative AI providers. +type GenAI interface { + Generate(prompt string, opts ...Option) (*Result, error) + Stream(prompt string, opts ...Option) (*Stream, error) +} + +// Option is a functional option for configuring providers. +type Option func(*Options) + +// Options holds configuration for providers. +type Options struct { + APIKey string + Endpoint string + Type string // "text", "image", "audio", etc. + Model string // model name, e.g. "gemini-2.5-pro" + // Add more fields as needed +} + +// Option functions for generation type +func Text(o *Options) { o.Type = "text" } +func Image(o *Options) { o.Type = "image" } +func Audio(o *Options) { o.Type = "audio" } + +// Provider registry +var providers = make(map[string]GenAI) + +// Register a GenAI provider by name. +func Register(name string, provider GenAI) { + providers[name] = provider +} + +// Get a GenAI provider by name. +func Get(name string) GenAI { + return providers[name] +} diff --git a/genai/noop.go b/genai/noop.go new file mode 100644 index 00000000..b01d17b0 --- /dev/null +++ b/genai/noop.go @@ -0,0 +1,16 @@ +package genai + +type noopGenAI struct{} + +func (n *noopGenAI) Generate(prompt string, opts ...Option) (*Result, error) { + return &Result{Prompt: prompt, Type: "noop", Text: "noop response"}, nil +} + +func (n *noopGenAI) Stream(prompt string, opts ...Option) (*Stream, error) { + results := make(chan *Result, 1) + results <- &Result{Prompt: prompt, Type: "noop", Text: "noop response"} + close(results) + return &Stream{Results: results}, nil +} + +var Default = &noopGenAI{} diff --git a/genai/openai/openai.go b/genai/openai/openai.go new file mode 100644 index 00000000..55769116 --- /dev/null +++ b/genai/openai/openai.go @@ -0,0 +1,151 @@ +package openai + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + "go-micro.dev/v5/genai" +) + +type openAI struct { + options genai.Options +} + +func New(opts ...genai.Option) genai.GenAI { + var options genai.Options + for _, o := range opts { + o(&options) + } + if options.APIKey == "" { + options.APIKey = os.Getenv("OPENAI_API_KEY") + } + return &openAI{options: options} +} + +func (o *openAI) Generate(prompt string, opts ...genai.Option) (*genai.Result, error) { + options := o.options + for _, opt := range opts { + opt(&options) + } + + res := &genai.Result{Prompt: prompt, Type: options.Type} + + var url string + var body map[string]interface{} + + switch options.Type { + case "image": + model := options.Model + if model == "" { + model = "dall-e-3" + } + url = "https://api.openai.com/v1/images/generations" + body = map[string]interface{}{ + "prompt": prompt, + "n": 1, + "size": "1024x1024", + "model": model, + } + case "audio": + model := options.Model + if model == "" { + model = "tts-1" + } + url = "https://api.openai.com/v1/audio/speech" + body = map[string]interface{}{ + "model": model, + "input": prompt, + "voice": "alloy", // or another supported voice + } + case "text": + fallthrough + default: + model := options.Model + if model == "" { + model = "gpt-3.5-turbo" + } + url = "https://api.openai.com/v1/chat/completions" + body = map[string]interface{}{ + "model": model, + "messages": []map[string]string{{"role": "user", "content": prompt}}, + } + } + + b, _ := json.Marshal(body) + req, err := http.NewRequest("POST", url, bytes.NewReader(b)) + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "Bearer "+options.APIKey) + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + switch options.Type { + case "image": + var result struct { + Data []struct { + URL string `json:"url"` + } `json:"data"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + if len(result.Data) == 0 { + return nil, fmt.Errorf("no image returned") + } + res.Text = result.Data[0].URL + return res, nil + case "audio": + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + res.Data = data + return res, nil + case "text": + fallthrough + default: + var result struct { + Choices []struct { + Message struct { + Content string `json:"content"` + } `json:"message"` + } `json:"choices"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + if len(result.Choices) == 0 { + return nil, fmt.Errorf("no choices returned") + } + res.Text = result.Choices[0].Message.Content + return res, nil + } +} + +func (o *openAI) Stream(prompt string, opts ...genai.Option) (*genai.Stream, error) { + results := make(chan *genai.Result) + go func() { + defer close(results) + res, err := o.Generate(prompt, opts...) + if err != nil { + // Send error via Stream.Err, not channel + return + } + results <- res + }() + return &genai.Stream{Results: results}, nil +} + +func init() { + genai.Register("openai", New()) +} diff --git a/genai/openai/openai_test.go b/genai/openai/openai_test.go new file mode 100644 index 00000000..bf5b3570 --- /dev/null +++ b/genai/openai/openai_test.go @@ -0,0 +1,37 @@ +package openai + +import ( + "go-micro.dev/v5/genai" + "os" + "testing" +) + +func TestOpenAI_GenerateText(t *testing.T) { + apiKey := os.Getenv("OPENAI_API_KEY") + if apiKey == "" { + t.Skip("OPENAI_API_KEY not set") + } + client := New(genai.WithAPIKey(apiKey)) + res, err := client.Generate("Say hello world", genai.Text) + if err != nil { + t.Fatalf("Generate error: %v", err) + } + if res == nil || res.Text == "" { + t.Error("Expected non-empty text response") + } +} + +func TestOpenAI_GenerateImage(t *testing.T) { + apiKey := os.Getenv("OPENAI_API_KEY") + if apiKey == "" { + t.Skip("OPENAI_API_KEY not set") + } + client := New(genai.WithAPIKey(apiKey)) + res, err := client.Generate("A cat wearing sunglasses", genai.Image) + if err != nil { + t.Fatalf("Generate error: %v", err) + } + if res == nil || res.Text == "" { + t.Error("Expected non-empty image URL") + } +} diff --git a/genai/options.go b/genai/options.go new file mode 100644 index 00000000..d73246f1 --- /dev/null +++ b/genai/options.go @@ -0,0 +1,20 @@ +package genai + +// Option sets options for a GenAI provider. +func WithAPIKey(key string) Option { + return func(o *Options) { + o.APIKey = key + } +} + +func WithEndpoint(endpoint string) Option { + return func(o *Options) { + o.Endpoint = endpoint + } +} + +func WithModel(model string) Option { + return func(o *Options) { + o.Model = model + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 2f8e2ade..ed5a073d 100644 --- a/go.mod +++ b/go.mod @@ -40,12 +40,16 @@ require ( golang.org/x/crypto v0.37.0 golang.org/x/net v0.38.0 golang.org/x/sync v0.13.0 + google.golang.org/genai v1.12.0 google.golang.org/grpc v1.71.1 google.golang.org/grpc/examples v0.0.0-20250515150734-f2d3e11f3057 google.golang.org/protobuf v1.36.6 ) require ( + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.15.0 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect @@ -55,10 +59,16 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.16.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-tpm v0.9.3 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect @@ -91,6 +101,7 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect diff --git a/go.sum b/go.sum index 4c582d0c..0e17153c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= +cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -55,6 +61,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -95,8 +103,16 @@ github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc= github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= 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/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= @@ -355,6 +371,8 @@ go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= @@ -492,6 +510,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genai v1.12.0 h1:0JjAdwvEAha9ZpPH5hL6dVG8bpMnRbAMCgv2f2LDnz4= +google.golang.org/genai v1.12.0/go.mod h1:HFXR1zT3LCdLxd/NW6IOSCczOYyRAxwaShvYbgPSeVw= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= diff --git a/store/nats-js-kv/nats.go b/store/nats-js-kv/nats.go index c7f26e9d..879c4ca8 100644 --- a/store/nats-js-kv/nats.go +++ b/store/nats-js-kv/nats.go @@ -43,6 +43,7 @@ type natsStore struct { js nats.JetStreamContext buckets *hashmap.Map[string, nats.KeyValue] } + // NewStore will create a new NATS JetStream Object Store. func NewStore(opts ...store.Option) store.Store { options := store.Options{ diff --git a/store/postgres/pgx/pgx.go b/store/postgres/pgx/pgx.go index ed3ad615..5b29fc65 100644 --- a/store/postgres/pgx/pgx.go +++ b/store/postgres/pgx/pgx.go @@ -198,11 +198,11 @@ func (s *sqlStore) List(opts ...store.ListOption) ([]string, error) { var rows pgx.Rows if options.Limit > 0 { - rows, err = db.Query(s.options.Context, queries.ListAscLimit, pattern, options.Limit, options.Offset) + rows, err = db.Query(s.options.Context, queries.ListAscLimit, pattern, options.Limit, options.Offset) } else { - rows, err = db.Query(s.options.Context, queries.ListAsc, pattern) + rows, err = db.Query(s.options.Context, queries.ListAsc, pattern) } if err != nil { @@ -273,9 +273,7 @@ func (s *sqlStore) rowsToRecords(rows pgx.Rows) ([]*store.Record, error) { // Read a single key func (s *sqlStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) { - options := store.ReadOptions{ - - } + options := store.ReadOptions{} for _, o := range opts { o(&options) } @@ -307,11 +305,11 @@ func (s *sqlStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, var rows pgx.Rows if options.Limit > 0 { - rows, err = db.Query(s.options.Context, queries.ListAscLimit, pattern, options.Limit, options.Offset) + rows, err = db.Query(s.options.Context, queries.ListAscLimit, pattern, options.Limit, options.Offset) } else { - rows, err = db.Query(s.options.Context, queries.ListAsc, pattern) + rows, err = db.Query(s.options.Context, queries.ListAsc, pattern) } if err != nil { diff --git a/store/postgres/postgres.go b/store/postgres/postgres.go index 1ac14dca..494117e6 100644 --- a/store/postgres/postgres.go +++ b/store/postgres/postgres.go @@ -257,8 +257,6 @@ func (s *sqlStore) prepare(database, table, query string) (*sql.Stmt, error) { return nil, errors.New("unsupported statement") } - - // get DB database, table = s.getDB(database, table) diff --git a/transport/nats/nats.go b/transport/nats/nats.go index 9cb861b3..815e6b6f 100644 --- a/transport/nats/nats.go +++ b/transport/nats/nats.go @@ -61,8 +61,6 @@ var ( DefaultTimeout = time.Minute ) - - func configure(n *ntport, opts ...transport.Option) { for _, o := range opts { o(&n.opts)