From 7a44a86098e8b7713e8f88cf70188de0a26cdea3 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:09:53 +0200 Subject: [PATCH 01/17] added function --- sample-app/main.go | 3 +- semconv-ai/attributes.go | 1 + traceloop-sdk/sdk.go | 56 ++++++++++++++++++++++++++++++++++ traceloop-sdk/tracing_types.go | 8 +++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/sample-app/main.go b/sample-app/main.go index 363adba..bc7d707 100644 --- a/sample-app/main.go +++ b/sample-app/main.go @@ -24,7 +24,8 @@ func main() { } // Default to workflow example using prompt registry - workflowExample() + // workflowExample() + runJokeWorkflow() } func workflowExample() { diff --git a/semconv-ai/attributes.go b/semconv-ai/attributes.go index 78103c2..fac64f7 100644 --- a/semconv-ai/attributes.go +++ b/semconv-ai/attributes.go @@ -23,6 +23,7 @@ const ( LLMCompletions = attribute.Key("llm.completions") LLMChatStopSequence = attribute.Key("llm.chat.stop_sequences") LLMRequestFunctions = attribute.Key("llm.request.functions") + LLMAgentName = attribute.Key("llm.agent.name") // Vector DB VectorDBVendor = attribute.Key("vector_db.vendor") diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index d697072..b8a8a67 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -184,6 +184,62 @@ func (llmSpan *LLMSpan) LogCompletion(ctx context.Context, completion Completion return nil } +// LogToolCall logs a tool call with the specified name and prompt +func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttributes, prompt Prompt, workflowAttrs WorkflowAttributes) (LLMSpan, error) { + spanName := fmt.Sprintf("%s.tool", attrs.Name) + _, span := instance.getTracer().Start(ctx, spanName) + + spanAttrs := []attribute.KeyValue{ + semconvai.LLMVendor.String(prompt.Vendor), + semconvai.LLMRequestModel.String(prompt.Model), + semconvai.LLMRequestType.String(prompt.Mode), + semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), + semconvai.TraceloopSpanKind.String("tool"), + semconvai.TraceloopEntityName.String(attrs.Name), + } + + // Add association properties if provided + for key, value := range workflowAttrs.AssociationProperties { + spanAttrs = append(spanAttrs, attribute.String("traceloop.association.properties."+key, value)) + } + + span.SetAttributes(spanAttrs...) + setMessagesAttribute(span, "llm.prompts", prompt.Messages) + setToolsAttribute(span, prompt.Tools) + + return LLMSpan{ + span: span, + }, nil +} + +// LogAgent logs an agent with the specified name and prompt +func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, prompt Prompt, workflowAttrs WorkflowAttributes) (LLMSpan, error) { + spanName := fmt.Sprintf("%s.agent", attrs.Name) + _, span := instance.getTracer().Start(ctx, spanName) + + spanAttrs := []attribute.KeyValue{ + semconvai.LLMVendor.String(prompt.Vendor), + semconvai.LLMRequestModel.String(prompt.Model), + semconvai.LLMRequestType.String(prompt.Mode), + semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), + semconvai.TraceloopSpanKind.String("agent"), + semconvai.LLMAgentName.String(attrs.Name), + } + + // Add association properties if provided + for key, value := range workflowAttrs.AssociationProperties { + spanAttrs = append(spanAttrs, attribute.String("traceloop.association.properties."+key, value)) + } + + span.SetAttributes(spanAttrs...) + setMessagesAttribute(span, "llm.prompts", prompt.Messages) + setToolsAttribute(span, prompt.Tools) + + return LLMSpan{ + span: span, + }, nil +} + func (instance *Traceloop) Shutdown(ctx context.Context) { if instance.tracerProvider != nil { instance.tracerProvider.Shutdown(ctx) diff --git a/traceloop-sdk/tracing_types.go b/traceloop-sdk/tracing_types.go index f4efa32..06cb824 100644 --- a/traceloop-sdk/tracing_types.go +++ b/traceloop-sdk/tracing_types.go @@ -57,3 +57,11 @@ type ToolCallFunction struct { Name string `json:"name"` Arguments string `json:"arguments"` } + +type ToolCallAttributes struct { + Name string `json:"name"` +} + +type AgentAttributes struct { + Name string `json:"name"` +} From cb234b19f1876fb858be40edf7416ae4420c6ff8 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:10:46 +0200 Subject: [PATCH 02/17] added example --- .gitignore | 1 - sample-app/generate_joke_workflow_example.go | 300 +++++++++++++++++++ 2 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 sample-app/generate_joke_workflow_example.go diff --git a/.gitignore b/.gitignore index 27f1258..21a1703 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ *.so *.tar.gz /release/ -sample-app # Test binary, built with `go test -c` *.test diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go new file mode 100644 index 0000000..937be4d --- /dev/null +++ b/sample-app/generate_joke_workflow_example.go @@ -0,0 +1,300 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/sashabaranov/go-openai" + sdk "github.com/traceloop/go-openllmetry/traceloop-sdk" +) + +func createJoke(ctx context.Context, workflow *sdk.Workflow, client *openai.Client) (string, error) { + task := workflow.NewTask("joke_creation") + defer task.End() + + // Log prompt + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: "Tell me a joke about opentelemetry", + }, + }, + } + + llmSpan, err := task.LogPrompt(prompt) + if err != nil { + return "", fmt.Errorf("LogPrompt error: %w", err) + } + + // Make API call + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: "Tell me a joke about opentelemetry", + }, + }, + }) + if err != nil { + return "", fmt.Errorf("CreateChatCompletion error: %w", err) + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + return resp.Choices[0].Message.Content, nil +} + +func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workflow *sdk.Workflow, client *openai.Client, joke string) (string, error) { + // Log prompt + piratePrompt := fmt.Sprintf("Translate the below joke to pirate-like english:\n\n%s", joke) + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: piratePrompt, + }, + }, + } + + llmSpan, err := traceloop.LogAgent(ctx, sdk.AgentAttributes{ + Name: "joke_translation", + }, prompt, workflow.Attributes) + if err != nil { + return "", fmt.Errorf("LogPrompt error: %w", err) + } + + // Make API call + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: piratePrompt, + }, + }, + }) + if err != nil { + return "", fmt.Errorf("CreateChatCompletion error: %w", err) + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + // Call history jokes tool + _, err = historyJokesTool(ctx, traceloop, workflow, client) + if err != nil { + fmt.Printf("Warning: history_jokes_tool error: %v\n", err) + } + + return resp.Choices[0].Message.Content, nil +} + +func historyJokesTool(ctx context.Context, traceloop *sdk.Traceloop, workflow *sdk.Workflow, client *openai.Client) (string, error) { + // Log prompt + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: "get some history jokes", + }, + }, + } + + llmSpan, err := traceloop.LogToolCall(ctx, sdk.ToolCallAttributes{ + Name: "history_jokes", + }, prompt, workflow.Attributes) + if err != nil { + return "", fmt.Errorf("LogPrompt error: %w", err) + } + + // Make API call + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: "get some history jokes", + }, + }, + }) + if err != nil { + return "", fmt.Errorf("CreateChatCompletion error: %w", err) + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + return resp.Choices[0].Message.Content, nil +} + +func generateSignature(ctx context.Context, workflow *sdk.Workflow, client *openai.Client, joke string) (string, error) { + task := workflow.NewTask("signature_generation") + defer task.End() + + signaturePrompt := "add a signature to the joke:\n\n" + joke + + // Log prompt + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "completion", + Model: "davinci-002", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: signaturePrompt, + }, + }, + } + + llmSpan, err := task.LogPrompt(prompt) + if err != nil { + return "", fmt.Errorf("LogPrompt error: %w", err) + } + + // Make API call + resp, err := client.CreateCompletion(ctx, openai.CompletionRequest{ + Model: "davinci-002", + Prompt: signaturePrompt, + }) + if err != nil { + return "", fmt.Errorf("CreateCompletion error: %w", err) + } + + // Log completion + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: []sdk.Message{ + { + Index: 0, + Role: "assistant", + Content: resp.Choices[0].Text, + }, + }, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + return resp.Choices[0].Text, nil +} + +func runJokeWorkflow() { + ctx := context.Background() + + // Initialize Traceloop SDK + traceloop, err := sdk.NewClient(ctx, sdk.Config{ + APIKey: os.Getenv("TRACELOOP_API_KEY"), + }) + if err != nil { + fmt.Printf("NewClient error: %v\n", err) + return + } + defer func() { traceloop.Shutdown(ctx) }() + + // Create OpenAI client + client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) + + // Create workflow + wf := traceloop.NewWorkflow(ctx, sdk.WorkflowAttributes{ + Name: "go-joke_generator", + AssociationProperties: map[string]string{ + "user_id": "user_12345", + "chat_id": "chat_1234", + }, + }) + defer wf.End() + + // Execute workflow steps + fmt.Println("Creating joke...") + engJoke, err := createJoke(ctx, wf, client) + if err != nil { + fmt.Printf("Error creating joke: %v\n", err) + return + } + fmt.Printf("\nEnglish joke:\n%s\n\n", engJoke) + + fmt.Println("Translating to pirate...") + pirateJoke, err := translateJokeToPirate(ctx, traceloop, wf, client, engJoke) + if err != nil { + fmt.Printf("Error translating joke: %v\n", err) + return + } + fmt.Printf("\nPirate joke:\n%s\n\n", pirateJoke) + + fmt.Println("Generating signature...") + signature, err := generateSignature(ctx, wf, client, pirateJoke) + if err != nil { + fmt.Printf("Error generating signature: %v\n", err) + return + } + + // Combine result + result := pirateJoke + "\n\n" + signature + fmt.Printf("\n=== Final Result ===\n%s\n", result) +} From ad406acc250c147b839b5da397cb6421ef603328 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:43:43 +0200 Subject: [PATCH 03/17] split task --- sample-app/generate_joke_workflow_example.go | 21 ++++++++++---- traceloop-sdk/model/types.go | 10 +++++++ traceloop-sdk/sdk.go | 27 ++++++++---------- traceloop-sdk/task.go | 29 ++++++++++++++++++++ traceloop-sdk/workflow.go | 19 +++++-------- 5 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 traceloop-sdk/model/types.go create mode 100644 traceloop-sdk/task.go diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 937be4d..3e2830c 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -84,9 +84,14 @@ func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workfl }, } - llmSpan, err := traceloop.LogAgent(ctx, sdk.AgentAttributes{ + llmSpan, err := workflow.LogAgent(sdk.AgentAttributes{ Name: "joke_translation", - }, prompt, workflow.Attributes) + }) + if err != nil { + return "", fmt.Errorf("LogPrompt error: %w", err) + } + + llmSpan, err = workflow.LogPrompt(prompt) if err != nil { return "", fmt.Errorf("LogPrompt error: %w", err) } @@ -148,12 +153,17 @@ func historyJokesTool(ctx context.Context, traceloop *sdk.Traceloop, workflow *s }, } - llmSpan, err := traceloop.LogToolCall(ctx, sdk.ToolCallAttributes{ - Name: "history_jokes", - }, prompt, workflow.Attributes) + llmSpan, err := workflow.LogPrompt(prompt) if err != nil { return "", fmt.Errorf("LogPrompt error: %w", err) } + + _, err = workflow.LogToolCall(sdk.ToolCallAttributes{ + Name: "history_jokes", + }) + if err != nil { + return "", fmt.Errorf("LogToolCall error: %w", err) + } // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ @@ -257,7 +267,6 @@ func runJokeWorkflow() { } defer func() { traceloop.Shutdown(ctx) }() - // Create OpenAI client client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) // Create workflow diff --git a/traceloop-sdk/model/types.go b/traceloop-sdk/model/types.go new file mode 100644 index 0000000..5891434 --- /dev/null +++ b/traceloop-sdk/model/types.go @@ -0,0 +1,10 @@ +package model + +type SpanKind string + +const ( + SpanKindTool SpanKind = "tool" + SpanKindAgent SpanKind = "agent" + SpanKindTask SpanKind = "task" + SpanKindWorkflow SpanKind = "workflow" +) \ No newline at end of file diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index b8a8a67..dde3e50 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -21,6 +21,11 @@ import ( const PromptsPath = "/v1/traceloop/prompts" +const ( + ToolType = "tool" + AgentType = "agent" +) + type Traceloop struct { config Config promptRegistry model.PromptRegistry @@ -184,17 +189,14 @@ func (llmSpan *LLMSpan) LogCompletion(ctx context.Context, completion Completion return nil } -// LogToolCall logs a tool call with the specified name and prompt -func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttributes, prompt Prompt, workflowAttrs WorkflowAttributes) (LLMSpan, error) { +// LogToolCall logs a tool call with the specified name +func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttributes, workflowAttrs WorkflowAttributes) (LLMSpan, error) { spanName := fmt.Sprintf("%s.tool", attrs.Name) _, span := instance.getTracer().Start(ctx, spanName) spanAttrs := []attribute.KeyValue{ - semconvai.LLMVendor.String(prompt.Vendor), - semconvai.LLMRequestModel.String(prompt.Model), - semconvai.LLMRequestType.String(prompt.Mode), semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), - semconvai.TraceloopSpanKind.String("tool"), + semconvai.TraceloopSpanKind.String(ToolType), semconvai.TraceloopEntityName.String(attrs.Name), } @@ -204,25 +206,20 @@ func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttrib } span.SetAttributes(spanAttrs...) - setMessagesAttribute(span, "llm.prompts", prompt.Messages) - setToolsAttribute(span, prompt.Tools) return LLMSpan{ span: span, }, nil } -// LogAgent logs an agent with the specified name and prompt -func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, prompt Prompt, workflowAttrs WorkflowAttributes) (LLMSpan, error) { +// LogAgent logs an agent with the specified name +func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, workflowAttrs WorkflowAttributes) (LLMSpan, error) { spanName := fmt.Sprintf("%s.agent", attrs.Name) _, span := instance.getTracer().Start(ctx, spanName) spanAttrs := []attribute.KeyValue{ - semconvai.LLMVendor.String(prompt.Vendor), - semconvai.LLMRequestModel.String(prompt.Model), - semconvai.LLMRequestType.String(prompt.Mode), semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), - semconvai.TraceloopSpanKind.String("agent"), + semconvai.TraceloopSpanKind.String(AgentType), semconvai.LLMAgentName.String(attrs.Name), } @@ -232,8 +229,6 @@ func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, } span.SetAttributes(spanAttrs...) - setMessagesAttribute(span, "llm.prompts", prompt.Messages) - setToolsAttribute(span, prompt.Tools) return LLMSpan{ span: span, diff --git a/traceloop-sdk/task.go b/traceloop-sdk/task.go new file mode 100644 index 0000000..fc0a382 --- /dev/null +++ b/traceloop-sdk/task.go @@ -0,0 +1,29 @@ +package traceloop + +import ( + "context" + + "go.opentelemetry.io/otel/trace" +) + +type Task struct { + workflow *Workflow + ctx context.Context + Name string `json:"name"` +} + +func (task *Task) End() { + trace.SpanFromContext(task.ctx).End() +} + +func (task *Task) LogPrompt(prompt Prompt) (LLMSpan, error) { + return task.workflow.sdk.LogPrompt(task.ctx, prompt, task.workflow.Attributes) +} + +func (task *Task) LogAgent(attrs AgentAttributes) (LLMSpan, error) { + return task.workflow.sdk.LogAgent(task.ctx, attrs, task.workflow.Attributes) +} + +func (task *Task) LogToolCall(attrs ToolCallAttributes) (LLMSpan, error) { + return task.workflow.sdk.LogToolCall(task.ctx, attrs, task.workflow.Attributes) +} \ No newline at end of file diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 501a03a..8acf96c 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/traceloop/go-openllmetry/traceloop-sdk/model" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" "go.opentelemetry.io/otel/trace" ) @@ -14,18 +15,12 @@ type Workflow struct { Attributes WorkflowAttributes `json:"workflow_attributes"` } -type Task struct { - workflow *Workflow - ctx context.Context - Name string `json:"name"` -} - func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttributes) *Workflow { wCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.workflow", attrs.Name), trace.WithNewRoot()) span.SetAttributes( semconvai.TraceloopWorkflowName.String(attrs.Name), - semconvai.TraceloopSpanKind.String("workflow"), + semconvai.TraceloopSpanKind.String(string(model.SpanKindWorkflow)), semconvai.TraceloopEntityName.String(attrs.Name), ) @@ -49,7 +44,7 @@ func (workflow *Workflow) NewTask(name string) *Task { span.SetAttributes( semconvai.TraceloopWorkflowName.String(workflow.Attributes.Name), - semconvai.TraceloopSpanKind.String("task"), + semconvai.TraceloopSpanKind.String(string(model.SpanKindTask)), semconvai.TraceloopEntityName.String(name), ) @@ -60,10 +55,10 @@ func (workflow *Workflow) NewTask(name string) *Task { } } -func (task *Task) End() { - trace.SpanFromContext(task.ctx).End() +func (workflow *Workflow) LogAgent(attrs AgentAttributes) (LLMSpan, error) { + return workflow.sdk.LogAgent(workflow.ctx, attrs, workflow.Attributes) } -func (task *Task) LogPrompt(prompt Prompt) (LLMSpan, error) { - return task.workflow.sdk.LogPrompt(task.ctx, prompt, task.workflow.Attributes) +func (workflow *Workflow) LogToolCall(attrs ToolCallAttributes) (LLMSpan, error) { + return workflow.sdk.LogToolCall(workflow.ctx, attrs, workflow.Attributes) } From 37ff07db8b0f66763f5d430ede5d372016559bcd Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:20:13 +0200 Subject: [PATCH 04/17] divide --- sample-app/generate_joke_workflow_example.go | 24 ++++---------- sample-app/main.go | 6 +--- sample-app/tool_calling.go | 6 +--- traceloop-sdk/llm_span.go | 34 ++++++++++++++++++++ traceloop-sdk/sdk.go | 25 +++----------- traceloop-sdk/sdk_test.go | 6 ++-- traceloop-sdk/task.go | 4 +-- traceloop-sdk/workflow.go | 4 +-- 8 files changed, 53 insertions(+), 56 deletions(-) create mode 100644 traceloop-sdk/llm_span.go diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 3e2830c..ed82024 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -84,17 +84,12 @@ func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workfl }, } - llmSpan, err := workflow.LogAgent(sdk.AgentAttributes{ + llmSpan := workflow.LogAgent(sdk.AgentAttributes{ Name: "joke_translation", }) - if err != nil { - return "", fmt.Errorf("LogPrompt error: %w", err) - } - llmSpan, err = workflow.LogPrompt(prompt) - if err != nil { - return "", fmt.Errorf("LogPrompt error: %w", err) - } + llmSpan.LogPrompt(ctx, prompt) + // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ @@ -152,18 +147,13 @@ func historyJokesTool(ctx context.Context, traceloop *sdk.Traceloop, workflow *s }, }, } - - llmSpan, err := workflow.LogPrompt(prompt) - if err != nil { - return "", fmt.Errorf("LogPrompt error: %w", err) - } - _, err = workflow.LogToolCall(sdk.ToolCallAttributes{ + llmSpan := workflow.LogToolCall(sdk.ToolCallAttributes{ Name: "history_jokes", }) - if err != nil { - return "", fmt.Errorf("LogToolCall error: %w", err) - } + + llmSpan.LogPrompt(ctx, prompt) + // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ diff --git a/sample-app/main.go b/sample-app/main.go index bc7d707..1fa1e9e 100644 --- a/sample-app/main.go +++ b/sample-app/main.go @@ -104,7 +104,7 @@ func workflowExample() { } // Log the completion - err = llmSpan.LogCompletion(ctx, sdk.Completion{ + llmSpan.LogCompletion(ctx, sdk.Completion{ Model: resp.Model, Messages: completionMsgs, }, sdk.Usage{ @@ -112,10 +112,6 @@ func workflowExample() { CompletionTokens: resp.Usage.CompletionTokens, PromptTokens: resp.Usage.PromptTokens, }) - if err != nil { - fmt.Printf("LogCompletion error: %v\n", err) - return - } fmt.Println(resp.Choices[0].Message.Content) } \ No newline at end of file diff --git a/sample-app/tool_calling.go b/sample-app/tool_calling.go index f654899..c00df65 100644 --- a/sample-app/tool_calling.go +++ b/sample-app/tool_calling.go @@ -175,11 +175,7 @@ func runToolCallingExample() { PromptTokens: int(resp.Usage.PromptTokens), } - err = llmSpan.LogCompletion(ctx, completion, usage) - if err != nil { - fmt.Printf("Error logging completion: %v\n", err) - return - } + llmSpan.LogCompletion(ctx, completion, usage) // If tool calls were made, execute them if len(resp.Choices[0].Message.ToolCalls) > 0 { diff --git a/traceloop-sdk/llm_span.go b/traceloop-sdk/llm_span.go new file mode 100644 index 0000000..168fb19 --- /dev/null +++ b/traceloop-sdk/llm_span.go @@ -0,0 +1,34 @@ +package traceloop + +import ( + "context" + + semconvai "github.com/traceloop/go-openllmetry/semconv-ai" + apitrace "go.opentelemetry.io/otel/trace" +) + +type LLMSpan struct { + span apitrace.Span +} + +func (llmSpan *LLMSpan) LogPrompt(ctx context.Context, prompt Prompt) { + llmSpan.span.SetAttributes( + semconvai.LLMRequestModel.String(prompt.Model), + semconvai.LLMRequestType.String(prompt.Mode), + ) + + setMessagesAttribute(llmSpan.span, "llm.prompts", prompt.Messages) +} + +func (llmSpan *LLMSpan) LogCompletion(ctx context.Context, completion Completion, usage Usage) { + llmSpan.span.SetAttributes( + semconvai.LLMResponseModel.String(completion.Model), + semconvai.LLMUsageTotalTokens.Int(usage.TotalTokens), + semconvai.LLMUsageCompletionTokens.Int(usage.CompletionTokens), + semconvai.LLMUsagePromptTokens.Int(usage.PromptTokens), + ) + + setMessagesAttribute(llmSpan.span, "llm.completions", completion.Messages) + + defer llmSpan.span.End() +} \ No newline at end of file diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index dde3e50..01e8b89 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -34,9 +34,6 @@ type Traceloop struct { http.Client } -type LLMSpan struct { - span apitrace.Span -} func NewClient(ctx context.Context, config Config) (*Traceloop, error) { instance := Traceloop{ @@ -175,22 +172,8 @@ func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflo }, nil } -func (llmSpan *LLMSpan) LogCompletion(ctx context.Context, completion Completion, usage Usage) error { - llmSpan.span.SetAttributes( - semconvai.LLMResponseModel.String(completion.Model), - semconvai.LLMUsageTotalTokens.Int(usage.TotalTokens), - semconvai.LLMUsageCompletionTokens.Int(usage.CompletionTokens), - semconvai.LLMUsagePromptTokens.Int(usage.PromptTokens), - ) - - setMessagesAttribute(llmSpan.span, "llm.completions", completion.Messages) - - defer llmSpan.span.End() - return nil -} - // LogToolCall logs a tool call with the specified name -func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttributes, workflowAttrs WorkflowAttributes) (LLMSpan, error) { +func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttributes, workflowAttrs WorkflowAttributes) LLMSpan { spanName := fmt.Sprintf("%s.tool", attrs.Name) _, span := instance.getTracer().Start(ctx, spanName) @@ -209,11 +192,11 @@ func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttrib return LLMSpan{ span: span, - }, nil + } } // LogAgent logs an agent with the specified name -func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, workflowAttrs WorkflowAttributes) (LLMSpan, error) { +func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, workflowAttrs WorkflowAttributes) LLMSpan { spanName := fmt.Sprintf("%s.agent", attrs.Name) _, span := instance.getTracer().Start(ctx, spanName) @@ -232,7 +215,7 @@ func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, return LLMSpan{ span: span, - }, nil + } } func (instance *Traceloop) Shutdown(ctx context.Context) { diff --git a/traceloop-sdk/sdk_test.go b/traceloop-sdk/sdk_test.go index 5b53763..2ebf2a6 100644 --- a/traceloop-sdk/sdk_test.go +++ b/traceloop-sdk/sdk_test.go @@ -103,10 +103,8 @@ func TestLogPromptSpanAttributes(t *testing.T) { PromptTokens: 82, } - err = llmSpan.LogCompletion(context.Background(), completion, usage) - if err != nil { - t.Fatalf("LogCompletion failed: %v", err) - } + llmSpan.LogCompletion(context.Background(), completion, usage) + // Get the recorded spans spans := exporter.GetSpans() diff --git a/traceloop-sdk/task.go b/traceloop-sdk/task.go index fc0a382..133661d 100644 --- a/traceloop-sdk/task.go +++ b/traceloop-sdk/task.go @@ -20,10 +20,10 @@ func (task *Task) LogPrompt(prompt Prompt) (LLMSpan, error) { return task.workflow.sdk.LogPrompt(task.ctx, prompt, task.workflow.Attributes) } -func (task *Task) LogAgent(attrs AgentAttributes) (LLMSpan, error) { +func (task *Task) LogAgent(attrs AgentAttributes) LLMSpan { return task.workflow.sdk.LogAgent(task.ctx, attrs, task.workflow.Attributes) } -func (task *Task) LogToolCall(attrs ToolCallAttributes) (LLMSpan, error) { +func (task *Task) LogToolCall(attrs ToolCallAttributes) LLMSpan { return task.workflow.sdk.LogToolCall(task.ctx, attrs, task.workflow.Attributes) } \ No newline at end of file diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 8acf96c..8dab46b 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -55,10 +55,10 @@ func (workflow *Workflow) NewTask(name string) *Task { } } -func (workflow *Workflow) LogAgent(attrs AgentAttributes) (LLMSpan, error) { +func (workflow *Workflow) LogAgent(attrs AgentAttributes) LLMSpan { return workflow.sdk.LogAgent(workflow.ctx, attrs, workflow.Attributes) } -func (workflow *Workflow) LogToolCall(attrs ToolCallAttributes) (LLMSpan, error) { +func (workflow *Workflow) LogToolCall(attrs ToolCallAttributes) LLMSpan { return workflow.sdk.LogToolCall(workflow.ctx, attrs, workflow.Attributes) } From 6cfec4ef5f904dcb72d8078816d38b5b8cde3a2e Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:55:46 +0200 Subject: [PATCH 05/17] wip --- sample-app/generate_joke_workflow_example.go | 30 +++++-------- sample-app/main.go | 4 +- sample-app/tool_calling.go | 6 +-- sample-app/workflow_example.go | 8 +--- traceloop-sdk/agent.go | 47 ++++++++++++++++++++ traceloop-sdk/sdk.go | 4 +- traceloop-sdk/sdk_test.go | 5 +-- traceloop-sdk/task.go | 2 +- traceloop-sdk/tool.go | 23 ++++++++++ traceloop-sdk/tracing_types.go | 4 -- traceloop-sdk/workflow.go | 21 ++++++--- 11 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 traceloop-sdk/agent.go create mode 100644 traceloop-sdk/tool.go diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index ed82024..958caa1 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -27,10 +27,7 @@ func createJoke(ctx context.Context, workflow *sdk.Workflow, client *openai.Clie }, } - llmSpan, err := task.LogPrompt(prompt) - if err != nil { - return "", fmt.Errorf("LogPrompt error: %w", err) - } + llmSpan := task.LogPrompt(prompt) // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ @@ -84,13 +81,10 @@ func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workfl }, } - llmSpan := workflow.LogAgent(sdk.AgentAttributes{ - Name: "joke_translation", - }) + agent := workflow.NewAgent("joke_translation") - llmSpan.LogPrompt(ctx, prompt) + llmSpan := agent.LogPrompt(prompt) - // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ Model: "gpt-3.5-turbo", @@ -125,7 +119,7 @@ func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workfl }) // Call history jokes tool - _, err = historyJokesTool(ctx, traceloop, workflow, client) + _, err = historyJokesTool(ctx, agent, client) if err != nil { fmt.Printf("Warning: history_jokes_tool error: %v\n", err) } @@ -133,7 +127,7 @@ func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workfl return resp.Choices[0].Message.Content, nil } -func historyJokesTool(ctx context.Context, traceloop *sdk.Traceloop, workflow *sdk.Workflow, client *openai.Client) (string, error) { +func historyJokesTool(ctx context.Context, agent *sdk.Agent, client *openai.Client) (string, error) { // Log prompt prompt := sdk.Prompt{ Vendor: "openai", @@ -148,12 +142,13 @@ func historyJokesTool(ctx context.Context, traceloop *sdk.Traceloop, workflow *s }, } - llmSpan := workflow.LogToolCall(sdk.ToolCallAttributes{ - Name: "history_jokes", + tool := agent.NewTool("history_jokes", "function", sdk.ToolFunction{ + Name: "history_jokes", + Description: "Get some history jokes", + Parameters: map[string]interface{}{}, }) - llmSpan.LogPrompt(ctx, prompt) - + llmSpan := tool.LogPrompt(prompt) // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ @@ -211,10 +206,7 @@ func generateSignature(ctx context.Context, workflow *sdk.Workflow, client *open }, } - llmSpan, err := task.LogPrompt(prompt) - if err != nil { - return "", fmt.Errorf("LogPrompt error: %w", err) - } + llmSpan := task.LogPrompt(prompt) // Make API call resp, err := client.CreateCompletion(ctx, openai.CompletionRequest{ diff --git a/sample-app/main.go b/sample-app/main.go index 1fa1e9e..52ce875 100644 --- a/sample-app/main.go +++ b/sample-app/main.go @@ -62,7 +62,7 @@ func workflowExample() { } // Log the prompt - llmSpan, err := traceloop.LogPrompt( + llmSpan := traceloop.LogPrompt( ctx, sdk.Prompt{ Vendor: "openai", @@ -104,7 +104,7 @@ func workflowExample() { } // Log the completion - llmSpan.LogCompletion(ctx, sdk.Completion{ + llmSpan.LogCompletion(ctx, sdk.Completion{ Model: resp.Model, Messages: completionMsgs, }, sdk.Usage{ diff --git a/sample-app/tool_calling.go b/sample-app/tool_calling.go index c00df65..9792324 100644 --- a/sample-app/tool_calling.go +++ b/sample-app/tool_calling.go @@ -91,11 +91,7 @@ func runToolCallingExample() { fmt.Printf("User: %s\n", userPrompt) // Log the prompt - llmSpan, err := traceloop.LogPrompt(ctx, prompt, workflowAttrs) - if err != nil { - fmt.Printf("Error logging prompt: %v\n", err) - return - } + llmSpan := traceloop.LogPrompt(ctx, prompt, workflowAttrs) // Make API call to OpenAI startTime := time.Now() diff --git a/sample-app/workflow_example.go b/sample-app/workflow_example.go index 9ee3638..a0b498f 100644 --- a/sample-app/workflow_example.go +++ b/sample-app/workflow_example.go @@ -47,7 +47,7 @@ func workflowMain() { }) } - llmSpan, err := factGenTask.LogPrompt( + llmSpan := factGenTask.LogPrompt( tlp.Prompt{ Vendor: "openai", Mode: "chat", @@ -55,10 +55,6 @@ func workflowMain() { Messages: promptMsgs, }, ) - if err != nil { - fmt.Printf("LogPrompt error: %v\n", err) - return - } client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) resp, err := client.CreateChatCompletion( @@ -91,7 +87,7 @@ func workflowMain() { someOtherTask := wf.NewTask("some_other_task") defer someOtherTask.End() - otherPrompt, _ := someOtherTask.LogPrompt(tlp.Prompt{ + otherPrompt := someOtherTask.LogPrompt(tlp.Prompt{ Vendor: "openai", Mode: "chat", Model: request.Model, diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go new file mode 100644 index 0000000..2655108 --- /dev/null +++ b/traceloop-sdk/agent.go @@ -0,0 +1,47 @@ +package traceloop + +import ( + "context" + "fmt" + + semconvai "github.com/traceloop/go-openllmetry/semconv-ai" + "go.opentelemetry.io/otel/trace" + "github.com/traceloop/go-openllmetry/traceloop-sdk/model" +) + +type Agent struct { + sdk *Traceloop + workflow *Workflow + ctx context.Context + Name string `json:"name"` +} + +func (agent *Agent) End() { + trace.SpanFromContext(agent.ctx).End() +} + +func (agent *Agent) LogPrompt(prompt Prompt) LLMSpan { + if agent.workflow != nil { + return agent.workflow.LogPrompt(prompt) + } + return agent.sdk.LogPrompt(agent.ctx, prompt, WorkflowAttributes{}) +} + +func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunction) *Tool { + toolCtx, span := agent.sdk.getTracer().Start(agent.ctx, fmt.Sprintf("%s.tool", name)) + span.SetAttributes( + semconvai.LLMAgentName.String(agent.Name), + semconvai.TraceloopSpanKind.String(string(model.SpanKindTool)), + semconvai.TraceloopEntityName.String(name), + ) + + return &Tool{ + agent: agent, + ctx: toolCtx, + Name: name, + Type: toolType, + Function: toolFunction, + } +} + + diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 01e8b89..59caba8 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -147,7 +147,7 @@ func (instance *Traceloop) getTracer() apitrace.Tracer { } // New workflow-based API -func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflowAttrs WorkflowAttributes) (LLMSpan, error) { +func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflowAttrs WorkflowAttributes) LLMSpan { spanName := fmt.Sprintf("%s.%s", prompt.Vendor, prompt.Mode) _, span := instance.getTracer().Start(ctx, spanName) @@ -169,7 +169,7 @@ func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflo return LLMSpan{ span: span, - }, nil + } } // LogToolCall logs a tool call with the specified name diff --git a/traceloop-sdk/sdk_test.go b/traceloop-sdk/sdk_test.go index 2ebf2a6..512242f 100644 --- a/traceloop-sdk/sdk_test.go +++ b/traceloop-sdk/sdk_test.go @@ -70,10 +70,7 @@ func TestLogPromptSpanAttributes(t *testing.T) { } // Log the prompt using new workflow API - llmSpan, err := tl.LogPrompt(context.Background(), prompt, workflowAttrs) - if err != nil { - t.Fatalf("LogPrompt failed: %v", err) - } + llmSpan := tl.LogPrompt(context.Background(), prompt, workflowAttrs) // Log completion with tool calls completion := Completion{ diff --git a/traceloop-sdk/task.go b/traceloop-sdk/task.go index 133661d..95513e7 100644 --- a/traceloop-sdk/task.go +++ b/traceloop-sdk/task.go @@ -16,7 +16,7 @@ func (task *Task) End() { trace.SpanFromContext(task.ctx).End() } -func (task *Task) LogPrompt(prompt Prompt) (LLMSpan, error) { +func (task *Task) LogPrompt(prompt Prompt) LLMSpan { return task.workflow.sdk.LogPrompt(task.ctx, prompt, task.workflow.Attributes) } diff --git a/traceloop-sdk/tool.go b/traceloop-sdk/tool.go new file mode 100644 index 0000000..70d05e4 --- /dev/null +++ b/traceloop-sdk/tool.go @@ -0,0 +1,23 @@ +package traceloop + +import ( + "context" + + "go.opentelemetry.io/otel/trace" +) + +type Tool struct { + agent *Agent + ctx context.Context + Name string `json:"name"` + Type string `json:"type"` + Function ToolFunction `json:"function,omitempty"` +} + +func (tool *Tool) End() { + trace.SpanFromContext(tool.ctx).End() +} + +func (tool *Tool) LogPrompt(prompt Prompt) LLMSpan { + return tool.agent.LogPrompt(prompt) +} diff --git a/traceloop-sdk/tracing_types.go b/traceloop-sdk/tracing_types.go index 06cb824..f4fa0bb 100644 --- a/traceloop-sdk/tracing_types.go +++ b/traceloop-sdk/tracing_types.go @@ -42,10 +42,6 @@ type ToolFunction struct { Parameters interface{} `json:"parameters"` } -type Tool struct { - Type string `json:"type"` - Function ToolFunction `json:"function,omitempty"` -} type ToolCall struct { ID string `json:"id"` diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 8dab46b..25ff3e9 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -35,7 +35,7 @@ func (workflow *Workflow) End() { trace.SpanFromContext(workflow.ctx).End() } -func (workflow *Workflow) LogPrompt(prompt Prompt) (LLMSpan, error) { +func (workflow *Workflow) LogPrompt(prompt Prompt) LLMSpan { return workflow.sdk.LogPrompt(workflow.ctx, prompt, workflow.Attributes) } @@ -55,10 +55,19 @@ func (workflow *Workflow) NewTask(name string) *Task { } } -func (workflow *Workflow) LogAgent(attrs AgentAttributes) LLMSpan { - return workflow.sdk.LogAgent(workflow.ctx, attrs, workflow.Attributes) -} +func (workflow *Workflow) NewAgent(name string) *Agent { + aCtx, span := workflow.sdk.getTracer().Start(workflow.ctx, fmt.Sprintf("%s.agent", name)) + + span.SetAttributes( + semconvai.TraceloopWorkflowName.String(workflow.Attributes.Name), + semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), + semconvai.TraceloopEntityName.String(name), + ) -func (workflow *Workflow) LogToolCall(attrs ToolCallAttributes) LLMSpan { - return workflow.sdk.LogToolCall(workflow.ctx, attrs, workflow.Attributes) + return &Agent{ + workflow: workflow, + ctx: aCtx, + Name: name, + } } + From eba85cec5b9ed0fa14adcd6f2310f1dafb23ac6f Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:07:23 +0200 Subject: [PATCH 06/17] add end --- sample-app/generate_joke_workflow_example.go | 6 ++++-- traceloop-sdk/workflow.go | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 958caa1..119d260 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -65,7 +65,7 @@ func createJoke(ctx context.Context, workflow *sdk.Workflow, client *openai.Clie return resp.Choices[0].Message.Content, nil } -func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workflow *sdk.Workflow, client *openai.Client, joke string) (string, error) { +func translateJokeToPirate(ctx context.Context, workflow *sdk.Workflow, client *openai.Client, joke string) (string, error) { // Log prompt piratePrompt := fmt.Sprintf("Translate the below joke to pirate-like english:\n\n%s", joke) prompt := sdk.Prompt{ @@ -82,6 +82,7 @@ func translateJokeToPirate(ctx context.Context, traceloop *sdk.Traceloop, workfl } agent := workflow.NewAgent("joke_translation") + defer agent.End() llmSpan := agent.LogPrompt(prompt) @@ -147,6 +148,7 @@ func historyJokesTool(ctx context.Context, agent *sdk.Agent, client *openai.Clie Description: "Get some history jokes", Parameters: map[string]interface{}{}, }) + defer tool.End() llmSpan := tool.LogPrompt(prompt) @@ -271,7 +273,7 @@ func runJokeWorkflow() { fmt.Printf("\nEnglish joke:\n%s\n\n", engJoke) fmt.Println("Translating to pirate...") - pirateJoke, err := translateJokeToPirate(ctx, traceloop, wf, client, engJoke) + pirateJoke, err := translateJokeToPirate(ctx, wf, client, engJoke) if err != nil { fmt.Printf("Error translating joke: %v\n", err) return diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 25ff3e9..e95868b 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -65,6 +65,7 @@ func (workflow *Workflow) NewAgent(name string) *Agent { ) return &Agent{ + sdk: workflow.sdk, workflow: workflow, ctx: aCtx, Name: name, From aa91b3efb25f72aa4507752cc89842e8f093f0bc Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:11:15 +0200 Subject: [PATCH 07/17] works --- traceloop-sdk/agent.go | 2 +- traceloop-sdk/tool.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index 2655108..db94685 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -22,7 +22,7 @@ func (agent *Agent) End() { func (agent *Agent) LogPrompt(prompt Prompt) LLMSpan { if agent.workflow != nil { - return agent.workflow.LogPrompt(prompt) + return agent.sdk.LogPrompt(agent.ctx, prompt, agent.workflow.Attributes) } return agent.sdk.LogPrompt(agent.ctx, prompt, WorkflowAttributes{}) } diff --git a/traceloop-sdk/tool.go b/traceloop-sdk/tool.go index 70d05e4..ab22d8e 100644 --- a/traceloop-sdk/tool.go +++ b/traceloop-sdk/tool.go @@ -19,5 +19,5 @@ func (tool *Tool) End() { } func (tool *Tool) LogPrompt(prompt Prompt) LLMSpan { - return tool.agent.LogPrompt(prompt) + return tool.agent.sdk.LogPrompt(tool.ctx, prompt, tool.agent.workflow.Attributes) } From eb0a9739535de7739c4400bdf4cb495abae1fd80 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:17:43 +0200 Subject: [PATCH 08/17] lint --- traceloop-sdk/llm_span.go | 2 +- traceloop-sdk/model/types.go | 2 +- traceloop-sdk/sdk.go | 8 ++------ traceloop-sdk/task.go | 8 -------- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/traceloop-sdk/llm_span.go b/traceloop-sdk/llm_span.go index 168fb19..2dd91a6 100644 --- a/traceloop-sdk/llm_span.go +++ b/traceloop-sdk/llm_span.go @@ -31,4 +31,4 @@ func (llmSpan *LLMSpan) LogCompletion(ctx context.Context, completion Completion setMessagesAttribute(llmSpan.span, "llm.completions", completion.Messages) defer llmSpan.span.End() -} \ No newline at end of file +} diff --git a/traceloop-sdk/model/types.go b/traceloop-sdk/model/types.go index 5891434..f287599 100644 --- a/traceloop-sdk/model/types.go +++ b/traceloop-sdk/model/types.go @@ -7,4 +7,4 @@ const ( SpanKindAgent SpanKind = "agent" SpanKindTask SpanKind = "task" SpanKindWorkflow SpanKind = "workflow" -) \ No newline at end of file +) diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 59caba8..3463510 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -21,10 +21,6 @@ import ( const PromptsPath = "/v1/traceloop/prompts" -const ( - ToolType = "tool" - AgentType = "agent" -) type Traceloop struct { config Config @@ -179,7 +175,7 @@ func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttrib spanAttrs := []attribute.KeyValue{ semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), - semconvai.TraceloopSpanKind.String(ToolType), + semconvai.TraceloopSpanKind.String(string(model.SpanKindTool)), semconvai.TraceloopEntityName.String(attrs.Name), } @@ -202,7 +198,7 @@ func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, spanAttrs := []attribute.KeyValue{ semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), - semconvai.TraceloopSpanKind.String(AgentType), + semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), semconvai.LLMAgentName.String(attrs.Name), } diff --git a/traceloop-sdk/task.go b/traceloop-sdk/task.go index 95513e7..1b6a8e7 100644 --- a/traceloop-sdk/task.go +++ b/traceloop-sdk/task.go @@ -19,11 +19,3 @@ func (task *Task) End() { func (task *Task) LogPrompt(prompt Prompt) LLMSpan { return task.workflow.sdk.LogPrompt(task.ctx, prompt, task.workflow.Attributes) } - -func (task *Task) LogAgent(attrs AgentAttributes) LLMSpan { - return task.workflow.sdk.LogAgent(task.ctx, attrs, task.workflow.Attributes) -} - -func (task *Task) LogToolCall(attrs ToolCallAttributes) LLMSpan { - return task.workflow.sdk.LogToolCall(task.ctx, attrs, task.workflow.Attributes) -} \ No newline at end of file From 735f9685e31f940e833eb33cc83dfd22b892410f Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:26:17 +0200 Subject: [PATCH 09/17] build agent --- sample-app/main.go | 17 +- sample-app/recipe_agent_example.go | 323 +++++++++++++++++++++++++++++ sample-app/tool_calling.go | 2 +- traceloop-sdk/agent.go | 4 +- traceloop-sdk/sdk.go | 38 +++- traceloop-sdk/task.go | 2 +- traceloop-sdk/tool.go | 5 +- traceloop-sdk/workflow.go | 2 +- 8 files changed, 378 insertions(+), 15 deletions(-) create mode 100644 sample-app/recipe_agent_example.go diff --git a/sample-app/main.go b/sample-app/main.go index 52ce875..7677d2a 100644 --- a/sample-app/main.go +++ b/sample-app/main.go @@ -22,10 +22,19 @@ func main() { runToolCallingExample() return } - + + if len(os.Args) > 1 && os.Args[1] == "recipe-agent" { + runRecipeAgent() + return + } + + if len(os.Args) > 1 && os.Args[1] == "joke-workflow" { + runJokeWorkflow() + return + } + // Default to workflow example using prompt registry - // workflowExample() - runJokeWorkflow() + workflowExample() } func workflowExample() { @@ -70,7 +79,7 @@ func workflowExample() { Model: request.Model, Messages: promptMsgs, }, - sdk.WorkflowAttributes{ + &sdk.WorkflowAttributes{ Name: "example-workflow", AssociationProperties: map[string]string{ "user_id": "demo-user", diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go new file mode 100644 index 0000000..10d9732 --- /dev/null +++ b/sample-app/recipe_agent_example.go @@ -0,0 +1,323 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/sashabaranov/go-openai" + sdk "github.com/traceloop/go-openllmetry/traceloop-sdk" +) + +// ingredientValidatorTool validates that requested ingredients are available/safe +func ingredientValidatorTool(ctx context.Context, agent *sdk.Agent, client *openai.Client, ingredients string) (string, error) { + tool := agent.NewTool("ingredient_validator", "function", sdk.ToolFunction{ + Name: "ingredient_validator", + Description: "Validates that requested ingredients are available and safe to use", + Parameters: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "ingredients": map[string]string{ + "type": "string", + "description": "Comma-separated list of ingredients to validate", + }, + }, + }, + }) + defer tool.End() + + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: fmt.Sprintf("Validate these ingredients are commonly available and safe: %s. Respond with 'Valid' or list any concerns.", ingredients), + }, + }, + } + + llmSpan := tool.LogPrompt(prompt) + + // Make API call + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: prompt.Messages[0].Content, + }, + }, + }) + if err != nil { + return "", fmt.Errorf("CreateChatCompletion error: %w", err) + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + return resp.Choices[0].Message.Content, nil +} + +// nutritionCalculatorTool calculates nutritional information for the recipe +func nutritionCalculatorTool(ctx context.Context, agent *sdk.Agent, client *openai.Client, recipe string) (string, error) { + tool := agent.NewTool("nutrition_calculator", "function", sdk.ToolFunction{ + Name: "nutrition_calculator", + Description: "Calculates estimated nutritional information for a recipe", + Parameters: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "recipe": map[string]string{ + "type": "string", + "description": "The recipe description", + }, + }, + }, + }) + defer tool.End() + + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: fmt.Sprintf("Estimate the nutritional information (calories, protein, carbs, fat) per serving for this recipe: %s", recipe), + }, + }, + } + + llmSpan := tool.LogPrompt(prompt) + + // Make API call + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: prompt.Messages[0].Content, + }, + }, + }) + if err != nil { + return "", fmt.Errorf("CreateChatCompletion error: %w", err) + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + return resp.Choices[0].Message.Content, nil +} + +// cookingTimeEstimatorTool estimates preparation and cooking time +func cookingTimeEstimatorTool(ctx context.Context, agent *sdk.Agent, client *openai.Client, recipe string) (string, error) { + tool := agent.NewTool("cooking_time_estimator", "function", sdk.ToolFunction{ + Name: "cooking_time_estimator", + Description: "Estimates preparation and cooking time based on recipe complexity", + Parameters: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "recipe": map[string]string{ + "type": "string", + "description": "The recipe description", + }, + }, + }, + }) + defer tool.End() + + prompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: fmt.Sprintf("Estimate the preparation time and cooking time for this recipe: %s", recipe), + }, + }, + } + + llmSpan := tool.LogPrompt(prompt) + + // Make API call + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: prompt.Messages[0].Content, + }, + }, + }) + if err != nil { + return "", fmt.Errorf("CreateChatCompletion error: %w", err) + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + return resp.Choices[0].Message.Content, nil +} + +func runRecipeAgent() { + ctx := context.Background() + + // Initialize Traceloop SDK + traceloop, err := sdk.NewClient(ctx, sdk.Config{ + APIKey: os.Getenv("TRACELOOP_API_KEY"), + }) + if err != nil { + fmt.Printf("NewClient error: %v\n", err) + return + } + defer func() { traceloop.Shutdown(ctx) }() + + client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) + + // Create standalone agent with association properties + agent := traceloop.NewAgent(ctx, "recipe_generator", map[string]string{ + "ab_testing_variant": "variant_a", + "user_id": "user_67890", + }) + defer agent.End() + + // User request + userRequest := "Create a healthy pasta dish with vegetables" + fmt.Printf("User request: %s\n\n", userRequest) + + // Agent generates initial recipe + fmt.Println("Generating recipe...") + recipePrompt := sdk.Prompt{ + Vendor: "openai", + Mode: "chat", + Model: "gpt-3.5-turbo", + Messages: []sdk.Message{ + { + Index: 0, + Role: "user", + Content: fmt.Sprintf("Create a detailed recipe for: %s. Include ingredients and instructions.", userRequest), + }, + }, + } + + llmSpan := agent.LogPrompt(recipePrompt) + + resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ + Model: "gpt-3.5-turbo", + Messages: []openai.ChatCompletionMessage{ + { + Role: "user", + Content: recipePrompt.Messages[0].Content, + }, + }, + }) + if err != nil { + fmt.Printf("Error generating recipe: %v\n", err) + return + } + + // Log completion + var completionMsgs []sdk.Message + for _, choice := range resp.Choices { + completionMsgs = append(completionMsgs, sdk.Message{ + Index: choice.Index, + Content: choice.Message.Content, + Role: choice.Message.Role, + }) + } + + llmSpan.LogCompletion(ctx, sdk.Completion{ + Model: resp.Model, + Messages: completionMsgs, + }, sdk.Usage{ + TotalTokens: resp.Usage.TotalTokens, + CompletionTokens: resp.Usage.CompletionTokens, + PromptTokens: resp.Usage.PromptTokens, + }) + + recipe := resp.Choices[0].Message.Content + fmt.Printf("\nGenerated Recipe:\n%s\n\n", recipe) + + // Tool 1: Validate ingredients + fmt.Println("Validating ingredients...") + validation, err := ingredientValidatorTool(ctx, agent, client, "pasta, tomatoes, spinach, garlic, olive oil") + if err != nil { + fmt.Printf("Warning: ingredient validation error: %v\n", err) + } else { + fmt.Printf("Validation Result: %s\n\n", validation) + } + + // Tool 2: Calculate nutrition + fmt.Println("Calculating nutrition...") + nutrition, err := nutritionCalculatorTool(ctx, agent, client, recipe) + if err != nil { + fmt.Printf("Warning: nutrition calculation error: %v\n", err) + } else { + fmt.Printf("Nutrition Info:\n%s\n\n", nutrition) + } + + // Tool 3: Estimate cooking time + fmt.Println("Estimating cooking time...") + cookingTime, err := cookingTimeEstimatorTool(ctx, agent, client, recipe) + if err != nil { + fmt.Printf("Warning: cooking time estimation error: %v\n", err) + } else { + fmt.Printf("Time Estimate:\n%s\n\n", cookingTime) + } + + fmt.Println("=== Recipe Agent Complete ===") +} diff --git a/sample-app/tool_calling.go b/sample-app/tool_calling.go index 9792324..0eebb1d 100644 --- a/sample-app/tool_calling.go +++ b/sample-app/tool_calling.go @@ -91,7 +91,7 @@ func runToolCallingExample() { fmt.Printf("User: %s\n", userPrompt) // Log the prompt - llmSpan := traceloop.LogPrompt(ctx, prompt, workflowAttrs) + llmSpan := traceloop.LogPrompt(ctx, prompt, &workflowAttrs) // Make API call to OpenAI startTime := time.Now() diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index db94685..0325ab7 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -22,9 +22,9 @@ func (agent *Agent) End() { func (agent *Agent) LogPrompt(prompt Prompt) LLMSpan { if agent.workflow != nil { - return agent.sdk.LogPrompt(agent.ctx, prompt, agent.workflow.Attributes) + return agent.sdk.LogPrompt(agent.ctx, prompt, &agent.workflow.Attributes) } - return agent.sdk.LogPrompt(agent.ctx, prompt, WorkflowAttributes{}) + return agent.sdk.LogPrompt(agent.ctx, prompt, nil) } func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunction) *Tool { diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 3463510..0455b32 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -142,8 +142,33 @@ func (instance *Traceloop) getTracer() apitrace.Tracer { return (*instance.tracerProvider).Tracer(instance.tracerName()) } +// NewAgent creates a standalone agent (without a workflow) +func (instance *Traceloop) NewAgent(ctx context.Context, name string, associationProperties map[string]string) *Agent { + aCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.agent", name), apitrace.WithNewRoot()) + + attrs := []attribute.KeyValue{ + semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), + semconvai.TraceloopEntityName.String(name), + semconvai.LLMAgentName.String(name), + } + + // Add association properties if provided + for key, value := range associationProperties { + attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) + } + + span.SetAttributes(attrs...) + + return &Agent{ + sdk: instance, + workflow: nil, + ctx: aCtx, + Name: name, + } +} + // New workflow-based API -func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflowAttrs WorkflowAttributes) LLMSpan { +func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflowAttrs *WorkflowAttributes) LLMSpan { spanName := fmt.Sprintf("%s.%s", prompt.Vendor, prompt.Mode) _, span := instance.getTracer().Start(ctx, spanName) @@ -151,12 +176,15 @@ func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflo semconvai.LLMVendor.String(prompt.Vendor), semconvai.LLMRequestModel.String(prompt.Model), semconvai.LLMRequestType.String(prompt.Mode), - semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), } - // Add association properties if provided - for key, value := range workflowAttrs.AssociationProperties { - attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) + if workflowAttrs != nil { + attrs = append(attrs, semconvai.TraceloopWorkflowName.String(workflowAttrs.Name)) + + // Add association properties if provided + for key, value := range workflowAttrs.AssociationProperties { + attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) + } } span.SetAttributes(attrs...) diff --git a/traceloop-sdk/task.go b/traceloop-sdk/task.go index 1b6a8e7..362f7c5 100644 --- a/traceloop-sdk/task.go +++ b/traceloop-sdk/task.go @@ -17,5 +17,5 @@ func (task *Task) End() { } func (task *Task) LogPrompt(prompt Prompt) LLMSpan { - return task.workflow.sdk.LogPrompt(task.ctx, prompt, task.workflow.Attributes) + return task.workflow.sdk.LogPrompt(task.ctx, prompt, &task.workflow.Attributes) } diff --git a/traceloop-sdk/tool.go b/traceloop-sdk/tool.go index ab22d8e..b572544 100644 --- a/traceloop-sdk/tool.go +++ b/traceloop-sdk/tool.go @@ -19,5 +19,8 @@ func (tool *Tool) End() { } func (tool *Tool) LogPrompt(prompt Prompt) LLMSpan { - return tool.agent.sdk.LogPrompt(tool.ctx, prompt, tool.agent.workflow.Attributes) + if tool.agent.workflow != nil { + return tool.agent.sdk.LogPrompt(tool.ctx, prompt, &tool.agent.workflow.Attributes) + } + return tool.agent.sdk.LogPrompt(tool.ctx, prompt, nil) } diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index e95868b..01e593c 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -36,7 +36,7 @@ func (workflow *Workflow) End() { } func (workflow *Workflow) LogPrompt(prompt Prompt) LLMSpan { - return workflow.sdk.LogPrompt(workflow.ctx, prompt, workflow.Attributes) + return workflow.sdk.LogPrompt(workflow.ctx, prompt, &workflow.Attributes) } func (workflow *Workflow) NewTask(name string) *Task { From 3f52b3f652a8624e240bba2eb42bcfa12a51c778 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:49:25 +0200 Subject: [PATCH 10/17] added context --- sample-app/generate_joke_workflow_example.go | 4 ++- sample-app/main.go | 5 ++-- sample-app/tool_calling.go | 9 +++--- traceloop-sdk/agent.go | 25 ++++++++++++---- traceloop-sdk/sdk.go | 24 ++++++++++----- traceloop-sdk/sdk_test.go | 5 +++- traceloop-sdk/task.go | 6 +++- traceloop-sdk/tool.go | 21 +++++++++++-- traceloop-sdk/tracing_types.go | 10 +++++-- traceloop-sdk/workflow.go | 31 +++++++++++++++----- 10 files changed, 105 insertions(+), 35 deletions(-) diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 119d260..6ecf5c9 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -81,7 +81,9 @@ func translateJokeToPirate(ctx context.Context, workflow *sdk.Workflow, client * }, } - agent := workflow.NewAgent("joke_translation") + agent := workflow.NewAgent("joke_translation", map[string]string{ + "translation_type": "pirate", + }) defer agent.End() llmSpan := agent.LogPrompt(prompt) diff --git a/sample-app/main.go b/sample-app/main.go index 7677d2a..29f41ab 100644 --- a/sample-app/main.go +++ b/sample-app/main.go @@ -71,6 +71,7 @@ func workflowExample() { } // Log the prompt + workflowName := "example-workflow" llmSpan := traceloop.LogPrompt( ctx, sdk.Prompt{ @@ -79,8 +80,8 @@ func workflowExample() { Model: request.Model, Messages: promptMsgs, }, - &sdk.WorkflowAttributes{ - Name: "example-workflow", + sdk.ContextAttributes{ + WorkflowName: &workflowName, AssociationProperties: map[string]string{ "user_id": "demo-user", }, diff --git a/sample-app/tool_calling.go b/sample-app/tool_calling.go index 0eebb1d..6924786 100644 --- a/sample-app/tool_calling.go +++ b/sample-app/tool_calling.go @@ -81,17 +81,18 @@ func runToolCallingExample() { Tools: tools, } - workflowAttrs := sdk.WorkflowAttributes{ - Name: "tool-calling-example", + workflowName := "tool-calling-example" + contextAttrs := sdk.ContextAttributes{ + WorkflowName: &workflowName, AssociationProperties: map[string]string{ "user_id": "demo-user", }, } fmt.Printf("User: %s\n", userPrompt) - + // Log the prompt - llmSpan := traceloop.LogPrompt(ctx, prompt, &workflowAttrs) + llmSpan := traceloop.LogPrompt(ctx, prompt, contextAttrs) // Make API call to OpenAI startTime := time.Now() diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index 0325ab7..b7af78c 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -13,7 +13,7 @@ type Agent struct { sdk *Traceloop workflow *Workflow ctx context.Context - Name string `json:"name"` + Attributes AgentAttributes `json:"agent_attributes"` } func (agent *Agent) End() { @@ -21,22 +21,37 @@ func (agent *Agent) End() { } func (agent *Agent) LogPrompt(prompt Prompt) LLMSpan { + // Merge workflow and agent association properties + contextAttrs := ContextAttributes{ + AssociationProperties: make(map[string]string), + } + + // Start with workflow properties if available if agent.workflow != nil { - return agent.sdk.LogPrompt(agent.ctx, prompt, &agent.workflow.Attributes) + contextAttrs.WorkflowName = &agent.workflow.Attributes.Name + for key, value := range agent.workflow.Attributes.AssociationProperties { + contextAttrs.AssociationProperties[key] = value + } } - return agent.sdk.LogPrompt(agent.ctx, prompt, nil) + + // Agent properties override workflow properties + for key, value := range agent.Attributes.AssociationProperties { + contextAttrs.AssociationProperties[key] = value + } + + return agent.sdk.LogPrompt(agent.ctx, prompt, contextAttrs) } func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunction) *Tool { toolCtx, span := agent.sdk.getTracer().Start(agent.ctx, fmt.Sprintf("%s.tool", name)) span.SetAttributes( - semconvai.LLMAgentName.String(agent.Name), + semconvai.LLMAgentName.String(agent.Attributes.Name), semconvai.TraceloopSpanKind.String(string(model.SpanKindTool)), semconvai.TraceloopEntityName.String(name), ) return &Tool{ - agent: agent, + agent: *agent, ctx: toolCtx, Name: name, Type: toolType, diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 0455b32..392dd3d 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -163,12 +163,15 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, associatio sdk: instance, workflow: nil, ctx: aCtx, - Name: name, + Attributes: AgentAttributes{ + Name: name, + AssociationProperties: associationProperties, + }, } } // New workflow-based API -func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflowAttrs *WorkflowAttributes) LLMSpan { +func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, contextAttrs ContextAttributes) LLMSpan { spanName := fmt.Sprintf("%s.%s", prompt.Vendor, prompt.Mode) _, span := instance.getTracer().Start(ctx, spanName) @@ -178,14 +181,19 @@ func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, workflo semconvai.LLMRequestType.String(prompt.Mode), } - if workflowAttrs != nil { - attrs = append(attrs, semconvai.TraceloopWorkflowName.String(workflowAttrs.Name)) + if contextAttrs.WorkflowName != nil { + attrs = append(attrs, semconvai.TraceloopWorkflowName.String(*contextAttrs.WorkflowName)) + } - // Add association properties if provided - for key, value := range workflowAttrs.AssociationProperties { - attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) - } + if contextAttrs.AgentName != nil { + attrs = append(attrs, semconvai.LLMAgentName.String(*contextAttrs.AgentName)) + } + + // Add association properties + for key, value := range contextAttrs.AssociationProperties { + attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) } + span.SetAttributes(attrs...) setMessagesAttribute(span, "llm.prompts", prompt.Messages) diff --git a/traceloop-sdk/sdk_test.go b/traceloop-sdk/sdk_test.go index 512242f..8f845f2 100644 --- a/traceloop-sdk/sdk_test.go +++ b/traceloop-sdk/sdk_test.go @@ -70,7 +70,10 @@ func TestLogPromptSpanAttributes(t *testing.T) { } // Log the prompt using new workflow API - llmSpan := tl.LogPrompt(context.Background(), prompt, workflowAttrs) + llmSpan := tl.LogPrompt(context.Background(), prompt, ContextAttributes{ + WorkflowName: &workflowAttrs.Name, + AssociationProperties: workflowAttrs.AssociationProperties, + }) // Log completion with tool calls completion := Completion{ diff --git a/traceloop-sdk/task.go b/traceloop-sdk/task.go index 362f7c5..bb02e96 100644 --- a/traceloop-sdk/task.go +++ b/traceloop-sdk/task.go @@ -17,5 +17,9 @@ func (task *Task) End() { } func (task *Task) LogPrompt(prompt Prompt) LLMSpan { - return task.workflow.sdk.LogPrompt(task.ctx, prompt, &task.workflow.Attributes) + contextAttrs := ContextAttributes{ + WorkflowName: &task.workflow.Attributes.Name, + AssociationProperties: task.workflow.Attributes.AssociationProperties, + } + return task.workflow.sdk.LogPrompt(task.ctx, prompt, contextAttrs) } diff --git a/traceloop-sdk/tool.go b/traceloop-sdk/tool.go index b572544..c4063a9 100644 --- a/traceloop-sdk/tool.go +++ b/traceloop-sdk/tool.go @@ -7,7 +7,7 @@ import ( ) type Tool struct { - agent *Agent + agent Agent ctx context.Context Name string `json:"name"` Type string `json:"type"` @@ -19,8 +19,23 @@ func (tool *Tool) End() { } func (tool *Tool) LogPrompt(prompt Prompt) LLMSpan { + // Merge workflow and agent association properties + contextAttrs := ContextAttributes{ + AssociationProperties: make(map[string]string), + } + + // Start with workflow properties if available if tool.agent.workflow != nil { - return tool.agent.sdk.LogPrompt(tool.ctx, prompt, &tool.agent.workflow.Attributes) + contextAttrs.WorkflowName = &tool.agent.workflow.Attributes.Name + for key, value := range tool.agent.workflow.Attributes.AssociationProperties { + contextAttrs.AssociationProperties[key] = value + } } - return tool.agent.sdk.LogPrompt(tool.ctx, prompt, nil) + + // Agent properties override workflow properties + for key, value := range tool.agent.Attributes.AssociationProperties { + contextAttrs.AssociationProperties[key] = value + } + + return tool.agent.sdk.LogPrompt(tool.ctx, prompt, contextAttrs) } diff --git a/traceloop-sdk/tracing_types.go b/traceloop-sdk/tracing_types.go index f4fa0bb..92a37a4 100644 --- a/traceloop-sdk/tracing_types.go +++ b/traceloop-sdk/tracing_types.go @@ -30,6 +30,12 @@ type WorkflowAttributes struct { AssociationProperties map[string]string `json:"association_properties"` } +type ContextAttributes struct { + WorkflowName *string `json:"workflow_name,omitempty"` + AgentName *string `json:"agent_name,omitempty"` + AssociationProperties map[string]string `json:"association_properties,omitempty"` +} + type Usage struct { TotalTokens int `json:"total_tokens"` CompletionTokens int `json:"completion_tokens"` @@ -42,7 +48,6 @@ type ToolFunction struct { Parameters interface{} `json:"parameters"` } - type ToolCall struct { ID string `json:"id"` Type string `json:"type"` @@ -59,5 +64,6 @@ type ToolCallAttributes struct { } type AgentAttributes struct { - Name string `json:"name"` + Name string `json:"agent_name"` + AssociationProperties map[string]string `json:"association_properties"` } diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 01e593c..4d68d6a 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -6,6 +6,7 @@ import ( "github.com/traceloop/go-openllmetry/traceloop-sdk/model" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -36,7 +37,11 @@ func (workflow *Workflow) End() { } func (workflow *Workflow) LogPrompt(prompt Prompt) LLMSpan { - return workflow.sdk.LogPrompt(workflow.ctx, prompt, &workflow.Attributes) + contextAttrs := ContextAttributes{ + WorkflowName: &workflow.Attributes.Name, + AssociationProperties: workflow.Attributes.AssociationProperties, + } + return workflow.sdk.LogPrompt(workflow.ctx, prompt, contextAttrs) } func (workflow *Workflow) NewTask(name string) *Task { @@ -55,20 +60,30 @@ func (workflow *Workflow) NewTask(name string) *Task { } } -func (workflow *Workflow) NewAgent(name string) *Agent { +func (workflow *Workflow) NewAgent(name string, associationProperties map[string]string) *Agent { aCtx, span := workflow.sdk.getTracer().Start(workflow.ctx, fmt.Sprintf("%s.agent", name)) - span.SetAttributes( + attrs := []attribute.KeyValue{ semconvai.TraceloopWorkflowName.String(workflow.Attributes.Name), semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), semconvai.TraceloopEntityName.String(name), - ) + } + + // Add agent-specific association properties to the span + for key, value := range associationProperties { + attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) + } + + span.SetAttributes(attrs...) return &Agent{ - sdk: workflow.sdk, - workflow: workflow, - ctx: aCtx, - Name: name, + sdk: workflow.sdk, + workflow: workflow, + ctx: aCtx, + Attributes: AgentAttributes{ + Name: name, + AssociationProperties: associationProperties, + }, } } From 01c1198f4506ff7b9d4ff047d846e52fa962671c Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:02:54 +0200 Subject: [PATCH 11/17] add prop --- sample-app/generate_joke_workflow_example.go | 2 ++ sample-app/recipe_agent_example.go | 16 ++++++++++------ traceloop-sdk/agent.go | 18 +++++++++++++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 6ecf5c9..8fcc9e4 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -149,6 +149,8 @@ func historyJokesTool(ctx context.Context, agent *sdk.Agent, client *openai.Clie Name: "history_jokes", Description: "Get some history jokes", Parameters: map[string]interface{}{}, + }, map[string]string{ + "user_id": "user_12345", }) defer tool.End() diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go index 10d9732..f5bb024 100644 --- a/sample-app/recipe_agent_example.go +++ b/sample-app/recipe_agent_example.go @@ -9,6 +9,11 @@ import ( sdk "github.com/traceloop/go-openllmetry/traceloop-sdk" ) +var associationProperties = map[string]string{ + "user_id": "user_67890", + "ab_testing_variant": "variant_a", +} + // ingredientValidatorTool validates that requested ingredients are available/safe func ingredientValidatorTool(ctx context.Context, agent *sdk.Agent, client *openai.Client, ingredients string) (string, error) { tool := agent.NewTool("ingredient_validator", "function", sdk.ToolFunction{ @@ -23,7 +28,7 @@ func ingredientValidatorTool(ctx context.Context, agent *sdk.Agent, client *open }, }, }, - }) + }, associationProperties) defer tool.End() prompt := sdk.Prompt{ @@ -91,7 +96,7 @@ func nutritionCalculatorTool(ctx context.Context, agent *sdk.Agent, client *open }, }, }, - }) + }, associationProperties) defer tool.End() prompt := sdk.Prompt{ @@ -159,6 +164,8 @@ func cookingTimeEstimatorTool(ctx context.Context, agent *sdk.Agent, client *ope }, }, }, + }, map[string]string{ + "user_id": "user_67890", }) defer tool.End() @@ -229,10 +236,7 @@ func runRecipeAgent() { client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) // Create standalone agent with association properties - agent := traceloop.NewAgent(ctx, "recipe_generator", map[string]string{ - "ab_testing_variant": "variant_a", - "user_id": "user_67890", - }) + agent := traceloop.NewAgent(ctx, "recipe_generator", associationProperties) defer agent.End() // User request diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index b7af78c..bafba3b 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -5,6 +5,7 @@ import ( "fmt" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" "github.com/traceloop/go-openllmetry/traceloop-sdk/model" ) @@ -42,13 +43,24 @@ func (agent *Agent) LogPrompt(prompt Prompt) LLMSpan { return agent.sdk.LogPrompt(agent.ctx, prompt, contextAttrs) } -func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunction) *Tool { +func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunction, associationProperties map[string]string) *Tool { toolCtx, span := agent.sdk.getTracer().Start(agent.ctx, fmt.Sprintf("%s.tool", name)) - span.SetAttributes( + attrs := []attribute.KeyValue{ semconvai.LLMAgentName.String(agent.Attributes.Name), semconvai.TraceloopSpanKind.String(string(model.SpanKindTool)), semconvai.TraceloopEntityName.String(name), - ) + } + + for key, value := range agent.Attributes.AssociationProperties { + attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) + } + + // Add tool-specific association properties + for key, value := range associationProperties { + attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) + } + + span.SetAttributes(attrs...) return &Tool{ agent: *agent, From 9cf4d6110b84942aed0bab795b11f97eb7fdbd6e Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:32:54 +0200 Subject: [PATCH 12/17] works --- sample-app/generate_joke_workflow_example.go | 2 +- sample-app/recipe_agent_example.go | 14 ++++++++++++-- sample-app/workflow_example.go | 2 +- traceloop-sdk/agent.go | 5 ++--- traceloop-sdk/model/types.go | 5 +++++ traceloop-sdk/sdk.go | 19 +++++++++++++------ traceloop-sdk/workflow.go | 19 ++++++++++++++++++- 7 files changed, 52 insertions(+), 14 deletions(-) diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 8fcc9e4..5486307 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -264,7 +264,7 @@ func runJokeWorkflow() { "user_id": "user_12345", "chat_id": "chat_1234", }, - }) + }, nil) defer wf.End() // Execute workflow steps diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go index f5bb024..8e81a12 100644 --- a/sample-app/recipe_agent_example.go +++ b/sample-app/recipe_agent_example.go @@ -7,11 +7,18 @@ import ( "github.com/sashabaranov/go-openai" sdk "github.com/traceloop/go-openllmetry/traceloop-sdk" + "github.com/traceloop/go-openllmetry/traceloop-sdk/model" ) var associationProperties = map[string]string{ "user_id": "user_67890", - "ab_testing_variant": "variant_a", +} + +var abTest = &model.ABTest{ + VarientsKeys: map[string]bool{ + "variant_a": false, + "variant_b": true, + }, } // ingredientValidatorTool validates that requested ingredients are available/safe @@ -236,7 +243,10 @@ func runRecipeAgent() { client := openai.NewClient(os.Getenv("OPENAI_API_KEY")) // Create standalone agent with association properties - agent := traceloop.NewAgent(ctx, "recipe_generator", associationProperties) + agent := traceloop.NewAgent(ctx, "recipe_generator", sdk.AgentAttributes{ + Name: "recipe_generator", + AssociationProperties: associationProperties, + }, abTest) defer agent.End() // User request diff --git a/sample-app/workflow_example.go b/sample-app/workflow_example.go index a0b498f..fcfcd74 100644 --- a/sample-app/workflow_example.go +++ b/sample-app/workflow_example.go @@ -26,7 +26,7 @@ func workflowMain() { wf := traceloop.NewWorkflow(ctx, tlp.WorkflowAttributes{ Name: "history_generation", - }) + }, nil) defer wf.End() factGenTask := wf.NewTask("current_date_fact_generation") diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index bafba3b..492f3c2 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -5,9 +5,9 @@ import ( "fmt" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" + "github.com/traceloop/go-openllmetry/traceloop-sdk/model" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "github.com/traceloop/go-openllmetry/traceloop-sdk/model" ) type Agent struct { @@ -15,6 +15,7 @@ type Agent struct { workflow *Workflow ctx context.Context Attributes AgentAttributes `json:"agent_attributes"` + ABTest *model.ABTest `json:"ab_test"` } func (agent *Agent) End() { @@ -70,5 +71,3 @@ func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunct Function: toolFunction, } } - - diff --git a/traceloop-sdk/model/types.go b/traceloop-sdk/model/types.go index f287599..167b695 100644 --- a/traceloop-sdk/model/types.go +++ b/traceloop-sdk/model/types.go @@ -8,3 +8,8 @@ const ( SpanKindTask SpanKind = "task" SpanKindWorkflow SpanKind = "workflow" ) + +// The varient that is active will be added to the trace. +type ABTest struct { + VarientsKeys map[string]bool `json:"varients_keys"` +} diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 392dd3d..56362a6 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -143,7 +143,7 @@ func (instance *Traceloop) getTracer() apitrace.Tracer { } // NewAgent creates a standalone agent (without a workflow) -func (instance *Traceloop) NewAgent(ctx context.Context, name string, associationProperties map[string]string) *Agent { +func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs AgentAttributes, abTest *model.ABTest) *Agent { aCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.agent", name), apitrace.WithNewRoot()) attrs := []attribute.KeyValue{ @@ -152,8 +152,17 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, associatio semconvai.LLMAgentName.String(name), } + if abTest != nil { + for key, activeVarient := range abTest.VarientsKeys { + if activeVarient { + agentAttrs.AssociationProperties["ab_testing_variant"] = key + break + } + } + } + // Add association properties if provided - for key, value := range associationProperties { + for key, value := range agentAttrs.AssociationProperties { attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) } @@ -163,10 +172,8 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, associatio sdk: instance, workflow: nil, ctx: aCtx, - Attributes: AgentAttributes{ - Name: name, - AssociationProperties: associationProperties, - }, + Attributes: agentAttrs, + ABTest: abTest, } } diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 4d68d6a..b149a29 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -14,9 +14,10 @@ type Workflow struct { sdk *Traceloop ctx context.Context Attributes WorkflowAttributes `json:"workflow_attributes"` + ABTest *model.ABTest `json:"ab_test"` } -func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttributes) *Workflow { +func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttributes, abTest *model.ABTest) *Workflow { wCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.workflow", attrs.Name), trace.WithNewRoot()) span.SetAttributes( @@ -25,6 +26,15 @@ func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttrib semconvai.TraceloopEntityName.String(attrs.Name), ) + if abTest != nil { + for key, activeVarient := range abTest.VarientsKeys { + if activeVarient { + span.SetAttributes(attribute.String("traceloop.association.properties.ab_testing_variant", key)) + break + } + } + } + return &Workflow{ sdk: instance, ctx: wCtx, @@ -69,6 +79,13 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string semconvai.TraceloopEntityName.String(name), } + if workflow.ABTest != nil { + for key, activeVarient := range workflow.ABTest.VarientsKeys { + if activeVarient { + associationProperties["ab_testing_variant"] = key + } + } + } // Add agent-specific association properties to the span for key, value := range associationProperties { attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) From 95f583d78f70db4cb392931b4f1772ffadc9df09 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:43:06 +0200 Subject: [PATCH 13/17] move to att --- sample-app/generate_joke_workflow_example.go | 2 +- sample-app/main.go | 2 +- sample-app/recipe_agent_example.go | 11 ++--------- sample-app/workflow_example.go | 2 +- traceloop-sdk/agent.go | 1 - traceloop-sdk/sdk.go | 7 +++---- traceloop-sdk/tracing_types.go | 4 ++++ traceloop-sdk/workflow.go | 11 +++++------ 8 files changed, 17 insertions(+), 23 deletions(-) diff --git a/sample-app/generate_joke_workflow_example.go b/sample-app/generate_joke_workflow_example.go index 5486307..8fcc9e4 100644 --- a/sample-app/generate_joke_workflow_example.go +++ b/sample-app/generate_joke_workflow_example.go @@ -264,7 +264,7 @@ func runJokeWorkflow() { "user_id": "user_12345", "chat_id": "chat_1234", }, - }, nil) + }) defer wf.End() // Execute workflow steps diff --git a/sample-app/main.go b/sample-app/main.go index 29f41ab..eb49b3a 100644 --- a/sample-app/main.go +++ b/sample-app/main.go @@ -124,4 +124,4 @@ func workflowExample() { }) fmt.Println(resp.Choices[0].Message.Content) -} \ No newline at end of file +} diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go index 8e81a12..8f56e5a 100644 --- a/sample-app/recipe_agent_example.go +++ b/sample-app/recipe_agent_example.go @@ -53,7 +53,6 @@ func ingredientValidatorTool(ctx context.Context, agent *sdk.Agent, client *open llmSpan := tool.LogPrompt(prompt) - // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ Model: "gpt-3.5-turbo", Messages: []openai.ChatCompletionMessage{ @@ -67,7 +66,6 @@ func ingredientValidatorTool(ctx context.Context, agent *sdk.Agent, client *open return "", fmt.Errorf("CreateChatCompletion error: %w", err) } - // Log completion var completionMsgs []sdk.Message for _, choice := range resp.Choices { completionMsgs = append(completionMsgs, sdk.Message{ @@ -121,7 +119,6 @@ func nutritionCalculatorTool(ctx context.Context, agent *sdk.Agent, client *open llmSpan := tool.LogPrompt(prompt) - // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ Model: "gpt-3.5-turbo", Messages: []openai.ChatCompletionMessage{ @@ -135,7 +132,6 @@ func nutritionCalculatorTool(ctx context.Context, agent *sdk.Agent, client *open return "", fmt.Errorf("CreateChatCompletion error: %w", err) } - // Log completion var completionMsgs []sdk.Message for _, choice := range resp.Choices { completionMsgs = append(completionMsgs, sdk.Message{ @@ -191,7 +187,6 @@ func cookingTimeEstimatorTool(ctx context.Context, agent *sdk.Agent, client *ope llmSpan := tool.LogPrompt(prompt) - // Make API call resp, err := client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{ Model: "gpt-3.5-turbo", Messages: []openai.ChatCompletionMessage{ @@ -205,7 +200,6 @@ func cookingTimeEstimatorTool(ctx context.Context, agent *sdk.Agent, client *ope return "", fmt.Errorf("CreateChatCompletion error: %w", err) } - // Log completion var completionMsgs []sdk.Message for _, choice := range resp.Choices { completionMsgs = append(completionMsgs, sdk.Message{ @@ -246,10 +240,10 @@ func runRecipeAgent() { agent := traceloop.NewAgent(ctx, "recipe_generator", sdk.AgentAttributes{ Name: "recipe_generator", AssociationProperties: associationProperties, - }, abTest) + ABTest: abTest, + }) defer agent.End() - // User request userRequest := "Create a healthy pasta dish with vegetables" fmt.Printf("User request: %s\n\n", userRequest) @@ -284,7 +278,6 @@ func runRecipeAgent() { return } - // Log completion var completionMsgs []sdk.Message for _, choice := range resp.Choices { completionMsgs = append(completionMsgs, sdk.Message{ diff --git a/sample-app/workflow_example.go b/sample-app/workflow_example.go index fcfcd74..a0b498f 100644 --- a/sample-app/workflow_example.go +++ b/sample-app/workflow_example.go @@ -26,7 +26,7 @@ func workflowMain() { wf := traceloop.NewWorkflow(ctx, tlp.WorkflowAttributes{ Name: "history_generation", - }, nil) + }) defer wf.End() factGenTask := wf.NewTask("current_date_fact_generation") diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index 492f3c2..eee8c0e 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -15,7 +15,6 @@ type Agent struct { workflow *Workflow ctx context.Context Attributes AgentAttributes `json:"agent_attributes"` - ABTest *model.ABTest `json:"ab_test"` } func (agent *Agent) End() { diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 56362a6..81d4746 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -143,7 +143,7 @@ func (instance *Traceloop) getTracer() apitrace.Tracer { } // NewAgent creates a standalone agent (without a workflow) -func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs AgentAttributes, abTest *model.ABTest) *Agent { +func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs AgentAttributes) *Agent { aCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.agent", name), apitrace.WithNewRoot()) attrs := []attribute.KeyValue{ @@ -152,8 +152,8 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs semconvai.LLMAgentName.String(name), } - if abTest != nil { - for key, activeVarient := range abTest.VarientsKeys { + if agentAttrs.ABTest != nil { + for key, activeVarient := range agentAttrs.ABTest.VarientsKeys { if activeVarient { agentAttrs.AssociationProperties["ab_testing_variant"] = key break @@ -173,7 +173,6 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs workflow: nil, ctx: aCtx, Attributes: agentAttrs, - ABTest: abTest, } } diff --git a/traceloop-sdk/tracing_types.go b/traceloop-sdk/tracing_types.go index 92a37a4..a9f055b 100644 --- a/traceloop-sdk/tracing_types.go +++ b/traceloop-sdk/tracing_types.go @@ -1,5 +1,7 @@ package traceloop +import "github.com/traceloop/go-openllmetry/traceloop-sdk/model" + type Message struct { Index int `json:"index"` Role string `json:"role"` @@ -28,6 +30,7 @@ type Completion struct { type WorkflowAttributes struct { Name string `json:"workflow_name"` AssociationProperties map[string]string `json:"association_properties"` + ABTest *model.ABTest `json:"ab_test"` } type ContextAttributes struct { @@ -66,4 +69,5 @@ type ToolCallAttributes struct { type AgentAttributes struct { Name string `json:"agent_name"` AssociationProperties map[string]string `json:"association_properties"` + ABTest *model.ABTest `json:"ab_test"` } diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index b149a29..5e9e19a 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -14,10 +14,9 @@ type Workflow struct { sdk *Traceloop ctx context.Context Attributes WorkflowAttributes `json:"workflow_attributes"` - ABTest *model.ABTest `json:"ab_test"` } -func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttributes, abTest *model.ABTest) *Workflow { +func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttributes) *Workflow { wCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.workflow", attrs.Name), trace.WithNewRoot()) span.SetAttributes( @@ -26,8 +25,8 @@ func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttrib semconvai.TraceloopEntityName.String(attrs.Name), ) - if abTest != nil { - for key, activeVarient := range abTest.VarientsKeys { + if attrs.ABTest != nil { + for key, activeVarient := range attrs.ABTest.VarientsKeys { if activeVarient { span.SetAttributes(attribute.String("traceloop.association.properties.ab_testing_variant", key)) break @@ -79,8 +78,8 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string semconvai.TraceloopEntityName.String(name), } - if workflow.ABTest != nil { - for key, activeVarient := range workflow.ABTest.VarientsKeys { + if workflow.Attributes.ABTest != nil { + for key, activeVarient := range workflow.Attributes.ABTest.VarientsKeys { if activeVarient { associationProperties["ab_testing_variant"] = key } From 5d3cf1930ec06aafd7f50080c958200a58cbd962 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:47:51 +0200 Subject: [PATCH 14/17] comments --- sample-app/recipe_agent_example.go | 10 ++++------ traceloop-sdk/agent.go | 1 + traceloop-sdk/model/types.go | 8 ++++---- traceloop-sdk/sdk.go | 11 ++++------- traceloop-sdk/workflow.go | 9 ++++----- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go index 8f56e5a..a09687b 100644 --- a/sample-app/recipe_agent_example.go +++ b/sample-app/recipe_agent_example.go @@ -15,7 +15,7 @@ var associationProperties = map[string]string{ } var abTest = &model.ABTest{ - VarientsKeys: map[string]bool{ + VarientKeys: map[string]bool{ "variant_a": false, "variant_b": true, }, @@ -167,9 +167,7 @@ func cookingTimeEstimatorTool(ctx context.Context, agent *sdk.Agent, client *ope }, }, }, - }, map[string]string{ - "user_id": "user_67890", - }) + }, associationProperties) defer tool.End() prompt := sdk.Prompt{ @@ -238,9 +236,9 @@ func runRecipeAgent() { // Create standalone agent with association properties agent := traceloop.NewAgent(ctx, "recipe_generator", sdk.AgentAttributes{ - Name: "recipe_generator", + Name: "recipe_generator", AssociationProperties: associationProperties, - ABTest: abTest, + ABTest: abTest, }) defer agent.End() diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index eee8c0e..5cdf4c7 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -24,6 +24,7 @@ func (agent *Agent) End() { func (agent *Agent) LogPrompt(prompt Prompt) LLMSpan { // Merge workflow and agent association properties contextAttrs := ContextAttributes{ + AgentName: &agent.Attributes.Name, AssociationProperties: make(map[string]string), } diff --git a/traceloop-sdk/model/types.go b/traceloop-sdk/model/types.go index 167b695..cf7ba52 100644 --- a/traceloop-sdk/model/types.go +++ b/traceloop-sdk/model/types.go @@ -3,13 +3,13 @@ package model type SpanKind string const ( - SpanKindTool SpanKind = "tool" - SpanKindAgent SpanKind = "agent" - SpanKindTask SpanKind = "task" + SpanKindTool SpanKind = "tool" + SpanKindAgent SpanKind = "agent" + SpanKindTask SpanKind = "task" SpanKindWorkflow SpanKind = "workflow" ) // The varient that is active will be added to the trace. type ABTest struct { - VarientsKeys map[string]bool `json:"varients_keys"` + VarientKeys map[string]bool `json:"varient_keys"` } diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 81d4746..e77e966 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -21,7 +21,6 @@ import ( const PromptsPath = "/v1/traceloop/prompts" - type Traceloop struct { config Config promptRegistry model.PromptRegistry @@ -30,7 +29,6 @@ type Traceloop struct { http.Client } - func NewClient(ctx context.Context, config Config) (*Traceloop, error) { instance := Traceloop{ config: config, @@ -153,7 +151,7 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs } if agentAttrs.ABTest != nil { - for key, activeVarient := range agentAttrs.ABTest.VarientsKeys { + for key, activeVarient := range agentAttrs.ABTest.VarientKeys { if activeVarient { agentAttrs.AssociationProperties["ab_testing_variant"] = key break @@ -169,9 +167,9 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs span.SetAttributes(attrs...) return &Agent{ - sdk: instance, - workflow: nil, - ctx: aCtx, + sdk: instance, + workflow: nil, + ctx: aCtx, Attributes: agentAttrs, } } @@ -199,7 +197,6 @@ func (instance *Traceloop) LogPrompt(ctx context.Context, prompt Prompt, context for key, value := range contextAttrs.AssociationProperties { attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) } - span.SetAttributes(attrs...) setMessagesAttribute(span, "llm.prompts", prompt.Messages) diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 5e9e19a..87ffd68 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/traceloop/go-openllmetry/traceloop-sdk/model" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" + "github.com/traceloop/go-openllmetry/traceloop-sdk/model" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -26,12 +26,12 @@ func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttrib ) if attrs.ABTest != nil { - for key, activeVarient := range attrs.ABTest.VarientsKeys { + for key, activeVarient := range attrs.ABTest.VarientKeys { if activeVarient { span.SetAttributes(attribute.String("traceloop.association.properties.ab_testing_variant", key)) break } - } + } } return &Workflow{ @@ -79,7 +79,7 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string } if workflow.Attributes.ABTest != nil { - for key, activeVarient := range workflow.Attributes.ABTest.VarientsKeys { + for key, activeVarient := range workflow.Attributes.ABTest.VarientKeys { if activeVarient { associationProperties["ab_testing_variant"] = key } @@ -102,4 +102,3 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string }, } } - From e32cc3fdeff51af47029dea1127715a02b03fbb5 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:59:35 +0200 Subject: [PATCH 15/17] typo --- sample-app/recipe_agent_example.go | 2 +- traceloop-sdk/model/types.go | 4 ++-- traceloop-sdk/sdk.go | 4 ++-- traceloop-sdk/workflow.go | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go index a09687b..fcd46ab 100644 --- a/sample-app/recipe_agent_example.go +++ b/sample-app/recipe_agent_example.go @@ -15,7 +15,7 @@ var associationProperties = map[string]string{ } var abTest = &model.ABTest{ - VarientKeys: map[string]bool{ + VariantKeys: map[string]bool{ "variant_a": false, "variant_b": true, }, diff --git a/traceloop-sdk/model/types.go b/traceloop-sdk/model/types.go index cf7ba52..bbb0d52 100644 --- a/traceloop-sdk/model/types.go +++ b/traceloop-sdk/model/types.go @@ -9,7 +9,7 @@ const ( SpanKindWorkflow SpanKind = "workflow" ) -// The varient that is active will be added to the trace. +// The variant that is active will be added to the trace. type ABTest struct { - VarientKeys map[string]bool `json:"varient_keys"` + VariantKeys map[string]bool `json:"variant_keys"` } diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index e77e966..f22ff5c 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -151,8 +151,8 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs } if agentAttrs.ABTest != nil { - for key, activeVarient := range agentAttrs.ABTest.VarientKeys { - if activeVarient { + for key, activeVariant := range agentAttrs.ABTest.VariantKeys { + if activeVariant { agentAttrs.AssociationProperties["ab_testing_variant"] = key break } diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 87ffd68..4609e47 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -26,8 +26,8 @@ func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttrib ) if attrs.ABTest != nil { - for key, activeVarient := range attrs.ABTest.VarientKeys { - if activeVarient { + for key, activeVariant := range attrs.ABTest.VariantKeys { + if activeVariant { span.SetAttributes(attribute.String("traceloop.association.properties.ab_testing_variant", key)) break } @@ -79,8 +79,8 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string } if workflow.Attributes.ABTest != nil { - for key, activeVarient := range workflow.Attributes.ABTest.VarientKeys { - if activeVarient { + for key, activeVariant := range workflow.Attributes.ABTest.VariantKeys { + if activeVariant { associationProperties["ab_testing_variant"] = key } } From 2e9a52cb1224d3f387cfd84e236fba934e2c1486 Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:09:36 +0200 Subject: [PATCH 16/17] comm --- traceloop-sdk/agent.go | 4 ++++ traceloop-sdk/sdk.go | 3 +++ traceloop-sdk/workflow.go | 11 +++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index 5cdf4c7..f806d7d 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -52,6 +52,10 @@ func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunct semconvai.TraceloopEntityName.String(name), } + if agent.workflow != nil { + attrs = append(attrs, semconvai.TraceloopWorkflowName.String(agent.workflow.Attributes.Name)) + } + for key, value := range agent.Attributes.AssociationProperties { attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) } diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index f22ff5c..58873aa 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -151,6 +151,9 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs } if agentAttrs.ABTest != nil { + if agentAttrs.AssociationProperties == nil { + agentAttrs.AssociationProperties = make(map[string]string) + } for key, activeVariant := range agentAttrs.ABTest.VariantKeys { if activeVariant { agentAttrs.AssociationProperties["ab_testing_variant"] = key diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 4609e47..73993a7 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -3,6 +3,7 @@ package traceloop import ( "context" "fmt" + "maps" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" "github.com/traceloop/go-openllmetry/traceloop-sdk/model" @@ -78,15 +79,17 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string semconvai.TraceloopEntityName.String(name), } + agentAssociationProps := make(map[string]string, len(associationProperties)+1) + maps.Copy(agentAssociationProps, associationProperties) + if workflow.Attributes.ABTest != nil { for key, activeVariant := range workflow.Attributes.ABTest.VariantKeys { if activeVariant { - associationProperties["ab_testing_variant"] = key + agentAssociationProps["ab_testing_variant"] = key } } } - // Add agent-specific association properties to the span - for key, value := range associationProperties { + for key, value := range agentAssociationProps { attrs = append(attrs, attribute.String("traceloop.association.properties."+key, value)) } @@ -98,7 +101,7 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string ctx: aCtx, Attributes: AgentAttributes{ Name: name, - AssociationProperties: associationProperties, + AssociationProperties: agentAssociationProps, }, } } From 6f6e7c030d5e51f6c638916ffa578e81607ba70e Mon Sep 17 00:00:00 2001 From: nina-kollman <59646487+nina-kollman@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:47:52 +0200 Subject: [PATCH 17/17] semiconv --- sample-app/recipe_agent_example.go | 4 ++-- {traceloop-sdk/model => semconv-ai}/types.go | 2 +- traceloop-sdk/agent.go | 3 +-- traceloop-sdk/sdk.go | 6 +++--- traceloop-sdk/tracing_types.go | 6 +++--- traceloop-sdk/workflow.go | 7 +++---- 6 files changed, 13 insertions(+), 15 deletions(-) rename {traceloop-sdk/model => semconv-ai}/types.go (94%) diff --git a/sample-app/recipe_agent_example.go b/sample-app/recipe_agent_example.go index fcd46ab..efd003d 100644 --- a/sample-app/recipe_agent_example.go +++ b/sample-app/recipe_agent_example.go @@ -6,15 +6,15 @@ import ( "os" "github.com/sashabaranov/go-openai" + semconvai "github.com/traceloop/go-openllmetry/semconv-ai" sdk "github.com/traceloop/go-openllmetry/traceloop-sdk" - "github.com/traceloop/go-openllmetry/traceloop-sdk/model" ) var associationProperties = map[string]string{ "user_id": "user_67890", } -var abTest = &model.ABTest{ +var abTest = &semconvai.ABTest{ VariantKeys: map[string]bool{ "variant_a": false, "variant_b": true, diff --git a/traceloop-sdk/model/types.go b/semconv-ai/types.go similarity index 94% rename from traceloop-sdk/model/types.go rename to semconv-ai/types.go index bbb0d52..c73bc03 100644 --- a/traceloop-sdk/model/types.go +++ b/semconv-ai/types.go @@ -1,4 +1,4 @@ -package model +package semconvai type SpanKind string diff --git a/traceloop-sdk/agent.go b/traceloop-sdk/agent.go index f806d7d..f667a56 100644 --- a/traceloop-sdk/agent.go +++ b/traceloop-sdk/agent.go @@ -5,7 +5,6 @@ import ( "fmt" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" - "github.com/traceloop/go-openllmetry/traceloop-sdk/model" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -48,7 +47,7 @@ func (agent *Agent) NewTool(name string, toolType string, toolFunction ToolFunct toolCtx, span := agent.sdk.getTracer().Start(agent.ctx, fmt.Sprintf("%s.tool", name)) attrs := []attribute.KeyValue{ semconvai.LLMAgentName.String(agent.Attributes.Name), - semconvai.TraceloopSpanKind.String(string(model.SpanKindTool)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindTool)), semconvai.TraceloopEntityName.String(name), } diff --git a/traceloop-sdk/sdk.go b/traceloop-sdk/sdk.go index 58873aa..9c36f85 100644 --- a/traceloop-sdk/sdk.go +++ b/traceloop-sdk/sdk.go @@ -145,7 +145,7 @@ func (instance *Traceloop) NewAgent(ctx context.Context, name string, agentAttrs aCtx, span := instance.getTracer().Start(ctx, fmt.Sprintf("%s.agent", name), apitrace.WithNewRoot()) attrs := []attribute.KeyValue{ - semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindAgent)), semconvai.TraceloopEntityName.String(name), semconvai.LLMAgentName.String(name), } @@ -217,7 +217,7 @@ func (instance *Traceloop) LogToolCall(ctx context.Context, attrs ToolCallAttrib spanAttrs := []attribute.KeyValue{ semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), - semconvai.TraceloopSpanKind.String(string(model.SpanKindTool)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindTool)), semconvai.TraceloopEntityName.String(attrs.Name), } @@ -240,7 +240,7 @@ func (instance *Traceloop) LogAgent(ctx context.Context, attrs AgentAttributes, spanAttrs := []attribute.KeyValue{ semconvai.TraceloopWorkflowName.String(workflowAttrs.Name), - semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindAgent)), semconvai.LLMAgentName.String(attrs.Name), } diff --git a/traceloop-sdk/tracing_types.go b/traceloop-sdk/tracing_types.go index a9f055b..d3ac056 100644 --- a/traceloop-sdk/tracing_types.go +++ b/traceloop-sdk/tracing_types.go @@ -1,6 +1,6 @@ package traceloop -import "github.com/traceloop/go-openllmetry/traceloop-sdk/model" +import "github.com/traceloop/go-openllmetry/semconv-ai" type Message struct { Index int `json:"index"` @@ -30,7 +30,7 @@ type Completion struct { type WorkflowAttributes struct { Name string `json:"workflow_name"` AssociationProperties map[string]string `json:"association_properties"` - ABTest *model.ABTest `json:"ab_test"` + ABTest *semconvai.ABTest `json:"ab_test"` } type ContextAttributes struct { @@ -69,5 +69,5 @@ type ToolCallAttributes struct { type AgentAttributes struct { Name string `json:"agent_name"` AssociationProperties map[string]string `json:"association_properties"` - ABTest *model.ABTest `json:"ab_test"` + ABTest *semconvai.ABTest `json:"ab_test"` } diff --git a/traceloop-sdk/workflow.go b/traceloop-sdk/workflow.go index 73993a7..f563aac 100644 --- a/traceloop-sdk/workflow.go +++ b/traceloop-sdk/workflow.go @@ -6,7 +6,6 @@ import ( "maps" semconvai "github.com/traceloop/go-openllmetry/semconv-ai" - "github.com/traceloop/go-openllmetry/traceloop-sdk/model" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -22,7 +21,7 @@ func (instance *Traceloop) NewWorkflow(ctx context.Context, attrs WorkflowAttrib span.SetAttributes( semconvai.TraceloopWorkflowName.String(attrs.Name), - semconvai.TraceloopSpanKind.String(string(model.SpanKindWorkflow)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindWorkflow)), semconvai.TraceloopEntityName.String(attrs.Name), ) @@ -59,7 +58,7 @@ func (workflow *Workflow) NewTask(name string) *Task { span.SetAttributes( semconvai.TraceloopWorkflowName.String(workflow.Attributes.Name), - semconvai.TraceloopSpanKind.String(string(model.SpanKindTask)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindTask)), semconvai.TraceloopEntityName.String(name), ) @@ -75,7 +74,7 @@ func (workflow *Workflow) NewAgent(name string, associationProperties map[string attrs := []attribute.KeyValue{ semconvai.TraceloopWorkflowName.String(workflow.Attributes.Name), - semconvai.TraceloopSpanKind.String(string(model.SpanKindAgent)), + semconvai.TraceloopSpanKind.String(string(semconvai.SpanKindAgent)), semconvai.TraceloopEntityName.String(name), }