> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# CloudWeGo Eino

> Trace CloudWeGo Eino chat model, tool, and embedding calls in Braintrust to debug and evaluate your LLM apps

If you are a coding agent, prefer the Braintrust [`bt` CLI](/reference/cli/quickstart) for repeatable, scriptable work: running evals, instrumenting code, querying logs, syncing data, managing functions, and configuring coding agents. Use the MCP server for reasoning over Braintrust data in conversation, such as ad-hoc lookups and exploration from your IDE.

[CloudWeGo Eino](https://github.com/cloudwego/eino) is a Go framework for building LLM applications. Braintrust traces Eino's chat model, tool, and embedding components, including streaming responses and tool calls.

<View title="Go" icon="https://img.logo.dev/go.dev?token=pk_BdcHD9e5SCW3j1rnJkNyMQ">
  <h2 id="setup-go">
    Setup
  </h2>

  Install the Braintrust Go SDK alongside the Eino packages, then configure your API keys.

  <Steps>
    <Step title="Install packages">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      go get github.com/braintrustdata/braintrust-sdk-go
      go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/cloudwego/eino
      go get github.com/cloudwego/eino-ext/components/model/openai
      ```
    </Step>

    <Step title="Set environment variables">
      ```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      BRAINTRUST_API_KEY=your-braintrust-api-key
      OPENAI_API_KEY=your-openai-api-key
      ```
    </Step>
  </Steps>

  <h2 id="auto-instrumentation-go">
    Auto-instrumentation
  </h2>

  To trace Eino without wiring Braintrust into your application code, build your app with [Orchestrion](https://github.com/DataDog/orchestrion), a compile-time tool that appends Braintrust's handler to your `callbacks.AppendGlobalHandlers` call. Once the handler is registered, all `ChatModel`, tool, and embedding calls are traced.

  <Steps>
    <Step title="Install Orchestrion">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      go install github.com/DataDog/orchestrion@latest
      ```
    </Step>

    <Step title="Create orchestrion.tool.go in your project root">
      ```go title="orchestrion.tool.go" #skip-compile theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      //go:build tools

      package main

      import (
      _ "github.com/DataDog/orchestrion"
      _ "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/cloudwego/eino"
      )
      ```
    </Step>

    <Step title="Write your app">
      Register Eino's global handlers with `callbacks.AppendGlobalHandlers()`. Orchestrion appends `traceeino.DefaultHandler()` to that call at build time, so you don't import or construct the Braintrust handler yourself.

      <CodeGroup>
        ```go Go theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
        package main

        import (
        "context"
        "fmt"
        "log"
        "os"

        einoopenai "github.com/cloudwego/eino-ext/components/model/openai"
        "github.com/cloudwego/eino/callbacks"
        "github.com/cloudwego/eino/schema"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/sdk/trace"

        "github.com/braintrustdata/braintrust-sdk-go"
        )

        func main() {
        tp := trace.NewTracerProvider()
        defer tp.Shutdown(context.Background())
        otel.SetTracerProvider(tp)

        bt, err := braintrust.New(tp,
        	braintrust.WithProject("my-eino-project"),
        	braintrust.WithAPIKey(os.Getenv("BRAINTRUST_API_KEY")),
        )
        if err != nil {
        	log.Fatal(err)
        }

        // Orchestrion appends traceeino.DefaultHandler() to this call at build time.
        callbacks.AppendGlobalHandlers()

        ctx := context.Background()
        tracer := otel.Tracer("eino-example")
        ctx, span := tracer.Start(ctx, "eino-chat")
        defer span.End()

        chatModel, err := einoopenai.NewChatModel(ctx, &einoopenai.ChatModelConfig{
        	Model:  "gpt-4o-mini",
        	APIKey: os.Getenv("OPENAI_API_KEY"),
        })
        if err != nil {
        	log.Fatal(err)
        }

        messages := []*schema.Message{{
        	Role:    schema.User,
        	Content: "What is the capital of France?",
        }}
        resp, err := chatModel.Generate(ctx, messages)
        if err != nil {
        	log.Fatal(err)
        }

        fmt.Printf("Response: %s\n", resp.Content)
        fmt.Printf("View trace: %s\n", bt.Permalink(span))
        }
        ```
      </CodeGroup>
    </Step>

    <Step title="Build with Orchestrion and run">
      ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
      go mod tidy
      orchestrion go build -o myapp
      ./myapp
      ```

      <Accordion title="Enable Orchestrion via GOFLAGS">
        Instead of running `orchestrion go build`, you can set a `GOFLAGS` environment variable to enable Orchestrion for normal `go build` commands:

        ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
        export GOFLAGS="-toolexec='orchestrion toolexec'"
        go build ./...
        ```
      </Accordion>
    </Step>
  </Steps>

  <h2 id="manual-instrumentation-go">
    Manual instrumentation
  </h2>

  To trace Eino manually, register Braintrust's handler yourself by passing `traceeino.NewHandler()` to `callbacks.AppendGlobalHandlers`. All subsequent `ChatModel` calls are traced.

  <CodeGroup>
    ```go Go theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    package main

    import (
    	"context"
    	"fmt"
    	"log"
    	"os"

    	einoopenai "github.com/cloudwego/eino-ext/components/model/openai"
    	"github.com/cloudwego/eino/callbacks"
    	"github.com/cloudwego/eino/schema"
    	"go.opentelemetry.io/otel"
    	"go.opentelemetry.io/otel/sdk/trace"

    	"github.com/braintrustdata/braintrust-sdk-go"
    	traceeino "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/cloudwego/eino"
    )

    func main() {
    	tp := trace.NewTracerProvider()
    	defer tp.Shutdown(context.Background())
    	otel.SetTracerProvider(tp)

    	bt, err := braintrust.New(tp,
    		braintrust.WithProject("my-eino-project"),
    		braintrust.WithAPIKey(os.Getenv("BRAINTRUST_API_KEY")),
    	)
    	if err != nil {
    		log.Fatal(err)
    	}

    	handler := traceeino.NewHandler()
    	callbacks.AppendGlobalHandlers(handler)

    	ctx := context.Background()
    	tracer := otel.Tracer("eino-example")
    	ctx, span := tracer.Start(ctx, "eino-chat")
    	defer span.End()

    	chatModel, err := einoopenai.NewChatModel(ctx, &einoopenai.ChatModelConfig{
    		Model:  "gpt-4o-mini",
    		APIKey: os.Getenv("OPENAI_API_KEY"),
    	})
    	if err != nil {
    		log.Fatal(err)
    	}

    	messages := []*schema.Message{{
    		Role:    schema.User,
    		Content: "What is the capital of France?",
    	}}
    	resp, err := chatModel.Generate(ctx, messages)
    	if err != nil {
    		log.Fatal(err)
    	}

    	fmt.Printf("Response: %s\n", resp.Content)
    	fmt.Printf("View trace: %s\n", bt.Permalink(span))
    }
    ```
  </CodeGroup>

  <h2 id="streaming-go">
    Streaming
  </h2>

  For streaming, call `Wait()` on the handler after closing the reader so asynchronously finalized span attributes are flushed before exit. Get the handler from `traceeino.NewHandler()` in the manual path, or from `traceeino.DefaultHandler()` when auto-instrumenting.

  ```go #skip-compile theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  reader, err := chatModel.Stream(ctx, messages)
  if err != nil {
  	log.Fatal(err)
  }
  // ... consume the stream ...
  reader.Close()
  handler.Wait()
  ```

  <h2 id="tracing-embeddings-go">
    Tracing embeddings
  </h2>

  Eino's callback handler captures `EmbedStrings` calls with no setup beyond registering the handler. Embedding calls appear as LLM spans in Braintrust, with input texts, embedding count, and model metadata captured.

  <h2 id="what-traced-go">
    What Braintrust traces
  </h2>

  Braintrust captures:

  * Chat model spans (`eino.<model>`, for example `eino.OpenAI`), with input messages, model parameters (model, max\_tokens, temperature, top\_p, stop) in metadata, output message with finish reason and tool calls, and token usage metrics (prompt, completion, total, cached prompt, and reasoning tokens).
  * Streaming chat model output reconstructed from streamed chunks, with a `time_to_first_token` metric.
  * Tool spans (`eino.<tool>`), with the tool's JSON arguments as input and its response as output.
  * Embedding spans (`eino.<embedder>`), with input texts, an embeddings count and vector length summary as output, model and encoding format in metadata, and prompt and total token metrics.

  <h2 id="resources-go">
    Resources
  </h2>

  * [CloudWeGo Eino documentation](https://github.com/cloudwego/eino)
  * [Go SDK example](https://github.com/braintrustdata/braintrust-sdk-go/tree/main/examples/cloudwego/eino)
  * [Trace LLM calls](/instrument/trace-llm-calls) for general Go auto-instrumentation setup
</View>
