diff --git a/.gitignore b/.gitignore index 4c7c72676..42bbc01b7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,10 @@ cover.cov # dev .env +.envrc vendor/* service-account.json embeddings/cybertron/models/* examples/cybertron-embedding-example/models/* + diff --git a/callbacks/callbacks.go b/callbacks/callbacks.go index 2f4336b14..249456ba8 100644 --- a/callbacks/callbacks.go +++ b/callbacks/callbacks.go @@ -13,7 +13,6 @@ import ( //nolint:all type Handler interface { HandleText(ctx context.Context, text string) - HandleLLMStart(ctx context.Context, prompts []string) HandleLLMGenerateContentStart(ctx context.Context, ms []llms.MessageContent) HandleLLMGenerateContentEnd(ctx context.Context, res *llms.ContentResponse) HandleLLMError(ctx context.Context, err error) diff --git a/callbacks/combining.go b/callbacks/combining.go index 2e95e80aa..f247c1bba 100644 --- a/callbacks/combining.go +++ b/callbacks/combining.go @@ -20,12 +20,6 @@ func (l CombiningHandler) HandleText(ctx context.Context, text string) { } } -func (l CombiningHandler) HandleLLMStart(ctx context.Context, prompts []string) { - for _, handle := range l.Callbacks { - handle.HandleLLMStart(ctx, prompts) - } -} - func (l CombiningHandler) HandleLLMGenerateContentStart(ctx context.Context, ms []llms.MessageContent) { for _, handle := range l.Callbacks { handle.HandleLLMGenerateContentStart(ctx, ms) diff --git a/callbacks/context.go b/callbacks/context.go new file mode 100644 index 000000000..574b5db3c --- /dev/null +++ b/callbacks/context.go @@ -0,0 +1,20 @@ +package callbacks + +import "context" + +type contextKeyType int + +// nolint: gochecknoglobals +var _callbackHandlerKey = contextKeyType(0) + +func GetHandlerFromContext(ctx context.Context) Handler { + handler := ctx.Value(_callbackHandlerKey) + if t, ok := handler.(Handler); ok { + return t + } + return nil +} + +func SetHandlerInContext(ctx context.Context, handler Handler) context.Context { + return context.WithValue(ctx, _callbackHandlerKey, handler) +} diff --git a/callbacks/context_test.go b/callbacks/context_test.go new file mode 100644 index 000000000..b9c8cc682 --- /dev/null +++ b/callbacks/context_test.go @@ -0,0 +1,28 @@ +package callbacks + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCallbackHandler(t *testing.T) { + t.Parallel() + // Test case 1: Context with handler + handler := &SimpleHandler{} + ctx := SetHandlerInContext(context.Background(), handler) + + got := GetHandlerFromContext(ctx) + require.NotNil(t, got) + if got != handler { + t.Errorf("CallbackHandler() = %v, want %v", got, handler) + } + + // Test case 2: Context without handler + emptyCtx := context.Background() + got = GetHandlerFromContext(emptyCtx) + if got != nil { + t.Errorf("CallbackHandler() = %v, want nil", got) + } +} diff --git a/callbacks/log.go b/callbacks/log.go index e123cf698..1fb44b42e 100644 --- a/callbacks/log.go +++ b/callbacks/log.go @@ -59,10 +59,6 @@ func (l LogHandler) HandleText(_ context.Context, text string) { fmt.Println(text) } -func (l LogHandler) HandleLLMStart(_ context.Context, prompts []string) { - fmt.Println("Entering LLM with prompts:", prompts) -} - func (l LogHandler) HandleLLMError(_ context.Context, err error) { fmt.Println("Exiting LLM with error:", err) } diff --git a/callbacks/simple.go b/callbacks/simple.go index 94cf54174..102cacf35 100644 --- a/callbacks/simple.go +++ b/callbacks/simple.go @@ -13,7 +13,6 @@ type SimpleHandler struct{} var _ Handler = SimpleHandler{} func (SimpleHandler) HandleText(context.Context, string) {} -func (SimpleHandler) HandleLLMStart(context.Context, []string) {} func (SimpleHandler) HandleLLMGenerateContentStart(context.Context, []llms.MessageContent) {} func (SimpleHandler) HandleLLMGenerateContentEnd(context.Context, *llms.ContentResponse) {} func (SimpleHandler) HandleLLMError(context.Context, error) {} diff --git a/chains/chains.go b/chains/chains.go index 2741f0bcf..896af834c 100644 --- a/chains/chains.go +++ b/chains/chains.go @@ -28,6 +28,8 @@ type Chain interface { // Call is the standard function used for executing chains. func Call(ctx context.Context, c Chain, inputValues map[string]any, options ...ChainCallOption) (map[string]any, error) { // nolint: lll + ctx = setupChainCallbackHandler(ctx, c, options) + fullValues := make(map[string]any, 0) for key, value := range inputValues { fullValues[key] = value @@ -42,21 +44,22 @@ func Call(ctx context.Context, c Chain, inputValues map[string]any, options ...C fullValues[key] = value } - callbacksHandler := getChainCallbackHandler(c) - if callbacksHandler != nil { - callbacksHandler.HandleChainStart(ctx, inputValues) + chainCallbackHandlers := callbacks.GetHandlerFromContext(ctx) + + if chainCallbackHandlers != nil { + chainCallbackHandlers.HandleChainStart(ctx, inputValues) } outputValues, err := callChain(ctx, c, fullValues, options...) if err != nil { - if callbacksHandler != nil { - callbacksHandler.HandleChainError(ctx, err) + if chainCallbackHandlers != nil { + chainCallbackHandlers.HandleChainError(ctx, err) } return outputValues, err } - if callbacksHandler != nil { - callbacksHandler.HandleChainEnd(ctx, outputValues) + if chainCallbackHandlers != nil { + chainCallbackHandlers.HandleChainEnd(ctx, outputValues) // Call the chain end } if err = c.GetMemory().SaveContext(ctx, inputValues, outputValues); err != nil { @@ -66,6 +69,20 @@ func Call(ctx context.Context, c Chain, inputValues map[string]any, options ...C return outputValues, nil } +// setupChainCallbackHandler sets up the chain callback handler in the context, which will be passed down to Chain calls. +// we will prioritize a callback handler set in the options, and fallback to a chain specific if set. +func setupChainCallbackHandler(ctx context.Context, c Chain, options []ChainCallOption) context.Context { + // if callback handler is set in options, prioritize that + if handler := getChainCallCallbackHandler(options); handler != nil { + return callbacks.SetHandlerInContext(ctx, handler) + } + + if handler := getChainCallbackHandler(c); handler != nil { + return callbacks.SetHandlerInContext(ctx, handler) + } + return ctx +} + func callChain( ctx context.Context, c Chain, diff --git a/chains/options.go b/chains/options.go index bb77ae7d0..b3ab6cd76 100644 --- a/chains/options.go +++ b/chains/options.go @@ -209,3 +209,11 @@ func getLLMCallOptions(options ...ChainCallOption) []llms.CallOption { //nolint: return chainCallOption } + +func getChainCallCallbackHandler(options []ChainCallOption) callbacks.Handler { + opts := &chainCallOption{} + for _, option := range options { + option(opts) + } + return opts.CallbackHandler +} diff --git a/doc.go b/doc.go index d5313f8b4..70443cac5 100644 --- a/doc.go +++ b/doc.go @@ -1,2 +1,10 @@ -// Package langchaingo implements the Go language version of the langchain project. -package langchaingo +/* +The langsmith package provides a client for interacting with LangSmith services. It offers a flexible configuration system using functional options, enabling easy customization of client behavior. The client integrates with LLM chain calls via the CallbackHandler interface, ensuring seamless interaction. + +The package defines two main types: + 1. Client: Handles interactions with the LangSmith API. This type is designed to be instantiated once for the lifetime of the application. + 2. Tracer: Tracks the execution of LangChain processes. A new instance should be created for each run. + +This design promotes clean and extensible client initialization, making the package adaptable to various use cases. +*/ +package langsmith diff --git a/examples/anthropic-tool-call-example/anthropic-tool-call-example.go b/examples/anthropic-tool-call-example/anthropic-tool-call-example.go index 72e53f0f8..9a1e455d7 100644 --- a/examples/anthropic-tool-call-example/anthropic-tool-call-example.go +++ b/examples/anthropic-tool-call-example/anthropic-tool-call-example.go @@ -87,7 +87,6 @@ func main() { log.Fatal(err) } fmt.Println(resp.Choices[0].Content) - } // executeToolCalls executes the tool calls in the response and returns the diff --git a/examples/langsmith-llm-example/README.md b/examples/langsmith-llm-example/README.md new file mode 100644 index 000000000..23c78cdb9 --- /dev/null +++ b/examples/langsmith-llm-example/README.md @@ -0,0 +1,45 @@ +# LangSmith Tracing Example with OpenAI + +Welcome to this example of using LangSmith tracing with OpenAI and Go! 🎉 + +This project demonstrates how to integrate LangSmith tracing into your LangChain Go applications, allowing you to monitor and debug your LLM interactions. + +## What This Example Does + +This example showcases several key features: + +1. 🤖 Sets up a connection to OpenAI's GPT-4 model +2. 📊 Configures LangSmith tracing for monitoring LLM interactions +3. 🌐 Creates a translation chain that converts text between languages +4. 📝 Logs all langchain interactions using a custom logger + +## How It Works + +1. Creates an OpenAI client with the GPT-4 model +2. Sets up LangSmith client and tracer with: + - API key configuration + - Custom logging + - Project name tracking +3. Creates a translation chain with: + - System prompt defining the AI as a translation expert + - Human prompt template for translation requests +4. Executes the chain with tracing enabled + +## Running the Example + +To run this example, you'll need: + +1. Go installed on your system +2. Environment variables set up: + - `OPENAI_API_KEY` - Your OpenAI API key + - `LANGCHAIN_API_KEY` - Your LangSmith API key + - `LANGCHAIN_PROJECT` - Your LangSmith project name (optional) + +You can also provide the LangSmith configuration via flags: +```bash +go run . --langchain-api-key=your_key --langchain-project=your_project +``` + +## Example Output + +The program will output the translation results in JSON format, and all interactions will be traced in your LangSmith dashboard. \ No newline at end of file diff --git a/examples/langsmith-llm-example/go.mod b/examples/langsmith-llm-example/go.mod new file mode 100644 index 000000000..c5c88a39b --- /dev/null +++ b/examples/langsmith-llm-example/go.mod @@ -0,0 +1,42 @@ +module github.com/tmc/langchaingo/examples/langsmith-llm-example + +go 1.22.0 + +toolchain go1.22.1 + +require ( + github.com/google/uuid v1.6.0 + github.com/tmc/langchaingo v0.1.13-pre.0 +) + +require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/goph/emperror v0.17.2 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nikolalohinski/gonja v1.5.3 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pkoukk/tiktoken-go v0.1.6 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/yargevad/filepathx v1.0.0 // indirect + go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/sys v0.20.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +// test for new pkg version +replace github.com/tmc/langchaingo => ../.. diff --git a/examples/langsmith-llm-example/go.sum b/examples/langsmith-llm-example/go.sum new file mode 100644 index 000000000..1f6593c69 --- /dev/null +++ b/examples/langsmith-llm-example/go.sum @@ -0,0 +1,332 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.114.0 h1:OIPFAdfrFDFO2ve2U7r/H5SwSbBzEdrBdE7xkgwc+kY= +cloud.google.com/go v0.114.0/go.mod h1:ZV9La5YYxctro1HTPug5lXH/GefROyW8PPD4T8n9J8E= +cloud.google.com/go/ai v0.7.0 h1:P6+b5p4gXlza5E+u7uvcgYlzZ7103ACg70YdZeC6oGE= +cloud.google.com/go/ai v0.7.0/go.mod h1:7ozuEcraovh4ABsPbrec3o4LmFl9HigNI3D5haxYeQo= +cloud.google.com/go/aiplatform v1.68.0 h1:EPPqgHDJpBZKRvv+OsB3cr0jYz3EL2pZ+802rBPcG8U= +cloud.google.com/go/aiplatform v1.68.0/go.mod h1:105MFA3svHjC3Oazl7yjXAmIR89LKhRAeNdnDKJczME= +cloud.google.com/go/auth v0.5.1 h1:0QNO7VThG54LUzKiQxv8C6x1YX7lUrzlAa1nVLF8CIw= +cloud.google.com/go/auth v0.5.1/go.mod h1:vbZT8GjzDf3AVqCcQmqeeM32U9HBFc32vVVAbwDsa6s= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0= +cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= +cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU= +cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= +cloud.google.com/go/vertexai v0.12.0 h1:zTadEo/CtsoyRXNx3uGCncoWAP1H2HakGqwznt+iMo8= +cloud.google.com/go/vertexai v0.12.0/go.mod h1:8u+d0TsvBfAAd2x5R6GMgbYhsLgo3J7lmP4bR8g2ig8= +github.com/AssemblyAI/assemblyai-go-sdk v1.3.0 h1:AtOVgGxUycvK4P4ypP+1ZupecvFgnfH+Jsum0o5ILoU= +github.com/AssemblyAI/assemblyai-go-sdk v1.3.0/go.mod h1:H0naZbvpIW49cDA5ZZ/gggeXqi7ojSGB1mqshRk6kNE= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +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/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/getzep/zep-go v1.0.4 h1:09o26bPP2RAPKFjWuVWwUWLbtFDF/S8bfbilxzeZAAg= +github.com/getzep/zep-go v1.0.4/go.mod h1:HC1Gz7oiyrzOTvzeKC4dQKUiUy87zpIJl0ZFXXdHuss= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/generative-ai-go v0.15.1 h1:n8aQUpvhPOlGVuM2DRkJ2jvx04zpp42B778AROJa+pQ= +github.com/google/generative-ai-go v0.15.1/go.mod h1:AAucpWZjXsDKhQYWvCYuP6d0yB1kX998pJlOW1rAesw= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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.1/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/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.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg= +github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= +github.com/goph/emperror v0.17.2 h1:yLapQcmEsO0ipe9p5TaN22djm3OFV/TfM/fcYP0/J18= +github.com/goph/emperror v0.17.2/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= +github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c= +github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= +github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= +github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181 h1:K+bMSIx9A7mLES1rtG+qKduLIXq40DAzYHtb0XuCukA= +gitlab.com/golang-commonmark/html v0.0.0-20191124015941-a22733972181/go.mod h1:dzYhVIwWCtzPAa4QP98wfB9+mzt33MSmM8wsKiMi2ow= +gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 h1:oYrL81N608MLZhma3ruL8qTM4xcpYECGut8KSxRY59g= +gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8= +gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a h1:O85GKETcmnCNAfv4Aym9tepU8OE0NmcZNqPlXcsBKBs= +gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a/go.mod h1:LaSIs30YPGs1H5jwGgPhLzc8vkNc/k0rDX/fEZqiU/M= +gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84 h1:qqjvoVXdWIcZCLPMlzgA7P9FZWdPGPvP/l3ef8GzV6o= +gitlab.com/golang-commonmark/mdurl v0.0.0-20191124015652-932350d1cb84/go.mod h1:IJZ+fdMvbW2qW6htJx7sLJ04FEs4Ldl/MDsJtMKywfw= +gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f h1:Wku8eEdeJqIOFHtrfkYUByc4bCaTeA6fL0UJgfEiFMI= +gitlab.com/golang-commonmark/puny v0.0.0-20191124015043-9f83538fa04f/go.mod h1:Tiuhl+njh/JIg0uS/sOJVYi0x2HEa5rc1OAaVsb5tAs= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfGcjnmfEJOBgSbemCtg= +go.starlark.net v0.0.0-20230302034142-4b1e35fe2254/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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/api v0.183.0 h1:PNMeRDwo1pJdgNcFQ9GstuLe/noWKIc89pRWRLMvLwE= +google.golang.org/api v0.183.0/go.mod h1:q43adC5/pHoSZTx5h2mSmdF7NcyfW9JuDyIOJAgS9ZQ= +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 v0.0.0-20240528184218-531527333157 h1:u7WMYrIrVvs0TF5yaKwKNbcJyySYf+HAIFXxWltJOXE= +google.golang.org/genproto v0.0.0-20240528184218-531527333157/go.mod h1:ubQlAQnzejB8uZzszhrTCU2Fyp6Vi7ZE5nn0c3W8+qQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +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.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.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +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= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/examples/langsmith-llm-example/langsmith_llm_example.go b/examples/langsmith-llm-example/langsmith_llm_example.go new file mode 100644 index 000000000..8cf5503e3 --- /dev/null +++ b/examples/langsmith-llm-example/langsmith_llm_example.go @@ -0,0 +1,114 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "os" + + "github.com/google/uuid" + "github.com/tmc/langchaingo/chains" + "github.com/tmc/langchaingo/langsmith" + "github.com/tmc/langchaingo/llms/openai" + "github.com/tmc/langchaingo/prompts" +) + +var ( + flagLangchainApiKey = flag.String("langchain-api-key", "", "sets langchain API Key, if value not set will check LANGCHAIN_API_KEY") + flagLangchainProject = flag.String("langchain-project", "langchain_go_example", "sets langchain project, if value not set will check LANGCHAIN_PROJECT") +) + +func main() { + ctx := context.Background() + + llm, err := openai.New(openai.WithModel("gpt-4o")) + if err != nil { + log.Fatal(err) + } + + langchainAPIKey := getFlagOrEnv(flagLangchainApiKey, "LANGCHAIN_API_KEY") + if err != nil { + log.Fatal("langchain API Key not set") + } + + langchainProject := getFlagOrEnv(flagLangchainProject, "LANGCHAIN_PROJECT") + if err != nil { + log.Fatal("langchain Project not set") + } + + fmt.Println("Using langchain project: ", langchainProject) + + logger := &logger{} + langsmithClient, err := langsmith.NewClient( + langsmith.WithAPIKey(langchainAPIKey), + langsmith.WithClientLogger(logger), + langsmith.WithAPIURL("https://api.smith.langchain.com"), + ) + if err != nil { + log.Fatal(err) + } + + langsmithTracer, err := langsmith.NewTracer( + langsmith.WithLogger(logger), + langsmith.WithProjectName(langchainProject), + langsmith.WithClient(langsmithClient), + langsmith.WithRunID(uuid.NewString()), + ) + if err != nil { + log.Fatal(err) + } + + translatePrompt := prompts.NewChatPromptTemplate([]prompts.MessageFormatter{ + prompts.NewSystemMessagePromptTemplate("You are a translation expert", nil), + prompts.NewHumanMessagePromptTemplate("Translate the following text from {{.inputLanguage}} to {{.outputLanguage}}. {{.text}}", nil), + }) + + // Initiate your llm chain with the langsmith tracer + llmChain := chains.NewLLMChain(llm, translatePrompt, chains.WithCallback(langsmithTracer)) + + // To get full tracing we must pass through the Static Call function + outputValues, err := chains.Call(ctx, llmChain, map[string]any{ + "inputLanguage": "English", + "outputLanguage": "French", + "text": "I love programming.", + }) + if err != nil { + log.Fatal(err) + } + + cnt, err := json.Marshal(outputValues) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(cnt)) +} + +func getFlagOrEnv(flagValue *string, envName string) string { + if flagValue == nil { + return os.Getenv(envName) + } + if *flagValue == "" { + return os.Getenv(envName) + } + return *flagValue +} + +type logger struct{} + +func (l *logger) Debugf(format string, v ...interface{}) { + fmt.Printf("[DEBUG] "+format, v...) +} + +func (l *logger) Errorf(format string, v ...interface{}) { + fmt.Printf("[ERROR] "+format, v...) +} + +func (l *logger) Infof(format string, v ...interface{}) { + fmt.Printf("[INFO] "+format, v...) +} + +func (l *logger) Warnf(format string, v ...interface{}) { + fmt.Printf("[WARN] "+format, v...) +} diff --git a/examples/llamafile-completion-example/llamafile_completion_example.go b/examples/llamafile-completion-example/llamafile_completion_example.go index 194a01a98..4ef7c3806 100644 --- a/examples/llamafile-completion-example/llamafile_completion_example.go +++ b/examples/llamafile-completion-example/llamafile_completion_example.go @@ -9,13 +9,11 @@ import ( ) func main() { - options := []llamafile.Option{ llamafile.WithEmbeddingSize(2048), llamafile.WithTemperature(0.8), } llm, err := llamafile.New(options...) - if err != nil { panic(err) } @@ -35,7 +33,6 @@ func main() { fmt.Print(string(chunk)) return nil })) - if err != nil { panic(err) } diff --git a/examples/maritaca-example/maritaca-chat-example.go b/examples/maritaca-example/maritaca-chat-example.go index 3b4628257..2c0854e9a 100644 --- a/examples/maritaca-example/maritaca-chat-example.go +++ b/examples/maritaca-example/maritaca-chat-example.go @@ -16,7 +16,6 @@ func main() { maritaca.WithModel("sabia-2-medium"), } llm, err := maritaca.New(opts...) - if err != nil { panic(err) } @@ -28,5 +27,4 @@ func main() { panic(err) } fmt.Println(resp) - } diff --git a/examples/pinecone-vectorstore-example/pinecone_vectorstore_example.go b/examples/pinecone-vectorstore-example/pinecone_vectorstore_example.go index 1b1be6c33..d64df8667 100644 --- a/examples/pinecone-vectorstore-example/pinecone_vectorstore_example.go +++ b/examples/pinecone-vectorstore-example/pinecone_vectorstore_example.go @@ -15,10 +15,10 @@ import ( func main() { // Create an embeddings client using the OpenAI API. Requires environment variable OPENAI_API_KEY to be set. - - llm, err := openai.New(openai.WithEmbeddingModel("text-embedding-3-small"))// Specify your preferred embedding model + + llm, err := openai.New(openai.WithEmbeddingModel("text-embedding-3-small")) // Specify your preferred embedding model if err != nil { - log.Fatal(err) + log.Fatal(err) } e, err := embeddings.NewEmbedder(llm) diff --git a/examples/zep-memory-chain-example/main.go b/examples/zep-memory-chain-example/main.go index 6d9b1a916..c17228275 100644 --- a/examples/zep-memory-chain-example/main.go +++ b/examples/zep-memory-chain-example/main.go @@ -3,13 +3,14 @@ package main import ( "context" "fmt" + "os" + "github.com/getzep/zep-go" zepClient "github.com/getzep/zep-go/client" zepOption "github.com/getzep/zep-go/option" "github.com/tmc/langchaingo/chains" "github.com/tmc/langchaingo/llms/openai" zepLangchainMemory "github.com/tmc/langchaingo/memory/zep" - "os" ) func main() { diff --git a/langsmith/client.go b/langsmith/client.go new file mode 100644 index 000000000..16e5b2b74 --- /dev/null +++ b/langsmith/client.go @@ -0,0 +1,137 @@ +package langsmith + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/google/uuid" +) + +type Client struct { + apiKey string + apiURL string + webURL *string + + hideInputs bool + hideOutputs bool + + httpClient *http.Client + logger LeveledLogger +} + +// NewClient insantiate a new LangSmith client. Use options to customize the client. +func NewClient(options ...ClientOption) (*Client, error) { + c := &Client{ + apiKey: os.Getenv("LANGCHAIN_API_KEY"), + apiURL: envOr("LANGCHAIN_ENDPOINT", ""), + hideInputs: envOr("LANGCHAIN_HIDE_INPUTS", "false") == "true", + hideOutputs: envOr("LANGCHAIN_HIDE_OUTPUTS", "false") == "true", + webURL: nil, + logger: &NopLogger{}, + + httpClient: &http.Client{ + Timeout: 4 * time.Second, + }, + } + + for _, option := range options { + option.apply(c) + } + + // Sanitization(s) + c.apiURL = strings.TrimSuffix(c.apiURL, "/") + + // Validation(s) + if len(c.apiKey) == 0 && len(c.apiURL) > 0 { + return nil, ErrMissingAPIKey + } + + return c, nil +} + +func (c *Client) CreateRun(ctx context.Context, run *runCreate) error { + // FIXME: Add back getRuntimeEnv logic, for now we assume RunCreate will have populated the fields correctly + + if c.hideInputs { + run.Inputs = nil + } + + if c.hideOutputs { + run.Outputs = nil + } + + body, err := json.Marshal(run) + if err != nil { + return fmt.Errorf("marshal run: %w", err) + } + + return c.executeHTTPRequest(ctx, "POST", "/runs", nil, bytes.NewBuffer(body)) +} + +func (c *Client) UpdateRun(ctx context.Context, runID string, run *runUpdate) error { + if !isValidUUID(runID) { + return ErrInvalidUUID + } + + if c.hideInputs { + run.Inputs = nil + } + + if c.hideOutputs { + run.Outputs = nil + } + + body, err := json.Marshal(run) + if err != nil { + return fmt.Errorf("marshal run: %w", err) + } + + return c.executeHTTPRequest(ctx, "PATCH", fmt.Sprintf("/runs/%s", runID), nil, bytes.NewBuffer(body)) +} + +func isValidUUID(s string) bool { + _, err := uuid.Parse(s) + return err == nil +} + +func (c *Client) executeHTTPRequest(ctx context.Context, method string, path string, query url.Values, body *bytes.Buffer) error { + if path[0] != '/' { + path = "/" + path + } + + callURL := c.apiURL + path + if len(query) > 0 { + callURL += "?" + query.Encode() + } + + req, err := http.NewRequestWithContext(ctx, method, callURL, body) + if err != nil { + return err + } + + c.logger.Debugf("[REQUEST] %s [%s] %s\n", callURL, method, body.Bytes()) + + req.Header.Set("Content-Type", "application/json") + if c.apiKey != "" { + req.Header.Set("x-api-key", c.apiKey) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return newLangSmitAPIErrorFromHTTP(req, resp) + } + + return nil +} diff --git a/langsmith/client_options.go b/langsmith/client_options.go new file mode 100644 index 000000000..222f3e7ba --- /dev/null +++ b/langsmith/client_options.go @@ -0,0 +1,62 @@ +package langsmith + +import "net/http" + +type ClientOption interface { + apply(c *Client) +} + +type clientOptionFunc func(c *Client) + +func (f clientOptionFunc) apply(c *Client) { + f(c) +} + +// WithAPIKey sets the API key for authentication. +func WithAPIKey(apiKey string) ClientOption { + return clientOptionFunc(func(c *Client) { + c.apiKey = apiKey + }) +} + +// WithAPIURL sets the API URL. +func WithAPIURL(apiURL string) ClientOption { + return clientOptionFunc(func(c *Client) { + c.apiURL = apiURL + }) +} + +// WithWebURL sets the web URL. +func WithWebURL(webURL string) ClientOption { + return clientOptionFunc(func(c *Client) { + c.webURL = &webURL + }) +} + +// WithHTTPClient sets the HTTP client. +func WithHTTPClient(httpClient *http.Client) ClientOption { + return clientOptionFunc(func(c *Client) { + c.httpClient = httpClient + }) +} + +// WithHideInputs sets whether to hide input instrumentation. +func WithHideInputs(hide bool) ClientOption { + return clientOptionFunc(func(c *Client) { + c.hideInputs = hide + }) +} + +// WithHideOutputs sets whether to hide output instrumentation. +func WithHideOutputs(hide bool) ClientOption { + return clientOptionFunc(func(c *Client) { + c.hideOutputs = hide + }) +} + +// WithClientLogger sets the logger. +func WithClientLogger(logger LeveledLogger) ClientOption { + return clientOptionFunc(func(c *Client) { + c.logger = logger + }) +} diff --git a/langsmith/doc.go b/langsmith/doc.go new file mode 100644 index 000000000..efe6c7c13 --- /dev/null +++ b/langsmith/doc.go @@ -0,0 +1,7 @@ +// Package langsmith provides a client for interacting with LangSmith services. +// The package offers a flexible client configuration system through various options +// that can be used to customize the client behavior. The client hooks into the llm chain +// calls via the Callback Handler interface. +// The package uses a functional options pattern for client configuration, allowing for clean and +// extensible client initialization. +package langsmith diff --git a/langsmith/environment.go b/langsmith/environment.go new file mode 100644 index 000000000..6283f4a70 --- /dev/null +++ b/langsmith/environment.go @@ -0,0 +1,12 @@ +package langsmith + +import "os" + +func envOr(key string, defaultValue string) string { + value := os.Getenv(key) + if len(value) == 0 { + return defaultValue + } + + return value +} diff --git a/langsmith/errors.go b/langsmith/errors.go new file mode 100644 index 000000000..ed24553cf --- /dev/null +++ b/langsmith/errors.go @@ -0,0 +1,36 @@ +package langsmith + +import ( + "errors" + "fmt" + "io" + "net/http" +) + +var ( + ErrInvalidUUID = errors.New("invalid UUID") + ErrMissingAPIKey = errors.New("the LangSmith API key was not defined, define LANGCHAIN_API_KEY environment variable or configure your client with WithAPIKey()") +) + +type LangSmitAPIError struct { + StatusCode int + URL string + Body []byte +} + +func newLangSmitAPIErrorFromHTTP(req *http.Request, resp *http.Response) *LangSmitAPIError { + body, err := io.ReadAll(resp.Body) + if err != nil { + body = []byte(fmt.Sprintf("", err)) + } + + return &LangSmitAPIError{ + StatusCode: resp.StatusCode, + URL: req.URL.String(), + Body: body, + } +} + +func (e *LangSmitAPIError) Error() string { + return fmt.Sprintf("LangSmith API error on %q received status %d: %s", e.URL, e.StatusCode, string(e.Body)) +} diff --git a/langsmith/logging.go b/langsmith/logging.go new file mode 100644 index 000000000..8e60f8abc --- /dev/null +++ b/langsmith/logging.go @@ -0,0 +1,25 @@ +package langsmith + +type LeveledLogger interface { + Debugf(format string, v ...interface{}) + Errorf(format string, v ...interface{}) + Infof(format string, v ...interface{}) + Warnf(format string, v ...interface{}) +} + +var _ LeveledLogger = &NopLogger{} + +// NopLogger is a logger that does nothing. Set as the default logger for the client, which can be overridden via options. +type NopLogger struct{} + +func (n *NopLogger) Debugf(_ string, _ ...interface{}) { +} + +func (n *NopLogger) Errorf(_ string, _ ...interface{}) { +} + +func (n *NopLogger) Infof(_ string, _ ...interface{}) { +} + +func (n *NopLogger) Warnf(_ string, _ ...interface{}) { +} diff --git a/langsmith/run_trees.go b/langsmith/run_trees.go new file mode 100644 index 000000000..bb9153f5e --- /dev/null +++ b/langsmith/run_trees.go @@ -0,0 +1,257 @@ +package langsmith + +import ( + "context" + "fmt" + "runtime/debug" + "time" + + "github.com/google/uuid" +) + +type runTree struct { + ID string + Name string + RunType string + ProjectName string + ParentRun *runTree + ChildRuns []*runTree + ExecutionOrder int + ChildExecutionOrder int + StartTime time.Time + EndTime time.Time + Extra KVMap + Error string + Serialized KVMap + Inputs KVMap + Outputs KVMap + ReferenceExampleID *string + Client *Client + Events []KVMap +} + +func newRunTree(id string) *runTree { + r := runTree{ + ID: id, + ProjectName: envOr("LANGCHAIN_PROJECT", "default"), + ExecutionOrder: 1, + ChildExecutionOrder: 1, + } + return r.setStartTime(time.Now()) +} + +func (t *runTree) setParent(parent *runTree) *runTree { + t.ParentRun = parent + return t +} + +func (t *runTree) setName(name string) *runTree { + t.Name = name + return t +} + +func (t *runTree) setProjectName(name string) *runTree { + t.ProjectName = name + return t +} + +func (t *runTree) setClient(client *Client) *runTree { + t.Client = client + return t +} + +func (t *runTree) setExecutionOrder(order int) *runTree { + t.ExecutionOrder = order + return t +} + +func (t *runTree) setChildExecutionOrder(order int) *runTree { + t.ChildExecutionOrder = order + return t +} + +func (t *runTree) setStartTime(startTime time.Time) *runTree { + t.StartTime = startTime + return t +} + +func (t *runTree) setEndTime(endTime time.Time) *runTree { + t.EndTime = endTime + return t +} + +func (t *runTree) setExtra(extra KVMap) *runTree { + t.Extra = extra + return t +} + +func (t *runTree) setError(err string) *runTree { + t.Error = err + return t +} + +func (t *runTree) setInputs(inputs KVMap) *runTree { + t.Inputs = inputs + + return t +} + +func (t *runTree) setOutputs(outputs KVMap) *runTree { + t.Outputs = outputs + return t +} + +func (t *runTree) setRunType(runType string) *runTree { + t.RunType = runType + return t +} + +func (t *runTree) createChild() *runTree { + return newRunTree(uuid.New().String()). + setParent(t). + setProjectName(t.ProjectName). + setClient(t.Client). + setExecutionOrder(t.ChildExecutionOrder + 1). + setChildExecutionOrder(t.ChildExecutionOrder + 1) +} + +func (t *runTree) appendChild(child *runTree) { + t.ChildRuns = append(t.ChildRuns, child) +} + +func (t *runTree) getChild(childName string) *runTree { + for _, child := range t.ChildRuns { + if child.Name == childName { + return child + } + } + return nil +} + +func (t *runTree) convertToCreate(excludeChildRuns bool) (*runCreate, error) { + runExtra := t.Extra + if runExtra == nil { + runExtra = make(KVMap) + } + + var runExtraRuntime KVMap + if v := runExtra["runtime"]; v == nil { + runExtraRuntime = make(KVMap) + runExtra["runtime"] = runExtraRuntime + } else if runExtraRuntimeCast, ok := v.(KVMap); ok { + runExtraRuntime = runExtraRuntimeCast + } else { + return nil, fmt.Errorf("extra must be a map") + } + + runExtraRuntime["library"] = "langsmith-go" + runExtraRuntime["commit"] = getGitCommit() + + var childRuns []*runCreate + var parentRunID *string + if !excludeChildRuns { + for _, childRun := range t.ChildRuns { + childRunCreate, err := childRun.convertToCreate(excludeChildRuns) + if err != nil { + return nil, err + } + childRuns = append(childRuns, childRunCreate) + } + parentRunID = nil + } else { + if t.ParentRun != nil { + parentRunID = &t.ParentRun.ID + } + childRuns = []*runCreate{} + } + + persistedRun := &runCreate{ + baseRun: baseRun{ + ID: t.ID, + Name: t.Name, + StartTime: timeToMillisecondsPtr(t.StartTime), + EndTime: timeToMillisecondsPtr(t.EndTime), + RunType: t.RunType, + ReferenceExampleID: t.ReferenceExampleID, + Extra: runExtra, + ExecutionOrder: t.ExecutionOrder, + Serialized: t.Serialized, + Error: t.Error, + Inputs: t.Inputs, + Outputs: t.Outputs, + ParentRunID: parentRunID, + }, + SessionName: &t.ProjectName, + ChildRuns: childRuns, + } + return persistedRun, nil +} + +// postRun will start the run or the child run. +func (t *runTree) postRun(ctx context.Context, excludeChildRuns bool) error { + runCreate, err := t.convertToCreate(true) + if err != nil { + return err + } + + err = t.Client.CreateRun(ctx, runCreate) + if err != nil { + return err + } + + if !excludeChildRuns { + for _, childRun := range t.ChildRuns { + err = childRun.postRun(ctx, excludeChildRuns) + if err != nil { + return fmt.Errorf("post child: %w", err) + } + } + } + + return nil +} + +// patchRun will close the run or the child run of a run tree that +// was started with the postRun. +func (t *runTree) patchRun(ctx context.Context) error { + var parentRunID *string + if t.ParentRun != nil { + parentRunID = valueIfSetOtherwiseNil(t.ParentRun.ID) + } + + runUpdate := &runUpdate{ + EndTime: timeToMillisecondsPtr(t.EndTime), + Error: valueIfSetOtherwiseNil(t.Error), + Outputs: t.Outputs, + ParentRunID: parentRunID, + ReferenceExampleID: t.ReferenceExampleID, + Extra: t.Extra, + Events: t.Events, + } + + err := t.Client.UpdateRun(ctx, t.ID, runUpdate) + if err != nil { + return err + } + + return nil +} + +func getGitCommit() string { + info, ok := debug.ReadBuildInfo() + if !ok { + panic("we should have been able to retrieve info from 'runtime/debug#ReadBuildInfo'") + } + + findSetting := func(key string, settings []debug.BuildSetting) string { + for _, setting := range settings { + if setting.Key == key { + return setting.Value + } + } + + return "" + } + + return findSetting("vcs.revision", info.Settings) +} diff --git a/langsmith/schemas.go b/langsmith/schemas.go new file mode 100644 index 000000000..8250ba32f --- /dev/null +++ b/langsmith/schemas.go @@ -0,0 +1,75 @@ +package langsmith + +import "time" + +type TracerSession struct { + TenantID string + ID string + StartTime time.Time + EndTime time.Time + Description *string + Name *string +} + +type TracerSessionResult struct { + TracerSession + RunCount *int + LatencyP50 *float64 + LatencyP99 *float64 + TotalTokens *int + PromptTokens *int + CompletionTokens *int + LastRunStartTime *int64 + FeedbackStats *KVMap + ReferenceDatasetIDs *[]string + RunFacets *[]KVMap +} + +type ( + RunType string + ScoreType interface{} + ValueType interface{} + DataType string +) + +type BaseExample struct { + DatasetID string + Inputs KVMap + Outputs *KVMap +} + +type baseRun struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + ExecutionOrder int `json:"execution_order,omitempty"` + StartTime *int64 `json:"start_time,omitempty"` + RunType string `json:"run_type,omitempty"` + EndTime *int64 `json:"end_time,omitempty"` + Extra KVMap `json:"extra,omitempty"` + Error string `json:"error,omitempty"` + Serialized any `json:"serialized,omitempty"` + Events []KVMap `json:"events,omitempty"` + Inputs KVMap `json:"inputs,omitempty"` + Outputs KVMap `json:"outputs,omitempty"` + ReferenceExampleID *string `json:"reference_example_id,omitempty"` + ParentRunID *string `json:"parent_run_id,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +type runCreate struct { + baseRun + ChildRuns []*runCreate `json:"child_runs,omitempty"` + SessionName *string `json:"session_name,omitempty"` +} + +type runUpdate struct { + EndTime *int64 `json:"end_time,omitempty"` + Extra KVMap `json:"extra,omitempty"` + Error *string `json:"error,omitempty"` + Inputs KVMap `json:"inputs,omitempty"` + Outputs KVMap `json:"outputs,omitempty"` + ParentRunID *string `json:"parent_run_id,omitempty"` + ReferenceExampleID *string `json:"reference_example_id,omitempty"` + Events []KVMap `json:"events,omitempty"` + SessionID *string `json:"session_id,omitempty"` +} diff --git a/langsmith/schemas_test.go b/langsmith/schemas_test.go new file mode 100644 index 000000000..77a900a03 --- /dev/null +++ b/langsmith/schemas_test.go @@ -0,0 +1,43 @@ +package langsmith + +import ( + "encoding/json" + "testing" + "time" + + assert "github.com/stretchr/testify/assert" + require "github.com/stretchr/testify/require" +) + +func TestRunCreate_AsJson(t *testing.T) { + t.Parallel() + tests := []struct { + name string + data *runCreate + want string + }{ + { + "base", + &runCreate{}, + `{}`, + }, + { + "start time", + &runCreate{ + baseRun: baseRun{ + StartTime: timeToMillisecondsPtr(time.UnixMilli(1234567890)), + }, + }, + `{"start_time":1234567890}`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + out, err := json.Marshal(tt.data) + require.NoError(t, err) + + assert.Equal(t, tt.want, string(out)) + }) + } +} diff --git a/langsmith/tracer.go b/langsmith/tracer.go new file mode 100644 index 000000000..9a13f79a7 --- /dev/null +++ b/langsmith/tracer.go @@ -0,0 +1,209 @@ +package langsmith + +import ( + "context" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/tmc/langchaingo/callbacks" + "github.com/tmc/langchaingo/llms" + "github.com/tmc/langchaingo/schema" +) + +var _ callbacks.Handler = (*Tracer)(nil) + +type Tracer struct { + name string + projectName string + client *Client + + runID string + activeTree *runTree + extras KVMap + logger LeveledLogger +} + +func NewTracer(opts ...LangChainTracerOption) (*Tracer, error) { + tracer := &Tracer{ + name: "langchain_tracer", + projectName: "default", + client: nil, + runID: uuid.New().String(), + logger: &NopLogger{}, + } + + for _, opt := range opts { + opt.apply(tracer) + } + + if tracer.client == nil { + var err error + tracer.client, err = NewClient() + if err != nil { + return nil, fmt.Errorf("new langsmith client: %w", err) + } + } + + return tracer, nil +} + +// GetRunID returns the run ID of the tracer. +func (t *Tracer) GetRunID() string { + return t.runID +} + +func (t *Tracer) resetActiveTree() { + t.activeTree = nil +} + +func (t *Tracer) HandleText(_ context.Context, _ string) { +} + +// HandleLLMStart implements callbacks.Handler. +func (t *Tracer) HandleLLMGenerateContentStart(ctx context.Context, ms []llms.MessageContent) { + childTree := t.activeTree.createChild() + + inputs := []struct { + Role string `json:"role"` + Content []llms.ContentPart `json:"content"` + }{} + + for _, prompt := range ms { + inputs = append(inputs, struct { + Role string `json:"role"` + Content []llms.ContentPart `json:"content"` + }{ + Role: string(prompt.Role), + Content: prompt.Parts, + }) + } + + childTree. + setName("LLMGenerateContent"). + setRunType("llm"). + setInputs(KVMap{ + "messages": inputs, + }) + + t.activeTree.appendChild(childTree) + + // Start the run + if err := childTree.postRun(ctx, true); err != nil { + t.logLangSmithError("llm_start", "post run", err) + return + } +} + +// HandleLLMGenerateContentEnd implements callbacks.Handler. +func (t *Tracer) HandleLLMGenerateContentEnd(ctx context.Context, res *llms.ContentResponse) { + childTree := t.activeTree.getChild("LLMGenerateContent") + + childTree.setName("LLMGenerateContent").setRunType("llm").setOutputs(KVMap{ + "res_content": res, + }) + + // Close the run + if err := childTree.patchRun(ctx); err != nil { + t.logLangSmithError("llm_start", "post run", err) + return + } +} + +// HandleLLMError implements callbacks.Handler. +func (t *Tracer) HandleLLMError(ctx context.Context, err error) { + t.activeTree.setError(err.Error()).setEndTime(time.Now()) + + if err := t.activeTree.patchRun(ctx); err != nil { + t.logLangSmithError("llm_error", "patch run", err) + return + } + + t.activeTree = nil +} + +// HandleChainStart implements callbacks.Handler. +func (t *Tracer) HandleChainStart(ctx context.Context, inputs map[string]any) { + t.activeTree = newRunTree(t.runID). + setName("RunnableSequence"). + setClient(t.client). + setProjectName(t.projectName). + setRunType("chain"). + setInputs(inputs). + setExtra(t.extras) + + if err := t.activeTree.postRun(ctx, true); err != nil { + t.logLangSmithError("handle_chain_start", "post run", err) + return + } +} + +// HandleChainEnd implements callbacks.Handler. +func (t *Tracer) HandleChainEnd(ctx context.Context, outputs map[string]any) { + t.activeTree. + setOutputs(outputs). + setEndTime(time.Now()) + + if err := t.activeTree.patchRun(ctx); err != nil { + t.logLangSmithError("handle_chain_end", "patch run", err) + return + } + + t.resetActiveTree() +} + +// HandleChainError implements callbacks.Handler. +func (t *Tracer) HandleChainError(ctx context.Context, err error) { + t.activeTree.setError(err.Error()).setEndTime(time.Now()) + + if err := t.activeTree.patchRun(ctx); err != nil { + t.logLangSmithError("handle_chain_error", "patch run", err) + return + } + + t.activeTree = nil +} + +// HandleToolStart implements callbacks.Handler. +func (t *Tracer) HandleToolStart(_ context.Context, input string) { + t.logger.Debugf("handle tool start: input: %s", input) +} + +// HandleToolEnd implements callbacks.Handler. +func (t *Tracer) HandleToolEnd(_ context.Context, output string) { + t.logger.Debugf("handle tool end: output: %s", output) +} + +// HandleToolError implements callbacks.Handler. +func (t *Tracer) HandleToolError(_ context.Context, err error) { + t.logger.Warnf("handle tool error: %s", err) +} + +// HandleAgentAction implements callbacks.Handler. +func (t *Tracer) HandleAgentAction(_ context.Context, action schema.AgentAction) { + t.logger.Debugf("handle agent action, action: %v", action) +} + +// HandleAgentFinish implements callbacks.Handler. +func (t *Tracer) HandleAgentFinish(_ context.Context, finish schema.AgentFinish) { + t.logger.Debugf("handle agent finish, finish: %v", finish) +} + +// HandleRetrieverStart implements callbacks.Handler. +func (t *Tracer) HandleRetrieverStart(_ context.Context, query string) { + t.logger.Debugf("handle retriever start, query: %s, documents: %v", query) +} + +// HandleRetrieverEnd implements callbacks.Handler. +func (t *Tracer) HandleRetrieverEnd(_ context.Context, query string, documents []schema.Document) { + t.logger.Debugf("handle retriever end, query: %s, documents: %v", query, documents) +} + +// HandleStreamingFunc implements callbacks.Handler. +func (t *Tracer) HandleStreamingFunc(_ context.Context, _ []byte) { + // do nothing +} + +func (t *Tracer) logLangSmithError(handlerName string, tag string, err error) { + t.logger.Debugf("we were not able to %s to LangSmith server via handler %q: %s", handlerName, tag, err) +} diff --git a/langsmith/tracer_options.go b/langsmith/tracer_options.go new file mode 100644 index 000000000..7b19c9859 --- /dev/null +++ b/langsmith/tracer_options.go @@ -0,0 +1,53 @@ +package langsmith + +type LangChainTracerOption interface { + apply(t *Tracer) +} + +type langChainTracerOptionFunc func(t *Tracer) + +func (f langChainTracerOptionFunc) apply(t *Tracer) { + f(t) +} + +// WithClient sets the client to use for the tracer. +func WithClient(client *Client) LangChainTracerOption { + return langChainTracerOptionFunc(func(t *Tracer) { + t.client = client + }) +} + +// WithLogger sets the logger to use for the tracer. +func WithLogger(logger LeveledLogger) LangChainTracerOption { + return langChainTracerOptionFunc(func(t *Tracer) { + t.logger = logger + }) +} + +// WithProjectName sets the project name to use for the tracer. +func WithName(name string) LangChainTracerOption { + return langChainTracerOptionFunc(func(t *Tracer) { + t.name = name + }) +} + +// WithProjectName sets the project name to use for the tracer. +func WithProjectName(projectName string) LangChainTracerOption { + return langChainTracerOptionFunc(func(t *Tracer) { + t.projectName = projectName + }) +} + +// WithRunID sets the run ID to use for the tracer. +func WithRunID(runID string) LangChainTracerOption { + return langChainTracerOptionFunc(func(t *Tracer) { + t.runID = runID + }) +} + +// WithExtras sets the extras instrumented in tracer. +func WithExtras(extras KVMap) LangChainTracerOption { + return langChainTracerOptionFunc(func(t *Tracer) { + t.extras = extras + }) +} diff --git a/langsmith/types.go b/langsmith/types.go new file mode 100644 index 000000000..c5ea63119 --- /dev/null +++ b/langsmith/types.go @@ -0,0 +1,26 @@ +package langsmith + +import "time" + +type KVMap map[string]any + +func valueIfSetOtherwiseNil[T comparable](v T) *T { + var empty T + if v == empty { + return nil + } + + return &v +} + +func timeToMillisecondsPtr(t time.Time) *int64 { + if t.IsZero() { + return nil + } + + return ptr(t.UnixMilli()) +} + +func ptr[T any](v T) *T { + return &v +} diff --git a/llms/openai/openaillm.go b/llms/openai/openaillm.go index 78f8334d2..f893e92da 100644 --- a/llms/openai/openaillm.go +++ b/llms/openai/openaillm.go @@ -43,10 +43,17 @@ func (o *LLM) Call(ctx context.Context, prompt string, options ...llms.CallOptio return llms.GenerateFromSinglePrompt(ctx, o, prompt, options...) } +func (o *LLM) getCallbackHandler(ctx context.Context) callbacks.Handler { + if callbacksHandler := callbacks.GetHandlerFromContext(ctx); callbacksHandler != nil { + return callbacksHandler + } + return o.CallbacksHandler +} + // GenerateContent implements the Model interface. func (o *LLM) GenerateContent(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) { //nolint: lll, cyclop, goerr113, funlen - if o.CallbacksHandler != nil { - o.CallbacksHandler.HandleLLMGenerateContentStart(ctx, messages) + if callbacksHandler := o.getCallbackHandler(ctx); callbacksHandler != nil { + callbacksHandler.HandleLLMGenerateContentStart(ctx, messages) } opts := llms.CallOptions{} @@ -186,8 +193,9 @@ func (o *LLM) GenerateContent(ctx context.Context, messages []llms.MessageConten } } response := &llms.ContentResponse{Choices: choices} - if o.CallbacksHandler != nil { - o.CallbacksHandler.HandleLLMGenerateContentEnd(ctx, response) + + if callbacksHandler := o.getCallbackHandler(ctx); callbacksHandler != nil { + callbacksHandler.HandleLLMGenerateContentEnd(ctx, response) } return response, nil }