# Integrate Lens with agent frameworks

```json metadata
{
  "schema_version": 2,
  "title": "Integrate Lens with agent frameworks",
  "description": "Trace LangGraph, OpenAI Agents SDK, Google ADK, and your own agent loop with Lens, across Agent Memory, Agent Playbooks, and Context Gateway.",
  "categories": ["docs","develop","ai"],
  "tableOfContents": {"sections":[{"id":"where-the-turn-opens","title":"Where the turn opens"},{"id":"trace-one-turn-across-iris-services","title":"Trace one turn across Iris services"},{"children":[{"id":"attach-an-iris-service","title":"Attach an Iris service"}],"id":"langgraph","title":"LangGraph"},{"id":"openai-agents-sdk","title":"OpenAI Agents SDK"},{"id":"google-adk","title":"Google ADK"},{"id":"your-own-agent-loop","title":"Your own agent loop"},{"id":"next-steps","title":"Next steps"}]}

,
  "codeExamples": [{"codetabsId":"iris_lens_iris_products-stepiris_products_turn","difficulty":"beginner","id":"iris_products_turn","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_lens_iris_products-stepiris_products_turn"}]},{"codetabsId":"iris_lens_langgraph-steplanggraph_turn","difficulty":"beginner","id":"langgraph_turn","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_lens_langgraph-steplanggraph_turn"}]},{"codetabsId":"iris_lens_langgraph_attach-steplanggraph_attach","difficulty":"beginner","id":"langgraph_attach","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_lens_langgraph_attach-steplanggraph_attach"}]},{"codetabsId":"iris_lens_openai_agents-stepopenai_agents_turn","difficulty":"beginner","id":"openai_agents_turn","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_lens_openai_agents-stepopenai_agents_turn"}]},{"codetabsId":"iris_lens_google_adk-stepgoogle_adk_turn","difficulty":"beginner","id":"google_adk_turn","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_lens_google_adk-stepgoogle_adk_turn"}]},{"codetabsId":"iris_lens_own_loop-stepown_loop_turn","difficulty":"beginner","id":"own_loop_turn","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_lens_own_loop-stepown_loop_turn"}]}]
}
```

## Code examples

These examples connect to Iris services using SDKs or HTTP requests with curl. Follow one language throughout each walkthrough.


To trace an agent run in Lens, open a turn around it. Each step the agent takes inside the run, such as a memory recall or a model call, is recorded in that turn. For more about turns and steps, see [Lens concepts](https://redis.io/docs/latest/develop/ai/context-engine/lens/concepts).

Before you start, get the Lens connection values from the [quickstart](https://redis.io/docs/latest/develop/ai/context-engine/lens/quickstart#configure-the-sdk). The framework examples also need a model provider key, such as `OPENAI_API_KEY`.

## Where the turn opens

| Framework | Open the turn around |
| --------- | -------------------- |
| LangGraph | `app.invoke()` or `app.ainvoke()`. With an Iris service attached, `attach()` opens the turn for you. |
| OpenAI Agents SDK | `Runner.run()` |
| Google Agent Development Kit (ADK) | `runner.run_async()` |
| Your own loop | The handler for one message |

To trace model calls inside the turn, install `redis-iris[llm-tracing]` and call `lens.init()` at startup. See [Trace model calls](https://redis.io/docs/latest/develop/ai/context-engine/lens/instrument#trace-model-calls).

## Trace one turn across Iris services

When your agent uses Agent Memory, Agent Playbooks, and Context Gateway, a single Lens turn shows how each Iris service contributed to the agent's response. This example needs the connection values for each Iris service.

`workflows.turn()` runs each service's context step before the model and its write-back after. Every service call joins the turn:

**Available in:** Python (Iris)

##### Python (Iris)

```python
from redis_iris import Iris, lens, workflows
from redis_iris.hooks import stack

lens.init(agent_id="support-bot")

iris = Iris.from_env()
memory = iris.agent_memory(assistant_id="support-bot")
playbook = iris.agent_playbooks()
gateway = iris.context_gateway()

hooks = stack(memory.hooks(), playbook.disclosure(audience="support"))


def run_agent(context, message, tools):
    # Your model and tool loop goes here. It receives the Iris context and tools.
    return "Your refund was issued on May 2."


def handle(owner, message, thread_id):
    with lens.turn(thread_id, user_id=owner) as outer:
        with workflows.turn(hooks, message, owner=owner, session=thread_id) as turn:
            tools = [*gateway.model_tools(), *hooks.tools()]
            answer = run_agent(turn.context, message, tools)
            turn.record(answer)
        outer.set_outcome("completed")
    return answer


print(handle("user-1042", "Where can I see my refund status?", "thread-1"))
```



The turn shows the memory recall and the Agent Playbooks discovery, then any Context Gateway tool calls your agent makes, and the write-back. In asynchronous code, `workflows.aturn()` runs the services' context steps at the same time. The outer `lens.turn()` sets the outcome. When a turn is already open, `workflows.turn()` and `attach()` add their steps to it rather than starting another.

## LangGraph

Install LangGraph and the LangChain OpenAI integration:

```bash
python -m pip install 'redis-iris[llm-tracing]' langgraph langchain-openai
```

Wrap the graph run in `lens.turn()`. LangGraph's thread ID identifies the conversation, so pass it as the Lens session ID:

**Available in:** Python (Iris)

##### Python (Iris)

```python
from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessagesState, StateGraph

from redis_iris import lens

lens.init(agent_id="support-bot")
model = ChatOpenAI(model="gpt-4o-mini")


def assistant(state: MessagesState):
    return {"messages": [model.invoke(state["messages"])]}


graph = StateGraph(MessagesState)
graph.add_node("assistant", assistant)
graph.add_edge(START, "assistant")
graph.add_edge("assistant", END)
app = graph.compile()

thread_id = "thread-1"
with lens.turn(thread_id, user_id="user-1042") as turn:
    result = app.invoke(
        {"messages": [("user", "Where can I see my refund status?")]},
        {"configurable": {"thread_id": thread_id}},
    )
    turn.set_outcome("completed")

print(result["messages"][-1].content)
```



Each model call in the graph joins the turn with its token counts.

### Attach an Iris service

When the graph uses Agent Memory or Agent Playbooks, `attach()` adds the service's context before your first node and its write-back after your last. With Lens on, every run of an attached graph is one turn. The session comes from `thread_id` and the user from `user_id` in the run's configuration:

**Available in:** Python (Iris)

##### Python (Iris)

```python
from redis_iris import AgentMemory
from redis_iris.integrations.langgraph import attach

memory = AgentMemory.from_env()
app = attach(memory, graph).compile()

result = app.invoke(
    {"messages": [("user", "Where can I see my refund status?")]},
    {"configurable": {"user_id": "user-1042", "thread_id": "thread-1"}},
)
```



`attach()` needs the `langgraph` extra: `python -m pip install 'redis-iris[langgraph]'`.

## OpenAI Agents SDK

Install the OpenAI Agents SDK:

```bash
python -m pip install 'redis-iris[llm-tracing]' openai-agents
```

Wrap `Runner.run()` in `lens.turn()`:

**Available in:** Python (Iris)

##### Python (Iris)

```python
import asyncio

from agents import Agent, Runner

from redis_iris import lens

lens.init(agent_id="support-bot")
agent = Agent(name="Support", instructions="Answer refund questions in two sentences.")


async def handle(session_id, user_id, message):
    with lens.turn(session_id, user_id=user_id) as turn:
        result = await Runner.run(agent, message)
        turn.set_outcome("completed")
    return result.final_output


print(asyncio.run(handle("session-1", "user-1042", "Where can I see my refund status?")))
```



The OpenAI Agents SDK calls models through the OpenAI client, so each model call joins the turn with its token counts.

## Google ADK

Install the Google ADK:

```bash
python -m pip install redis-iris google-adk
```

Wrap the `runner.run_async()` loop in `lens.turn()`:

**Available in:** Python (Iris)

##### Python (Iris)

```python
import asyncio

from google.adk.agents import Agent
from google.adk.runners import InMemoryRunner
from google.genai import types

from redis_iris import lens

lens.init(agent_id="support-bot")
agent = Agent(
    name="support",
    model="gemini-2.0-flash",
    instruction="Answer refund questions in two sentences.",
)
runner = InMemoryRunner(agent, app_name="support")


async def handle(session_id, user_id, message):
    await runner.session_service.create_session(
        app_name="support", user_id=user_id, session_id=session_id
    )
    content = types.Content(role="user", parts=[types.Part(text=message)])
    answer = ""
    with lens.turn(session_id, user_id=user_id) as turn:
        async for event in runner.run_async(
            user_id=user_id, session_id=session_id, new_message=content
        ):
            if event.is_final_response() and event.content:
                answer = event.content.parts[0].text
        turn.set_outcome("completed")
    return answer


print(asyncio.run(handle("session-1", "user-1042", "Where can I see my refund status?")))
```



The turn records the agent run, its outcome, and every Iris service call the agent makes. The example uses a Gemini model and needs `GOOGLE_API_KEY`.

## Your own agent loop

Wrap the handler for one message in `lens.turn()`, and set the outcome on each path:

**Available in:** Python (Iris)

##### Python (Iris)

```python
from redis_iris import lens


def call_model(message, context):
    # Your model call goes here.
    return "Your refund was issued on May 2."


def handle(session_id, user_id, message):
    with lens.turn(session_id, user_id=user_id) as turn:
        try:
            answer = call_model(message, context=[])
        except Exception:
            turn.set_outcome("failed")
            raise
        turn.set_outcome("completed")
    return answer


print(handle("session-1", "user-1042", "Where can I see my refund status?"))
```



Each call to `handle()` records one turn under the session and user IDs you pass. The turn is marked `completed` when the model answers, or `failed` when the call raises. Record your retrievals and prompt assembly inside the same turn to see which context reached the model.

## Next steps

- [Instrument an agent](https://redis.io/docs/latest/develop/ai/context-engine/lens/instrument): record retrievals, memory recalls, prompt assembly, and custom steps.
- [Inspect sessions](https://redis.io/docs/latest/develop/ai/context-engine/lens/inspect-sessions): read the turns you traced in the Lens user interface (UI).
- [Query sessions and turns](https://redis.io/docs/latest/develop/ai/context-engine/lens/read-api): read turns from code.

