diff --git a/docs/integrations/agent-frameworks/agent-framework-dotnet.md b/docs/integrations/agent-frameworks/agent-framework-dotnet.md new file mode 100644 index 000000000..9a89ceca5 --- /dev/null +++ b/docs/integrations/agent-frameworks/agent-framework-dotnet.md @@ -0,0 +1,100 @@ +--- +title: "Pydantic Logfire Integrations: Microsoft Agent Framework (.NET)" +description: "Send Microsoft Agent Framework (.NET, Microsoft.Agents.AI) telemetry to Pydantic Logfire using the OpenTelemetry .NET SDK and an OTLP exporter." +integration: otel +--- +# Microsoft Agent Framework (.NET) + +The [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/) (namespace +`Microsoft.Agents.AI`) is Microsoft's GA framework that unifies Semantic Kernel and AutoGen. It builds on +`Microsoft.Extensions.AI` and emits OpenTelemetry traces and metrics following the +[OTel GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/), so you send its +telemetry to **Logfire** with the standard OpenTelemetry .NET SDK plus an OTLP exporter. + +## Installation + +```bash +dotnet add package Microsoft.Agents.AI +dotnet add package Microsoft.Agents.AI.OpenAI +dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol +``` + +## Usage + +Enable OpenTelemetry on the chat client and/or the agent with a shared `sourceName`, register that source, and +export over OTLP to **Logfire**: + +```csharp title="Program.cs" +using System.ClientModel; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using OpenAI; +using OpenTelemetry; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +const string LogfireBase = "https://logfire-us.pydantic.dev"; // or logfire-eu.pydantic.dev +const string SourceName = "weather-agent"; // one name for client + agent +string logfireToken = Environment.GetEnvironmentVariable("LOGFIRE_TOKEN")!; + +var resource = ResourceBuilder.CreateDefault().AddService("maf-agent"); + +// HttpProtobuf + per-signal exporter => supply the FULL /v1/traces path. +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(resource) + .AddSource(SourceName) // must match the UseOpenTelemetry/WithOpenTelemetry sourceName + .AddOtlpExporter(o => + { + o.Endpoint = new Uri($"{LogfireBase}/v1/traces"); + o.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf; + o.Headers = $"Authorization={logfireToken}"; + }) + .Build(); + +// Build an instrumented IChatClient from the OpenAI client. +IChatClient chatClient = new OpenAIClient( + new ApiKeyCredential(Environment.GetEnvironmentVariable("OPENAI_API_KEY")!)) + .GetChatClient("gpt-4o") + .AsIChatClient() + .AsBuilder() + .UseOpenTelemetry(sourceName: SourceName, configure: cfg => cfg.EnableSensitiveData = true) + .Build(); + +AIAgent agent = new ChatClientAgent( + chatClient, + name: "WeatherAgent", + instructions: "You are a concise, helpful assistant.") + .WithOpenTelemetry(sourceName: SourceName, configure: cfg => cfg.EnableSensitiveData = true); + +var response = await agent.RunAsync("Why is the sky blue in one sentence?"); +Console.WriteLine(response); +``` + +You'll see `invoke_agent`, `chat`, and (if the agent calls tools) `execute_tool` spans, plus +`gen_ai.client.*` metrics, in **Logfire**. + +!!! warning "Common pitfalls" + - **Default OTLP protocol is gRPC.** Set `OtlpExportProtocol.HttpProtobuf` and supply the full `/v1/traces` + path with per-signal `AddOtlpExporter` (it isn't appended automatically). + - **`sourceName` must match `AddSource`.** If you omit `sourceName`, register the defaults instead: + `AddSource("Experimental.Microsoft.Agents.AI")` (agent) and + `AddSource("Experimental.Microsoft.Extensions.AI")` (chat client). + - **Don't double-instrument unnecessarily.** Enabling OTel on **both** the chat client and the agent (with + `EnableSensitiveData`) duplicates prompt/response content across `chat` and `invoke_agent` spans. + Instrument one layer if duplication is a problem. + - **`EnableSensitiveData = true`** captures prompts, responses, and tool args — dev/test only. + +!!! tip "Env-var alternative" + Set `OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-us.pydantic.dev` (base URL), + `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`, and `OTEL_EXPORTER_OTLP_HEADERS=Authorization=`, then a + bare `.AddOtlpExporter()` picks them up and appends the signal paths. + +## Managed prompts + +Managed prompts are authored and versioned in +[Prompt Management](../../reference/advanced/prompt-management/index.md). The dedicated prompt-fetching SDK +helpers currently ship in the [Python](../../reference/advanced/prompt-management/application.md) and +[TypeScript](https://pydantic.dev/docs/logfire/typescript-sdk/) SDKs. From .NET you can consume managed +variables over the language-agnostic [OFREP HTTP API](../../reference/advanced/managed-variables/external.md), +or resolve the prompt in a small Python/TypeScript sidecar and pass the rendered text into the agent's +`instructions`. diff --git a/docs/integrations/agent-frameworks/eino.md b/docs/integrations/agent-frameworks/eino.md new file mode 100644 index 000000000..124c22864 --- /dev/null +++ b/docs/integrations/agent-frameworks/eino.md @@ -0,0 +1,116 @@ +--- +title: "Pydantic Logfire Integrations: Eino (Go)" +description: "Send CloudWeGo Eino (Go) agent telemetry to Pydantic Logfire with a small OpenTelemetry callback handler over OTLP." +integration: otel +--- +# Eino (Go) + +[Eino](https://www.cloudwego.io/docs/eino/) is ByteDance/CloudWeGo's Go framework for LLM and agent +applications. Its observability is built on a **callbacks** system rather than automatic OpenTelemetry. To send +Eino traces to **Logfire**, register a standard OpenTelemetry OTLP exporter (pointed at **Logfire**) and a +small custom `callbacks.Handler` that opens a span per node on that provider. + +!!! note + The official `eino-ext` callback handlers (`apmplus`, `cozeloop`, `langfuse`) are vendor-specific and don't + expose a generic OTLP endpoint with auth headers, so a tiny custom handler is the cleanest path to Logfire. + +## Installation + +```bash +go get github.com/cloudwego/eino +go get github.com/cloudwego/eino-ext/components/model/openai +go get go.opentelemetry.io/otel \ + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \ + go.opentelemetry.io/otel/sdk +``` + +## Usage + +```go title="main.go" +package main + +import ( + "context" + "fmt" + + "github.com/cloudwego/eino-ext/components/model/openai" + "github.com/cloudwego/eino/callbacks" + "github.com/cloudwego/eino/components/model" + "github.com/cloudwego/eino/schema" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/sdk/trace" + oteltrace "go.opentelemetry.io/otel/trace" +) + +// Minimal handler that wraps each Eino node in an OTel span. +type otelHandler struct{ tr oteltrace.Tracer } + +func (h *otelHandler) OnStart(ctx context.Context, info *callbacks.RunInfo, in callbacks.CallbackInput) context.Context { + ctx, _ = h.tr.Start(ctx, info.Name) + return ctx +} +func (h *otelHandler) OnEnd(ctx context.Context, info *callbacks.RunInfo, out callbacks.CallbackOutput) context.Context { + if s := oteltrace.SpanFromContext(ctx); s != nil { + s.End() + } + return ctx +} +func (h *otelHandler) OnError(ctx context.Context, info *callbacks.RunInfo, err error) context.Context { + if s := oteltrace.SpanFromContext(ctx); s != nil { + s.RecordError(err) + s.End() + } + return ctx +} +func (h *otelHandler) OnStartWithStreamInput(ctx context.Context, _ *callbacks.RunInfo, in *schema.StreamReader[callbacks.CallbackInput]) context.Context { + in.Close() + return ctx +} +func (h *otelHandler) OnEndWithStreamOutput(ctx context.Context, _ *callbacks.RunInfo, out *schema.StreamReader[callbacks.CallbackOutput]) context.Context { + out.Close() + return ctx +} + +func main() { + ctx := context.Background() + + // 1. OTel -> Logfire (HTTP/protobuf). + exp, _ := otlptracehttp.New(ctx, + otlptracehttp.WithEndpointURL("https://logfire-us.pydantic.dev/v1/traces"), + otlptracehttp.WithHeaders(map[string]string{"Authorization": "your-write-token"}), + ) + tp := trace.NewTracerProvider(trace.WithBatcher(exp)) + defer tp.Shutdown(ctx) + otel.SetTracerProvider(tp) + + // 2. Register the handler globally (init only — not thread-safe). + callbacks.AppendGlobalHandlers(&otelHandler{tr: tp.Tracer("eino")}) + + // 3. One model call -> traced to Logfire. + cm, _ := openai.NewChatModel(ctx, &openai.ChatModelConfig{APIKey: "sk-...", Model: "gpt-4o-mini"}) + out, _ := cm.Generate(ctx, + []*schema.Message{schema.UserMessage("One-line joke about Go?")}, + model.WithTemperature(0.7), + ) + fmt.Println(out.Content) +} +``` + +Use `https://logfire-eu.pydantic.dev/v1/traces` for the EU region. + +!!! warning "Common pitfalls" + - **`callbacks.AppendGlobalHandlers` is not thread-safe** — call it only at process init. + - **Close the stream readers** in the streaming callback variants or you'll leak. + - **Use the HTTP exporter** with the `/v1/traces` path, and **flush on exit** via `defer tp.Shutdown(ctx)`. + - Component constructors (the OpenAI chat model here) live in the separate `eino-ext` module, not core + `eino`. + +## Managed prompts + +Managed prompts are authored and versioned in +[Prompt Management](../../reference/advanced/prompt-management/index.md). The dedicated prompt-fetching SDK +helpers currently ship in the [Python](../../reference/advanced/prompt-management/application.md) and +[TypeScript](https://pydantic.dev/docs/logfire/typescript-sdk/) SDKs. From Go you can consume managed variables +over the language-agnostic [OFREP HTTP API](../../reference/advanced/managed-variables/external.md), or resolve +the prompt in a small Python/TypeScript sidecar and pass the rendered text into your Eino messages. diff --git a/docs/integrations/agent-frameworks/genkit-go.md b/docs/integrations/agent-frameworks/genkit-go.md new file mode 100644 index 000000000..f02374cb7 --- /dev/null +++ b/docs/integrations/agent-frameworks/genkit-go.md @@ -0,0 +1,86 @@ +--- +title: "Pydantic Logfire Integrations: Genkit (Go)" +description: "Send Firebase Genkit (Go) agent telemetry to Pydantic Logfire by registering a standard OpenTelemetry OTLP exporter as the global tracer provider." +integration: otel +--- +# Genkit (Go) + +[Firebase Genkit](https://genkit.dev/go/) for Go is built directly on the OpenTelemetry Go SDK and emits its +spans through the **global** `TracerProvider`. So to send Genkit traces to **Logfire**, register a standard +OpenTelemetry OTLP exporter (pointed at **Logfire**) as the global provider **before** `genkit.Init(...)` — and +Genkit's agent, model, and tool spans flow in automatically. + +!!! note + Don't use Genkit's `googlecloud.EnableGoogleCloudTelemetry` for **Logfire** — it targets Google Cloud + Operations and has no generic OTLP endpoint. The standard OTel exporter below has no extra dependency on + Genkit internals. + +## Installation + +```bash +go get github.com/firebase/genkit/go +go get go.opentelemetry.io/otel \ + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \ + go.opentelemetry.io/otel/sdk +``` + +## Usage + +```go title="main.go" +package main + +import ( + "context" + "fmt" + + "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" + "github.com/firebase/genkit/go/plugins/googlegenai" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/sdk/trace" +) + +func main() { + ctx := context.Background() + + // 1. Point the global OTel TracerProvider at Logfire (HTTP/protobuf). + // Endpoint + Authorization can also come from + // OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS. + exp, _ := otlptracehttp.New(ctx, + otlptracehttp.WithEndpointURL("https://logfire-us.pydantic.dev/v1/traces"), + otlptracehttp.WithHeaders(map[string]string{"Authorization": "your-write-token"}), + ) + tp := trace.NewTracerProvider(trace.WithBatcher(exp)) + defer tp.Shutdown(ctx) + otel.SetTracerProvider(tp) // MUST be before genkit.Init + + // 2. Init Genkit; its spans now export to Logfire. + g := genkit.Init(ctx, + genkit.WithPlugins(&googlegenai.GoogleAI{}), + genkit.WithDefaultModel("googleai/gemini-2.5-flash"), + ) + + resp, _ := genkit.Generate(ctx, g, ai.WithPrompt("Tell me a one-line joke about Go.")) + fmt.Println(resp.Text()) +} +``` + +Set `GEMINI_API_KEY` (or your provider's key) and run `go run .`. You'll see the generation span with model and +token attributes in **Logfire**. Use `https://logfire-eu.pydantic.dev/v1/traces` for the EU region. + +!!! warning "Common pitfalls" + - **Set the global provider before `genkit.Init`**, or early spans are dropped. + - **Use the HTTP exporter** (`otlptracehttp`) with the `/v1/traces` path — the gRPC exporter won't match + Logfire's HTTP ingest. + - **Flush on exit.** The `defer tp.Shutdown(ctx)` flushes the batch exporter; without it a short program may + exit before spans are sent. + +## Managed prompts + +Managed prompts are authored and versioned in +[Prompt Management](../../reference/advanced/prompt-management/index.md). The dedicated prompt-fetching SDK +helpers currently ship in the [Python](../../reference/advanced/prompt-management/application.md) and +[TypeScript](https://pydantic.dev/docs/logfire/typescript-sdk/) SDKs. From Go you can consume managed variables +over the language-agnostic [OFREP HTTP API](../../reference/advanced/managed-variables/external.md), or resolve +the prompt in a small Python/TypeScript sidecar and pass the rendered text into `ai.WithPrompt(...)`. diff --git a/docs/integrations/agent-frameworks/index.md b/docs/integrations/agent-frameworks/index.md new file mode 100644 index 000000000..cf20d5b5a --- /dev/null +++ b/docs/integrations/agent-frameworks/index.md @@ -0,0 +1,84 @@ +--- +title: Agent Frameworks +description: "How to send telemetry from popular AI agent frameworks — in Python, TypeScript, Go, Rust, and .NET — to Pydantic Logfire, and how to use managed prompts with each." +--- +# Agent Frameworks + +**Pydantic Logfire** is built on [OpenTelemetry](https://opentelemetry.io/), so it can ingest traces from +essentially any AI agent framework. Each guide below shows three things: + +- **How to send telemetry to Logfire** — the exact setup for that framework. +- **A runnable sample agent** you can copy, run, and watch appear in the Logfire Live view. +- **How to use managed prompts** — authoring and versioning prompts in + [Prompt Management](../../reference/advanced/prompt-management/index.md) and fetching them at runtime. + +There are three integration patterns, depending on the framework: + +1. **Native OpenTelemetry** — the framework already emits OTel spans through the global tracer provider. In + Python, calling [`logfire.configure()`][logfire.configure] is enough (e.g. Pydantic AI, Google ADK, Strands, + Semantic Kernel). In other languages, point the standard OTel SDK at Logfire's OTLP endpoint. +2. **An instrumentor** — an [OpenInference](https://github.com/Arize-ai/openinference) or + [OpenLLMetry](https://github.com/traceloop/openllmetry) package adds the spans. Because + [`logfire.configure()`][logfire.configure] sets the global provider, the instrumentor's spans flow to + Logfire automatically (e.g. CrewAI, AutoGen, smolagents, Haystack, Agno). +3. **OTLP over the wire** — for languages and frameworks without an in-process exporter, send to Logfire's + OTLP endpoint directly (or via an [OpenTelemetry Collector](../../how-to-guides/otel-collector/otel-collector-overview.md)). + +## Python + +| Framework | Guide | +| --------- | ----- | +| Pydantic AI | [Pydantic AI](../llms/pydanticai.md) | +| OpenAI Agents SDK | [OpenAI](../llms/openai.md#openai-agents) | +| LangChain | [LangChain](../llms/langchain.md) | +| LangGraph | [LangGraph](../llms/langgraph.md) | +| CrewAI | [CrewAI](../llms/crewai.md) | +| AutoGen | [AutoGen](../llms/autogen.md) | +| Google ADK | [Google ADK](../llms/google-adk.md) | +| smolagents | [smolagents](../llms/smolagents.md) | +| Strands Agents | [Strands Agents](../llms/strands.md) | +| Agno | [Agno](../llms/agno.md) | +| Haystack | [Haystack](../llms/haystack.md) | +| LlamaIndex | [LlamaIndex](../llms/llamaindex.md) | +| DSPy | [DSPy](../llms/dspy.md) | +| Instructor | [Instructor](../llms/instructor.md) | +| Semantic Kernel | [Semantic Kernel (Python)](../llms/semantic-kernel.md) | +| Letta | [Letta](../llms/letta.md) | +| Claude Agent SDK | [Claude Agent SDK](../llms/claude-agent-sdk.md) | + +## TypeScript / JavaScript + +| Framework | Guide | +| --------- | ----- | +| Vercel AI SDK | [Vercel AI SDK](vercel-ai-sdk.md) | +| Mastra | [Mastra](mastra.md) | +| LangChain.js / LangGraph.js | [LangChain.js](langchain-js.md) | +| OpenAI Agents SDK (TS) | [OpenAI Agents SDK (TS)](openai-agents-js.md) | +| VoltAgent | [VoltAgent](voltagent.md) | +| LlamaIndex.TS | [LlamaIndex.TS](llamaindex-ts.md) | + +## Go + +| Framework | Guide | +| --------- | ----- | +| Firebase Genkit | [Genkit (Go)](genkit-go.md) | +| Eino | [Eino (Go)](eino.md) | + +## Rust + +| Framework | Guide | +| --------- | ----- | +| Rig | [Rig (Rust)](rig.md) | + +## .NET + +| Framework | Guide | +| --------- | ----- | +| Semantic Kernel | [Semantic Kernel (.NET)](semantic-kernel-dotnet.md) | +| Microsoft Agent Framework | [Microsoft Agent Framework (.NET)](agent-framework-dotnet.md) | + +!!! tip "Don't see your framework?" + Any OpenTelemetry-compatible library works with Logfire. See + [Use Alternative Clients](../../how-to-guides/alternative-clients.md) for the generic OTLP setup in any + language, and let us know on [Slack](https://pydantic.dev/docs/logfire/join-slack/) what you'd like us to + document next. diff --git a/docs/integrations/agent-frameworks/langchain-js.md b/docs/integrations/agent-frameworks/langchain-js.md new file mode 100644 index 000000000..f3614b1ae --- /dev/null +++ b/docs/integrations/agent-frameworks/langchain-js.md @@ -0,0 +1,92 @@ +--- +title: "Pydantic Logfire Integrations: LangChain.js" +description: "Send LangChain.js and LangGraph.js telemetry to Pydantic Logfire via the LangSmith OpenTelemetry exporter over OTLP." +integration: otel +--- +# LangChain.js / LangGraph.js + +[LangChain.js](https://js.langchain.com/) (and [LangGraph.js](https://langchain-ai.github.io/langgraphjs/)) +trace through **LangSmith**, whose JS SDK ships native OpenTelemetry export. Point that OTLP exporter at +**Logfire** and your traces flow straight in — no Logfire-specific JS code required. + +## Installation + +```bash +npm install langchain @langchain/core @langchain/openai langsmith \ + @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-proto +``` + +(Add `@langchain/langgraph` if you build with LangGraph.) + +## Usage + +Set the LangSmith env vars and the OTLP endpoint **before importing** any LangChain code, and call +`initializeOTEL()` once at process start: + +```typescript title="index.ts" +// Run with: OPENAI_API_KEY=... LOGFIRE_WRITE_TOKEN=... npx tsx index.ts + +// 1. Env MUST be set before importing langchain/langsmith. +process.env.LANGSMITH_OTEL_ENABLED = 'true'; +process.env.LANGSMITH_TRACING = 'true'; +process.env.LANGSMITH_OTEL_ONLY = 'true'; +process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'https://logfire-us.pydantic.dev/v1/traces'; +process.env.OTEL_EXPORTER_OTLP_HEADERS = `Authorization=${process.env.LOGFIRE_WRITE_TOKEN}`; + +// 2. Initialize OTel before importing LangChain. +import { initializeOTEL } from 'langsmith/experimental/otel/setup'; +const { DEFAULT_LANGSMITH_SPAN_PROCESSOR } = initializeOTEL(); + +import { ChatOpenAI } from '@langchain/openai'; +import { HumanMessage } from '@langchain/core/messages'; + +async function main() { + const model = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 }); + const res = await model.invoke([new HumanMessage("What's 123 + 456?")]); + console.log(res.content); + + // 3. Flush spans to Logfire before the process exits. + await DEFAULT_LANGSMITH_SPAN_PROCESSOR.forceFlush?.(); + await DEFAULT_LANGSMITH_SPAN_PROCESSOR.shutdown(); +} + +main(); +``` + +For a LangGraph agent, swap the model call for `createReactAgent({ llm: model, tools: [...] })` from +`@langchain/langgraph/prebuilt` and `.invoke(...)` it — the same OTel setup captures the graph and tool spans. + +!!! warning "Common pitfalls" + - **Import order is critical.** The `LANGSMITH_*` env vars and `initializeOTEL()` must run before importing + `langchain` / `langsmith` / `@langchain/*`. + - **Endpoint URL.** The LangSmith JS exporter treats `OTEL_EXPORTER_OTLP_ENDPOINT` as the full URL, so give + it the `/v1/traces` form (use `logfire-eu.pydantic.dev` for the EU region). + - **Header format.** `Authorization=` — the raw Logfire write token, no `Bearer` prefix. + - **`LANGSMITH_OTEL_ONLY=true`** stops LangSmith from also shipping traces to its own backend, so you don't + need a `LANGSMITH_API_KEY`. + - **Flush on exit.** Short-lived scripts and serverless must `await ...shutdown()` (or `forceFlush()`) or + spans are lost. Use the HTTP/protobuf exporter (`@opentelemetry/exporter-trace-otlp-proto`), not gRPC. + +## Managed prompts + +Author and version prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and +fetch them with the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/): + +```typescript +import { defineTemplateVar } from '@pydantic/logfire-node/vars'; + +const systemPrompt = defineTemplateVar('prompt__system', { + default: 'You are a helpful assistant about {{topic}}.', + templateInputsSchema: { + type: 'object', + properties: { topic: { type: 'string' } }, + required: ['topic'], + }, +}); + +const resolved = await systemPrompt.get({ topic: 'math' }); +// Use resolved.value as the SystemMessage content for your chain or agent. +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/agent-frameworks/llamaindex-ts.md b/docs/integrations/agent-frameworks/llamaindex-ts.md new file mode 100644 index 000000000..f675d8d6f --- /dev/null +++ b/docs/integrations/agent-frameworks/llamaindex-ts.md @@ -0,0 +1,103 @@ +--- +title: "Pydantic Logfire Integrations: LlamaIndex.TS" +description: "Send LlamaIndex.TS (TypeScript) agent telemetry to Pydantic Logfire via OpenLLMetry / Traceloop over OTLP." +integration: otel +--- +# LlamaIndex.TS + +[LlamaIndex.TS](https://developers.llamaindex.ai/typescript) is the TypeScript edition of LlamaIndex. Its +documented observability path is [OpenLLMetry / Traceloop](https://github.com/traceloop/openllmetry-js), which +is built on OpenTelemetry and emits standard OTLP — so you point its exporter at **Logfire**'s OTLP endpoint +with your [write token](../../how-to-guides/create-write-tokens.md). + +## Installation + +```bash +npm install @traceloop/node-server-sdk llamaindex @llamaindex/openai @llamaindex/workflow +``` + +## Usage + +Keep the instrumentation in a **separate file that is imported first**, so it loads before any LlamaIndex code: + +```typescript title="instrumentation.ts" +// MUST be imported before llamaindex +import * as traceloop from '@traceloop/node-server-sdk'; +import * as LlamaIndex from 'llamaindex'; + +traceloop.initialize({ + appName: 'llamaindex-ts-logfire', + disableBatch: true, // flush immediately (dev only) + baseUrl: 'https://logfire-us.pydantic.dev', // EU: https://logfire-eu.pydantic.dev + // Use `headers` (raw token), NOT `apiKey` — `apiKey` would send "Authorization: Bearer ", + // which Logfire rejects. The `headers` format is comma-separated `key=value` pairs. + headers: `Authorization=${process.env.LOGFIRE_TOKEN}`, + // Pass the framework module explicitly so ESM auto-patching attaches (camelCase key `llamaIndex`): + instrumentModules: { + llamaIndex: LlamaIndex, + }, +}); +``` + +```typescript title="main.ts" +import './instrumentation'; // FIRST — registers the OTel hooks +import { agent } from '@llamaindex/workflow'; +import { openai } from '@llamaindex/openai'; +import * as traceloop from '@traceloop/node-server-sdk'; + +async function main() { + const myAgent = agent({ + llm: openai({ model: 'gpt-4o-mini' }), + tools: [], + }); + + const result = await myAgent.run('Say hello to Logfire in one sentence.'); + console.log(result.data.result); + + await traceloop.forceFlush(); // ensure spans ship before exit +} + +main(); +``` + +Run it with `LOGFIRE_TOKEN= OPENAI_API_KEY= npx tsx main.ts`, then open the Live view of your +project — you'll see the agent run, its LLM calls, and any tool calls as a nested trace. + +!!! warning "Common pitfalls" + - **Import order is the #1 issue.** `traceloop.initialize()` must run before `llamaindex` / + `@llamaindex/openai` are imported. Use a dedicated `instrumentation.ts` imported on the first line (or + Node's `--import ./instrumentation.ts`). + - **`instrumentModules` is mandatory under ESM/bundlers.** Auto-patching relies on intercepting `require`; + under ESM the modules are already resolved, so pass them explicitly as shown. + - **`baseUrl`, not the full path.** Give the bare host; the SDK appends `/v1/traces`. The auth header value + is the raw Logfire write token (no `Bearer` prefix). The env-var equivalent is + `TRACELOOP_BASE_URL` + `TRACELOOP_HEADERS="Authorization="`. + - **`disableBatch: true` is dev-only.** In production, drop it and rely on graceful shutdown / `forceFlush()`. + +## Managed prompts + +You can author and version prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) +and fetch them at runtime with the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/) +using `defineTemplateVar`: + +```typescript +import * as logfire from '@pydantic/logfire-node'; +import { defineTemplateVar } from '@pydantic/logfire-node/vars'; + +logfire.configure({ serviceName: 'llamaindex-ts-logfire' }); + +const systemPrompt = defineTemplateVar('prompt__system', { + default: 'You are a helpful assistant about {{topic}}.', + templateInputsSchema: { + type: 'object', + properties: { topic: { type: 'string' } }, + required: ['topic'], + }, +}); + +const resolved = await systemPrompt.get({ topic: 'observability' }); +// Pass resolved.value into your agent's system prompt. +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the +production workflow (promoting versions, rollout targeting). diff --git a/docs/integrations/agent-frameworks/mastra.md b/docs/integrations/agent-frameworks/mastra.md new file mode 100644 index 000000000..8c299ff6f --- /dev/null +++ b/docs/integrations/agent-frameworks/mastra.md @@ -0,0 +1,105 @@ +--- +title: "Pydantic Logfire Integrations: Mastra" +description: "Send Mastra (TypeScript agent framework) telemetry to Pydantic Logfire using its OpenTelemetry exporter over OTLP." +integration: otel +--- +# Mastra + +[Mastra](https://mastra.ai/) is a TypeScript agent framework with built-in observability ("AI Tracing"). You +configure it with an `Observability` instance whose exporters send OpenTelemetry data to **Logfire** via the +`@mastra/otel-exporter` package pointed at **Logfire**'s OTLP endpoint. + +## Installation + +```bash +npm install @mastra/core @mastra/observability @mastra/otel-exporter \ + @opentelemetry/exporter-trace-otlp-proto @ai-sdk/openai zod +``` + +## Usage + +```typescript title="mastra.ts" +import { Mastra } from '@mastra/core'; +import { Agent } from '@mastra/core/agent'; +import { createTool } from '@mastra/core/tools'; +import { Observability } from '@mastra/observability'; +import { OtelExporter } from '@mastra/otel-exporter'; +import { openai } from '@ai-sdk/openai'; +import { z } from 'zod'; + +const weatherTool = createTool({ + id: 'get-weather', + description: 'Get the weather for a city', + inputSchema: z.object({ city: z.string() }), + execute: async ({ context }) => ({ city: context.city, tempC: 21 }), +}); + +const agent = new Agent({ + name: 'weather-agent', + instructions: 'You are a helpful weather assistant. Use the tool.', + model: openai('gpt-4o-mini'), + tools: { weatherTool }, +}); + +export const mastra = new Mastra({ + agents: { agent }, + observability: new Observability({ + configs: { + otel: { + serviceName: 'mastra-weather-agent', + exporters: [ + new OtelExporter({ + provider: { + custom: { + // Give the full /v1/traces path for the custom provider. + endpoint: 'https://logfire-us.pydantic.dev/v1/traces', + protocol: 'http/protobuf', + headers: { Authorization: process.env.LOGFIRE_WRITE_TOKEN! }, + }, + }, + }), + ], + }, + }, + }), +}); + +const res = await mastra.getAgent('agent').generate('Weather in Paris?'); +console.log(res.text); +``` + +Set `OPENAI_API_KEY` and `LOGFIRE_WRITE_TOKEN`, then run. The agent run, model call, and tool call appear as a +nested trace in **Logfire**. + +!!! warning "Common pitfalls" + - **Use the current `observability` config.** The older top-level `telemetry: {}` (`OtelConfig`) on + `new Mastra()` is deprecated; the `Observability` + `OtelExporter` shape shown here is current. + - **Full `/v1/traces` URL for the `custom` provider**, and the raw write token as the `Authorization` + header value (no `Bearer`). Use `logfire-eu.pydantic.dev` for the EU region. + - **Install the matching low-level exporter.** `http/protobuf` needs + `@opentelemetry/exporter-trace-otlp-proto`; gRPC needs `@opentelemetry/exporter-trace-otlp-grpc` + + `@grpc/grpc-js`. + +## Managed prompts + +Author and version prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and +fetch them with the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/): + +```typescript +import { defineTemplateVar } from '@pydantic/logfire-node/vars'; + +const instructionsVar = defineTemplateVar('prompt__agent_instructions', { + default: 'You are a helpful {{role}}.', + templateInputsSchema: { + type: 'object', + properties: { role: { type: 'string' } }, + required: ['role'], + }, +}); + +const resolved = await instructionsVar.get({ role: 'weather assistant' }); +// Use resolved.value as the Agent's `instructions`. +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/agent-frameworks/openai-agents-js.md b/docs/integrations/agent-frameworks/openai-agents-js.md new file mode 100644 index 000000000..a2c4f0df7 --- /dev/null +++ b/docs/integrations/agent-frameworks/openai-agents-js.md @@ -0,0 +1,113 @@ +--- +title: "Pydantic Logfire Integrations: OpenAI Agents SDK (TypeScript)" +description: "Send OpenAI Agents SDK (TypeScript / @openai/agents) traces to Pydantic Logfire by bridging the Agents tracing API to OpenTelemetry." +integration: otel +--- +# OpenAI Agents SDK (TypeScript) + +The [OpenAI Agents SDK for TypeScript](https://openai.github.io/openai-agents-js/) (`@openai/agents`) has its +own tracing system but ships no built-in OpenTelemetry exporter. To send its traces to **Logfire**, configure +the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/) (which registers a global +OpenTelemetry tracer provider exporting to **Logfire**) and add a small custom `TracingProcessor` that turns +Agents spans into OpenTelemetry spans. + +!!! note + The first-class `logfire.instrument_openai_agents()` helper on the [OpenAI](../llms/openai.md) page is + **Python-only**. For the TypeScript SDK, the bridge below is the working pattern. + +## Installation + +```bash +npm install @openai/agents @pydantic/logfire-node @opentelemetry/api zod +``` + +!!! note "Zod version" + `@openai/agents` 0.11+ requires **Zod v4** (`zod@^4`). Older 0.0.x releases required pinning to + `zod@<=3.25`. Match the Zod version to your installed `@openai/agents`. + +## Usage + +```typescript title="index.ts" +// Run with: OPENAI_API_KEY=... LOGFIRE_TOKEN=... node --experimental-strip-types index.ts +import * as logfire from '@pydantic/logfire-node'; +import { trace, SpanStatusCode } from '@opentelemetry/api'; +import { Agent, run, addTraceProcessor } from '@openai/agents'; +import type { Span, Trace } from '@openai/agents'; + +// 1. Configure Logfire -> registers a global OTel tracer provider exporting to Logfire. +// Reads LOGFIRE_TOKEN from env; region (US/EU) is inferred from the token. +logfire.configure({ serviceName: 'agents-demo' }); + +const tracer = trace.getTracer('openai-agents'); + +// 2. Bridge: turn each Agents span into an OTel span on Logfire's provider. +const otelSpans = new Map>(); +addTraceProcessor({ + async onTraceStart(_t: Trace) {}, + async onSpanStart(span: Span) { + const s = tracer.startSpan(span.spanData?.type ?? 'agent.span'); + s.setAttribute('agents.span_id', span.spanId); + otelSpans.set(span.spanId, s); + }, + async onSpanEnd(span: Span) { + const s = otelSpans.get(span.spanId); + if (!s) return; + s.setAttribute('agents.data', JSON.stringify(span.spanData ?? {})); + if (span.error) s.setStatus({ code: SpanStatusCode.ERROR, message: String(span.error) }); + s.end(); + otelSpans.delete(span.spanId); + }, + async onTraceEnd(_t: Trace) {}, + async forceFlush() {}, + async shutdown() {}, +}); + +// 3. Tiny agent + one LLM call. +const agent = new Agent({ name: 'Haiku bot', instructions: 'Reply with one short haiku.' }); +const result = await run(agent, 'Write a haiku about telemetry.'); +console.log(result.finalOutput); + +await logfire.forceFlush?.(); // ensure spans are exported before exit +``` + +`addTraceProcessor` keeps OpenAI's own default trace backend too; use `setTraceProcessors([...])` instead to +send **only** to **Logfire**. + +!!! warning "Common pitfalls" + - **Configure Logfire first**, before registering the bridge, so the global provider exists when + `trace.getTracer()` is called. **Flush on exit** or short-lived scripts drop spans. + - **`OPENAI_API_KEY` is required** for the model call (and for the SDK's default trace exporter, unless you + use `setTraceProcessors`). + - **Tracing is on by default** in Node/Deno/Bun, disabled in browsers and when `NODE_ENV=test`. Disable + globally with `OPENAI_AGENTS_DISABLE_TRACING=1`. + - **ESM / Node version.** Both packages are ESM — use a recent Node LTS with `"type": "module"` (or `tsx`). + - The bridge above is minimal. For production fidelity (parent/child nesting via `span.parentId`, GenAI + semantic-convention attributes), expand it to set OTel context from each Agents span's `parentId`. + +If you prefer pure env-var OTLP config instead of `@pydantic/logfire-node`, point any standard OTel `NodeSDK` +at `OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-us.pydantic.dev` with +`OTEL_EXPORTER_OTLP_HEADERS='Authorization=your-write-token'`, then register the bridge processor. + +## Managed prompts + +Author and version prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and +fetch them with the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/): + +```typescript +import { defineTemplateVar } from '@pydantic/logfire-node/vars'; + +const instructions = defineTemplateVar('prompt__agent_instructions', { + default: 'Reply with one short {{style}}.', + templateInputsSchema: { + type: 'object', + properties: { style: { type: 'string' } }, + required: ['style'], + }, +}); + +const resolved = await instructions.get({ style: 'haiku' }); +const agent = new Agent({ name: 'Poet', instructions: resolved.value }); +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/agent-frameworks/rig.md b/docs/integrations/agent-frameworks/rig.md new file mode 100644 index 000000000..c95747234 --- /dev/null +++ b/docs/integrations/agent-frameworks/rig.md @@ -0,0 +1,135 @@ +--- +title: "Pydantic Logfire Integrations: Rig (Rust)" +description: "Send Rig (rig-core) agent telemetry to Pydantic Logfire using the native Logfire Rust SDK or a standard OpenTelemetry OTLP exporter." +integration: otel +--- +# Rig (Rust) + +[Rig](https://docs.rig.rs/) (the `rig-core` crate) is a Rust framework for building LLM and agent applications. +It is instrumented with the [`tracing`](https://docs.rs/tracing) crate and emits **GenAI semantic-convention** +spans for completions, agents, and tools. Because Rig uses `tracing`, you export its spans to **Logfire** by +installing a subscriber that ships them over OpenTelemetry — the simplest being the native +[Logfire Rust SDK](https://github.com/pydantic/logfire-rust). + +!!! note + Rig emits spans, but nothing is exported until you install a subscriber. Set one up **before** building and + prompting your agent. + +## Option A — Logfire Rust SDK (recommended) + +The `logfire` crate sets up the whole `tracing` + OpenTelemetry + OTLP-to-Logfire pipeline for you, so Rig's +spans flow through automatically. + +```toml title="Cargo.toml" +[dependencies] +rig-core = "0.38" +logfire = "0.10" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tracing = "0.1" +``` + +```rust title="src/main.rs" +use logfire::config::SendToLogfire; +use rig::{completion::Prompt, providers::openai}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Installs the global tracing subscriber + OTLP exporter to Logfire. + // Reads LOGFIRE_TOKEN from the environment. + let logfire = logfire::configure() + .send_to_logfire(SendToLogfire::IfTokenPresent) + .finish()?; + let _guard = logfire.shutdown_guard(); // flushes spans on drop + + // Rig agent — spans emitted via `tracing` are captured automatically. + let client = openai::Client::from_env(); + let agent = client + .agent(openai::GPT_4O) + .preamble("You are a helpful assistant.") + .build(); + + let answer = agent.prompt("In one sentence, what is observability?").await?; + println!("{answer}"); + + Ok(()) // `_guard` drops here, flushing to Logfire +} +``` + +Run with `LOGFIRE_TOKEN= OPENAI_API_KEY= cargo run`. You'll see the agent's completion span +with GenAI attributes (model, token usage) in **Logfire**. + +For an EU-region project, set `OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-eu.pydantic.dev`. + +## Option B — Standard OpenTelemetry OTLP exporter + +If you'd rather not use the Logfire crate, wire `tracing-subscriber` → `tracing-opentelemetry` → +`opentelemetry-otlp` and point the exporter at **Logfire**'s OTLP/HTTP endpoint. + +```toml title="Cargo.toml" +[dependencies] +rig-core = "0.38" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +opentelemetry = "0.31" +opentelemetry_sdk = { version = "0.31", features = ["rt-tokio"] } +opentelemetry-otlp = { version = "0.31", features = ["http-proto", "reqwest-client"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-opentelemetry = "0.32" # 0.32 pairs with opentelemetry 0.31 +``` + +```rust title="src/main.rs" +use opentelemetry::trace::TracerProvider as _; +use opentelemetry::KeyValue; +use opentelemetry_sdk::{trace::SdkTracerProvider, Resource}; +use rig::{completion::Prompt, providers::openai}; +use tracing_subscriber::prelude::*; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Reads OTEL_EXPORTER_OTLP_ENDPOINT and + // OTEL_EXPORTER_OTLP_HEADERS (Authorization=) from env. + let exporter = opentelemetry_otlp::SpanExporter::builder().with_http().build()?; + let provider = SdkTracerProvider::builder() + .with_batch_exporter(exporter) + .with_resource(Resource::builder().with_attributes([KeyValue::new("service.name", "rig-demo")]).build()) + .build(); + + let tracer = provider.tracer("rig-demo"); + tracing_subscriber::registry() + .with(tracing_opentelemetry::layer().with_tracer(tracer)) + .init(); + + let client = openai::Client::from_env(); + let agent = client.agent(openai::GPT_4O).preamble("You are a helpful assistant.").build(); + let answer = agent.prompt("In one sentence, what is observability?").await?; + println!("{answer}"); + + provider.shutdown()?; // MUST flush the batch exporter before exit, or spans are lost + Ok(()) +} +``` + +```bash +export OTEL_EXPORTER_OTLP_ENDPOINT="https://logfire-us.pydantic.dev" +export OTEL_EXPORTER_OTLP_HEADERS="Authorization=your-write-token" +``` + +!!! warning "Common pitfalls" + - **Install a subscriber.** Rig's spans are dropped unless a subscriber is registered, before the agent + runs. + - **Flush on shutdown** — the most common cause of "no data". Keep the `ShutdownGuard` alive (Option A) or + call `provider.shutdown()` (Option B) before the process exits. + - **Version compatibility is the sharpest edge.** All `opentelemetry*` crates must share the same minor + (all `0.31`), and `tracing-opentelemetry` is one minor ahead (`0.32` ↔ `opentelemetry 0.31`). The + `logfire` crate already pins a consistent set. Verify current versions on crates.io before pinning. + - **Use OTLP HTTP/protobuf** (feature `http-proto`); the SDK appends `/v1/traces` to the base endpoint. + +## Managed prompts + +Managed prompts are authored and versioned in +[Prompt Management](../../reference/advanced/prompt-management/index.md). The dedicated prompt-fetching SDK +helpers are currently available in the [Python](../../reference/advanced/prompt-management/application.md) and +[TypeScript](https://pydantic.dev/docs/logfire/typescript-sdk/) SDKs. From Rust, you can still consume managed +variables over the language-agnostic +[OFREP HTTP API](../../reference/advanced/managed-variables/external.md), or resolve the prompt in a small +Python/TypeScript sidecar and pass the rendered text into your Rig `preamble`/prompt. diff --git a/docs/integrations/agent-frameworks/semantic-kernel-dotnet.md b/docs/integrations/agent-frameworks/semantic-kernel-dotnet.md new file mode 100644 index 000000000..0714d8adb --- /dev/null +++ b/docs/integrations/agent-frameworks/semantic-kernel-dotnet.md @@ -0,0 +1,103 @@ +--- +title: "Pydantic Logfire Integrations: Semantic Kernel (.NET)" +description: "Send Microsoft Semantic Kernel (.NET) telemetry to Pydantic Logfire using the OpenTelemetry .NET SDK and an OTLP exporter." +integration: otel +--- +# Semantic Kernel (.NET) + +[Microsoft Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/) for .NET has native +OpenTelemetry support via `ActivitySource` and `Meter` (model diagnostics following the +[OTel GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/)). You send that +telemetry to **Logfire** with the standard OpenTelemetry .NET SDK plus an OTLP exporter pointed at **Logfire**. + +## Installation + +```bash +dotnet add package Microsoft.SemanticKernel +dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol +``` + +## Usage + +Semantic Kernel emits GenAI spans only when you enable its experimental diagnostics. Enable them (the +`Sensitive` switch also records prompts and completions), register the `Microsoft.SemanticKernel*` sources and +meters, and export over OTLP to **Logfire**: + +```csharp title="Program.cs" +using Microsoft.SemanticKernel; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +const string LogfireBase = "https://logfire-us.pydantic.dev"; // or logfire-eu.pydantic.dev +string logfireToken = Environment.GetEnvironmentVariable("LOGFIRE_TOKEN")!; +string authHeader = $"Authorization={logfireToken}"; + +// Enable GenAI diagnostics + prompt/response content. Set before building providers. +AppContext.SetSwitch("Microsoft.SemanticKernel.Experimental.GenAI.EnableOTelDiagnosticsSensitive", true); + +var resource = ResourceBuilder.CreateDefault().AddService("sk-agent"); + +// NOTE: with HttpProtobuf + per-signal AddOtlpExporter you must supply the FULL /v1/* path. +using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(resource) + .AddSource("Microsoft.SemanticKernel*") + .AddOtlpExporter(o => + { + o.Endpoint = new Uri($"{LogfireBase}/v1/traces"); + o.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf; + o.Headers = authHeader; + }) + .Build(); + +using var meterProvider = Sdk.CreateMeterProviderBuilder() + .SetResourceBuilder(resource) + .AddMeter("Microsoft.SemanticKernel*") + .AddOtlpExporter(o => + { + o.Endpoint = new Uri($"{LogfireBase}/v1/metrics"); + o.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf; + o.Headers = authHeader; + }) + .Build(); + +var kernel = Kernel.CreateBuilder() + .AddOpenAIChatCompletion("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!) + .Build(); + +var answer = await kernel.InvokePromptAsync("Why is the sky blue in one sentence?"); +Console.WriteLine(answer); +// Providers flush on Dispose (end of `using` scope). +``` + +You'll see the LLM call span with model, token usage, and (because the sensitive switch is on) the prompt and +completion in **Logfire**. + +!!! warning "Common pitfalls" + - **Default OTLP protocol is gRPC.** **Logfire** ingests OTLP/HTTP, so you must set + `OtlpExportProtocol.HttpProtobuf`, and with per-signal `AddOtlpExporter` supply the **full** path + (`/v1/traces`, `/v1/metrics`) yourself — it isn't appended automatically. + - **No diagnostics, no spans.** Without `EnableOTelDiagnostics` (metadata) or + `EnableOTelDiagnosticsSensitive` (also prompts/completions), SK's AI connectors emit nothing. Set the + switch (or env var `SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS[_SENSITIVE]=true`) before + the first call. Enable `Sensitive` only in dev/test. + - **Wildcard sources/meters.** Use `Microsoft.SemanticKernel*` — individual connector meters live under + sub-namespaces. + - **Providers must outlive your app and be disposed** (they flush on dispose). In a long-lived service, + register them in DI as singletons. + +!!! tip "Env-var alternative" + Instead of per-signal endpoints in code, set `OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-us.pydantic.dev` + (base URL), `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`, and + `OTEL_EXPORTER_OTLP_HEADERS=Authorization=`. A bare `.AddOtlpExporter()` then picks these up and + appends the signal paths for you. + +## Managed prompts + +Managed prompts are authored and versioned in +[Prompt Management](../../reference/advanced/prompt-management/index.md). The dedicated prompt-fetching SDK +helpers currently ship in the [Python](../../reference/advanced/prompt-management/application.md) and +[TypeScript](https://pydantic.dev/docs/logfire/typescript-sdk/) SDKs. From .NET you can consume managed +variables over the language-agnostic [OFREP HTTP API](../../reference/advanced/managed-variables/external.md), +or resolve the prompt in a small Python/TypeScript sidecar and pass the rendered text into your kernel prompt. diff --git a/docs/integrations/agent-frameworks/vercel-ai-sdk.md b/docs/integrations/agent-frameworks/vercel-ai-sdk.md new file mode 100644 index 000000000..340d73039 --- /dev/null +++ b/docs/integrations/agent-frameworks/vercel-ai-sdk.md @@ -0,0 +1,91 @@ +--- +title: "Pydantic Logfire Integrations: Vercel AI SDK" +description: "Send Vercel AI SDK (the `ai` package) telemetry to Pydantic Logfire using its built-in OpenTelemetry support." +integration: otel +--- +# Vercel AI SDK + +The [Vercel AI SDK](https://ai-sdk.dev/) (the `ai` npm package) has built-in OpenTelemetry support. You enable +it per call with `experimental_telemetry: { isEnabled: true }`, and it writes to the global OpenTelemetry +tracer. Configure the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/) (which +registers that global tracer pointed at **Logfire**) at process start, and your spans flow in. + +## Installation + +```bash +npm install ai @ai-sdk/openai @pydantic/logfire-node zod +``` + +## Usage + +Configure Logfire **first** so the global tracer exists before any `ai` call: + +```typescript title="agent.ts" +import * as logfire from '@pydantic/logfire-node'; + +logfire.configure({ serviceName: 'vercel-ai-agent' }); // sets the global OTel tracer provider + +import { generateText, tool, stepCountIs } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import { z } from 'zod'; + +const weather = tool({ + description: 'Get the weather for a city', + inputSchema: z.object({ city: z.string() }), + execute: async ({ city }) => ({ city, tempC: 21 }), +}); + +async function main() { + const { text } = await generateText({ + model: openai('gpt-4o-mini'), + prompt: 'What is the weather in Paris? Use the tool.', + tools: { weather }, + stopWhen: stepCountIs(5), + experimental_telemetry: { isEnabled: true, functionId: 'weather-agent' }, + }); + console.log(text); + await logfire.shutdown(); // flush spans before exit +} + +main(); +``` + +Set your `OPENAI_API_KEY` and `LOGFIRE_TOKEN`, then run with `npx tsx agent.ts`. You'll see spans for the +prompt, response, token counts, and tool calls in **Logfire**. + +!!! warning "Common pitfalls" + - **Configure Logfire before importing/calling `ai`**, or spans go to the no-op global tracer and silently + vanish. + - **Flush on exit.** Short-lived scripts must `await logfire.shutdown()` or the batch processor drops + unflushed spans. + - **Content capture.** `recordInputs` / `recordOutputs` default to `true`, so prompts and completions are + captured. Set them to `false` in `experimental_telemetry` to redact sensitive content. + +!!! tip "Next.js" + In Next.js, register OpenTelemetry in `instrumentation.ts` (e.g. with `@vercel/otel`'s `registerOTel`) and + set `OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-us.pydantic.dev` plus + `OTEL_EXPORTER_OTLP_HEADERS='Authorization='`. The `experimental_telemetry` call is identical. + +## Managed prompts + +Author and version prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and +fetch them with the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/): + +```typescript +import { defineTemplateVar } from '@pydantic/logfire-node/vars'; + +const promptVar = defineTemplateVar('prompt__weather', { + default: 'What is the weather in {{city}}? Use the tool.', + templateInputsSchema: { + type: 'object', + properties: { city: { type: 'string' } }, + required: ['city'], + }, +}); + +const resolved = await promptVar.get({ city: 'Paris' }); +// Pass resolved.value as the `prompt` to generateText. +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/agent-frameworks/voltagent.md b/docs/integrations/agent-frameworks/voltagent.md new file mode 100644 index 000000000..ae720e45c --- /dev/null +++ b/docs/integrations/agent-frameworks/voltagent.md @@ -0,0 +1,84 @@ +--- +title: "Pydantic Logfire Integrations: VoltAgent" +description: "Send VoltAgent (TypeScript agent framework) telemetry to Pydantic Logfire using its OpenTelemetry span processors over OTLP." +integration: otel +--- +# VoltAgent + +[VoltAgent](https://voltagent.dev/) is a TypeScript agent framework whose observability is built on +OpenTelemetry. You construct a `VoltAgentObservability` with span processors that wrap a standard OTLP exporter +pointed at **Logfire**, then pass it to `new VoltAgent({ ... })`. + +## Installation + +```bash +npm install @voltagent/core @ai-sdk/openai \ + @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-proto +``` + +## Usage + +```typescript title="agent.ts" +import { VoltAgent, Agent, VoltAgentObservability } from '@voltagent/core'; +import { openai } from '@ai-sdk/openai'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; + +const logfireExporter = new OTLPTraceExporter({ + url: 'https://logfire-us.pydantic.dev/v1/traces', // full path; EU: logfire-eu.pydantic.dev + headers: { Authorization: process.env.LOGFIRE_WRITE_TOKEN! }, // raw token, no "Bearer" +}); + +const observability = new VoltAgentObservability({ + spanProcessors: [new BatchSpanProcessor(logfireExporter)], +}); + +const agent = new Agent({ + name: 'weather-agent', + instructions: 'A helpful assistant that answers questions.', + model: openai('gpt-4o-mini'), +}); + +new VoltAgent({ + agents: { agent }, + observability, +}); + +const res = await agent.generateText('What is a good city to visit in spring?'); +console.log(res.text); +``` + +Set `OPENAI_API_KEY` and `LOGFIRE_WRITE_TOKEN`, then run. The agent run and model call appear in **Logfire**. + +!!! warning "Common pitfalls" + - **Full `/v1/traces` URL.** When you pass `url` explicitly to `OTLPTraceExporter`, it does not append the + path — give the full `/v1/traces` URL. The `Authorization` header value is the raw write token. + - **VoltOps is a separate path.** The built-in VoltOps remote export + (`VOLTAGENT_PUBLIC_KEY` / `VOLTAGENT_SECRET_KEY`) is independent; for **Logfire** use the explicit + `spanProcessors` route shown here. + - Adding `spanProcessors` doesn't disable VoltAgent's local dev console — local debugging stays available + alongside the **Logfire** export. + +## Managed prompts + +Author and version prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and +fetch them with the [Logfire TypeScript SDK](https://pydantic.dev/docs/logfire/typescript-sdk/): + +```typescript +import { defineTemplateVar } from '@pydantic/logfire-node/vars'; + +const instructionsVar = defineTemplateVar('prompt__agent_instructions', { + default: 'A helpful assistant that answers questions about {{role}}.', + templateInputsSchema: { + type: 'object', + properties: { role: { type: 'string' } }, + required: ['role'], + }, +}); + +const resolved = await instructionsVar.get({ role: 'travel' }); +// Use resolved.value as the Agent's `instructions`. +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/index.md b/docs/integrations/index.md index 38f01b88a..c4efbe491 100644 --- a/docs/integrations/index.md +++ b/docs/integrations/index.md @@ -30,6 +30,7 @@ If a package you are using is not listed in this documentation, please let us kn **Logfire** has documented integrations with many technologies, including: - _LLM Clients and AI Frameworks_: Pydantic AI, OpenAI, Anthropic, LangChain, LlamaIndex, Mirascope, LiteLLM, Magentic +- _AI Agent Frameworks_ (Python, TypeScript, Go, Rust, .NET): CrewAI, AutoGen, Google ADK, smolagents, Strands, Agno, Haystack, Instructor, Semantic Kernel, Vercel AI SDK, Mastra, Rig, and more — see [Agent Frameworks](agent-frameworks/index.md) - _Web Frameworks_: FastAPI, Django, Flask, Starlette, AIOHTTP, ASGI, WSGI - _Database Clients_: Psycopg, SQLAlchemy, Asyncpg, PyMongo, MySQL, SQLite3, Redis, BigQuery - _HTTP Clients_: HTTPX, Requests, AIOHTTP @@ -47,6 +48,17 @@ The below table lists these integrations and any corresponding `logfire.instrume | [AIOHTTP](http-clients/aiohttp.md) | HTTP Client | [`logfire.instrument_aiohttp_client()`,][logfire.Logfire.instrument_aiohttp_client] [`logfire.instrument_aiohttp_server()`][logfire.Logfire.instrument_aiohttp_server] | | [Airflow](event-streams/airflow.md) | Task Scheduler | N/A (built in, config needed) | | [Anthropic](llms/anthropic.md) | AI | [`logfire.instrument_anthropic()`][logfire.Logfire.instrument_anthropic] | +| [Agno](llms/agno.md) | AI Agent Framework | N/A (OpenInference instrumentor) | +| [AutoGen](llms/autogen.md) | AI Agent Framework | N/A (OpenInference instrumentor) | +| [CrewAI](llms/crewai.md) | AI Agent Framework | N/A (OpenInference instrumentor) | +| [Google ADK](llms/google-adk.md) | AI Agent Framework | N/A (native OpenTelemetry support) | +| [Haystack](llms/haystack.md) | AI Framework | N/A (OpenInference instrumentor) | +| [Instructor](llms/instructor.md) | AI | [`logfire.instrument_openai()`][logfire.Logfire.instrument_openai] | +| [LangGraph](llms/langgraph.md) | AI Agent Framework | N/A (built-in OpenTelemetry support) | +| [Letta](llms/letta.md) | AI Agent Framework | N/A (OpenTelemetry Collector) | +| [Semantic Kernel](llms/semantic-kernel.md) | AI Agent Framework | N/A (native OpenTelemetry support) | +| [smolagents](llms/smolagents.md) | AI Agent Framework | N/A (OpenInference instrumentor) | +| [Strands Agents](llms/strands.md) | AI Agent Framework | N/A (native OpenTelemetry support) | | [ASGI](web-frameworks/asgi.md) | Web Framework Interface | [`logfire.instrument_asgi()`][logfire.Logfire.instrument_asgi] | | [AWS Lambda](aws-lambda.md) | Cloud Function | [`logfire.instrument_aws_lambda()`][logfire.Logfire.instrument_aws_lambda] | | [Asyncpg](databases/asyncpg.md) | Database | [`logfire.instrument_asyncpg()`][logfire.Logfire.instrument_asyncpg] | diff --git a/docs/integrations/llms/agno.md b/docs/integrations/llms/agno.md new file mode 100644 index 000000000..39bcacb63 --- /dev/null +++ b/docs/integrations/llms/agno.md @@ -0,0 +1,97 @@ +--- +title: "Pydantic Logfire Integrations: Agno" +description: "Instrument Agno agents (formerly Phidata) with Pydantic Logfire using OpenInference. Trace agent runs, tool calls, and model requests." +integration: otel +--- +# Agno + +[Agno](https://docs.agno.com) (formerly **Phidata**) is a framework for building multi-agent systems with +memory, knowledge, and tools. You can send full traces of every agent run, tool call, and model request to +**Logfire**. + +Agno works with **Logfire** via the [OpenInference](https://github.com/Arize-ai/openinference) instrumentor. +Because [`logfire.configure()`][logfire.configure] sets up the global OpenTelemetry tracer provider, the +instrumentor's spans are exported to **Logfire** automatically. + +## Installation + +```bash +pip install logfire agno openai openinference-instrumentation-agno +``` + +## Usage + +Call [`logfire.configure()`][logfire.configure] and then `AgnoInstrumentor().instrument()` **before** creating +and running your agent: + +```python skip-run="true" skip-reason="external-connection" +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from agno.tools.duckduckgo import DuckDuckGoTools +from openinference.instrumentation.agno import AgnoInstrumentor + +import logfire + +logfire.configure() +AgnoInstrumentor().instrument() + +agent = Agent( + name='Web Search Agent', + model=OpenAIChat(id='gpt-4o'), + tools=[DuckDuckGoTools()], + instructions='Answer questions concisely, using web search when helpful.', + markdown=True, +) +agent.print_response('What is Pydantic Logfire, in one sentence?') +``` + +You'll see a trace in **Logfire** with the agent run at the top and the underlying tool calls and model +requests nested beneath it. + +!!! note + Agno's own docs show configuring a raw OTLP exporter. With **Logfire** you don't need to — just call + [`logfire.configure()`][logfire.configure], which reads your `LOGFIRE_TOKEN` (or `logfire auth` + credentials) and sets up export and region handling for you. + +## Managed prompts + +Keep your agents' instructions in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class InstructionInputs(BaseModel): + audience: str + + +instructions_var = logfire.template_var( + name='prompt__assistant_instructions', + type=str, + default='Answer questions concisely.', + inputs_type=InstructionInputs, +) + +with instructions_var.get(InstructionInputs(audience='developers'), label='production') as resolved: + instructions = resolved.value + +agent = Agent( + name='Assistant', + model=OpenAIChat(id='gpt-4o'), + instructions=instructions, +) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/autogen.md b/docs/integrations/llms/autogen.md new file mode 100644 index 000000000..b4dbe4a9f --- /dev/null +++ b/docs/integrations/llms/autogen.md @@ -0,0 +1,108 @@ +--- +title: "Pydantic Logfire Integrations: AutoGen" +description: "Instrument Microsoft AutoGen agents with Pydantic Logfire using OpenInference. Trace agent runs, tool calls, and LLM messages." +integration: otel +--- +# AutoGen + +[AutoGen](https://microsoft.github.io/autogen/) is Microsoft's framework for building multi-agent applications. +You can send full traces of agent runs, tool calls, and LLM messages to **Logfire**. + +AutoGen works with **Logfire** via the [OpenInference](https://github.com/Arize-ai/openinference) instrumentor. +Because [`logfire.configure()`][logfire.configure] sets up the global OpenTelemetry tracer provider, the +instrumentor's spans are exported to **Logfire** automatically. + +!!! note "AutoGen vs. AG2" + "AutoGen" split into two projects. This page covers **Microsoft AutoGen** (the `autogen-agentchat` / + `autogen-core` v0.4+ rewrite), which is the most widely used branch. The community fork + [AG2](https://docs.ag2.ai/) (package `ag2`) has its own, different API and its own OpenTelemetry support — + make sure the instrumentor you install matches the framework you use. + +## Installation + +Install `logfire`, Microsoft AutoGen, and the matching OpenInference instrumentor: + +```bash +pip install logfire "autogen-agentchat>=0.5.0" "autogen-ext[openai]" openinference-instrumentation-autogen-agentchat +``` + +## Usage + +```python skip-run="true" skip-reason="external-connection" +import asyncio + +from autogen_agentchat.agents import AssistantAgent +from autogen_ext.models.openai import OpenAIChatCompletionClient +from openinference.instrumentation.autogen_agentchat import AutogenAgentChatInstrumentor + +import logfire + +logfire.configure() +AutogenAgentChatInstrumentor().instrument() + + +async def main(): + model_client = OpenAIChatCompletionClient(model='gpt-4o') # needs OPENAI_API_KEY + agent = AssistantAgent( + name='assistant', + model_client=model_client, + system_message='You are a helpful assistant.', + ) + result = await agent.run(task='Say hello to Logfire in one short sentence.') + print(result.messages[-1].content) + await model_client.close() + + +asyncio.run(main()) +``` + +You'll see a trace in **Logfire** with the agent run at the top and the underlying LLM messages and any tool +calls nested beneath it. + +!!! warning + The `openinference-instrumentation-autogen-agentchat` package only instruments **Microsoft AutoGen**. + For the AG2 fork, use AG2's own OpenTelemetry support (`pip install "ag2[openai,tracing]"`) instead — the + `-agentchat` instrumentor will produce no spans for it. + +## Managed prompts + +Keep your agents' system messages in [Prompt Management](../../reference/advanced/prompt-management/index.md) +and fetch them at runtime with the Logfire SDK: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from autogen_agentchat.agents import AssistantAgent +from autogen_ext.models.openai import OpenAIChatCompletionClient +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class SystemInputs(BaseModel): + tone: str + + +system_var = logfire.template_var( + name='prompt__assistant_system', + type=str, + default='You are a helpful assistant.', + inputs_type=SystemInputs, +) + +with system_var.get(SystemInputs(tone='friendly'), label='production') as resolved: + system_message = resolved.value + +agent = AssistantAgent( + name='assistant', + model_client=OpenAIChatCompletionClient(model='gpt-4o'), + system_message=system_message, +) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/crewai.md b/docs/integrations/llms/crewai.md new file mode 100644 index 000000000..033bd7bca --- /dev/null +++ b/docs/integrations/llms/crewai.md @@ -0,0 +1,118 @@ +--- +title: "Pydantic Logfire Integrations: CrewAI" +description: "Instrument CrewAI multi-agent crews with Pydantic Logfire using OpenInference. Trace every agent, task, tool call, and LLM request." +integration: otel +--- +# CrewAI + +[CrewAI](https://docs.crewai.com/) orchestrates role-playing autonomous agents into collaborating "crews". +You can send full traces of every agent, task, tool call, and LLM request to **Logfire**. + +CrewAI doesn't have a dedicated `logfire.instrument_crewai()` method, but it works out of the box with the +[OpenInference](https://github.com/Arize-ai/openinference) CrewAI instrumentor. This is possible because +[`logfire.configure()`][logfire.configure] sets up the global OpenTelemetry tracer provider, and the +OpenInference instrumentor exports its spans to that provider — so they end up in **Logfire** automatically. + +## Installation + +Install `logfire`, `crewai`, and the OpenInference CrewAI instrumentor: + +```bash +pip install logfire crewai openinference-instrumentation-crewai +``` + +## Usage + +Call [`logfire.configure()`][logfire.configure] and then `CrewAIInstrumentor().instrument()` **before** you +build and run your crew: + +```python skip-run="true" skip-reason="external-connection" +import os + +from crewai import Agent, Crew, Process, Task +from openinference.instrumentation.crewai import CrewAIInstrumentor + +import logfire + +os.environ['OPENAI_API_KEY'] = 'your-openai-key' + +logfire.configure() +CrewAIInstrumentor().instrument() + +researcher = Agent( + role='Researcher', + goal='Explain a topic clearly and concisely', + backstory='You are a knowledgeable analyst who values brevity.', + llm='openai/gpt-4o-mini', +) + +task = Task( + description='Summarize what OpenTelemetry is in two sentences.', + expected_output='A two-sentence summary.', + agent=researcher, +) + +crew = Crew(agents=[researcher], tasks=[task], process=Process.sequential) +print(crew.kickoff()) +``` + +You'll see a nested trace in **Logfire** with the crew kickoff at the top, a span per task and agent, and the +underlying LLM and tool calls beneath them. + +!!! tip + CrewAI uses [LiteLLM](./litellm.md)-style model strings, so the provider-prefixed form + `'openai/gpt-4o-mini'` is the safest way to specify a model. + +!!! note + If you also call [`logfire.instrument_openai()`][logfire.Logfire.instrument_openai] (or another LLM + instrumentation), you may get duplicate LLM spans. Pass + `CrewAIInstrumentor().instrument(create_llm_spans=False)` to let your dedicated LLM instrumentation own + those spans instead. + +## Managed prompts + +You can keep your agents' prompts (roles, goals, backstories, and task descriptions) in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at runtime with the +Logfire SDK, so non-engineers can iterate on them without redeploying. + +Install the variables extra: + +```bash +pip install 'logfire[variables]' +``` + +Then fetch a versioned prompt and pass it into your agent: + +```python skip="true" +from crewai import Agent +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class BackstoryInputs(BaseModel): + domain: str + + +backstory_var = logfire.template_var( + name='prompt__researcher_backstory', + type=str, + default='You are a knowledgeable analyst who values brevity.', + inputs_type=BackstoryInputs, +) + +with backstory_var.get(BackstoryInputs(domain='observability'), label='production') as resolved: + backstory = resolved.value + +researcher = Agent( + role='Researcher', + goal='Explain a topic clearly and concisely', + backstory=backstory, + llm='openai/gpt-4o-mini', +) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow, including promoting versions and rollout targeting. diff --git a/docs/integrations/llms/google-adk.md b/docs/integrations/llms/google-adk.md new file mode 100644 index 000000000..a9ea2b211 --- /dev/null +++ b/docs/integrations/llms/google-adk.md @@ -0,0 +1,133 @@ +--- +title: "Pydantic Logfire Integrations: Google ADK" +description: "Send Google Agent Development Kit (ADK) telemetry to Pydantic Logfire. ADK's native OpenTelemetry spans flow to Logfire with no extra instrumentor." +integration: otel +--- +# Google ADK + +[Google ADK](https://adk.dev/) (the Agent Development Kit, `google-adk`) is Google's framework for building and +deploying agents. ADK has **native OpenTelemetry instrumentation** and resolves its tracer from the *global* +tracer provider — so once [`logfire.configure()`][logfire.configure] has set **Logfire** as the global +provider, ADK's agent, LLM, and tool spans flow to **Logfire** automatically, with no instrumentor. + +## Installation + +```bash +pip install logfire google-adk +``` + +## Usage + +Call [`logfire.configure()`][logfire.configure] **before** you run the agent. That's the entire integration: + +```python skip-run="true" skip-reason="external-connection" +import asyncio + +from google.adk.agents import Agent # Agent is an alias of LlmAgent +from google.adk.runners import InMemoryRunner +from google.genai import types + +import logfire + +# Sets the global OTel tracer provider — ADK's native spans flow here automatically. +logfire.configure(service_name='adk-demo') + + +def get_weather(city: str) -> dict: + """Return the current weather for a city.""" + return {'status': 'success', 'report': f"It's sunny and 25C in {city}."} + + +agent = Agent( + name='weather_agent', + model='gemini-2.5-flash', + instruction='You are a helpful assistant. Use tools to answer questions.', + tools=[get_weather], +) + + +async def main(): + runner = InMemoryRunner(agent=agent, app_name='weather_app') + session = await runner.session_service.create_session(app_name='weather_app', user_id='user1') + message = types.Content(role='user', parts=[types.Part(text='Weather in Paris?')]) + async for event in runner.run_async(user_id='user1', session_id=session.id, new_message=message): + if event.is_final_response(): + print(event.content.parts[0].text) + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +You'll see a trace in **Logfire** with the agent run, the underlying LLM (Gemini) call, and the `get_weather` +tool call nested as a timeline. + +!!! note "Capturing message content" + ADK attaches prompt and response text to spans only when you opt in. Set these environment variables + before running for the richest traces: + + ```python skip="true" + import os + + os.environ['OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT'] = 'true' + os.environ['OTEL_SEMCONV_STABILITY_OPT_IN'] = 'gen_ai_latest_experimental' + ``` + +!!! tip "Optional: OpenInference attributes" + For richer GenAI semantic-convention attributes you can additionally enable the OpenInference layer. It + composes with **Logfire** because it also writes to the global provider: + + ```bash + pip install openinference-instrumentation-google-adk + ``` + + ```python skip="true" + from openinference.instrumentation.google_adk import GoogleADKInstrumentor + + GoogleADKInstrumentor().instrument() # after logfire.configure() + ``` + + The native spans are usually sufficient on their own — add this only if you want the OpenInference schema. + +!!! warning + `gemini-*` models need credentials: set `GOOGLE_API_KEY` (AI Studio) or `GOOGLE_GENAI_USE_VERTEXAI=true` + with Vertex AI configured. You don't need `OTEL_EXPORTER_OTLP_*` env vars — `logfire.configure()` sets up + the exporter and region for you. + +## Managed prompts + +Keep your agents' instructions in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from google.adk.agents import Agent +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class InstructionInputs(BaseModel): + persona: str + + +instruction_var = logfire.template_var( + name='prompt__weather_agent_instruction', + type=str, + default='You are a helpful assistant. Use tools to answer questions.', + inputs_type=InstructionInputs, +) + +with instruction_var.get(InstructionInputs(persona='a friendly meteorologist'), label='production') as resolved: + instruction = resolved.value + +agent = Agent(name='weather_agent', model='gemini-2.5-flash', instruction=instruction) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/haystack.md b/docs/integrations/llms/haystack.md new file mode 100644 index 000000000..9f917439e --- /dev/null +++ b/docs/integrations/llms/haystack.md @@ -0,0 +1,98 @@ +--- +title: "Pydantic Logfire Integrations: Haystack" +description: "Instrument deepset Haystack pipelines with Pydantic Logfire using OpenInference. Trace each component, generator, and LLM call." +integration: otel +--- +# Haystack + +[Haystack](https://haystack.deepset.ai/) is deepset's framework for building LLM applications and pipelines +(package `haystack-ai`). You can send full traces of every pipeline component and LLM call to **Logfire**. + +Haystack works with **Logfire** via the [OpenInference](https://github.com/Arize-ai/openinference) +instrumentor. Because [`logfire.configure()`][logfire.configure] sets up the global OpenTelemetry tracer +provider, the instrumentor's spans are exported to **Logfire** automatically. + +## Installation + +```bash +pip install logfire haystack-ai openinference-instrumentation-haystack +``` + +## Usage + +Haystack omits prompt and response content from its spans by default. Set +`HAYSTACK_CONTENT_TRACING_ENABLED=true` (before your app starts) to capture it, then call +[`logfire.configure()`][logfire.configure] and `HaystackInstrumentor().instrument()`: + +```python skip-run="true" skip-reason="external-connection" +import os + +os.environ['HAYSTACK_CONTENT_TRACING_ENABLED'] = 'true' + +from haystack import Pipeline +from haystack.components.builders import ChatPromptBuilder +from haystack.components.generators.chat import OpenAIChatGenerator +from haystack.dataclasses import ChatMessage +from openinference.instrumentation.haystack import HaystackInstrumentor + +import logfire + +logfire.configure() +HaystackInstrumentor().instrument() + +pipeline = Pipeline() +pipeline.add_component('prompt_builder', ChatPromptBuilder()) +pipeline.add_component('llm', OpenAIChatGenerator(model='gpt-4o-mini')) +pipeline.connect('prompt_builder.prompt', 'llm.messages') + +result = pipeline.run( + { + 'prompt_builder': { + 'template': [ChatMessage.from_user('Tell me a one-line fun fact about {{topic}}.')], + 'template_variables': {'topic': 'the Roman Empire'}, + } + } +) +print(result['llm']['replies'][0].text) +``` + +You'll see a trace in **Logfire** with the pipeline run at the top and a span for each component, including the +LLM request and response. + +## Managed prompts + +Keep your pipeline's prompt templates in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from haystack.dataclasses import ChatMessage +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class FactInputs(BaseModel): + topic: str + + +prompt_var = logfire.template_var( + name='prompt__fun_fact', + type=str, + default='Tell me a one-line fun fact about {{topic}}.', + inputs_type=FactInputs, +) + +with prompt_var.get(FactInputs(topic='the Roman Empire'), label='production') as resolved: + user_message = ChatMessage.from_user(resolved.value) + +# Pass `user_message` straight to your generator / pipeline. +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/instructor.md b/docs/integrations/llms/instructor.md new file mode 100644 index 000000000..659d1b996 --- /dev/null +++ b/docs/integrations/llms/instructor.md @@ -0,0 +1,112 @@ +--- +title: "Pydantic Logfire Integrations: Instructor" +description: "Instrument Instructor structured outputs with Pydantic Logfire. Trace each LLM request and Pydantic validation of the response model." +integration: logfire +--- +# Instructor + +[Instructor](https://python.useinstructor.com/) gives you validated, structured outputs from LLMs by patching +an underlying client (such as OpenAI) and adding a `response_model`. Because it wraps a normal client, the +simplest way to send data to **Logfire** is to instrument that client directly — which is exactly what +[Instructor's own docs](https://python.useinstructor.com/blog/2024/05/01/instructor-logfire/) recommend. + +## Installation + +```bash +pip install logfire instructor openai +``` + +## Usage + +Call [`logfire.instrument_openai()`][logfire.Logfire.instrument_openai] to trace the LLM requests, and +optionally [`logfire.instrument_pydantic()`][logfire.Logfire.instrument_pydantic] to record validation of the +`response_model`: + +```python skip-run="true" skip-reason="external-connection" +import instructor +from openai import OpenAI +from pydantic import BaseModel + +import logfire + +logfire.configure() +logfire.instrument_openai() # trace the OpenAI client that Instructor wraps +logfire.instrument_pydantic() # optional: record response_model validation + +client = instructor.from_openai(OpenAI()) + + +class UserInfo(BaseModel): + name: str + age: int + + +user = client.chat.completions.create( + model='gpt-4o-mini', + response_model=UserInfo, + messages=[{'role': 'user', 'content': 'John Doe is 30 years old.'}], +) +print(user) +#> name='John Doe' age=30 +``` + +You'll see the LLM conversation in the **Logfire** UI along with the structured output and, if you enabled it, +the **Pydantic** validation of the `UserInfo` model. + +!!! tip + Instrument the **base** OpenAI client, not the Instructor wrapper. Calling + [`logfire.instrument_openai()`][logfire.Logfire.instrument_openai] with no argument instruments all OpenAI + clients globally, so it works even when Instructor builds the client internally (e.g. via + `instructor.from_provider('openai/gpt-4o-mini')`). + +## Managed prompts + +Keep your prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at +runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +import instructor +from openai import OpenAI +from pydantic import BaseModel + +import logfire + +logfire.configure() +logfire.instrument_openai() + + +class ExtractInputs(BaseModel): + text: str + + +prompt_var = logfire.template_var( + name='prompt__extract_user', + type=str, + default='Extract the user info from: {{text}}', + inputs_type=ExtractInputs, +) + +with prompt_var.get(ExtractInputs(text='John Doe is 30 years old.'), label='production') as resolved: + content = resolved.value + + +class UserInfo(BaseModel): + name: str + age: int + + +client = instructor.from_openai(OpenAI()) +user = client.chat.completions.create( + model='gpt-4o-mini', + response_model=UserInfo, + messages=[{'role': 'user', 'content': content}], +) +print(user) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/langgraph.md b/docs/integrations/llms/langgraph.md new file mode 100644 index 000000000..2f8317a1f --- /dev/null +++ b/docs/integrations/llms/langgraph.md @@ -0,0 +1,112 @@ +--- +title: "Pydantic Logfire Integrations: LangGraph" +description: "Send LangGraph telemetry to Pydantic Logfire via the LangSmith OpenTelemetry bridge. Trace each node, tool call, and LLM request in your graph." +integration: otel +--- +# LangGraph + +[LangGraph](https://www.langchain.com/langgraph) builds stateful, multi-step agents as graphs. It emits +OpenTelemetry traces through the **LangSmith SDK** (bundled with `langchain`). When you call +[`logfire.configure()`][logfire.configure], **Logfire** installs the global OpenTelemetry tracer provider, and +the LangSmith tracer detects that provider and uses it — so your graph's spans flow straight into **Logfire** +with no exporter, endpoint, or API key configuration. + +!!! note + This is the same mechanism described on the [LangChain](./langchain.md) page. This page focuses on building + a graph with `StateGraph`; the env vars and setup are identical. + +## Installation + +```bash +pip install logfire langchain langgraph langchain-openai +``` + +## Usage + +Set the three `LANGSMITH_*` environment variables **before importing** `langchain`/`langgraph`, then call +[`logfire.configure()`][logfire.configure]: + +```python skip-run="true" skip-reason="external-connection" +import os + +# Must be set before importing langchain/langgraph +os.environ['LANGSMITH_OTEL_ENABLED'] = 'true' +os.environ['LANGSMITH_OTEL_ONLY'] = 'true' # OTel only; no LangSmith backend, no API key needed +os.environ['LANGSMITH_TRACING'] = 'true' + +from langchain_openai import ChatOpenAI +from langgraph.graph import END, START, StateGraph +from typing_extensions import TypedDict + +import logfire + +logfire.configure() # sets the global OTel tracer provider that LangSmith detects + + +class State(TypedDict): + topic: str + joke: str + + +llm = ChatOpenAI(model='gpt-5-mini') + + +def tell_joke(state: State) -> dict: + response = llm.invoke(f"Tell me a joke about {state['topic']}") + return {'joke': response.content} + + +builder = StateGraph(State) +builder.add_node('tell_joke', tell_joke) +builder.add_edge(START, 'tell_joke') +builder.add_edge('tell_joke', END) +graph = builder.compile() + +print(graph.invoke({'topic': 'otters'})['joke']) +``` + +You'll see a trace in **Logfire** with a span for the graph run, a span per node, and the underlying LLM and +tool calls nested beneath them. + +!!! tip + `LANGSMITH_OTEL_ONLY=true` stops LangSmith from also sending traces to its own backend, so you get + **Logfire** only and don't need a `LANGSMITH_API_KEY`. + +## Managed prompts + +Keep your nodes' prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch +them at runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class JokeInputs(BaseModel): + topic: str + + +prompt_var = logfire.template_var( + name='prompt__joke', + type=str, + default='Tell me a joke about {{topic}}', + inputs_type=JokeInputs, +) + + +def tell_joke(state): + with prompt_var.get(JokeInputs(topic=state['topic']), label='production') as resolved: + prompt = resolved.value + response = llm.invoke(prompt) # llm defined as in the example above + return {'joke': response.content} +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/letta.md b/docs/integrations/llms/letta.md new file mode 100644 index 000000000..254dc78be --- /dev/null +++ b/docs/integrations/llms/letta.md @@ -0,0 +1,144 @@ +--- +title: "Pydantic Logfire Integrations: Letta" +description: "Send Letta (formerly MemGPT) server telemetry to Pydantic Logfire via an OpenTelemetry Collector." +integration: otel +--- +# Letta + +[Letta](https://docs.letta.com/) (formerly **MemGPT**) is a framework and server for building stateful agents +with long-term memory. The Letta **server** has native OTLP export built in, but it speaks **OTLP/gRPC only** +and exposes a single endpoint env var with no way to set auth headers. **Logfire**'s direct ingest is +OTLP/HTTP, so the robust path is to run a small [OpenTelemetry Collector](../../how-to-guides/otel-collector/otel-collector-overview.md) +that receives Letta's gRPC traces and forwards them to **Logfire** over HTTP with your write token. + +```mermaid +flowchart LR + L[Letta server] -- OTLP/gRPC :4317 --> C[OTel Collector] + C -- OTLP/HTTP + Authorization --> LF[Logfire] +``` + +## Installation + +```bash +# Letta server + Python client +pip install letta letta-client +# The collector runs as a container: +# docker pull otel/opentelemetry-collector-contrib +``` + +## Collector configuration + +Create `otel-collector-config.yaml`: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 # Letta exports here + +exporters: + otlphttp/logfire: + endpoint: 'https://logfire-us.pydantic.dev' # use logfire-eu.pydantic.dev for the EU region + headers: + Authorization: '${LOGFIRE_WRITE_TOKEN}' + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [otlphttp/logfire] +``` + +## Running it + +```bash +# 1. Start the collector (forwards to Logfire) +export LOGFIRE_WRITE_TOKEN="your-logfire-write-token" +docker run -p 4317:4317 \ + -v "$PWD/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml" \ + -e LOGFIRE_WRITE_TOKEN \ + otel/opentelemetry-collector-contrib + +# 2. Start the Letta server pointed at the collector +export LETTA_OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" +letta server +``` + +Then talk to the server with the client: + +```python skip-run="true" skip-reason="external-connection" +from letta_client import Letta + +client = Letta(base_url='http://localhost:8283') + +agent = client.agents.create( + model='openai/gpt-4o-mini', + embedding='openai/text-embedding-3-small', + memory_blocks=[ + {'label': 'human', 'value': "The user's name is Will."}, + {'label': 'persona', 'value': 'You are a helpful assistant.'}, + ], +) + +response = client.agents.messages.create( + agent_id=agent.id, + messages=[{'role': 'user', 'content': 'Hello! Remember my name.'}], +) +for message in response.messages: + print(message) +``` + +Each request to the Letta server now produces traces (including the underlying LLM provider requests) that flow +through the collector into **Logfire**. + +!!! warning "Important details" + - **Telemetry is server-side.** Tracing is emitted by the `letta server` process, not by `letta-client`. + Set `LETTA_OTEL_EXPORTER_OTLP_ENDPOINT` where the server runs. + - **gRPC only.** `LETTA_OTEL_EXPORTER_OTLP_ENDPOINT` must point at a gRPC OTLP receiver (port `4317`), not + an HTTP `/v1/traces` URL. You can't point it directly at **Logfire** — hence the collector. + - **Region.** Match `logfire-us.pydantic.dev` or `logfire-eu.pydantic.dev` to your project's region. + +## Managed prompts + +You can keep an agent's persona / system text in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch it with the Logfire SDK +before creating the agent: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from letta_client import Letta +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class PersonaInputs(BaseModel): + tone: str + + +persona_var = logfire.template_var( + name='prompt__letta_persona', + type=str, + default='You are a helpful assistant.', + inputs_type=PersonaInputs, +) + +with persona_var.get(PersonaInputs(tone='warm'), label='production') as resolved: + persona = resolved.value + +client = Letta(base_url='http://localhost:8283') +agent = client.agents.create( + model='openai/gpt-4o-mini', + embedding='openai/text-embedding-3-small', + memory_blocks=[{'label': 'persona', 'value': persona}], +) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/semantic-kernel.md b/docs/integrations/llms/semantic-kernel.md new file mode 100644 index 000000000..1cc58baf8 --- /dev/null +++ b/docs/integrations/llms/semantic-kernel.md @@ -0,0 +1,114 @@ +--- +title: "Pydantic Logfire Integrations: Semantic Kernel (Python)" +description: "Send Microsoft Semantic Kernel (Python) telemetry to Pydantic Logfire. SK's native OpenTelemetry spans flow to Logfire once you enable its GenAI diagnostics." +integration: otel +--- +# Semantic Kernel (Python) + +[Microsoft Semantic Kernel](https://learn.microsoft.com/en-us/semantic-kernel/) for Python emits native +OpenTelemetry spans, metrics, and logs to the **global** OpenTelemetry providers. Because +[`logfire.configure()`][logfire.configure] sets those global providers, SK's telemetry flows to **Logfire** +automatically once you enable SK's experimental GenAI diagnostics with an environment variable. + +!!! note + Unlike the [.NET version](../agent-frameworks/semantic-kernel-dotnet.md), Semantic Kernel for Python does + **not** require a source allowlist — it uses the global tracer/meter directly. So + [`logfire.configure()`][logfire.configure] plus the diagnostics env var is the whole integration. + +## Installation + +```bash +pip install logfire semantic-kernel +``` + +## Usage + +Set `SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true` (to record prompts and +completions; use the non-`SENSITIVE` variant for metadata only) and call +[`logfire.configure()`][logfire.configure]: + +```python skip-run="true" skip-reason="external-connection" +import asyncio +import os + +from semantic_kernel import Kernel +from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion +from semantic_kernel.functions import kernel_function + +import logfire + +# Emit gen_ai spans including prompts/completions. Set before configuring. +os.environ['SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE'] = 'true' + +# Sets the global OTel tracer + meter provider exporting to Logfire. +logfire.configure(service_name='semantic-kernel-agent') + + +class WeatherPlugin: + @kernel_function(description='Get the weather for a city') + def get_weather(self, city: str) -> str: + return f'The weather in {city} is sunny, 21C.' + + +async def main() -> None: + kernel = Kernel() + kernel.add_service(OpenAIChatCompletion(ai_model_id='gpt-4o-mini')) # uses OPENAI_API_KEY + kernel.add_plugin(WeatherPlugin(), plugin_name='weather') + + answer = await kernel.invoke_prompt("What's the weather in Paris? Use the weather plugin.") + print(answer) + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +You'll see SK's `chat.completions` spans (with `gen_ai.*` attributes), token-usage metrics, and +function-invocation spans in **Logfire**. + +!!! warning "Common pitfalls" + - **No diagnostics, no `gen_ai` spans.** Without the `SEMANTICKERNEL_EXPERIMENTAL_GENAI_*` env var, you get + function/kernel spans but no AI-connector spans. Set it before the kernel runs. Use the `_SENSITIVE` + variant only when you want prompts/completions recorded. + - **Don't replace the global provider.** Don't call `set_tracer_provider()` / `set_meter_provider()` + yourself after [`logfire.configure()`][logfire.configure] — that would override **Logfire**'s exporter. + +## Managed prompts + +Keep your prompts in [Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at +runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from pydantic import BaseModel +from semantic_kernel import Kernel + +import logfire + +logfire.configure() + + +class WeatherInputs(BaseModel): + city: str + + +prompt_var = logfire.template_var( + name='prompt__weather', + type=str, + default="What's the weather in {{city}}? Use the weather plugin.", + inputs_type=WeatherInputs, +) + +with prompt_var.get(WeatherInputs(city='Paris'), label='production') as resolved: + prompt = resolved.value + +kernel = Kernel() +# ... add services/plugins ... +answer = await kernel.invoke_prompt(prompt) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/smolagents.md b/docs/integrations/llms/smolagents.md new file mode 100644 index 000000000..732dbb128 --- /dev/null +++ b/docs/integrations/llms/smolagents.md @@ -0,0 +1,92 @@ +--- +title: "Pydantic Logfire Integrations: smolagents" +description: "Instrument Hugging Face smolagents with Pydantic Logfire using OpenInference. Trace agent steps, code execution, tool calls, and LLM requests." +integration: otel +--- +# smolagents + +[smolagents](https://huggingface.co/docs/smolagents) is Hugging Face's minimal library for building agents that +"think in code". You can send full traces of every agent step, tool call, and LLM request to **Logfire**. + +smolagents works with **Logfire** via the [OpenInference](https://github.com/Arize-ai/openinference) +instrumentor. Because [`logfire.configure()`][logfire.configure] sets up the global OpenTelemetry tracer +provider, the instrumentor's spans are exported to **Logfire** automatically. + +## Installation + +```bash +pip install logfire smolagents openinference-instrumentation-smolagents +``` + +## Usage + +Call [`logfire.configure()`][logfire.configure] and then `SmolagentsInstrumentor().instrument()` **before** +building and running your agent: + +```python skip-run="true" skip-reason="external-connection" +import os + +from openinference.instrumentation.smolagents import SmolagentsInstrumentor +from smolagents import CodeAgent, OpenAIServerModel, WebSearchTool + +import logfire + +logfire.configure() +SmolagentsInstrumentor().instrument() + +model = OpenAIServerModel( + model_id='gpt-4o', + api_base='https://api.openai.com/v1', + api_key=os.environ['OPENAI_API_KEY'], +) +agent = CodeAgent(tools=[WebSearchTool()], model=model) +agent.run('What is the current population of Tokyo? Search the web.') +``` + +You'll see a trace in **Logfire** with the agent run at the top and a span for each step, including the code it +generated, the tools it called, and the underlying LLM requests. + +!!! warning + Don't pass a `tracer_provider` argument to `instrument()` — omit it so the instrumentor uses the global + provider that [`logfire.configure()`][logfire.configure] set up. Passing your own provider would bypass + **Logfire**. + +## Managed prompts + +Keep the user-facing instructions you send to your agent in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from smolagents import CodeAgent, OpenAIServerModel, WebSearchTool +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class TaskInputs(BaseModel): + city: str + + +task_var = logfire.template_var( + name='prompt__population_task', + type=str, + default='What is the current population of {{city}}? Search the web.', + inputs_type=TaskInputs, +) + +with task_var.get(TaskInputs(city='Tokyo'), label='production') as resolved: + task = resolved.value + +model = OpenAIServerModel(model_id='gpt-4o') +agent = CodeAgent(tools=[WebSearchTool()], model=model) +agent.run(task) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/integrations/llms/strands.md b/docs/integrations/llms/strands.md new file mode 100644 index 000000000..3659c2bc6 --- /dev/null +++ b/docs/integrations/llms/strands.md @@ -0,0 +1,102 @@ +--- +title: "Pydantic Logfire Integrations: Strands Agents" +description: "Send AWS Strands Agents telemetry to Pydantic Logfire. Strands' native OpenTelemetry spans flow to Logfire with no extra exporter." +integration: otel +--- +# Strands Agents + +[Strands Agents](https://strandsagents.com/) (the `strands-agents` package, by AWS) has **native OpenTelemetry +tracing**. It emits spans through the OTel *global* tracer provider, so once +[`logfire.configure()`][logfire.configure] has set **Logfire** as the global provider, Strands traces flow to +**Logfire** automatically — no extra exporter needed. + +## Installation + +```bash +pip install logfire strands-agents strands-agents-tools +``` + +## Usage + +Call [`logfire.configure()`][logfire.configure] **before** you construct your `Agent`: + +```python skip-run="true" skip-reason="external-connection" +import os + +from strands import Agent, tool + +import logfire + +# Logfire registers itself as the global OTel tracer provider. +logfire.configure() + +# Opt in to the latest GenAI semantic conventions for rich prompt/tool/token rendering. +os.environ['OTEL_SEMCONV_STABILITY_OPT_IN'] = 'gen_ai_latest_experimental' + + +@tool +def weather(city: str) -> str: + """Get the current weather for a city.""" + return f"It's sunny in {city}." + + +agent = Agent( + tools=[weather], + # trace_attributes are attached to every span this agent produces. + trace_attributes={'session.id': 'demo-1', 'user.id': 'you@example.com'}, +) + +result = agent("What's the weather in Lisbon?") +print(result) +``` + +You'll see a trace in **Logfire** with the agent invocation, the model (LLM) call, and the `weather` tool call +as a nested timeline. + +!!! warning + Do **not** call `StrandsTelemetry().setup_otlp_exporter()` when using **Logfire**. Per the Strands docs, + you should skip `StrandsTelemetry` when a global tracer provider is already configured — your existing + OpenTelemetry setup is used automatically. Calling `setup_otlp_exporter()` would register a competing + provider and send traces to a separate endpoint instead of **Logfire**. + +!!! note "Model provider" + A bare `Agent(...)` defaults to **Amazon Bedrock** (and needs AWS credentials). To run without AWS, pass + another model, e.g. `Agent(model='gpt-4o', ...)` with `OPENAI_API_KEY` set. + +## Managed prompts + +Keep your agents' system prompts in +[Prompt Management](../../reference/advanced/prompt-management/index.md) and fetch them at runtime: + +```bash +pip install 'logfire[variables]' +``` + +```python skip="true" +from strands import Agent +from pydantic import BaseModel + +import logfire + +logfire.configure() + + +class SystemInputs(BaseModel): + role: str + + +system_var = logfire.template_var( + name='prompt__strands_system', + type=str, + default='You are a helpful assistant.', + inputs_type=SystemInputs, +) + +with system_var.get(SystemInputs(role='a travel assistant'), label='production') as resolved: + system_prompt = resolved.value + +agent = Agent(system_prompt=system_prompt) +``` + +See [Use Prompts in Your Application](../../reference/advanced/prompt-management/application.md) for the full +workflow. diff --git a/docs/languages.md b/docs/languages.md index 430a6f415..1435ab285 100644 --- a/docs/languages.md +++ b/docs/languages.md @@ -14,3 +14,6 @@ These SDKs offer a streamlined developer experience. You can check our [Alternative Clients](how-to-guides/alternative-clients.md) section to see how you can send data to Logfire from other languages. + +Building with an AI agent framework? See [Agent Frameworks](integrations/agent-frameworks/index.md) for +guides on sending telemetry to Logfire from popular frameworks in Python, TypeScript, Go, Rust, and .NET. diff --git a/docs/nav.json b/docs/nav.json index 504dc1e7f..b754eceb1 100644 --- a/docs/nav.json +++ b/docs/nav.json @@ -69,7 +69,35 @@ { "label": "Claude Agent SDK", "link": "/logfire/integrations/llms/claude-agent-sdk/" }, { "label": "LLamaIndex", "link": "/logfire/integrations/llms/llamaindex/" }, { "label": "Mirascope", "link": "/logfire/integrations/llms/mirascope/" }, - { "label": "Magentic", "link": "/logfire/integrations/llms/magentic/" } + { "label": "Magentic", "link": "/logfire/integrations/llms/magentic/" }, + { "label": "CrewAI", "link": "/logfire/integrations/llms/crewai/" }, + { "label": "AutoGen", "link": "/logfire/integrations/llms/autogen/" }, + { "label": "Google ADK", "link": "/logfire/integrations/llms/google-adk/" }, + { "label": "smolagents", "link": "/logfire/integrations/llms/smolagents/" }, + { "label": "Strands Agents", "link": "/logfire/integrations/llms/strands/" }, + { "label": "Agno", "link": "/logfire/integrations/llms/agno/" }, + { "label": "Haystack", "link": "/logfire/integrations/llms/haystack/" }, + { "label": "LangGraph", "link": "/logfire/integrations/llms/langgraph/" }, + { "label": "Instructor", "link": "/logfire/integrations/llms/instructor/" }, + { "label": "Semantic Kernel", "link": "/logfire/integrations/llms/semantic-kernel/" }, + { "label": "Letta", "link": "/logfire/integrations/llms/letta/" } + ] + }, + { + "label": "Agent Frameworks (JS, Go, Rust, .NET)", + "items": [ + { "label": "Overview", "link": "/logfire/integrations/agent-frameworks/" }, + { "label": "Vercel AI SDK", "link": "/logfire/integrations/agent-frameworks/vercel-ai-sdk/" }, + { "label": "Mastra", "link": "/logfire/integrations/agent-frameworks/mastra/" }, + { "label": "LangChain.js", "link": "/logfire/integrations/agent-frameworks/langchain-js/" }, + { "label": "OpenAI Agents SDK (TS)", "link": "/logfire/integrations/agent-frameworks/openai-agents-js/" }, + { "label": "VoltAgent", "link": "/logfire/integrations/agent-frameworks/voltagent/" }, + { "label": "LlamaIndex.TS", "link": "/logfire/integrations/agent-frameworks/llamaindex-ts/" }, + { "label": "Genkit (Go)", "link": "/logfire/integrations/agent-frameworks/genkit-go/" }, + { "label": "Eino (Go)", "link": "/logfire/integrations/agent-frameworks/eino/" }, + { "label": "Rig (Rust)", "link": "/logfire/integrations/agent-frameworks/rig/" }, + { "label": "Semantic Kernel (.NET)", "link": "/logfire/integrations/agent-frameworks/semantic-kernel-dotnet/" }, + { "label": "Microsoft Agent Framework (.NET)", "link": "/logfire/integrations/agent-frameworks/agent-framework-dotnet/" } ] }, {