Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/development/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ export OPENAI_API_KEY=your_key_here
pytest tests/integration/quickstarts/ -v -m integration

# Run specific test file
pytest tests/integration/quickstarts/test_01_hello_world.py -v
pytest tests/integration/quickstarts/test_01_dapr_agents_fundamentals.py -v

# Run specific test func
pytest -m integration -v integration/quickstarts/test_01_hello_world.py::TestHelloWorldQuickstart::test_chain_tasks
pytest -m integration -v integration/quickstarts/test_01_dapr_agents_fundamentals.py::TestHelloWorldQuickstart::test_chain_tasks

# Run with coverage
pytest tests/integration/quickstarts/ -v -m integration --cov=dapr_agents
Expand Down Expand Up @@ -250,7 +250,7 @@ Mental model:

For Dapr component files that reference environment variables (e.g., `{{OPENAI_API_KEY}}` or `${{OPENAI_API_KEY}}`), use the helper to render a temporary resources folder and pass it to Dapr:
```bash
dapr run --resources-path $(quickstarts/resolve_env_templates.py quickstarts/01-hello-world/components) -- python 03_durable_agent.py
dapr run --resources-path $(quickstarts/resolve_env_templates.py quickstarts/01-dapr-agents-fundamentals/components) -- python 03_durable_agent.py
```

The helper scans only `.yaml`/`.yml` files (non-recursive), replaces placeholders with matching env var values, writes processed files to a temp directory, and prints that directory path.
10 changes: 10 additions & 0 deletions quickstarts/01-dapr-agents-fundamentals/01_llm_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from dapr_agents.llm import DaprChatClient
from dapr_agents.types import LLMChatResponse

# Basic chat completion
llm = DaprChatClient(component_name="llm-provider")
response: LLMChatResponse = llm.generate(
"Guess what is the weather in London right now!"
)

print("Response: ", response.get_message().content)
31 changes: 31 additions & 0 deletions quickstarts/01-dapr-agents-fundamentals/02_agent_llm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import asyncio

from dapr_agents.llm import DaprChatClient

from dapr_agents import Agent


def main() -> None:
# Simple agent: LLM, no tools, no memory
weather_agent = Agent(
name="WeatherAgent",
role="Weather Assistant",
instructions=["Provide concise, friendly weather info."],
llm=DaprChatClient(component_name="llm-provider"),
)

try:
response = asyncio.run(
weather_agent.run("What's a quick weather update for London right now?")
)
print(f"Agent: {response}")
except Exception as e:
print(f"Error: {e}")
raise


if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting gracefully...")
33 changes: 33 additions & 0 deletions quickstarts/01-dapr-agents-fundamentals/03_agent_llm_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import asyncio

from dapr_agents.llm import DaprChatClient

from dapr_agents import Agent
from function_tools import weather_func


def main() -> None:
# Simple agent: LLM + tools, but no memory
weather_agent = Agent(
name="WeatherAgent",
role="Weather Assistant",
instructions=["Provide concise, friendly weather info."],
tools=[weather_func],
llm=DaprChatClient(component_name="llm-provider"),
)

try:
response = asyncio.run(
weather_agent.run("What's a quick weather update for London right now?")
)
print(f"Agent: {response}")
except Exception as e:
print(f"Error: {e}")
raise


if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting gracefully...")
50 changes: 50 additions & 0 deletions quickstarts/01-dapr-agents-fundamentals/04_agent_mcp_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import asyncio
import sys

from dapr_agents.llm import DaprChatClient

from dapr_agents import Agent
from dapr_agents.tool.mcp import MCPClient


async def _load_mcp_tools() -> list:
client = MCPClient()
try:
await client.connect_stdio(
server_name="local",
command=sys.executable,
args=["mcp_tools.py"],
)
return client.get_all_tools()
finally:
await client.close()


def main() -> None:
tools = asyncio.run(_load_mcp_tools())

weather_agent = Agent(
name="WeatherAgent",
role="Weather Assistant",
instructions=[
"Provide concise, friendly weather info. Use MCP tools as needed."
],
tools=tools,
llm=DaprChatClient(component_name="llm-provider"),
)

try:
response = asyncio.run(
weather_agent.run("What's a quick weather update for London right now?")
)
print(f"Agent: {response}")
except Exception as e:
print(f"Error: {e}")
raise


if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting gracefully...")
49 changes: 49 additions & 0 deletions quickstarts/01-dapr-agents-fundamentals/05_agent_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import asyncio

from dapr_agents.llm import DaprChatClient

from dapr_agents import Agent
from dapr_agents.agents.configs import AgentMemoryConfig
from dapr_agents.memory import ConversationDaprStateMemory
from function_tools import weather_func


async def main() -> None:
# Create an agent with memory and tools
weather_agent = Agent(
name="WeatherAgent",
role="Weather Assistant",
instructions=["Help users with weather information"],
tools=[weather_func],
# Configure this agent to use Dapr Conversation API.
llm=DaprChatClient(component_name="llm-provider"),
# Configure the agent to use Dapr State Store for conversation history.
memory=AgentMemoryConfig(
store=ConversationDaprStateMemory(
store_name="conversation-statestore",
session_id="03-agent-with-memory",
)
),
)
try:
response = await weather_agent.run(
"I like warm and dry places. What is the weather in London now?"
)
print(f"Agent: {response}")

# Second interaction - agent remembers the preference from the first interaction
response = await weather_agent.run(
"Given my preference, is London’s current weather a good match?"
)
print(f"Agent: {response}")

except Exception as e:
print(f"Error: {e}")
raise


if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting gracefully...")
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from dapr_agents.llm import DaprChatClient

from dapr_agents import DurableAgent
from dapr_agents.agents.configs import AgentMemoryConfig, AgentStateConfig
from dapr_agents.memory import ConversationDaprStateMemory
from dapr_agents.storage.daprstores.stateservice import StateStoreService
from dapr_agents.workflow.runners import AgentRunner
from function_tools import slow_weather_func


def main() -> None:
# This agent is of type durable agent where the execution is durable
weather_agent = DurableAgent(
name="WeatherAgent",
role="Weather Assistant",
instructions=["Help users with weather information"],
tools=[slow_weather_func],
# Configure this agent to use Dapr Conversation API.
llm=DaprChatClient(component_name="llm-provider"),
# Configure the agent to use Dapr State Store for conversation history.
memory=AgentMemoryConfig(
store=ConversationDaprStateMemory(
store_name="conversation-statestore",
session_id="04-durable-agent",
)
),
# This is where the execution state is stored
state=AgentStateConfig(
store=StateStoreService(store_name="workflow-statestore"),
),
)

# This runner will run the agent and expose it on port 8001
runner = AgentRunner()
try:
runner.serve(weather_agent, port=8001)
finally:
runner.shutdown()


if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting gracefully...")
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import asyncio

from dapr_agents.llm import DaprChatClient

from dapr_agents import DurableAgent
from dapr_agents.agents.configs import (
AgentMemoryConfig,
AgentPubSubConfig,
AgentStateConfig,
)
from dapr_agents.memory import ConversationDaprStateMemory
from dapr_agents.storage.daprstores.stateservice import StateStoreService
from dapr_agents.workflow.runners import AgentRunner
from dapr_agents.workflow.utils.core import wait_for_shutdown
from function_tools import slow_weather_func


def main() -> None:
weather_agent = DurableAgent(
name="WeatherAgent",
role="Weather Assistant",
instructions=["Help users with weather information"],
tools=[slow_weather_func],
llm=DaprChatClient(component_name="llm-provider"),
memory=AgentMemoryConfig(
store=ConversationDaprStateMemory(
store_name="conversation-statestore",
session_id="05-durable-agent-sub",
)
),
state=AgentStateConfig(
store=StateStoreService(store_name="workflow-statestore"),
),
pubsub=AgentPubSubConfig(
pubsub_name="message-pubsub",
agent_topic="weather.requests",
broadcast_topic="agents.broadcast",
),
)

runner = AgentRunner()
try:
runner.subscribe(weather_agent)
asyncio.run(wait_for_shutdown())
finally:
runner.shutdown(weather_agent)


if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting gracefully...")
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import time

import dapr.ext.workflow as wf
from dapr.ext.workflow import DaprWorkflowContext
from dapr.ext.workflow import DaprWorkflowContext, WorkflowRuntime
from dotenv import load_dotenv

from dapr_agents.llm.dapr import DaprChatClient
Expand All @@ -10,23 +10,23 @@
load_dotenv()

# Initialize workflow runtime + LLM client
runtime = wf.WorkflowRuntime()
llm = DaprChatClient(component_name="openai")
wfr = WorkflowRuntime()
llm = DaprChatClient(component_name="llm-provider")


@runtime.workflow(name="analyze_topic")
@wfr.workflow(name="analyze_topic")
def analyze_topic(ctx: DaprWorkflowContext, topic: str):
# Each step is durable and can be retried
outline = yield ctx.call_activity(create_outline, input=topic)
if not ctx.is_replaying and len(outline) > 0:
if len(outline) > 0:
print("Outline:", outline, flush=True)
blog_post = yield ctx.call_activity(write_blog, input=outline)
if not ctx.is_replaying and len(blog_post) > 0:
if len(blog_post) > 0:
print("Blog post:", blog_post, flush=True)
return blog_post


@runtime.activity(name="create_outline")
@wfr.activity(name="create_outline")
@llm_activity(
prompt="Create a very short outline about the topic '{topic}'. Provide 5 bullet points only.",
llm=llm,
Expand All @@ -36,7 +36,7 @@ def create_outline(ctx, topic: str) -> str:
pass


@runtime.activity(name="write_blog")
@wfr.activity(name="write_blog")
@llm_activity(
prompt="Write a short (2 paragraphs) friendly blog post following this outline:\n{outline}",
llm=llm,
Expand All @@ -46,7 +46,7 @@ def write_blog(ctx, outline: str) -> str:


if __name__ == "__main__":
runtime.start()
wfr.start()
time.sleep(5) # give the runtime time to initialize

client = wf.DaprWorkflowClient()
Expand All @@ -70,4 +70,4 @@ def write_blog(ctx, outline: str) -> str:
print("Failure message:", fd.message)
print("Stack trace:\n", fd.stack_trace)

runtime.shutdown()
wfr.shutdown()
Loading