dao-ai 0.1.18__py3-none-any.whl → 0.1.19__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/genie/cache/lru.py CHANGED
@@ -124,9 +124,7 @@ class LRUCacheService(GenieServiceBase):
124
124
  if self._cache:
125
125
  oldest_key: str = next(iter(self._cache))
126
126
  del self._cache[oldest_key]
127
- logger.trace(
128
- "Evicted cache entry", layer=self.name, key_prefix=oldest_key[:50]
129
- )
127
+ logger.trace("Evicted cache entry", layer=self.name, key=oldest_key[:50])
130
128
 
131
129
  def _get(self, key: str) -> SQLCacheEntry | None:
132
130
  """Get from cache, returning None if not found or expired."""
@@ -137,7 +135,7 @@ class LRUCacheService(GenieServiceBase):
137
135
 
138
136
  if self._is_expired(entry):
139
137
  del self._cache[key]
140
- logger.trace("Expired cache entry", layer=self.name, key_prefix=key[:50])
138
+ logger.trace("Expired cache entry", layer=self.name, key=key[:50])
141
139
  return None
142
140
 
143
141
  self._cache.move_to_end(key)
@@ -157,11 +155,11 @@ class LRUCacheService(GenieServiceBase):
157
155
  conversation_id=response.conversation_id,
158
156
  created_at=datetime.now(),
159
157
  )
160
- logger.info(
158
+ logger.debug(
161
159
  "Stored cache entry",
162
160
  layer=self.name,
163
- key_prefix=key[:50],
164
- sql_prefix=response.query[:50] if response.query else None,
161
+ key=key[:50],
162
+ sql=response.query[:50] if response.query else None,
165
163
  cache_size=len(self._cache),
166
164
  capacity=self.capacity,
167
165
  )
@@ -180,7 +178,7 @@ class LRUCacheService(GenieServiceBase):
180
178
  w: WorkspaceClient = self.warehouse.workspace_client
181
179
  warehouse_id: str = str(self.warehouse.warehouse_id)
182
180
 
183
- logger.trace("Executing cached SQL", layer=self.name, sql_prefix=sql[:100])
181
+ logger.trace("Executing cached SQL", layer=self.name, sql=sql[:100])
184
182
 
185
183
  statement_response: StatementResponse = w.statement_execution.execute_statement(
186
184
  statement=sql,
@@ -258,13 +256,17 @@ class LRUCacheService(GenieServiceBase):
258
256
  cached: SQLCacheEntry | None = self._get(key)
259
257
 
260
258
  if cached is not None:
259
+ cache_age_seconds = (datetime.now() - cached.created_at).total_seconds()
261
260
  logger.info(
262
261
  "Cache HIT",
263
262
  layer=self.name,
264
- question_prefix=question[:50],
263
+ question=question[:80],
265
264
  conversation_id=conversation_id,
265
+ cached_sql=cached.query[:80] if cached.query else None,
266
+ cache_age_seconds=round(cache_age_seconds, 1),
266
267
  cache_size=self.size,
267
268
  capacity=self.capacity,
269
+ ttl_seconds=self.parameters.time_to_live_seconds,
268
270
  )
269
271
 
270
272
  # Re-execute the cached SQL to get fresh data
@@ -286,17 +288,19 @@ class LRUCacheService(GenieServiceBase):
286
288
  logger.info(
287
289
  "Cache MISS",
288
290
  layer=self.name,
289
- question_prefix=question[:50],
291
+ question=question[:80],
290
292
  conversation_id=conversation_id,
291
293
  cache_size=self.size,
292
294
  capacity=self.capacity,
295
+ ttl_seconds=self.parameters.time_to_live_seconds,
293
296
  delegating_to=type(self.impl).__name__,
294
297
  )
295
298
 
296
299
  result: CacheResult = self.impl.ask_question(question, conversation_id)
297
300
  with self._lock:
298
301
  self._put(key, result.response)
299
- return CacheResult(response=result.response, cache_hit=False, served_by=None)
302
+ # Propagate the inner cache's result - if it was a hit there, preserve that info
303
+ return result
300
304
 
301
305
  @property
302
306
  def space_id(self) -> str:
@@ -497,6 +497,7 @@ class SemanticCacheService(GenieServiceBase):
497
497
  conversation_context: str,
498
498
  question_embedding: list[float],
499
499
  context_embedding: list[float],
500
+ conversation_id: str | None = None,
500
501
  ) -> tuple[SQLCacheEntry, float] | None:
501
502
  """
502
503
  Find a semantically similar cached entry using dual embedding matching.
@@ -509,6 +510,7 @@ class SemanticCacheService(GenieServiceBase):
509
510
  conversation_context: The conversation context string
510
511
  question_embedding: The embedding vector of just the question
511
512
  context_embedding: The embedding vector of the conversation context
513
+ conversation_id: Optional conversation ID (for logging)
512
514
 
513
515
  Returns:
514
516
  Tuple of (SQLCacheEntry, combined_similarity_score) if found, None otherwise
@@ -576,8 +578,9 @@ class SemanticCacheService(GenieServiceBase):
576
578
  logger.info(
577
579
  "Cache MISS (no entries)",
578
580
  layer=self.name,
579
- question_prefix=question[:50],
581
+ question=question[:50],
580
582
  space=self.space_id,
583
+ delegating_to=type(self.impl).__name__,
581
584
  )
582
585
  return None
583
586
 
@@ -602,8 +605,8 @@ class SemanticCacheService(GenieServiceBase):
602
605
  context_sim=f"{context_similarity:.4f}",
603
606
  combined_sim=f"{combined_similarity:.4f}",
604
607
  is_valid=is_valid,
605
- cached_question_prefix=cached_question[:50],
606
- cached_context_prefix=cached_context[:80],
608
+ cached_question=cached_question[:50],
609
+ cached_context=cached_context[:80],
607
610
  )
608
611
 
609
612
  # Check BOTH similarity thresholds (dual embedding precision check)
@@ -613,6 +616,7 @@ class SemanticCacheService(GenieServiceBase):
613
616
  layer=self.name,
614
617
  question_sim=f"{question_similarity:.4f}",
615
618
  threshold=self.parameters.similarity_threshold,
619
+ delegating_to=type(self.impl).__name__,
616
620
  )
617
621
  return None
618
622
 
@@ -622,6 +626,7 @@ class SemanticCacheService(GenieServiceBase):
622
626
  layer=self.name,
623
627
  context_sim=f"{context_similarity:.4f}",
624
628
  threshold=self.parameters.context_similarity_threshold,
629
+ delegating_to=type(self.impl).__name__,
625
630
  )
626
631
  return None
627
632
 
@@ -635,17 +640,32 @@ class SemanticCacheService(GenieServiceBase):
635
640
  layer=self.name,
636
641
  combined_sim=f"{combined_similarity:.4f}",
637
642
  ttl_seconds=ttl_seconds,
638
- cached_question_prefix=cached_question[:50],
643
+ cached_question=cached_question[:50],
644
+ delegating_to=type(self.impl).__name__,
639
645
  )
640
646
  return None
641
647
 
648
+ from datetime import datetime as dt
649
+
650
+ cache_age_seconds = (
651
+ (dt.now(created_at.tzinfo) - created_at).total_seconds()
652
+ if created_at
653
+ else None
654
+ )
642
655
  logger.info(
643
656
  "Cache HIT",
644
657
  layer=self.name,
645
- question_sim=f"{question_similarity:.4f}",
646
- context_sim=f"{context_similarity:.4f}",
647
- combined_sim=f"{combined_similarity:.4f}",
648
- cached_question_prefix=cached_question[:50],
658
+ question=question[:80],
659
+ conversation_id=conversation_id,
660
+ matched_question=cached_question[:80],
661
+ cache_age_seconds=round(cache_age_seconds, 1)
662
+ if cache_age_seconds
663
+ else None,
664
+ question_similarity=f"{question_similarity:.4f}",
665
+ context_similarity=f"{context_similarity:.4f}",
666
+ combined_similarity=f"{combined_similarity:.4f}",
667
+ cached_sql=sql_query[:80] if sql_query else None,
668
+ ttl_seconds=self.parameters.time_to_live_seconds,
649
669
  )
650
670
 
651
671
  entry = SQLCacheEntry(
@@ -696,12 +716,12 @@ class SemanticCacheService(GenieServiceBase):
696
716
  response.conversation_id,
697
717
  ),
698
718
  )
699
- logger.info(
719
+ logger.debug(
700
720
  "Stored cache entry",
701
721
  layer=self.name,
702
- question_prefix=question[:50],
703
- context_prefix=conversation_context[:80],
704
- sql_prefix=response.query[:50] if response.query else None,
722
+ question=question[:50],
723
+ context=conversation_context[:80],
724
+ sql=response.query[:50] if response.query else None,
705
725
  space=self.space_id,
706
726
  table=self.table_name,
707
727
  )
@@ -796,7 +816,11 @@ class SemanticCacheService(GenieServiceBase):
796
816
 
797
817
  # Check cache using dual embedding similarity
798
818
  cache_result: tuple[SQLCacheEntry, float] | None = self._find_similar(
799
- question, conversation_context, question_embedding, context_embedding
819
+ question,
820
+ conversation_context,
821
+ question_embedding,
822
+ context_embedding,
823
+ conversation_id,
800
824
  )
801
825
 
802
826
  if cache_result is not None:
@@ -805,7 +829,8 @@ class SemanticCacheService(GenieServiceBase):
805
829
  "Semantic cache hit",
806
830
  layer=self.name,
807
831
  combined_similarity=f"{combined_similarity:.3f}",
808
- question_prefix=question[:50],
832
+ question=question[:50],
833
+ conversation_id=conversation_id,
809
834
  )
810
835
 
811
836
  # Re-execute the cached SQL to get fresh data
@@ -825,16 +850,25 @@ class SemanticCacheService(GenieServiceBase):
825
850
  return CacheResult(response=response, cache_hit=True, served_by=self.name)
826
851
 
827
852
  # Cache miss - delegate to wrapped service
828
- logger.trace("Cache miss", layer=self.name, question_prefix=question[:50])
853
+ logger.info(
854
+ "Cache MISS",
855
+ layer=self.name,
856
+ question=question[:80],
857
+ conversation_id=conversation_id,
858
+ space_id=self.space_id,
859
+ similarity_threshold=self.similarity_threshold,
860
+ delegating_to=type(self.impl).__name__,
861
+ )
829
862
 
830
863
  result: CacheResult = self.impl.ask_question(question, conversation_id)
831
864
 
832
865
  # Store in cache if we got a SQL query
833
866
  if result.response.query:
834
- logger.info(
867
+ logger.debug(
835
868
  "Storing new cache entry",
836
869
  layer=self.name,
837
- question_prefix=question[:50],
870
+ question=question[:50],
871
+ conversation_id=conversation_id,
838
872
  space=self.space_id,
839
873
  )
840
874
  self._store_entry(
@@ -848,7 +882,7 @@ class SemanticCacheService(GenieServiceBase):
848
882
  logger.warning(
849
883
  "Not caching: response has no SQL query",
850
884
  layer=self.name,
851
- question_prefix=question[:50],
885
+ question=question[:50],
852
886
  )
853
887
 
854
888
  return CacheResult(response=result.response, cache_hit=False, served_by=None)
dao_ai/tools/genie.py CHANGED
@@ -25,13 +25,19 @@ from pydantic import BaseModel
25
25
  from dao_ai.config import (
26
26
  AnyVariable,
27
27
  CompositeVariableModel,
28
+ GenieInMemorySemanticCacheParametersModel,
28
29
  GenieLRUCacheParametersModel,
29
30
  GenieRoomModel,
30
31
  GenieSemanticCacheParametersModel,
31
32
  value_of,
32
33
  )
33
34
  from dao_ai.genie import GenieService, GenieServiceBase
34
- from dao_ai.genie.cache import CacheResult, LRUCacheService, SemanticCacheService
35
+ from dao_ai.genie.cache import (
36
+ CacheResult,
37
+ InMemorySemanticCacheService,
38
+ LRUCacheService,
39
+ SemanticCacheService,
40
+ )
35
41
  from dao_ai.state import AgentState, Context, SessionState
36
42
 
37
43
 
@@ -67,6 +73,9 @@ def create_genie_tool(
67
73
  semantic_cache_parameters: GenieSemanticCacheParametersModel
68
74
  | dict[str, Any]
69
75
  | None = None,
76
+ in_memory_semantic_cache_parameters: GenieInMemorySemanticCacheParametersModel
77
+ | dict[str, Any]
78
+ | None = None,
70
79
  ) -> Callable[..., Command]:
71
80
  """
72
81
  Create a tool for interacting with Databricks Genie for natural language queries to databases.
@@ -84,7 +93,9 @@ def create_genie_tool(
84
93
  truncate_results: Whether to truncate large query results to fit token limits
85
94
  lru_cache_parameters: Optional LRU cache configuration for SQL query caching
86
95
  semantic_cache_parameters: Optional semantic cache configuration using pg_vector
87
- for similarity-based query matching
96
+ for similarity-based query matching (requires PostgreSQL/Lakebase)
97
+ in_memory_semantic_cache_parameters: Optional in-memory semantic cache configuration
98
+ for similarity-based query matching (no database required)
88
99
 
89
100
  Returns:
90
101
  A LangGraph tool that processes natural language queries through Genie
@@ -97,6 +108,7 @@ def create_genie_tool(
97
108
  name=name,
98
109
  has_lru_cache=lru_cache_parameters is not None,
99
110
  has_semantic_cache=semantic_cache_parameters is not None,
111
+ has_in_memory_semantic_cache=in_memory_semantic_cache_parameters is not None,
100
112
  )
101
113
 
102
114
  if isinstance(genie_room, dict):
@@ -110,6 +122,11 @@ def create_genie_tool(
110
122
  **semantic_cache_parameters
111
123
  )
112
124
 
125
+ if isinstance(in_memory_semantic_cache_parameters, dict):
126
+ in_memory_semantic_cache_parameters = GenieInMemorySemanticCacheParametersModel(
127
+ **in_memory_semantic_cache_parameters
128
+ )
129
+
113
130
  space_id: AnyVariable = genie_room.space_id or os.environ.get(
114
131
  "DATABRICKS_GENIE_SPACE_ID"
115
132
  )
@@ -165,7 +182,7 @@ GenieResponse: A response object containing the conversation ID and result from
165
182
 
166
183
  genie_service: GenieServiceBase = GenieService(genie)
167
184
 
168
- # Wrap with semantic cache first (checked second due to decorator pattern)
185
+ # Wrap with semantic cache first (checked second/third due to decorator pattern)
169
186
  if semantic_cache_parameters is not None:
170
187
  genie_service = SemanticCacheService(
171
188
  impl=genie_service,
@@ -173,6 +190,14 @@ GenieResponse: A response object containing the conversation ID and result from
173
190
  workspace_client=workspace_client,
174
191
  ).initialize()
175
192
 
193
+ # Wrap with in-memory semantic cache (alternative to PostgreSQL semantic cache)
194
+ if in_memory_semantic_cache_parameters is not None:
195
+ genie_service = InMemorySemanticCacheService(
196
+ impl=genie_service,
197
+ parameters=in_memory_semantic_cache_parameters,
198
+ workspace_client=workspace_client,
199
+ ).initialize()
200
+
176
201
  # Wrap with LRU cache last (checked first - fast O(1) exact match)
177
202
  if lru_cache_parameters is not None:
178
203
  genie_service = LRUCacheService(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dao-ai
3
- Version: 0.1.18
3
+ Version: 0.1.19
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
@@ -409,7 +409,8 @@ The `config/examples/` directory contains ready-to-use configurations organized
409
409
 
410
410
  - `01_getting_started/minimal.yaml` - Simplest possible agent
411
411
  - `02_tools/vector_search_with_reranking.yaml` - RAG with improved accuracy
412
- - `04_genie/genie_semantic_cache.yaml` - NL-to-SQL with two-tier caching
412
+ - `04_genie/genie_semantic_cache.yaml` - NL-to-SQL with PostgreSQL semantic caching
413
+ - `04_genie/genie_in_memory_semantic_cache.yaml` - NL-to-SQL with in-memory semantic caching (no database)
413
414
  - `05_memory/conversation_summarization.yaml` - Long conversation handling
414
415
  - `06_on_behalf_of_user/obo_basic.yaml` - User-level access control
415
416
  - `07_human_in_the_loop/human_in_the_loop.yaml` - Approval workflows
@@ -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=Mcw03hemsT4O63lAH6mqTaPZjx0Q01YTgj5CN0thODI,52121
4
- dao_ai/config.py,sha256=lPWOCTL8NxoARkI8MRZlDJ2HiCAXgBB-SAyqCQQUVC8,145474
4
+ dao_ai/config.py,sha256=CseNaB42FRKFz0NnfoCKmFROwVItwAHNAiipQqdwZbA,150445
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
@@ -20,11 +20,12 @@ 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=vdEyGhrt6L8GlK75SyYvTnl8QpHKDCJC5hJKLg4DesQ,1063
22
22
  dao_ai/genie/core.py,sha256=HPKbocvhnnw_PkQwfoq5bpgQmL9lZyyS6_goTJL8yiY,1073
23
- dao_ai/genie/cache/__init__.py,sha256=JfgCJl1NYQ1aZvZ4kly4T6uQK6ZCJ6PX_htuq7nJF50,1203
23
+ dao_ai/genie/cache/__init__.py,sha256=ssLDdBgPFeiCcvHgd1cujomUu2R0COHCmBpPGDMU17w,1318
24
24
  dao_ai/genie/cache/base.py,sha256=_MhHqYrHejVGrJjSLX26TdHwvQZb-HgiantRYSB8fJY,1961
25
- dao_ai/genie/cache/core.py,sha256=1hvV72kMJ5md78Am4cdAi6wpTC6vV7mn6Srpb8Pti_M,2544
26
- dao_ai/genie/cache/lru.py,sha256=E7zBqdLvuFj_hzZhjcoMyfacbtfTgkVRNuFqVYi2Ic4,11835
27
- dao_ai/genie/cache/semantic.py,sha256=P1TrlFLMmSgLPFeZYbQTSbsjNpgXmovgqxBO92zINFM,38075
25
+ dao_ai/genie/cache/core.py,sha256=48sDY7dbrsmflb96OFEE8DYarNB6zyiFxZQG-qfhXD4,2537
26
+ dao_ai/genie/cache/in_memory_semantic.py,sha256=1Q7dpqZUcnpxRkzwWp13G9u7-iV4DG2gcx2N7KbUJx0,32426
27
+ dao_ai/genie/cache/lru.py,sha256=c8_qkJ6NkCz9Jnr3KuCgTZDBWvHeuGnWyjMNy-l510I,12130
28
+ dao_ai/genie/cache/semantic.py,sha256=Fshc2qB1UGgLAO_1gwH7v_taEJw3838bppfrUI_x4o4,39411
28
29
  dao_ai/hooks/__init__.py,sha256=uA4DQdP9gDf4SyNjNx9mWPoI8UZOcTyFsCXV0NraFvQ,463
29
30
  dao_ai/hooks/core.py,sha256=yZAfRfB0MyMo--uwGr4STtVxxen5s4ZUrNTnR3a3qkA,1721
30
31
  dao_ai/memory/__init__.py,sha256=Us3wFehvug_h83m-UJ7OXdq2qZ0e9nHBQE7m5RwoAd8,559
@@ -63,7 +64,7 @@ dao_ai/tools/__init__.py,sha256=NfRpAKds_taHbx6gzLPWgtPXve-YpwzkoOAUflwxceM,1734
63
64
  dao_ai/tools/agent.py,sha256=plIWALywRjaDSnot13nYehBsrHRpBUpsVZakoGeajOE,1858
64
65
  dao_ai/tools/core.py,sha256=bRIN3BZhRQX8-Kpu3HPomliodyskCqjxynQmYbk6Vjs,3783
65
66
  dao_ai/tools/email.py,sha256=A3TsCoQgJR7UUWR0g45OPRGDpVoYwctFs1MOZMTt_d4,7389
66
- dao_ai/tools/genie.py,sha256=b0R51N5D58H1vpOCUCA88ALjLs58KSMn6nl80ap8_c0,11009
67
+ dao_ai/tools/genie.py,sha256=Zq3k7sfz0Jy5cm-RM5uNaWR5Q2sq-syaTwcgmxCWXUs,12114
67
68
  dao_ai/tools/instructed_retriever.py,sha256=iEu7oH1Z9_-Id0SMaq-dAgCNigeRrJDDTSZTcOJLl6k,12990
68
69
  dao_ai/tools/instruction_reranker.py,sha256=_1kGwrXkJk4QR2p8n3lAaYkUVoidxCxV9wNCtoS0qco,6730
69
70
  dao_ai/tools/mcp.py,sha256=4uvag52OJPInUEnxFLwpE0JRugTrgHeWbkP5lzIx4lg,22620
@@ -77,8 +78,8 @@ dao_ai/tools/time.py,sha256=tufJniwivq29y0LIffbgeBTIDE6VgrLpmVf8Qr90qjw,9224
77
78
  dao_ai/tools/unity_catalog.py,sha256=oBlW6pH-Ne08g60QW9wVi_tyeVYDiecuNoxQbIIFmN8,16515
78
79
  dao_ai/tools/vector_search.py,sha256=34uhd58FKHzvcdgHHoACRdZAUJWTaUuPYiwIqBwvGqk,29061
79
80
  dao_ai/tools/verifier.py,sha256=ociBVsGkQNyhWS6F6G8x17V7zAQfSuTe4Xcd6Y-7lPE,4975
80
- dao_ai-0.1.18.dist-info/METADATA,sha256=_458v0KntzrWOU-pc8hs0YsRjfW0Oacgf91gqNloT5w,16836
81
- dao_ai-0.1.18.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
82
- dao_ai-0.1.18.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
83
- dao_ai-0.1.18.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
84
- dao_ai-0.1.18.dist-info/RECORD,,
81
+ dao_ai-0.1.19.dist-info/METADATA,sha256=KnF0S-dwcgBA2YPOWBoVQFhBk9mVW57XAQ0bV8rL3RY,16954
82
+ dao_ai-0.1.19.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
83
+ dao_ai-0.1.19.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
84
+ dao_ai-0.1.19.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
85
+ dao_ai-0.1.19.dist-info/RECORD,,