package main
import (
"context"
"log"
"os"
"time"
langwatch "github.com/langwatch/langwatch/sdk-go"
otelopenai "github.com/langwatch/langwatch/sdk-go/instrumentation/openai"
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
// Setup OpenTelemetry with timeout
setupCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
shutdown := setupLangWatch(setupCtx)
defer shutdown(ctx)
// Create instrumented OpenAI client
client := openai.NewClient(
option.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
option.WithMiddleware(otelopenai.Middleware("rag-app",
otelopenai.WithCaptureInput(),
otelopenai.WithCaptureOutput(),
)),
)
// Process user query
if err := processQuery(ctx, client, "How do I implement JWT authentication in Go?"); err != nil {
log.Fatalf("Failed to process query: %v", err)
}
}
func processQuery(ctx context.Context, client *openai.Client, query string) error {
tracer := langwatch.Tracer("rag-app")
ctx, span := tracer.Start(ctx, "ProcessUserQuery")
defer span.End()
span.SetType(langwatch.SpanTypeRAG)
span.SetThreadID("user-session-" + time.Now().Format("20060102"))
span.SetUserID("user-123")
span.RecordInputString(query)
// Step 1: Retrieve relevant documents
documents, err := retrieveDocuments(ctx, query)
if err != nil {
span.RecordError(err)
return err
}
// Step 2: Generate response with context
response, err := generateResponse(ctx, client, query, documents)
if err != nil {
span.RecordError(err)
return err
}
span.RecordOutputString(response)
return nil
}
func retrieveDocuments(ctx context.Context, query string) ([]string, error) {
tracer := langwatch.Tracer("retrieval")
ctx, span := tracer.Start(ctx, "RetrieveDocuments")
defer span.End()
span.SetType(langwatch.SpanTypeRetrieval)
span.RecordInputString(query)
// Simulate document retrieval
documents := []string{
"JWT tokens are commonly used for authentication...",
"Use the crypto/bcrypt package for password hashing...",
}
chunks := []langwatch.SpanRAGContextChunk{
{Content: documents[0], Source: "auth-guide", Score: 0.92},
{Content: documents[1], Source: "go-docs", Score: 0.88},
}
span.SetRAGContextChunks(chunks)
return documents, nil
}
func generateResponse(ctx context.Context, client *openai.Client, query string, documents []string) (string, error) {
tracer := langwatch.Tracer("llm")
ctx, span := tracer.Start(ctx, "GenerateResponse")
defer span.End()
span.SetType(langwatch.SpanTypeLLM)
span.SetRequestModel("gpt-4o-mini")
// Prepare context for LLM
context := "Context:\n" + documents[0] + "\n" + documents[1]
response, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
Model: openai.ChatModelGPT4oMini,
Messages: []openai.ChatCompletionMessageParamUnion{
openai.SystemMessage("You are a helpful assistant. Use the provided context to answer questions."),
openai.UserMessage(context + "\n\nQuestion: " + query),
},
})
if err != nil {
return "", err
}
content := response.Choices[0].Message.Content
span.RecordOutputString(content)
span.SetResponseModel(response.Model)
return content, nil
}
func setupLangWatch(ctx context.Context) func(context.Context) {
apiKey := os.Getenv("LANGWATCH_API_KEY")
if apiKey == "" {
log.Fatal("LANGWATCH_API_KEY environment variable not set")
}
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpointURL("https://app.langwatch.ai/api/otel/v1/traces"),
otlptracehttp.WithHeaders(map[string]string{
"Authorization": "Bearer " + apiKey,
}),
)
if err != nil {
log.Fatalf("failed to create OTLP exporter: %v", err)
}
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
otel.SetTracerProvider(tp)
return func(ctx context.Context) {
if err := tp.Shutdown(ctx); err != nil {
log.Printf("Error shutting down tracer provider: %v", err)
}
}
}