dao-ai 0.1.20__py3-none-any.whl → 0.1.21__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.
- dao_ai/config.py +114 -33
- dao_ai/genie/cache/__init__.py +11 -9
- dao_ai/genie/cache/context_aware/__init__.py +21 -0
- dao_ai/genie/cache/context_aware/base.py +54 -1
- dao_ai/genie/cache/context_aware/in_memory.py +112 -0
- dao_ai/genie/cache/{optimization.py → context_aware/optimization.py} +83 -43
- dao_ai/genie/cache/context_aware/postgres.py +177 -0
- dao_ai/middleware/__init__.py +8 -1
- dao_ai/middleware/tool_call_observability.py +227 -0
- dao_ai/utils.py +7 -3
- {dao_ai-0.1.20.dist-info → dao_ai-0.1.21.dist-info}/METADATA +1 -1
- {dao_ai-0.1.20.dist-info → dao_ai-0.1.21.dist-info}/RECORD +15 -14
- {dao_ai-0.1.20.dist-info → dao_ai-0.1.21.dist-info}/WHEEL +0 -0
- {dao_ai-0.1.20.dist-info → dao_ai-0.1.21.dist-info}/entry_points.txt +0 -0
- {dao_ai-0.1.20.dist-info → dao_ai-0.1.21.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool call observability middleware for DAO AI agents.
|
|
3
|
+
|
|
4
|
+
This middleware logs detailed information about tool call patterns to help
|
|
5
|
+
diagnose whether tools are being called in parallel or sequentially.
|
|
6
|
+
|
|
7
|
+
Example YAML config::
|
|
8
|
+
|
|
9
|
+
middleware:
|
|
10
|
+
- name: dao_ai.middleware.tool_call_observability.\
|
|
11
|
+
create_tool_call_observability_middleware
|
|
12
|
+
args:
|
|
13
|
+
log_level: INFO
|
|
14
|
+
include_args: false
|
|
15
|
+
track_timing: true
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import time
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from langchain.agents.middleware import AgentMiddleware
|
|
24
|
+
from langchain_core.messages import AIMessage, BaseMessage
|
|
25
|
+
from langgraph.runtime import Runtime
|
|
26
|
+
from loguru import logger
|
|
27
|
+
|
|
28
|
+
from dao_ai.state import AgentState, Context
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"ToolCallObservabilityMiddleware",
|
|
32
|
+
"create_tool_call_observability_middleware",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ToolCallObservabilityMiddleware(AgentMiddleware[AgentState, Context]):
|
|
37
|
+
"""
|
|
38
|
+
Middleware that observes and logs tool call patterns.
|
|
39
|
+
|
|
40
|
+
Tracks:
|
|
41
|
+
- Number of tool calls per model response
|
|
42
|
+
- Whether tools are called in parallel (multiple per response) or sequentially
|
|
43
|
+
- Tool execution timing
|
|
44
|
+
- Cumulative statistics across the conversation
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
log_level: str = "INFO",
|
|
50
|
+
include_args: bool = False,
|
|
51
|
+
track_timing: bool = True,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Initialize the observability middleware.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
log_level: Logging level ("DEBUG", "INFO", "WARNING")
|
|
58
|
+
include_args: Whether to log tool call arguments (may be verbose)
|
|
59
|
+
track_timing: Whether to track execution timing
|
|
60
|
+
"""
|
|
61
|
+
self.log_level = log_level.upper()
|
|
62
|
+
self.include_args = include_args
|
|
63
|
+
self.track_timing = track_timing
|
|
64
|
+
|
|
65
|
+
# Statistics tracking (per-run, reset on before_agent)
|
|
66
|
+
self._total_model_calls = 0
|
|
67
|
+
self._total_tool_calls = 0
|
|
68
|
+
self._parallel_batches = 0 # Responses with 2+ tool calls
|
|
69
|
+
self._sequential_calls = 0 # Responses with exactly 1 tool call
|
|
70
|
+
self._tool_execution_times: dict[str, list[float]] = {}
|
|
71
|
+
self._run_start_time: float | None = None
|
|
72
|
+
|
|
73
|
+
def _log(self, message: str, **kwargs: Any) -> None:
|
|
74
|
+
"""Log at the configured level."""
|
|
75
|
+
log_fn = getattr(logger, self.log_level.lower(), logger.info)
|
|
76
|
+
log_fn(message, **kwargs)
|
|
77
|
+
|
|
78
|
+
def before_agent(
|
|
79
|
+
self,
|
|
80
|
+
state: AgentState,
|
|
81
|
+
runtime: Runtime[Context],
|
|
82
|
+
) -> dict[str, Any] | None:
|
|
83
|
+
"""Reset statistics at the start of each agent run."""
|
|
84
|
+
self._total_model_calls = 0
|
|
85
|
+
self._total_tool_calls = 0
|
|
86
|
+
self._parallel_batches = 0
|
|
87
|
+
self._sequential_calls = 0
|
|
88
|
+
self._tool_execution_times = {}
|
|
89
|
+
self._run_start_time = time.time()
|
|
90
|
+
|
|
91
|
+
self._log(
|
|
92
|
+
"Tool call observability: Agent run started",
|
|
93
|
+
thread_id=runtime.context.thread_id if runtime.context else None,
|
|
94
|
+
)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def after_model(
|
|
98
|
+
self,
|
|
99
|
+
state: AgentState,
|
|
100
|
+
runtime: Runtime[Context],
|
|
101
|
+
) -> dict[str, Any] | None:
|
|
102
|
+
"""Analyze tool calls in model response."""
|
|
103
|
+
self._total_model_calls += 1
|
|
104
|
+
|
|
105
|
+
# Extract tool calls from state messages (last message is model response)
|
|
106
|
+
messages: list[BaseMessage] = state.get("messages", [])
|
|
107
|
+
|
|
108
|
+
# Look at the last message which should be the model's response
|
|
109
|
+
for msg in messages[-1:]:
|
|
110
|
+
if isinstance(msg, AIMessage) and msg.tool_calls:
|
|
111
|
+
num_tool_calls = len(msg.tool_calls)
|
|
112
|
+
self._total_tool_calls += num_tool_calls
|
|
113
|
+
|
|
114
|
+
if num_tool_calls > 1:
|
|
115
|
+
self._parallel_batches += 1
|
|
116
|
+
self._log(
|
|
117
|
+
"PARALLEL tool calls detected",
|
|
118
|
+
num_tools=num_tool_calls,
|
|
119
|
+
tool_names=[tc["name"] for tc in msg.tool_calls],
|
|
120
|
+
model_call_number=self._total_model_calls,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if self.include_args:
|
|
124
|
+
for tc in msg.tool_calls:
|
|
125
|
+
self._log(
|
|
126
|
+
f" Tool: {tc['name']}",
|
|
127
|
+
args=tc.get("args", {}),
|
|
128
|
+
)
|
|
129
|
+
elif num_tool_calls == 1:
|
|
130
|
+
self._sequential_calls += 1
|
|
131
|
+
tc = msg.tool_calls[0]
|
|
132
|
+
log_kwargs: dict[str, Any] = {
|
|
133
|
+
"tool_name": tc["name"],
|
|
134
|
+
"model_call_number": self._total_model_calls,
|
|
135
|
+
}
|
|
136
|
+
if self.include_args:
|
|
137
|
+
log_kwargs["args"] = tc.get("args", {})
|
|
138
|
+
self._log("Sequential tool call", **log_kwargs)
|
|
139
|
+
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
def after_agent(
|
|
143
|
+
self,
|
|
144
|
+
state: AgentState,
|
|
145
|
+
runtime: Runtime[Context],
|
|
146
|
+
) -> dict[str, Any] | None:
|
|
147
|
+
"""Log final statistics."""
|
|
148
|
+
total_time = time.time() - self._run_start_time if self._run_start_time else 0
|
|
149
|
+
|
|
150
|
+
# Calculate parallelism ratio
|
|
151
|
+
total_responses_with_tools = self._parallel_batches + self._sequential_calls
|
|
152
|
+
parallelism_ratio = (
|
|
153
|
+
self._parallel_batches / total_responses_with_tools * 100
|
|
154
|
+
if total_responses_with_tools > 0
|
|
155
|
+
else 0
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Calculate average tool times
|
|
159
|
+
avg_times = {
|
|
160
|
+
name: round(sum(times) / len(times) * 1000, 2)
|
|
161
|
+
for name, times in self._tool_execution_times.items()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
self._log(
|
|
165
|
+
"Tool Call Observability Summary",
|
|
166
|
+
total_model_calls=self._total_model_calls,
|
|
167
|
+
total_tool_calls=self._total_tool_calls,
|
|
168
|
+
parallel_batches=self._parallel_batches,
|
|
169
|
+
sequential_calls=self._sequential_calls,
|
|
170
|
+
parallelism_ratio=f"{parallelism_ratio:.1f}%",
|
|
171
|
+
total_time_ms=round(total_time * 1000, 2),
|
|
172
|
+
avg_tool_times_ms=avg_times,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Log verdict
|
|
176
|
+
if self._parallel_batches > 0:
|
|
177
|
+
logger.success(
|
|
178
|
+
f"Parallel tool calling IS happening: "
|
|
179
|
+
f"{self._parallel_batches} batches with multiple tools"
|
|
180
|
+
)
|
|
181
|
+
elif self._sequential_calls > 0:
|
|
182
|
+
logger.warning(
|
|
183
|
+
f"All tool calls are SEQUENTIAL: "
|
|
184
|
+
f"{self._sequential_calls} single-tool responses. "
|
|
185
|
+
f"Consider prompt engineering to encourage parallel calls."
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def create_tool_call_observability_middleware(
|
|
192
|
+
log_level: str = "INFO",
|
|
193
|
+
include_args: bool = False,
|
|
194
|
+
track_timing: bool = True,
|
|
195
|
+
) -> ToolCallObservabilityMiddleware:
|
|
196
|
+
"""
|
|
197
|
+
Factory function to create tool call observability middleware.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
log_level: Logging level ("DEBUG", "INFO", "WARNING")
|
|
201
|
+
include_args: Whether to log tool call arguments
|
|
202
|
+
track_timing: Whether to track execution timing
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
ToolCallObservabilityMiddleware instance
|
|
206
|
+
|
|
207
|
+
Example YAML config::
|
|
208
|
+
|
|
209
|
+
middleware:
|
|
210
|
+
- name: dao_ai.middleware.tool_call_observability.\
|
|
211
|
+
create_tool_call_observability_middleware
|
|
212
|
+
args:
|
|
213
|
+
log_level: INFO
|
|
214
|
+
include_args: false
|
|
215
|
+
track_timing: true
|
|
216
|
+
"""
|
|
217
|
+
logger.debug(
|
|
218
|
+
"Creating tool call observability middleware",
|
|
219
|
+
log_level=log_level,
|
|
220
|
+
include_args=include_args,
|
|
221
|
+
track_timing=track_timing,
|
|
222
|
+
)
|
|
223
|
+
return ToolCallObservabilityMiddleware(
|
|
224
|
+
log_level=log_level,
|
|
225
|
+
include_args=include_args,
|
|
226
|
+
track_timing=track_timing,
|
|
227
|
+
)
|
dao_ai/utils.py
CHANGED
|
@@ -152,10 +152,14 @@ def dao_ai_version() -> str:
|
|
|
152
152
|
return "dev"
|
|
153
153
|
|
|
154
154
|
|
|
155
|
-
def get_installed_packages() ->
|
|
156
|
-
"""Get all installed packages with versions
|
|
155
|
+
def get_installed_packages() -> list[str]:
|
|
156
|
+
"""Get all installed packages with versions.
|
|
157
157
|
|
|
158
|
-
packages
|
|
158
|
+
Returns a list of pip requirement strings for packages used by dao-ai.
|
|
159
|
+
This is used for MLflow model logging to ensure all dependencies are captured.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
packages: list[str] = [
|
|
159
163
|
f"databricks-agents=={version('databricks-agents')}",
|
|
160
164
|
f"databricks-langchain[memory]=={version('databricks-langchain')}",
|
|
161
165
|
f"databricks-mcp=={version('databricks-mcp')}",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dao-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.21
|
|
4
4
|
Summary: DAO AI: A modular, multi-agent orchestration framework for complex AI workflows. Supports agent handoff, tool integration, and dynamic configuration via YAML.
|
|
5
5
|
Project-URL: Homepage, https://github.com/natefleming/dao-ai
|
|
6
6
|
Project-URL: Documentation, https://natefleming.github.io/dao-ai
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
dao_ai/__init__.py,sha256=18P98ExEgUaJ1Byw440Ct1ty59v6nxyWtc5S6Uq2m9Q,1062
|
|
2
2
|
dao_ai/catalog.py,sha256=sPZpHTD3lPx4EZUtIWeQV7VQM89WJ6YH__wluk1v2lE,4947
|
|
3
3
|
dao_ai/cli.py,sha256=7hVCC8mn9S3c4wW-eRt-WoFKzV1wPdJVAeNhkyhfUGc,53251
|
|
4
|
-
dao_ai/config.py,sha256=
|
|
4
|
+
dao_ai/config.py,sha256=alAPiAtglojEIBUm4dwrpnirkRbgDPBS-6DZAKCxyuE,159798
|
|
5
5
|
dao_ai/evaluation.py,sha256=4dveWDwFnUxaybswr0gag3ydZ5RGVCTRaiE3eKLClD4,18161
|
|
6
6
|
dao_ai/graph.py,sha256=1-uQlo7iXZQTT3uU8aYu0N5rnhw5_g_2YLwVsAs6M-U,1119
|
|
7
7
|
dao_ai/logging.py,sha256=lYy4BmucCHvwW7aI3YQkQXKJtMvtTnPDu9Hnd7_O4oc,1556
|
|
@@ -11,7 +11,7 @@ dao_ai/nodes.py,sha256=H7_C0ev0TpS5KWkGZD6eE4Wn6ouBwnN5HgYUyBeKe0A,10881
|
|
|
11
11
|
dao_ai/optimization.py,sha256=phK6t4wYmWPObCjGUBHdZzsaFXGhQOjhAek2bAEfwXo,22971
|
|
12
12
|
dao_ai/state.py,sha256=ifDTAC7epdowk3Z1CP3Xqw4uH2dIxQEVF3C747dA8yI,6436
|
|
13
13
|
dao_ai/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
dao_ai/utils.py,sha256=
|
|
14
|
+
dao_ai/utils.py,sha256=nQ5U92IU-JWLIikBg04s4O5gHUNX3j_Dy3YfAnT_ccM,17340
|
|
15
15
|
dao_ai/vector_search.py,sha256=PfmT2PDMymk-3dTm2uszlOZNyHyiDge--imxKpKJRsY,4440
|
|
16
16
|
dao_ai/apps/__init__.py,sha256=RLuhZf4gQ4pemwKDz1183aXib8UfaRhwfKvRx68GRlM,661
|
|
17
17
|
dao_ai/apps/handlers.py,sha256=6-IhhklHSPnS8aqKp155wPaSnYWTU1BSOPwbdWYBkFU,3594
|
|
@@ -20,16 +20,16 @@ dao_ai/apps/resources.py,sha256=5l6UxfMq6uspOql-HNDyUikfqRAa9eH_TiJHrGgMb6s,4002
|
|
|
20
20
|
dao_ai/apps/server.py,sha256=neWbVnC2z9f-tJZBnho70FytNDEVOdOM1YngoGc5KHI,1264
|
|
21
21
|
dao_ai/genie/__init__.py,sha256=UpSvP6gZO8H-eAPokYpkshvFxYD4ETYZHz-pRPoK2sI,2786
|
|
22
22
|
dao_ai/genie/core.py,sha256=eKZo4pRagwI6QglAXqHYjUaC3AicmGMiy_9WIuZ-tzw,9119
|
|
23
|
-
dao_ai/genie/cache/__init__.py,sha256=
|
|
23
|
+
dao_ai/genie/cache/__init__.py,sha256=gUKBcbSMicalQj6sc_bvuBUeVUUTZ90xIMytf9Bk_m8,2242
|
|
24
24
|
dao_ai/genie/cache/base.py,sha256=nbWl-KTstUPGagdUtO8xtVUSosuqkaNc_hx-PgT1ROo,7155
|
|
25
25
|
dao_ai/genie/cache/core.py,sha256=48sDY7dbrsmflb96OFEE8DYarNB6zyiFxZQG-qfhXD4,2537
|
|
26
26
|
dao_ai/genie/cache/lru.py,sha256=dWoNottME8y6y_OKnQZ1xH4NmQxk2PdXvUgKcdzjlxI,19935
|
|
27
|
-
dao_ai/genie/cache/
|
|
28
|
-
dao_ai/genie/cache/context_aware/
|
|
29
|
-
dao_ai/genie/cache/context_aware/
|
|
30
|
-
dao_ai/genie/cache/context_aware/
|
|
27
|
+
dao_ai/genie/cache/context_aware/__init__.py,sha256=8uxG-0_9SIqjmiNKyPXjn_pOhOi82RrcziljQOYdrwc,1946
|
|
28
|
+
dao_ai/genie/cache/context_aware/base.py,sha256=rxiXqJF5VZFTKEAr_MKqGH1hcqF5rxeqsC75Z7nlrWk,42819
|
|
29
|
+
dao_ai/genie/cache/context_aware/in_memory.py,sha256=9V7uDyXYe3A8GU3rxJq4icbt3IxSTuHhoPN6sJxOEDU,26809
|
|
30
|
+
dao_ai/genie/cache/context_aware/optimization.py,sha256=foUCjK5g0aRUH1wXWoPIVaGC6DqSstTrsP5naQGpw7U,31758
|
|
31
31
|
dao_ai/genie/cache/context_aware/persistent.py,sha256=cpn25Go6ZyN65lY_vh5cWcuqr__nNH7RPWJA_LP7wcE,28154
|
|
32
|
-
dao_ai/genie/cache/context_aware/postgres.py,sha256=
|
|
32
|
+
dao_ai/genie/cache/context_aware/postgres.py,sha256=HGadN7FmSXbDXc8itq6vz1tSbhyklxOnWB3qDegrVKc,53087
|
|
33
33
|
dao_ai/hooks/__init__.py,sha256=uA4DQdP9gDf4SyNjNx9mWPoI8UZOcTyFsCXV0NraFvQ,463
|
|
34
34
|
dao_ai/hooks/core.py,sha256=yZAfRfB0MyMo--uwGr4STtVxxen5s4ZUrNTnR3a3qkA,1721
|
|
35
35
|
dao_ai/memory/__init__.py,sha256=Us3wFehvug_h83m-UJ7OXdq2qZ0e9nHBQE7m5RwoAd8,559
|
|
@@ -37,7 +37,7 @@ dao_ai/memory/base.py,sha256=99nfr2UZJ4jmfTL_KrqUlRSCoRxzkZyWyx5WqeUoMdQ,338
|
|
|
37
37
|
dao_ai/memory/core.py,sha256=38H-JLIyUrRDIECLvpXK3iJlWG35X97E-DTo_4c3Jzc,6317
|
|
38
38
|
dao_ai/memory/databricks.py,sha256=SM6nwLjhSRJO4hLc3GUuht5YydYtTi3BAOae6jPwTm4,14377
|
|
39
39
|
dao_ai/memory/postgres.py,sha256=bSjtvEht0h6jy2ADN2vqISVQDxm_DeM586VDdGaShJQ,23168
|
|
40
|
-
dao_ai/middleware/__init__.py,sha256=
|
|
40
|
+
dao_ai/middleware/__init__.py,sha256=cmXRtyY4rvyv1p2BJktw-SllI5qZLk_EEeiWJtOkbiM,5358
|
|
41
41
|
dao_ai/middleware/assertions.py,sha256=C1K-TnNZfBEwWouioHCt6c48i1ux9QKfQaX6AzghhgE,27408
|
|
42
42
|
dao_ai/middleware/base.py,sha256=uG2tpdnjL5xY5jCKvb_m3UTBtl4ZC6fJQUkDsQvV8S4,1279
|
|
43
43
|
dao_ai/middleware/context_editing.py,sha256=5rNKqH1phFFQTVW-4nzlVH5cbqomD-HFEIy2Z841D4I,7687
|
|
@@ -50,6 +50,7 @@ dao_ai/middleware/model_retry.py,sha256=SlWjAcaEmvj6KBOkjUicChYjhlg7bAJM7-e6KLpH
|
|
|
50
50
|
dao_ai/middleware/pii.py,sha256=zetfoz1WlJ-V0vjJp37v8NGimXB27EkZfetUHpGCXno,5137
|
|
51
51
|
dao_ai/middleware/summarization.py,sha256=gp2s9uc4DEJat-mWjWEzMaR-zAAeUOXYvu5EEYtqae4,7143
|
|
52
52
|
dao_ai/middleware/tool_call_limit.py,sha256=WQ3NmA3pLo-pNPBmwM7KwkYpT1segEnWqkhgW1xNkCE,6321
|
|
53
|
+
dao_ai/middleware/tool_call_observability.py,sha256=mU52HwF82Yb05TIAYhu5cNkn3jcPGn6cKhsxT3rkS8M,7673
|
|
53
54
|
dao_ai/middleware/tool_retry.py,sha256=QfJ7yTHneME8VtnA88QcmnjXIegSFeJztyngy49wTgM,5568
|
|
54
55
|
dao_ai/middleware/tool_selector.py,sha256=POj72YdzZEiNGfW4AQXPBeVVS1RUBsiG7PBuSENEhe0,4516
|
|
55
56
|
dao_ai/orchestration/__init__.py,sha256=i85CLfRR335NcCFhaXABcMkn6WZfXnJ8cHH4YZsZN0s,1622
|
|
@@ -82,8 +83,8 @@ dao_ai/tools/time.py,sha256=tufJniwivq29y0LIffbgeBTIDE6VgrLpmVf8Qr90qjw,9224
|
|
|
82
83
|
dao_ai/tools/unity_catalog.py,sha256=oBlW6pH-Ne08g60QW9wVi_tyeVYDiecuNoxQbIIFmN8,16515
|
|
83
84
|
dao_ai/tools/vector_search.py,sha256=34uhd58FKHzvcdgHHoACRdZAUJWTaUuPYiwIqBwvGqk,29061
|
|
84
85
|
dao_ai/tools/verifier.py,sha256=ociBVsGkQNyhWS6F6G8x17V7zAQfSuTe4Xcd6Y-7lPE,4975
|
|
85
|
-
dao_ai-0.1.
|
|
86
|
-
dao_ai-0.1.
|
|
87
|
-
dao_ai-0.1.
|
|
88
|
-
dao_ai-0.1.
|
|
89
|
-
dao_ai-0.1.
|
|
86
|
+
dao_ai-0.1.21.dist-info/METADATA,sha256=v1iHbA1jzdcEUmkmOa0kyh16V7-BMhIx5HkAMWUnRlE,16954
|
|
87
|
+
dao_ai-0.1.21.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
88
|
+
dao_ai-0.1.21.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
|
|
89
|
+
dao_ai-0.1.21.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
|
|
90
|
+
dao_ai-0.1.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|