-
-
Notifications
You must be signed in to change notification settings - Fork 682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracing: Added Langsmith Support #1069
base: main
Are you sure you want to change the base?
Changes from 8 commits
43c04d3
2ba9c5a
7e34f5f
45881d1
83a615c
863dc3e
2028f09
8f4d6fb
ae03443
d76cda0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package callbacks | ||
|
||
import "context" | ||
|
||
type contextKeyType int | ||
|
||
// nolint: gochecknoglobals | ||
var callbackHandlerKey = contextKeyType(0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be const like _callbackHandlerKey to fix the lint. |
||
|
||
func CallbackHandler(ctx context.Context) Handler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this function name is a somewhat redundant and vague. Maybe something like GetHandlerFromContext instead? |
||
handler := ctx.Value(callbackHandlerKey) | ||
if t, ok := handler.(Handler); ok { | ||
return t | ||
} | ||
return nil | ||
} | ||
|
||
func WithCallback(ctx context.Context, handler Handler) context.Context { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think a name like this works outside of the context package: callbacks.WithCallback |
||
return context.WithValue(ctx, callbackHandlerKey, handler) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 := WithCallback(context.Background(), handler) | ||
|
||
got := CallbackHandler(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 = CallbackHandler(emptyCtx) | ||
if got != nil { | ||
t.Errorf("CallbackHandler() = %v, want nil", got) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also check if there is a callback handler in the chain |
||
if callbacksHandler != nil { | ||
callbacksHandler.HandleChainStart(ctx, inputValues) | ||
chainCallbackHandlers := callbacks.CallbackHandler(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.WithCallback(ctx, handler) | ||
} | ||
|
||
if handler := getChainCallbackHandler(c); handler != nil { | ||
return callbacks.WithCallback(ctx, handler) | ||
} | ||
return ctx | ||
} | ||
|
||
func callChain( | ||
ctx context.Context, | ||
c Chain, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
module github.com/tmc/langchaingo/examples/openai-gpt4o-example | ||
|
||
go 1.22.0 | ||
|
||
toolchain go1.22.1 | ||
|
||
require 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/davecgh/go-spew v1.1.1 // indirect | ||
github.com/dlclark/regexp2 v1.10.0 // indirect | ||
github.com/dustin/go-humanize v1.0.1 // indirect | ||
github.com/google/uuid v1.6.0 // 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 => ../.. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This call was dead code, it was not called anywhere