Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions agents/models_openai_responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ func (m OpenAIResponsesModel) GetResponse(
var response *responses.Response

err := tracing.ResponseSpan(
ctx, tracing.ResponseSpanParams{Disabled: params.Tracing.IsDisabled()},
ctx, tracing.ResponseSpanParams{
Disabled: params.Tracing.IsDisabled(),
Input: params.Input,
Model: string(m.Model),
Tools: params.Tools,
},
func(ctx context.Context, spanResponse tracing.Span) (err error) {
defer func() {
if err != nil {
Expand Down Expand Up @@ -137,7 +142,12 @@ func (m OpenAIResponsesModel) StreamResponse(
yield ModelStreamResponseCallback,
) error {
return tracing.ResponseSpan(
ctx, tracing.ResponseSpanParams{Disabled: params.Tracing.IsDisabled()},
ctx, tracing.ResponseSpanParams{
Disabled: params.Tracing.IsDisabled(),
Input: params.Input,
Model: string(m.Model),
Tools: params.Tools,
},
func(ctx context.Context, spanResponse tracing.Span) (err error) {
defer func() {
if err != nil {
Expand Down
211 changes: 211 additions & 0 deletions examples/tracing/traceloop/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2025 The NLP Odyssey Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"os"

"github.com/nlpodyssey/openai-agents-go/agents"
"github.com/nlpodyssey/openai-agents-go/tracing"
"github.com/nlpodyssey/openai-agents-go/tracing/wrappers/traceloop"
)

// Simple test tool for demonstration
type TestToolArgs struct {
Message string `json:"message"`
}

func TestTool(_ context.Context, args TestToolArgs) (string, error) {
return fmt.Sprintf("Tool executed with message: %s", args.Message), nil
}

var testTool = agents.NewFunctionTool("test_tool", "A simple test tool", TestTool)

// Test tool for verifying tool call extraction
type EchoArgs struct {
Message string `json:"message"`
}

func EchoTool(_ context.Context, args EchoArgs) (string, error) {
fmt.Printf("[TOOL EXECUTED] Echo: %s\n", args.Message)
return fmt.Sprintf("Echo: %s", args.Message), nil
}

var echoTool = agents.NewFunctionTool("echo", "Echoes the input message", EchoTool)

func testToolCalling() {
apiKey := "tl_4be59d06bb644ced90f8b21e2924a31e"

ctx := context.Background()

// Create the Traceloop processor
traceloopProcessor, err := traceloop.NewTracingProcessor(ctx, traceloop.ProcessorParams{
APIKey: apiKey,
BaseURL: "api.traceloop.com",
Metadata: map[string]any{
"environment": "test",
"version": "1.0.0",
},
Tags: []string{"tool-test", "golang"},
})
if err != nil {
fmt.Printf("Failed to create Traceloop processor: %v\n", err)
return
}

tracing.AddTraceProcessor(traceloopProcessor)

// Create agent that is forced to use the tool
agent := agents.New("Tool Test Agent").
WithInstructions("You MUST use the echo tool to repeat any message the user gives you. Always call the tool.").
WithTools(echoTool).
WithModel("gpt-4o-mini")

// Run the test
err = tracing.RunTrace(ctx, tracing.TraceParams{
WorkflowName: "Tool Calling Test",
TraceID: "trace_" + fmt.Sprintf("test_tool_%d", 123456),
}, func(ctx context.Context, trace tracing.Trace) error {
fmt.Println("Testing tool calling with traceloop integration...")

result, err := agents.Run(ctx, agent, "Echo this message: 'Tool calling works!'")
if err != nil {
return fmt.Errorf("agent run failed: %w", err)
}

fmt.Printf("Agent Response: %s\n", result.FinalOutput)
return nil
})

if err != nil {
fmt.Printf("Test failed: %v\n", err)
return
}

fmt.Println("Tool calling test completed!")
}

func main() {
// Set your Traceloop API key here or via environment variable
apiKey := "tl_4be59d06bb644ced90f8b21e2924a31e"
if apiKey == "" {
fmt.Println("Warning: TRACELOOP_API_KEY environment variable not set.")
fmt.Println("Please set your Traceloop API key to enable tracing.")
fmt.Println("You can get an API key from: https://app.traceloop.com")
return
}

ctx := context.Background()

// Create the Traceloop processor
traceloopProcessor, err := traceloop.NewTracingProcessor(ctx, traceloop.ProcessorParams{
APIKey: apiKey,
BaseURL: "api.traceloop.com", // Use "api-staging.traceloop.com" for staging
Metadata: map[string]any{
"environment": "development",
"version": "1.0.0",
},
Tags: []string{"demo", "golang", "openai-agents"},
})
if err != nil {
fmt.Printf("Failed to create Traceloop processor: %v\n", err)
return
}

// Add the Traceloop processor to the tracing system
// This will work alongside the default OpenAI backend processor
tracing.AddTraceProcessor(traceloopProcessor)

// Create agents for demonstration
weatherAgent := agents.New("Weather Agent").
WithInstructions("You are a helpful weather assistant. Always respond in a friendly manner.").
WithTools(agents.WebSearchTool{}).
WithModel("gpt-4o-mini")

jokeAgent := agents.New("Joke Agent").
WithInstructions("You tell funny jokes and make people laugh. Use the test tool if you need to send a message.").
WithTools(testTool).
WithModel("gpt-4o-mini")

// Main agent with handoffs
mainAgent := agents.New("Assistant").
WithInstructions(`You are a helpful assistant.
- For weather questions, handoff to the Weather Agent
- For jokes or funny requests, handoff to the Joke Agent
- For other questions, answer directly`).
WithAgentHandoffs(weatherAgent, jokeAgent).
WithModel("gpt-4o-mini")

// Run a trace that demonstrates various agent interactions
err = tracing.RunTrace(
ctx,
tracing.TraceParams{
WorkflowName: "Traceloop Integration Demo",
GroupID: "demo-conversation-001",
Metadata: map[string]any{
"user_id": "demo-user",
"session_id": "demo-session-001",
"source": "traceloop-example",
},
},
func(ctx context.Context, trace tracing.Trace) error {
fmt.Printf("Starting demo workflow - Trace ID: %s\n", trace.TraceID())

// First interaction - simple question
result1, err := agents.Run(ctx, mainAgent, "Hello! Can you introduce yourself?")
if err != nil {
return err
}
fmt.Printf("Introduction: %s\n\n", result1.FinalOutput)

// Second interaction - weather query
result2, err := agents.Run(ctx, mainAgent, "What's the weather like in New York today?")
if err != nil {
return err
}
fmt.Printf("Weather Response: %s\n\n", result2.FinalOutput)

// Third interaction - joke request with explicit tool usage
result3, err := agents.Run(ctx, mainAgent, "Tell me a programming joke! When you handoff to the joke agent, make sure they use the test tool to send a greeting message.")
if err != nil {
return err
}
fmt.Printf("Joke Response: %s\n\n", result3.FinalOutput)

return nil
},
)

if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}

fmt.Println("Demo completed! Check your Traceloop dashboard for the trace data.")

// Shutdown the processor to ensure all data is flushed
traceloopProcessor.Shutdown(ctx)

// Note about viewing traces
fmt.Println("\nYou can view your traces at: https://app.traceloop.com")
fmt.Println("The traces will appear as workflows with tasks for each agent interaction.")
fmt.Println("LLM calls will be captured with full prompt and response data.")

// Also run tool calling test
fmt.Println("\n--- Running Tool Calling Test ---")
testToolCalling()
}
87 changes: 87 additions & 0 deletions examples/tracing/traceloop/tool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2025 The NLP Odyssey Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"fmt"
"log"

"github.com/nlpodyssey/openai-agents-go/agents"
"github.com/nlpodyssey/openai-agents-go/tracing"
"github.com/nlpodyssey/openai-agents-go/tracing/wrappers/traceloop"
)

// Test tool for verifying tool call extraction
type EchoArgs struct {
Message string `json:"message"`
}

func EchoTool(_ context.Context, args EchoArgs) (string, error) {
fmt.Printf("[TOOL EXECUTED] Echo: %s\n", args.Message)
return fmt.Sprintf("Echo: %s", args.Message), nil
}

var echoTool = agents.NewFunctionTool("echo", "Echoes the input message", EchoTool)

func testToolCalling() {
apiKey := "tl_4be59d06bb644ced90f8b21e2924a31e"

ctx := context.Background()

// Create the Traceloop processor
traceloopProcessor, err := traceloop.NewTracingProcessor(ctx, traceloop.ProcessorParams{
APIKey: apiKey,
BaseURL: "api.traceloop.com",
Metadata: map[string]any{
"environment": "test",
"version": "1.0.0",
},
Tags: []string{"tool-test", "golang"},
})
if err != nil {
log.Fatalf("Failed to create Traceloop processor: %v", err)
}

tracing.AddTraceProcessor(traceloopProcessor)

// Create agent that is forced to use the tool
agent := agents.New("Tool Test Agent").
WithInstructions("You MUST use the echo tool to repeat any message the user gives you. Always call the tool.").
WithTools(echoTool).
WithModel("gpt-4o-mini")

// Run the test
err = tracing.RunTrace(ctx, tracing.TraceParams{
WorkflowName: "Tool Calling Test",
TraceID: "test-tool-trace-" + fmt.Sprintf("%d", 123456),
}, func(ctx context.Context, trace tracing.Trace) error {
fmt.Println("Testing tool calling with traceloop integration...")

result, err := agents.Run(ctx, agent, "Echo this message: 'Tool calling works!'")
if err != nil {
return fmt.Errorf("agent run failed: %w", err)
}

fmt.Printf("Agent Response: %s\n", result.FinalOutput)
return nil
})

if err != nil {
log.Fatalf("Test failed: %v", err)
}

fmt.Println("Tool calling test completed!")
}
39 changes: 38 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,69 @@ require (
github.com/openai/openai-go/v2 v2.0.2
github.com/playwright-community/playwright-go v0.5200.0
github.com/stretchr/testify v1.10.0
github.com/traceloop/go-openllmetry/traceloop-sdk v0.1.0
github.com/xeipuuv/gojsonschema v1.2.0
)

require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
github.com/go-audio/riff v1.0.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/kluctl/go-embed-python v0.0.0-3.11.6-20231002-1 // indirect
github.com/kluctl/go-jinja2 v0.0.0-20240108142937-8839259d2537 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sashabaranov/go-openai v1.18.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/traceloop/go-openllmetry/semconv-ai v0.0.0-20250405130248-6b2b4b41102b // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
google.golang.org/grpc v1.60.1 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading