pip install sherma
For running examples that use OpenAI models:
pip install sherma[examples]
If developing from source:
git clone https://github.com/MadaraUchiha-314/sherma.git
cd sherma
uv sync
The programmatic approach gives you full control over graph construction using LangGraph directly, with sherma providing the registry, A2A integration, and lifecycle management.
import asyncio
import json
from pathlib import Path
from langchain_openai import ChatOpenAI
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.graph.state import CompiledStateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from sherma import LLM, Prompt, Tool
from sherma.langgraph.agent import LangGraphAgent
from sherma.registry.base import RegistryEntry
from sherma.registry.llm import LLMRegistry
from sherma.registry.prompt import PromptRegistry
from sherma.registry.tool import ToolRegistry
class WeatherAgent(LangGraphAgent):
api_key: str
prompt_registry: PromptRegistry
llm_registry: LLMRegistry
tool_registry: ToolRegistry
async def get_graph(self) -> CompiledStateGraph:
# Retrieve entities from registries
llm_entity = await self.llm_registry.get("openai-gpt-4o-mini")
prompt_entity = await self.prompt_registry.get("weather-system-prompt")
llm = ChatOpenAI(model=llm_entity.model_name, api_key=self.api_key)
llm_with_tools = llm.bind_tools([get_weather])
async def call_model(state: MessagesState) -> dict:
system = {"role": "system", "content": prompt_entity.instructions}
response = await llm_with_tools.ainvoke([system, *state["messages"]])
return {"messages": [response]}
graph = StateGraph(MessagesState)
graph.add_node("agent", call_model)
graph.add_node("tools", ToolNode([get_weather]))
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", tools_condition)
graph.add_edge("tools", "agent")
return graph.compile()
To use the agent, send it A2A messages:
from a2a.types import Message, Part, Role, TextPart
message = Message(
message_id="user-1",
parts=[Part(root=TextPart(text="What's the weather in Tokyo?"))],
role=Role.user,
)
async for event in agent.send_message(message):
if isinstance(event, Message):
for part in event.parts:
if part.root.kind == "text":
print(part.root.text)
The same agent can be defined entirely in YAML:
# weather-agent.yaml
prompts:
- id: weather-system-prompt
version: "1.0.0"
instructions: >
You are a helpful weather assistant.
Use the get_weather tool to look up current weather
for any city the user asks about.
llms:
- id: openai-gpt-4o-mini
version: "1.0.0"
provider: openai
model_name: gpt-4o-mini
tools:
- id: get_weather
version: "1.0.0"
import_path: my_tools.get_weather
agents:
weather-agent:
state:
fields:
- name: messages
type: list
default: []
graph:
entry_point: agent
nodes:
- name: agent
type: call_llm
args:
llm:
id: openai-gpt-4o-mini
version: "1.0.0"
prompt: 'prompts["weather-system-prompt"]["instructions"]'
tools:
- id: get_weather
version: "1.0.0"
edges:
- source: agent
target: __end__
Load and run in Python:
from sherma import DeclarativeAgent
agent = DeclarativeAgent(
id="weather-agent",
version="1.0.0",
yaml_path="weather-agent.yaml",
)
# Same send_message interface as the programmatic agent
async for event in agent.send_message(message):
print(event)
When a call_llm node declares tools, sherma automatically injects a tool_node after it with the correct conditional edges. You don’t need to wire tool execution manually.
To expose any sherma agent as an A2A server:
from a2a.server.apps import A2AStarletteApplication
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.types import AgentCard, AgentCapabilities
from sherma.a2a import ShermaAgentExecutor
agent = DeclarativeAgent(
id="weather-agent",
version="1.0.0",
yaml_path="weather-agent.yaml",
)
executor = ShermaAgentExecutor(agent)
handler = DefaultRequestHandler(agent_executor=executor)
card = AgentCard(
name="Weather Agent",
description="Looks up weather for any city",
url="http://localhost:8000",
version="1.0.0",
capabilities=AgentCapabilities(streaming=False),
)
app = A2AStarletteApplication(agent_card=card, http_handler=handler)