Generate evaluations and iterate on your AI applications
The Simforge Go SDK captures your AI function calls to automatically generate evaluations. Re-run your prompts with different models, parameters, and inputs to iterate faster.
Copy this prompt into your coding agent (tested with Cursor and Claude Code using Sonnet 4.5):
Copy
Modify existing Go code to add Simforge tracing.Do NOT browse or web search. Use ONLY the API described below.Simforge Go SDK (authoritative excerpt):- Install: `go get github.com/Project-White-Rabbit/simforge-go`- Init: import simforge "github.com/Project-White-Rabbit/simforge-go" client := simforge.NewClient(os.Getenv("SIMFORGE_API_KEY"))- Start/End style (PREFERRED for existing functions): func myFunc(ctx context.Context, arg1 string) (Result, error) { ctx, span := client.Start(ctx, "<trace_function_key>", "<SpanName>", simforge.WithType("function")) defer span.End() span.SetInput(arg1) result, err := doWork(ctx, arg1) if err != nil { span.SetError(err) return Result{}, err } span.SetOutput(result) return result, nil }- Closure style (for inline code): result, err := client.Span(ctx, "<trace_function_key>", func(ctx context.Context) (any, error) { return doWork(ctx), nil }, simforge.WithName("<SpanName>"), simforge.WithType("function"), simforge.WithInput(args...))- Fluent API: fn := client.GetFunction("<trace_function_key>") ctx, span := fn.Start(ctx, "<SpanName>", simforge.WithType("function")) defer span.End()- Span types: "llm", "agent", "function", "guardrail", "handoff", "custom"- Always pass ctx from Start or Span callback into nested calls for parent-child linking.- Always call client.FlushTraces(5 * time.Second) before program exit.Task:1) Ensure the simforge-go module is added (`go get github.com/Project-White-Rabbit/simforge-go`).2) Ensure a client is initialized with the API key.3) Read the codebase and identify ALL AI workflows (LLM calls, agent runs, AI-driven decisions).4) Present me with a numbered list of workflows you found. For each, describe: - What it does - Why it's worth instrumenting — what visibility tracing gives you into each step5) After I choose which workflow(s) to instrument: - Add Start/End at the top of each function (preferred) or wrap in Span closure - Use SetInput/SetOutput/SetError to capture data - Instrument intermediate steps (not just the final output) so each trace has enough context to diagnose issues - Pass `ctx` through for nested span support6) Do not change function signature, behavior, or return value. Minimal diff.Output:- First: your numbered list of workflows with why each is worth instrumenting- After my selection: minimal diffs for go.mod and the instrumented functions
// Default (production URL)client := simforge.NewClient(apiKey)// Custom service URLclient := simforge.NewClient(apiKey, simforge.WithServiceURL("http://localhost:4000"))// Disable tracing (functions still execute, but no spans are sent)client := simforge.NewClient(apiKey, simforge.WithEnabled(false))
Missing API key doesn’t crash. If the API key is missing, empty, or whitespace-only, the SDK automatically disables tracing and logs a warning. All instrumented functions still execute normally — no spans are sent, no errors are thrown. You don’t need any conditional logic around the API key.
For projects with instrumented functions spread across multiple files, create a dedicated package that initializes the client and exposes a function handle. Import it wherever you need to instrument.
Copy
// pkg/tracing/tracing.go — single source of truthpackage tracingimport ( "os" simforge "github.com/Project-White-Rabbit/simforge-go")var Client = simforge.NewClient(os.Getenv("SIMFORGE_API_KEY"))var OrderService = Client.GetFunction("order-processing")
Use span.AddContext() on an ActiveSpan (Start/End style) to attach contextual key-value pairs at runtime — useful when context depends on computed values:
Use span.SetPrompt() on an ActiveSpan (Start/End style) to set the prompt string on the current span. This is stored in span_data.prompt and is useful for capturing the exact prompt text sent to an LLM:
The last SetPrompt call wins — it overwrites any previously set prompt on the span. Calling SetPrompt outside a span context is a no-op (it never crashes).
Use simforge.GetCurrentTrace(ctx) to set context that applies to the entire trace (all spans within a single execution). This is useful for grouping traces by session or attaching trace-level metadata:
Copy
result, err := client.Span(ctx, "order-processing", func(ctx context.Context) (any, error) { trace := simforge.GetCurrentTrace(ctx) // Set session ID (stored as database column, filterable in dashboard) trace.SetSessionID("session-123") // Set trace metadata (stored in raw trace data) trace.SetMetadata(map[string]any{"region": "us-west-2", "environment": "production"}) // Add context entries (stored as key-value pairs, accumulates across calls) trace.AddContext(map[string]any{"workflow": "checkout-flow", "batch_id": "batch-2024-01"}) return map[string]any{"status": "completed"}, nil}, simforge.WithName("ProcessOrder"), simforge.WithType("function"))
SetSessionID(id) — Groups traces by user session. Stored as a database column for efficient filtering.
SetMetadata(map) — Arbitrary key-value metadata on the trace. Merges with existing metadata.
AddContext(map) — Key-value context entries. Accumulates across multiple calls.
Errors are captured in the span and returned to the caller:
Copy
result, err := client.Span(ctx, "risky-service", func(ctx context.Context) (any, error) { return nil, errors.New("something went wrong")}, simforge.WithName("RiskyOperation"), simforge.WithType("function"))// err contains "something went wrong"// The span records the error message and timing