Skip to content

chore(sdk): testing repl#1507

Draft
Eugene Yurtsev (eyurtsev) wants to merge 10 commits intomainfrom
eugene/add_repl_support
Draft

chore(sdk): testing repl#1507
Eugene Yurtsev (eyurtsev) wants to merge 10 commits intomainfrom
eugene/add_repl_support

Conversation

@eyurtsev
Copy link
Collaborator

@eyurtsev Eugene Yurtsev (eyurtsev) commented Feb 24, 2026

Experimenting with REPL middleware to see if we can do anything interesting with it.

Here's some sample code that can help reason about current limitations.

from __future__ import annotations

import math
import tempfile
from io import BytesIO
from typing import Sequence
from pathlib import Path

from langchain_core.messages import HumanMessage

from deepagents.backends.filesystem import FilesystemBackend
from deepagents.graph import create_deep_agent
from deepagents.middleware import MontyMiddleware
import matplotlib.pyplot as plt


backend = FilesystemBackend(root_dir='./artifacts', virtual_mode=True)


def generate_plot(path: str, title: str, x: Sequence[float], y: Sequence[float]) -> None:
    """Generate a PNG plot and upload it via backend (in-memory bytes, no temp file)."""
    if len(x) != len(y):
        raise ValueError(f"x and y must be the same length (got {len(x)} and {len(y)})")

    # This logic cannot be implemented inside of monty
    # since Monty cannot run matplotlib code.
    # We also cannot pass the `fig` variable to monty for manipulation
    # since it does not have access to any of the objects in the python runtime
    fig, ax = plt.subplots()
    ax.plot(list(x), list(y), marker="o")
    ax.set_title(title)
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.grid(True, linewidth=0.5, alpha=0.4)

    fig.tight_layout()

    buf = BytesIO()
    fig.savefig(buf, format="png", dpi=200)
    plt.close(fig)

    png_bytes = buf.getvalue()
    backend.upload_files([(path, png_bytes)])
    
def cos(x):
    "Return the cosine of x (measured in radians)."
    return math.cos(x)

agent = create_deep_agent(
    backend=backend,
    middleware=[
        MontyMiddleware(
            backend=backend, 
            external_functions=['generate_plot', 'cos'], 
            external_function_implementations={'generate_plot': generate_plot, 'cos': cos},
            auto_include=True,
        )
    ]
)

result = agent.invoke(
    {
        "messages": [
            HumanMessage(
                content="Could you create a series of (x, y) of length 5 inspired by digits in 300! and generate a plot with them to foo.png"
            )
        ]
    }
)

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

@github-actions github-actions bot added internal User is a member of the `langchain-ai` GitHub organization dependencies Pull requests that update a dependency file deepagents Related to the `deepagents` SDK / agent harness infra Repo meta changes and removed internal User is a member of the `langchain-ai` GitHub organization infra Repo meta changes labels Feb 24, 2026
@github-actions github-actions bot added infra Repo meta changes and removed infra Repo meta changes labels Feb 24, 2026
@eyurtsev
Copy link
Collaborator Author

Separately this tracing seems to work, but repl isn't showing up chat model metadata

from langchain.agents.middleware import (
    AgentMiddleware,
    AgentState,
    ModelRequest,
    ModelResponse,
)
from langchain.agents import create_agent
from langgraph.runtime import Runtime
from typing import Any, Callable


from langchain.tools import tool, ToolRuntime
from langchain_core.tools import StructuredTool
from typing import Annotated

async def _get_weather(
    city: Annotated[str, "city."],
    runtime: ToolRuntime[None, dict[str, Any]],
    timeout: Annotated[int | None, "Optional timeout in seconds for this evaluation."] = None
) -> str:
    return "Sunny with tomatoes."""

def _get_weather_async(
    city: Annotated[str, "city"],
    runtime: ToolRuntime[None, dict[str, Any]],
    timeout: Annotated[int | None, "Optional timeout in seconds for this evaluation."] = None,
) -> str:
    return "Sunny with tomatoes."""

get_weather = StructuredTool.from_function(
    name="get_weather",
    description="get the weather",
    func=_get_weather,
    coroutine=_get_weather_async,
)

@tool
def get_time():
    """Get the time"""
    return "3 PM"
    

class LoggingMiddleware(AgentMiddleware):
    def __init__(self):
        self.tools = [get_weather]

agent = create_agent(
    model="gpt-4.1",
    middleware=[LoggingMiddleware()],
    tools=[get_time]
)

agent.invoke({"messages": [{"role": "user", "content": 'whats the weather?'}]})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deepagents Related to the `deepagents` SDK / agent harness dependencies Pull requests that update a dependency file infra Repo meta changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant