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

# interaction()

> Wrap AI functions with OpenTelemetry tracing to track interactions within a Gentrace pipeline

The `interaction()` function wraps your AI functions with [OpenTelemetry tracing](https://opentelemetry.io/docs/concepts/signals/traces/) to track interactions within a Gentrace pipeline. It creates [spans](https://opentelemetry.io/docs/concepts/signals/traces/#spans) for function execution, records arguments and outputs, and automatically manages [OpenTelemetry baggage](https://opentelemetry.io/docs/concepts/signals/baggage/) to ensure proper sampling and tracing context.

<Tip>
  The Gentrace SDK automatically configures OpenTelemetry when you
  call `init()`. If you have an existing OpenTelemetry setup or need
  custom configuration, see the [manual setup
  guide](/reference/opentelemetry-guide).
</Tip>

## Basic usage

<CodeGroup dropdown>
  ```typescript theme={null}
  import { init, interaction } from 'gentrace';
  import { Anthropic } from '@anthropic-ai/sdk';

  init({
    apiKey: process.env.GENTRACE_API_KEY,
  });

  const anthropic = new Anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY,
  });

  async function queryAI(prompt: string): Promise<string> {
    const response = await anthropic.messages.create({
      model: 'claude-opus-4-20250514',
      messages: [{ role: 'user', content: prompt }],
      max_tokens: 1024,
    });
    return response.content[0].text;
  }

  const tracedQueryAI = interaction('Query AI', queryAI);

  // Use the traced function
  const result = await tracedQueryAI('What is the capital of France?');
  console.log(result);
  ```

  ```python theme={null}
  import os
  from gentrace import init, interaction
  from anthropic import Anthropic

  init(api_key=os.environ["GENTRACE_API_KEY"])

  anthropic = Anthropic(
      api_key=os.environ["ANTHROPIC_API_KEY"]
  )

  # Define your AI function
  async def query_ai(prompt: str) -> str:
      response = await anthropic.messages.create(
          model="claude-opus-4-20250514",
          messages=[{"role": "user", "content": prompt}],
          max_tokens=1024
      )
      return response.content[0].text

  # Wrap with interaction tracing
  traced_query_ai = interaction("Query AI", query_ai)

  # Use the traced function
  result = await traced_query_ai("What is the capital of France?")
  print(result)
  ```
</CodeGroup>

The Gentrace SDK automatically configures the `GentraceSpanProcessor` when you call `init()`, which simplifies the process of converting baggage values to span attributes, ensuring proper trace routing to Gentrace. For manual configuration details, see the [OpenTelemetry setup guide](../reference/opentelemetry-guide).

<Tip>
  The `GentraceSpanProcessor` simplifies baggage-to-span attribute
  conversion by automatically copying baggage values (like
  `gentrace.sample=true`) to span attributes. This ensures that the
  OpenTelemetry Collector can properly identify and route
  Gentrace-related traces without requiring manual attribute
  management in your code.
</Tip>

## Overview

An interaction in Gentrace represents a single AI function call or operation within your pipeline. The `interaction()` function:

1. **Creates [OpenTelemetry spans](https://opentelemetry.io/docs/concepts/signals/traces/#spans)** for function execution with detailed tracing
2. **Records function arguments and outputs** as [span events](https://opentelemetry.io/docs/concepts/signals/traces/#span-events) for debugging
3. **Manages [OpenTelemetry baggage](https://opentelemetry.io/docs/concepts/signals/baggage/)** by setting `gentrace.sample="true"` for proper sampling
4. **Associates with pipelines** by adding the `gentrace.pipeline_id` attribute
5. **Handles errors gracefully** by recording exceptions and setting [span status](https://opentelemetry.io/docs/concepts/signals/traces/#span-status)

## Parameters

<CodeGroup dropdown>
  ```typescript theme={null}
  /**
   * Wrap a function with Gentrace interaction tracing
   *
   * @param name - The name for the OpenTelemetry span
   * @param fn - The function to wrap with tracing
   * @param options - Configuration options
   * @param options.pipelineId - The UUID of the Gentrace pipeline
   *                             this interaction belongs to
   * @param options.attributes - Additional attributes to set
   *                             on the span
   *
   * @returns The wrapped function with the same signature
   *          but enhanced with tracing capabilities
   */
  function interaction<F extends (...args: any[]) => any>(
    name: string,
    fn: F,
    options: {
      pipelineId: string;
      attributes?: Record<string, any>;
    },
  ): F;
  ```

  ```python theme={null}
  def interaction(
      *,
      pipeline_id: str,
      name: Optional[str] = None,
      attributes: Optional[Dict[str, Any]] = None,
  ) -> Callable[[F], F]:
      """
      Decorator to wrap a function with Gentrace interaction tracing

      Args:
          pipeline_id: The UUID of the Gentrace pipeline
                       this interaction belongs to
          name: Custom name for the OpenTelemetry span.
                Defaults to the function's __name__
          attributes: Additional attributes to set on the span

      Returns:
          A decorator that wraps the function with
          tracing capabilities
      """

  ```
</CodeGroup>

## Additional attributes

You can add custom [attributes](https://opentelemetry.io/docs/concepts/signals/traces/#attributes) to the OpenTelemetry span using the `attributes` option.

<CodeGroup dropdown>
  ```typescript theme={null}
  import { interaction } from 'gentrace';
  import { OpenAI } from 'openai';

  const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY,
  });

  async function generateText(prompt: string): Promise<string> {
    const response = await openai.chat.completions.create({
      model: 'o3',
      messages: [{ role: 'user', content: prompt }],
    });
    return response.choices[0].message.content || '';
  }

  const tracedGenerateText = interaction(
    'Generate Text',
    generateText,
    {
      attributes: {
        provider: 'openai',
        version: '1.0.0',
      },
    },
  );

  const result = await tracedGenerateText(
    'Write a haiku about coding',
    0.7,
  );
  ```

  ```python theme={null}
  import os
  from openai import AsyncOpenAI
  from gentrace import interaction

  openai = AsyncOpenAI(
      api_key=os.environ["OPENAI_API_KEY"]
  )

  @interaction(
      name="Generate Text",
      attributes={
          "model": "o3",
          "provider": "openai",
          "version": "1.0.0",
      }
  )
  async def generate_text(prompt: str) -> str:
      response = await openai.chat.completions.create(
          model="o3",
          messages=[{"role": "user", "content": prompt}],
      )
      return response.choices[0].message.content or ""

  result = await generate_text("Write a haiku about coding")
  ```
</CodeGroup>

## OpenTelemetry integration

The `interaction()` function provides deep integration with OpenTelemetry:

### Span creation and attributes

* **Span Name**: Uses the provided name parameter
* **Pipeline ID**: Automatically adds `gentrace.pipeline_id` attribute
* **Custom Attributes**: Merges any additional attributes you provide
* **Function Metadata**: Records function name and execution context

### Baggage management

The function automatically manages [OpenTelemetry baggage](https://opentelemetry.io/docs/concepts/signals/baggage/):

<CodeGroup dropdown>
  ```typescript theme={null}
  // Automatically sets baggage for the duration of the function
  import { context, propagation } from '@opentelemetry/api';

  const currentContext = context.active();
  const currentBaggage =
    propagation.getBaggage(currentContext) ??
    propagation.createBaggage();

  const newBaggage = currentBaggage.setEntry('gentrace.sample', {
    value: 'true',
  });
  const newContext = propagation.setBaggage(currentContext, newBaggage);
  ```

  ```python theme={null}
  # Automatically sets baggage for the duration of the function
  from opentelemetry import baggage, context

  current_context = context.get_current()
  context_with_modified_baggage = baggage.set_baggage(
      'gentrace.sample', 'true', context=current_context
  )
  ```
</CodeGroup>

This ensures that:

* All nested spans are properly sampled
* Gentrace can identify and process the traces
* Context is preserved across async boundaries

### Event recording

Function arguments and outputs are recorded as [span events](https://opentelemetry.io/docs/concepts/signals/traces/#span-events):

* **Arguments Event**: `gentrace.fn.args` with serialized function arguments
* **Output Event**: `gentrace.fn.output` with serialized return value
* **Exception Events**: Automatic exception recording with stack traces

## Error handling

The `interaction()` function handles errors gracefully and **automatically associates all errors and exceptions with the OpenTelemetry span**:

<CodeGroup dropdown>
  ```typescript theme={null}
  import { interaction } from 'gentrace';

  async function riskyAIFunction(input: string): Promise<string> {
    if (input.length === 0) {
      throw new Error('Input cannot be empty');
    }
    // AI processing logic
    return `Processed: ${input}`;
  }

  const tracedRiskyFunction = interaction(
    'Risky AI Function',
    riskyAIFunction,
  );

  try {
    const result = await tracedRiskyFunction('');
  } catch (error) {
    // Error is automatically recorded in the OpenTelemetry span with:
    // - Exception event with stack trace
    // - Span status set to ERROR
    // - Error type and message as attributes
  }
  ```

  ```python theme={null}
  import asyncio
  from gentrace import interaction

  @interaction(name="Risky AI Function")
  async def risky_ai_function(input_text: str) -> str:
      if len(input_text) == 0:
          raise ValueError("Input cannot be empty")

      return f"Processed: {input_text}"

  async def main():
      try:
          result = await risky_ai_function("")
      except ValueError as e:
          print(f"Function failed: {e}")
          # Exception is automatically recorded in the OpenTelemetry span with:
          # - Exception event with stack trace
          # - Span status set to ERROR
          # - Error type and message as attributes

  asyncio.run(main())
  ```
</CodeGroup>

### OTEL span error integration

When errors occur within interactions:

* **Automatic Capture**: All `Error` objects (TypeScript) and exceptions (Python) are automatically captured as [span events](https://opentelemetry.io/docs/concepts/signals/traces/#span-events)
* **Stack Traces**: Full stack traces are preserved in the span for debugging
* **Error Attributes**: Error messages, types, and metadata are recorded as [span attributes](https://opentelemetry.io/docs/concepts/signals/traces/#attributes)
* **Span Status**: The [span status](https://opentelemetry.io/docs/concepts/signals/traces/#span-status) is automatically set to `ERROR` when unhandled exceptions occur

## Usage in experiments

The `interaction()` function works seamlessly with Gentrace [experiments](../evaluation/experiments):

<CodeGroup dropdown>
  ```typescript theme={null}
  import { experiment, evalOnce, interaction } from 'gentrace';
  import { OpenAI } from 'openai';

  const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY,
  });

  // Define your AI function
  async function summarizeText(text: string): Promise<string> {
    // Your summarization logic
    const response = await openai.chat.completions.create({
      model: 'o3',
      messages: [
        {
          role: 'system',
          content: 'Summarize the following text concisely.',
        },
        { role: 'user', content: text },
      ],
    });
    return response.choices[0].message.content || '';
  }

  // Wrap with interaction tracing
  const tracedSummarizeText = interaction(
    'Summarize Text',
    summarizeText,
    {
      attributes: {
        model: 'o3',
        task: 'summarization',
      },
    },
  );

  // Use in experiments
  experiment(async () => {
    await evalOnce('summarization-test', async () => {
      const longText =
        'This is a very long article about artificial intelligence...';
      const summary = await tracedSummarizeText(longText);

      // Evaluate the summary quality
      return {
        summary,
        length: summary.length,
        quality_score: calculateQualityScore(summary),
      };
    });
  });
  ```

  ```python theme={null}
  import os
  from openai import AsyncOpenAI
  from gentrace import experiment, eval, interaction

  openai = AsyncOpenAI(
      api_key=os.environ["OPENAI_API_KEY"]
  )

  # Define your AI function
  async def summarize_text(text: str) -> str:
      # Your summarization logic
      response = await openai.chat.completions.create(
          model="o3",
          messages=[
              {"role": "system", "content": "Summarize the following text concisely."},
              {"role": "user", "content": text},
          ],
      )
      return response.choices[0].message.content or ""

  # Wrap with interaction tracing
  @interaction(
      name="Summarize Text",
      attributes={
          "model": "o3",
          "task": "summarization",
      }
  )
  async def traced_summarize_text(text: str) -> str:
      return await summarize_text(text)

  # Use in experiments
  @experiment
  async def summarization_experiment() -> None:
      @eval(name="summarization-test")
      async def summarization_test() -> dict:
          long_text = "This is a very long article about artificial intelligence..."
          summary = await traced_summarize_text(long_text)

          # Evaluate the summary quality
          return {
              "summary": summary,
              "length": len(summary),
              "quality_score": calculate_quality_score(summary),
          }

      await summarization_test()
  ```
</CodeGroup>

## Requirements

<ResponseField name="Gentrace SDK Initialization" type="configuration" required>
  Must call [`init()`](../getting-started/initialization) with a valid
  API key. The SDK automatically configures OpenTelemetry for you. For
  custom OpenTelemetry setups, see the [manual setup
  guide](../reference/opentelemetry-guide).
</ResponseField>

<ResponseField name="Valid Pipeline ID" type="UUID">
  When provided, must be a valid UUID for an existing Gentrace
  pipeline. If omitted, uses the default pipeline
</ResponseField>

<ResponseField name="Function Compatibility" type="function">
  Works with both synchronous and asynchronous functions
</ResponseField>

## Related functions

* [`init()`](../getting-started/initialization) - Initialize the Gentrace SDK
* [`traced()`](./traced) - Lower-level function tracing without pipeline association
* [`experiment()`](../evaluation/experiments) - Create testing contexts for grouping related evaluations
* [`eval()` / `evalOnce()`](../evaluation/unit-tests) - Run individual test cases within experiments
* [`evalDataset()` / `eval_dataset()`](../evaluation/dataset-tests) - Run tests against a dataset
