dao-ai 0.0.28__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. dao_ai/__init__.py +29 -0
  2. dao_ai/agent_as_code.py +2 -5
  3. dao_ai/cli.py +342 -58
  4. dao_ai/config.py +1610 -380
  5. dao_ai/genie/__init__.py +38 -0
  6. dao_ai/genie/cache/__init__.py +43 -0
  7. dao_ai/genie/cache/base.py +72 -0
  8. dao_ai/genie/cache/core.py +79 -0
  9. dao_ai/genie/cache/lru.py +347 -0
  10. dao_ai/genie/cache/semantic.py +970 -0
  11. dao_ai/genie/core.py +35 -0
  12. dao_ai/graph.py +27 -253
  13. dao_ai/hooks/__init__.py +9 -6
  14. dao_ai/hooks/core.py +27 -195
  15. dao_ai/logging.py +56 -0
  16. dao_ai/memory/__init__.py +10 -0
  17. dao_ai/memory/core.py +65 -30
  18. dao_ai/memory/databricks.py +402 -0
  19. dao_ai/memory/postgres.py +79 -38
  20. dao_ai/messages.py +6 -4
  21. dao_ai/middleware/__init__.py +158 -0
  22. dao_ai/middleware/assertions.py +806 -0
  23. dao_ai/middleware/base.py +50 -0
  24. dao_ai/middleware/context_editing.py +230 -0
  25. dao_ai/middleware/core.py +67 -0
  26. dao_ai/middleware/guardrails.py +420 -0
  27. dao_ai/middleware/human_in_the_loop.py +233 -0
  28. dao_ai/middleware/message_validation.py +586 -0
  29. dao_ai/middleware/model_call_limit.py +77 -0
  30. dao_ai/middleware/model_retry.py +121 -0
  31. dao_ai/middleware/pii.py +157 -0
  32. dao_ai/middleware/summarization.py +197 -0
  33. dao_ai/middleware/tool_call_limit.py +210 -0
  34. dao_ai/middleware/tool_retry.py +174 -0
  35. dao_ai/models.py +1306 -114
  36. dao_ai/nodes.py +240 -161
  37. dao_ai/optimization.py +674 -0
  38. dao_ai/orchestration/__init__.py +52 -0
  39. dao_ai/orchestration/core.py +294 -0
  40. dao_ai/orchestration/supervisor.py +279 -0
  41. dao_ai/orchestration/swarm.py +271 -0
  42. dao_ai/prompts.py +128 -31
  43. dao_ai/providers/databricks.py +584 -601
  44. dao_ai/state.py +157 -21
  45. dao_ai/tools/__init__.py +13 -5
  46. dao_ai/tools/agent.py +1 -3
  47. dao_ai/tools/core.py +64 -11
  48. dao_ai/tools/email.py +232 -0
  49. dao_ai/tools/genie.py +144 -294
  50. dao_ai/tools/mcp.py +223 -155
  51. dao_ai/tools/memory.py +50 -0
  52. dao_ai/tools/python.py +9 -14
  53. dao_ai/tools/search.py +14 -0
  54. dao_ai/tools/slack.py +22 -10
  55. dao_ai/tools/sql.py +202 -0
  56. dao_ai/tools/time.py +30 -7
  57. dao_ai/tools/unity_catalog.py +165 -88
  58. dao_ai/tools/vector_search.py +331 -221
  59. dao_ai/utils.py +166 -20
  60. dao_ai/vector_search.py +37 -0
  61. dao_ai-0.1.5.dist-info/METADATA +489 -0
  62. dao_ai-0.1.5.dist-info/RECORD +70 -0
  63. dao_ai/chat_models.py +0 -204
  64. dao_ai/guardrails.py +0 -112
  65. dao_ai/tools/human_in_the_loop.py +0 -100
  66. dao_ai-0.0.28.dist-info/METADATA +0 -1168
  67. dao_ai-0.0.28.dist-info/RECORD +0 -41
  68. {dao_ai-0.0.28.dist-info → dao_ai-0.1.5.dist-info}/WHEEL +0 -0
  69. {dao_ai-0.0.28.dist-info → dao_ai-0.1.5.dist-info}/entry_points.txt +0 -0
  70. {dao_ai-0.0.28.dist-info → dao_ai-0.1.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,174 @@
1
+ """
2
+ Tool retry middleware for DAO AI agents.
3
+
4
+ Automatically retries failed tool calls with configurable exponential backoff.
5
+
6
+ Example:
7
+ from dao_ai.middleware import create_tool_retry_middleware
8
+
9
+ # Retry failed tool calls with exponential backoff
10
+ middleware = create_tool_retry_middleware(
11
+ max_retries=3,
12
+ backoff_factor=2.0,
13
+ initial_delay=1.0,
14
+ )
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import Any, Callable, Literal
20
+
21
+ from langchain.agents.middleware import ToolRetryMiddleware
22
+ from langchain_core.tools import BaseTool
23
+ from loguru import logger
24
+
25
+ from dao_ai.config import BaseFunctionModel, ToolModel
26
+
27
+ __all__ = [
28
+ "ToolRetryMiddleware",
29
+ "create_tool_retry_middleware",
30
+ ]
31
+
32
+
33
+ def _resolve_tools(
34
+ tools: list[str | ToolModel | dict[str, Any]] | None,
35
+ ) -> list[str] | None:
36
+ """
37
+ Resolve tool specs to a list of tool name strings.
38
+
39
+ Returns None if tools is None (apply to all tools).
40
+ """
41
+ if tools is None:
42
+ return None
43
+
44
+ result: list[str] = []
45
+ for tool in tools:
46
+ if isinstance(tool, str):
47
+ result.append(tool)
48
+ elif isinstance(tool, dict):
49
+ try:
50
+ tool_model = ToolModel(**tool)
51
+ result.extend(_extract_tool_names(tool_model))
52
+ except Exception as e:
53
+ raise ValueError(f"Failed to construct ToolModel from dict: {e}") from e
54
+ elif isinstance(tool, ToolModel):
55
+ result.extend(_extract_tool_names(tool))
56
+ else:
57
+ raise TypeError(
58
+ f"Tool must be str, ToolModel, or dict, got {type(tool).__name__}"
59
+ )
60
+
61
+ return result if result else None
62
+
63
+
64
+ def _extract_tool_names(tool_model: ToolModel) -> list[str]:
65
+ """Extract tool names from ToolModel, falling back to ToolModel.name."""
66
+ function = tool_model.function
67
+
68
+ if not isinstance(function, BaseFunctionModel):
69
+ return [tool_model.name]
70
+
71
+ try:
72
+ tool_names = [
73
+ tool.name
74
+ for tool in function.as_tools()
75
+ if isinstance(tool, BaseTool) and tool.name
76
+ ]
77
+ return tool_names if tool_names else [tool_model.name]
78
+ except Exception:
79
+ return [tool_model.name]
80
+
81
+
82
+ def create_tool_retry_middleware(
83
+ max_retries: int = 3,
84
+ backoff_factor: float = 2.0,
85
+ initial_delay: float = 1.0,
86
+ max_delay: float | None = None,
87
+ jitter: bool = False,
88
+ tools: list[str | ToolModel | dict[str, Any]] | None = None,
89
+ retry_on: tuple[type[Exception], ...] | Callable[[Exception], bool] | None = None,
90
+ on_failure: Literal["continue", "error"] | Callable[[Exception], str] = "continue",
91
+ ) -> ToolRetryMiddleware:
92
+ """
93
+ Create a ToolRetryMiddleware for automatic tool call retries.
94
+
95
+ Handles transient failures in external API calls with exponential backoff.
96
+
97
+ Args:
98
+ max_retries: Max retry attempts after initial call. Default 3.
99
+ backoff_factor: Multiplier for exponential backoff. Default 2.0.
100
+ Delay = initial_delay * (backoff_factor ** retry_number)
101
+ Set to 0.0 for constant delay.
102
+ initial_delay: Initial delay in seconds before first retry. Default 1.0.
103
+ max_delay: Max delay in seconds (caps exponential growth). None = no cap.
104
+ jitter: Add ±25% random jitter to avoid thundering herd. Default False.
105
+ tools: List of tools to apply retry to. Can be:
106
+ - None: Apply to all tools (default)
107
+ - list of str: Tool names
108
+ - list of ToolModel: DAO AI tool models
109
+ - list of dict: Tool config dicts
110
+ retry_on: When to retry:
111
+ - None: Retry on all errors (default)
112
+ - tuple of Exception types: Retry only on these
113
+ - callable: Function(exception) -> bool
114
+ on_failure: Behavior when all retries exhausted:
115
+ - "continue": Return error message, let agent continue (default)
116
+ - "error": Re-raise exception, stop execution
117
+ - callable: Function(exception) -> str for custom message
118
+
119
+ Returns:
120
+ List containing ToolRetryMiddleware instance
121
+
122
+ Example:
123
+ # Basic retry with defaults
124
+ retry = create_tool_retry_middleware()
125
+
126
+ # Retry specific tools with custom backoff
127
+ retry = create_tool_retry_middleware(
128
+ max_retries=5,
129
+ backoff_factor=1.5,
130
+ initial_delay=0.5,
131
+ tools=["search_web", "query_database"],
132
+ )
133
+
134
+ # Retry only on specific exceptions
135
+ retry = create_tool_retry_middleware(
136
+ max_retries=3,
137
+ retry_on=(TimeoutError, ConnectionError),
138
+ on_failure="error",
139
+ )
140
+ """
141
+ tool_names = _resolve_tools(tools)
142
+
143
+ logger.debug(
144
+ "Creating tool retry middleware",
145
+ max_retries=max_retries,
146
+ backoff_factor=backoff_factor,
147
+ initial_delay=initial_delay,
148
+ max_delay=max_delay,
149
+ jitter=jitter,
150
+ tools=tool_names or "all",
151
+ on_failure=on_failure if isinstance(on_failure, str) else "custom",
152
+ )
153
+
154
+ # Build kwargs
155
+ kwargs: dict[str, Any] = {
156
+ "max_retries": max_retries,
157
+ "backoff_factor": backoff_factor,
158
+ "initial_delay": initial_delay,
159
+ "on_failure": on_failure,
160
+ }
161
+
162
+ if tool_names is not None:
163
+ kwargs["tools"] = tool_names
164
+
165
+ if max_delay is not None:
166
+ kwargs["max_delay"] = max_delay
167
+
168
+ if jitter:
169
+ kwargs["jitter"] = jitter
170
+
171
+ if retry_on is not None:
172
+ kwargs["retry_on"] = retry_on
173
+
174
+ return ToolRetryMiddleware(**kwargs)