Skip to content

Commit 700b9a4

Browse files
committed
Merge branch 'main' into filinto/dapr-default-better-devex
2 parents e233860 + 61c3dc4 commit 700b9a4

File tree

13 files changed

+910
-488
lines changed

13 files changed

+910
-488
lines changed

dapr_agents/agents/base.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,20 +135,20 @@ def set_name_from_role(cls, values: dict):
135135
return values
136136

137137
@model_validator(mode="after")
138-
def validate_llm(cls, values):
138+
def validate_llm(self):
139139
"""Validate that LLM is properly configured."""
140-
if hasattr(values, "llm"):
141-
if values.llm is None:
140+
if hasattr(self, "llm"):
141+
if self.llm is None:
142142
logger.warning("LLM client is None, some functionality may be limited.")
143143
else:
144144
try:
145145
# Validate LLM is properly configured by accessing it as this is required to be set.
146-
_ = values.llm
146+
_ = self.llm
147147
except Exception as e:
148148
logger.error(f"Failed to initialize LLM: {e}")
149-
values.llm = None
149+
self.llm = None
150150

151-
return values
151+
return self
152152

153153
def model_post_init(self, __context: Any) -> None:
154154
"""

dapr_agents/agents/durableagent/agent.py

Lines changed: 371 additions & 335 deletions
Large diffs are not rendered by default.

dapr_agents/agents/durableagent/state.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pydantic import BaseModel, Field
22
from typing import List, Optional, Dict, Any
33
from dapr_agents.types import MessageContent, ToolExecutionRecord
4+
from dapr_agents.types.workflow import DaprWorkflowStatus
45
from datetime import datetime
56
import uuid
67

@@ -60,6 +61,10 @@ class DurableAgentWorkflowEntry(BaseModel):
6061
default=None,
6162
description="OpenTelemetry trace context for workflow resumption.",
6263
)
64+
status: str = Field(
65+
default=DaprWorkflowStatus.RUNNING.value,
66+
description="Current status of the workflow.",
67+
)
6368

6469

6570
class DurableAgentWorkflowState(BaseModel):

dapr_agents/observability/context_storage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def create_resumed_workflow_context(
125125

126126
# Create AGENT span with proper agent name for resumed workflow
127127
agent_display_name = agent_name or "DurableAgent"
128-
span_name = f"{agent_display_name}.ToolCallingWorkflow"
128+
span_name = f"{agent_display_name}.AgenticWorkflow"
129129
with tracer.start_as_current_span(span_name) as span:
130130
# Set AGENT span attributes
131131
from .constants import OPENINFERENCE_SPAN_KIND

dapr_agents/observability/wrappers/workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def _extract_workflow_name(self, args: Any, kwargs: Any) -> str:
329329
workflow_name = (
330330
workflow
331331
if isinstance(workflow, str)
332-
else getattr(workflow, "__name__", "ToolCallingWorkflow")
332+
else getattr(workflow, "__name__", "AgenticWorkflow")
333333
)
334334
return workflow_name
335335

dapr_agents/observability/wrappers/workflow_task.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,7 @@ def _build_task_attributes(
192192

193193
# Add resource-level attributes for Phoenix UI grouping
194194
attributes["resource.workflow.instance_id"] = instance_id
195-
attributes[
196-
"resource.workflow.name"
197-
] = "ToolCallingWorkflow" # Could be dynamic
195+
attributes["resource.workflow.name"] = "AgenticWorkflow" # Could be dynamic
198196

199197
# Log the trace context for debugging (expected to be disconnected for Dapr Workflows)
200198
from opentelemetry import trace
@@ -323,7 +321,7 @@ def _create_context_for_resumed_workflow(
323321
logger.debug(f"Creating AGENT span for resumed workflow {instance_id}")
324322

325323
agent_name = getattr(instance, "name", "DurableAgent")
326-
workflow_name = instance_data.get("workflow_name", "ToolCallingWorkflow")
324+
workflow_name = instance_data.get("workflow_name", "AgenticWorkflow")
327325
span_name = f"{agent_name}.{workflow_name}"
328326
attributes = {
329327
"openinference.span.kind": "AGENT",
@@ -675,15 +673,13 @@ def _categorize_workflow_task(self, task_name: str) -> str:
675673
Returns:
676674
str: Semantic category for the task type
677675
"""
678-
if task_name in ["record_initial_entry", "get_workflow_entry_info"]:
676+
if task_name in ["record_initial_entry"]:
679677
return "initialization"
680-
elif task_name in ["append_assistant_message", "append_tool_message"]:
681-
return "state_management"
682678
elif task_name in ["finalize_workflow", "finish_workflow"]:
683679
return "finalization"
684680
elif task_name in ["broadcast_message_to_agents", "send_response_back"]:
685681
return "communication"
686-
elif task_name == "generate_response":
682+
elif task_name == "call_llm":
687683
return "llm_generation"
688684
elif task_name == "run_tool":
689685
return "tool_execution"

dapr_agents/tool/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ async def executor(**kwargs: Any) -> Any:
141141
tool_args_model = create_pydantic_model_from_schema(
142142
mcp_tool.inputSchema, f"{tool_name}Args"
143143
)
144-
except Exception:
144+
except Exception as e:
145+
logger.warning(f"Failed to create schema for tool '{tool_name}': {e}")
145146
pass
146147

147148
return cls(

dapr_agents/workflow/base.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from datetime import datetime, timezone
99
from typing import Any, Callable, Dict, List, Optional, TypeVar, Union
1010

11+
from pydantic import BaseModel
1112
from dapr.ext.workflow import (
1213
DaprWorkflowClient,
1314
WorkflowActivityContext,
@@ -83,12 +84,6 @@ def model_post_init(self, __context: Any) -> None:
8384

8485
self.start_runtime()
8586

86-
# Discover and register tasks and workflows
87-
discovered_tasks = self._discover_tasks()
88-
self._register_tasks(discovered_tasks)
89-
discovered_wfs = self._discover_workflows()
90-
self._register_workflows(discovered_wfs)
91-
9287
# Set up automatic signal handlers for graceful shutdown
9388
try:
9489
self.setup_signal_handlers()
@@ -350,6 +345,10 @@ def register_tasks_from_package(self, package_name: str) -> None:
350345
def _register_tasks(self, tasks: Dict[str, Callable]) -> None:
351346
"""Register each discovered task with the Dapr runtime using direct registration."""
352347
for task_name, method in tasks.items():
348+
# Don't reregister tasks that are already registered
349+
if task_name in self.tasks:
350+
continue
351+
353352
llm = self._choose_llm_for(method)
354353
logger.debug(
355354
f"Registering task '{task_name}' with llm={getattr(llm, '__class__', None)}"
@@ -436,6 +435,10 @@ def _discover_workflows(self) -> Dict[str, Callable]:
436435
def _register_workflows(self, wfs: Dict[str, Callable]) -> None:
437436
"""Register each discovered workflow with the Dapr runtime."""
438437
for wf_name, method in wfs.items():
438+
# Don't reregister workflows that are already registered
439+
if wf_name in self.workflows:
440+
continue
441+
439442
# Use a closure helper to avoid late-binding capture issues.
440443
def make_wrapped(meth: Callable) -> Callable:
441444
@functools.wraps(meth)
@@ -523,6 +526,17 @@ def start_runtime(self):
523526
else:
524527
logger.debug("Workflow runtime already running; skipping.")
525528

529+
self._ensure_activities_registered()
530+
531+
def _ensure_activities_registered(self):
532+
"""Ensure all workflow activities are registered with the Dapr runtime."""
533+
# Discover and register tasks and workflows
534+
discovered_tasks = self._discover_tasks()
535+
self._register_tasks(discovered_tasks)
536+
discovered_wfs = self._discover_workflows()
537+
self._register_workflows(discovered_wfs)
538+
logger.debug("Workflow activities registration completed.")
539+
526540
def _sync_workflow_state_after_startup(self):
527541
"""
528542
Sync database workflow state with actual Dapr workflow status after runtime startup.
@@ -540,25 +554,17 @@ def _sync_workflow_state_after_startup(self):
540554
)
541555
return
542556

543-
logger.debug("Syncing workflow state with Dapr after runtime startup...")
544557
self.load_state()
545-
546-
# Check if we have instances to sync
547-
instances = (
548-
getattr(self.state, "instances", {})
549-
if hasattr(self.state, "instances")
550-
else self.state.get("instances", {})
551-
)
552-
if not instances:
553-
return
558+
instances = self.state.get("instances", {})
554559

555560
logger.debug(f"Found {len(instances)} workflow instances to sync")
556561

557562
# Sync each instance with Dapr's actual status
558563
for instance_id, instance_data in instances.items():
559564
try:
560565
# Skip if already completed
561-
if instance_data.get("end_time") is not None:
566+
end_time = instance_data.get("end_time")
567+
if end_time is not None:
562568
continue
563569

564570
# Get actual status from Dapr
@@ -580,6 +586,7 @@ def _sync_workflow_state_after_startup(self):
580586
timezone.utc
581587
).isoformat()
582588
instance_data["status"] = runtime_status.lower()
589+
583590
logger.debug(
584591
f"Marked workflow {instance_id} as {runtime_status.lower()} in database"
585592
)
@@ -600,6 +607,7 @@ def _sync_workflow_state_after_startup(self):
600607
timezone.utc
601608
).isoformat()
602609
instance_data["status"] = DaprWorkflowStatus.COMPLETED.value
610+
603611
logger.debug(
604612
f"Workflow {instance_id} no longer in Dapr, marked as completed"
605613
)
@@ -804,9 +812,7 @@ async def _create_agent_span_for_resumed_workflow(
804812
# Get tracer and create AGENT span as child of the original trace
805813
tracer = trace.get_tracer(__name__)
806814
agent_name = getattr(self, "name", "DurableAgent")
807-
workflow_name = instance_data.get(
808-
"workflow_name", "ToolCallingWorkflow"
809-
)
815+
workflow_name = instance_data.get("workflow_name", "AgenticWorkflow")
810816
span_name = f"{agent_name}.{workflow_name}"
811817

812818
# Create the AGENT span that will show up in the trace

dapr_agents/workflow/mixins/state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def initialize_state(self) -> None:
3737

3838
if not isinstance(self.state, dict):
3939
raise TypeError(
40-
f"Invalid state type: {type(self.state)}. Expected dict or Pydantic model."
40+
f"Invalid state type: {type(self.state)}. Expected dict."
4141
)
4242

4343
logger.debug(f"Workflow state initialized with {len(self.state)} key(s).")

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dependencies = [
3535
"cloudevents>=1.11.0,<2.0.0",
3636
"numpy>=2.2.2,<3.0.0",
3737
"mcp>=1.7.1,<2.0.0",
38+
"websockets>=15.0.0,<16.0.0",
3839
"python-dotenv>=1.1.1,<2.0.0",
3940
"posthog<6.0.0",
4041
"nltk>=3.8.0,<4.0.0",

0 commit comments

Comments
 (0)