Skip to content

Commit ab4e7fc

Browse files
authored
Merge pull request #309 from CasperGN/feat/default-agent-stores
Feat/default agent stores
2 parents c2e4119 + 6bf452c commit ab4e7fc

File tree

5 files changed

+79
-2
lines changed

5 files changed

+79
-2
lines changed

dapr_agents/agents/base.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from datetime import datetime, timezone
66
from typing import Any, Dict, Iterable, List, Optional, Sequence, Union, Coroutine
77

8+
from dapr.clients import DaprClient
9+
from dapr.clients.grpc._response import GetMetadataResponse, RegisteredComponents
10+
811
from dapr_agents.agents.components import AgentComponents
912
from dapr_agents.agents.configs import (
1013
AgentMemoryConfig,
@@ -124,11 +127,27 @@ def __init__(
124127
workflow_grpc_options=workflow_grpc,
125128
)
126129

130+
if memory is None:
131+
with DaprClient() as _client:
132+
resp: GetMetadataResponse = _client.get_metadata()
133+
components: Sequence[RegisteredComponents] = resp.registered_components
134+
for component in components:
135+
if (
136+
"state" in component.type
137+
and component.name == "agent-statestore"
138+
):
139+
memory = AgentMemoryConfig(
140+
store=ConversationDaprStateMemory(
141+
store_name="agent-statestore",
142+
session_id=f"{name.replace(' ', '-').lower() if name else 'default'}-session",
143+
)
144+
)
145+
127146
# -----------------------------
128147
# Memory wiring
129148
# -----------------------------
130149
self._memory = memory or AgentMemoryConfig()
131-
if self._memory.store is None and state is not None:
150+
if self._memory.store and state is not None:
132151
# Auto-provision a Dapr-backed memory if we have a state store.
133152
self._memory.store = ConversationDaprStateMemory( # type: ignore[union-attr]
134153
store_name=state.store.store_name,

dapr_agents/agents/components.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from datetime import datetime, timezone
77
from typing import Any, Callable, Dict, Optional, Sequence
88

9+
from dapr.clients import DaprClient
910
from dapr.clients.grpc._state import Concurrency, Consistency, StateOptions
11+
from dapr.clients.grpc._response import GetMetadataResponse, RegisteredComponents
1012
from pydantic import BaseModel, ValidationError
1113

1214
from dapr_agents.agents.configs import (
@@ -18,7 +20,10 @@
1820
StateModelBundle,
1921
)
2022
from dapr_agents.agents.schemas import AgentWorkflowEntry
21-
from dapr_agents.storage.daprstores.stateservice import StateStoreError
23+
from dapr_agents.storage.daprstores.stateservice import (
24+
StateStoreError,
25+
StateStoreService,
26+
)
2227
from dapr_agents.types.workflow import DaprWorkflowStatus
2328

2429
logger = logging.getLogger(__name__)
@@ -63,6 +68,37 @@ def __init__(
6368
self.name = name
6469
self._workflow_grpc_options = workflow_grpc_options
6570

71+
if pubsub is None or state is None or registry is None:
72+
with DaprClient() as _client:
73+
resp: GetMetadataResponse = _client.get_metadata()
74+
components: Sequence[RegisteredComponents] = resp.registered_components
75+
for component in components:
76+
if (
77+
"state" in component.type
78+
and component.name == "agent-wfstatestore"
79+
and state is None
80+
):
81+
state = AgentStateConfig(
82+
store=StateStoreService(store_name=component.name),
83+
state_key=f"{name.replace(' ', '-').lower() if name else 'default'}:workflow_state",
84+
)
85+
if component.name == "agent-registry" and registry is None:
86+
registry = AgentRegistryConfig(
87+
store=StateStoreService(store_name="agent-registry"),
88+
team_name="default",
89+
)
90+
if (
91+
"pubsub" in component.type
92+
and component.name == "agent-pubsub"
93+
and pubsub is None
94+
):
95+
logger.info(f"topic: {name}.topic")
96+
pubsub = AgentPubSubConfig(
97+
pubsub_name="agent-pubsub",
98+
agent_topic=f"{name.replace(' ', '-').lower()}.topic",
99+
broadcast_topic="agents.broadcast",
100+
)
101+
66102
# -----------------------------
67103
# Pub/Sub configuration (copy)
68104
# -----------------------------

tests/agents/durableagent/test_durable_agent.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ def __exit__(self, exc_type, exc_val, exc_tb):
6565
def __call__(self, *args, **kwargs):
6666
return self
6767

68+
def get_metadata(self):
69+
"""Mock get_metadata that returns empty metadata."""
70+
from unittest.mock import MagicMock
71+
72+
response = MagicMock()
73+
response.registered_components = []
74+
return response
75+
6876

6977
class TestDurableAgent:
7078
"""Test cases for the DurableAgent class."""

tests/agents/durableagent/test_mcp_streamable_http.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ class R:
4747
def execute_state_transaction(self, *args, **kwargs):
4848
pass
4949

50+
def get_metadata(self):
51+
"""Mock get_metadata that returns empty metadata."""
52+
from unittest.mock import MagicMock
53+
54+
response = MagicMock()
55+
response.registered_components = []
56+
return response
57+
5058
statestore.DaprClient = MockDaprClient
5159

5260
# Patch out agent registration logic (skip state store entirely)

tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ def delete_state(self, store_name, key, **kwargs):
109109
"""Mock delete_state that does nothing."""
110110
pass
111111

112+
def get_metadata(self):
113+
"""Mock get_metadata that returns empty metadata."""
114+
response = MagicMock()
115+
response.registered_components = []
116+
return response
117+
112118

113119
class MockConversationInput:
114120
pass

0 commit comments

Comments
 (0)