dao-ai 0.1.1__py3-none-any.whl → 0.1.3__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 (47) hide show
  1. dao_ai/agent_as_code.py +2 -5
  2. dao_ai/cli.py +65 -15
  3. dao_ai/config.py +672 -218
  4. dao_ai/genie/cache/core.py +6 -2
  5. dao_ai/genie/cache/lru.py +29 -11
  6. dao_ai/genie/cache/semantic.py +95 -44
  7. dao_ai/hooks/core.py +5 -5
  8. dao_ai/logging.py +56 -0
  9. dao_ai/memory/core.py +61 -44
  10. dao_ai/memory/databricks.py +54 -41
  11. dao_ai/memory/postgres.py +77 -36
  12. dao_ai/middleware/assertions.py +45 -17
  13. dao_ai/middleware/core.py +13 -7
  14. dao_ai/middleware/guardrails.py +30 -25
  15. dao_ai/middleware/human_in_the_loop.py +9 -5
  16. dao_ai/middleware/message_validation.py +61 -29
  17. dao_ai/middleware/summarization.py +16 -11
  18. dao_ai/models.py +172 -69
  19. dao_ai/nodes.py +148 -19
  20. dao_ai/optimization.py +26 -16
  21. dao_ai/orchestration/core.py +15 -8
  22. dao_ai/orchestration/supervisor.py +22 -8
  23. dao_ai/orchestration/swarm.py +57 -12
  24. dao_ai/prompts.py +17 -17
  25. dao_ai/providers/databricks.py +365 -155
  26. dao_ai/state.py +24 -6
  27. dao_ai/tools/__init__.py +2 -0
  28. dao_ai/tools/agent.py +1 -3
  29. dao_ai/tools/core.py +7 -7
  30. dao_ai/tools/email.py +29 -77
  31. dao_ai/tools/genie.py +18 -13
  32. dao_ai/tools/mcp.py +223 -156
  33. dao_ai/tools/python.py +5 -2
  34. dao_ai/tools/search.py +1 -1
  35. dao_ai/tools/slack.py +21 -9
  36. dao_ai/tools/sql.py +202 -0
  37. dao_ai/tools/time.py +30 -7
  38. dao_ai/tools/unity_catalog.py +129 -86
  39. dao_ai/tools/vector_search.py +318 -244
  40. dao_ai/utils.py +15 -10
  41. dao_ai-0.1.3.dist-info/METADATA +455 -0
  42. dao_ai-0.1.3.dist-info/RECORD +64 -0
  43. dao_ai-0.1.1.dist-info/METADATA +0 -1878
  44. dao_ai-0.1.1.dist-info/RECORD +0 -62
  45. {dao_ai-0.1.1.dist-info → dao_ai-0.1.3.dist-info}/WHEEL +0 -0
  46. {dao_ai-0.1.1.dist-info → dao_ai-0.1.3.dist-info}/entry_points.txt +0 -0
  47. {dao_ai-0.1.1.dist-info → dao_ai-0.1.3.dist-info}/licenses/LICENSE +0 -0
dao_ai/agent_as_code.py CHANGED
@@ -1,11 +1,9 @@
1
- import sys
2
-
3
1
  import mlflow
4
- from loguru import logger
5
2
  from mlflow.models import ModelConfig
6
3
  from mlflow.pyfunc import ResponsesAgent
7
4
 
8
5
  from dao_ai.config import AppConfig
6
+ from dao_ai.logging import configure_logging
9
7
 
10
8
  mlflow.set_registry_uri("databricks-uc")
11
9
  mlflow.set_tracking_uri("databricks")
@@ -17,8 +15,7 @@ config: AppConfig = AppConfig(**model_config.to_dict())
17
15
 
18
16
  log_level: str = config.app.log_level
19
17
 
20
- logger.remove()
21
- logger.add(sys.stderr, level=log_level)
18
+ configure_logging(level=log_level)
22
19
 
23
20
  app: ResponsesAgent = config.as_responses_agent()
24
21
 
dao_ai/cli.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import argparse
2
+ import getpass
2
3
  import json
3
4
  import os
4
5
  import subprocess
@@ -13,11 +14,37 @@ from loguru import logger
13
14
 
14
15
  from dao_ai.config import AppConfig
15
16
  from dao_ai.graph import create_dao_ai_graph
17
+ from dao_ai.logging import configure_logging
16
18
  from dao_ai.models import save_image
17
19
  from dao_ai.utils import normalize_name
18
20
 
19
- logger.remove()
20
- logger.add(sys.stderr, level="ERROR")
21
+ configure_logging(level="ERROR")
22
+
23
+
24
+ def get_default_user_id() -> str:
25
+ """
26
+ Get the default user ID for the CLI session.
27
+
28
+ Tries to get the current user from Databricks, falls back to local user.
29
+
30
+ Returns:
31
+ User ID string (Databricks username or local username)
32
+ """
33
+ try:
34
+ # Try to get current user from Databricks SDK
35
+ from databricks.sdk import WorkspaceClient
36
+
37
+ w = WorkspaceClient()
38
+ current_user = w.current_user.me()
39
+ user_id = current_user.user_name
40
+ logger.debug(f"Using Databricks user: {user_id}")
41
+ return user_id
42
+ except Exception as e:
43
+ # Fall back to local system user
44
+ logger.debug(f"Could not get Databricks user, using local user: {e}")
45
+ local_user = getpass.getuser()
46
+ logger.debug(f"Using local user: {local_user}")
47
+ return local_user
21
48
 
22
49
 
23
50
  env_path: str = find_dotenv()
@@ -240,9 +267,9 @@ Use Ctrl-C to interrupt and exit immediately.
240
267
  """,
241
268
  epilog="""
242
269
  Examples:
243
- dao-ai chat -c config/model_config.yaml # Start chat with default settings
270
+ dao-ai chat -c config/model_config.yaml # Start chat (auto-detects user)
244
271
  dao-ai chat -c config/retail.yaml --custom-input store_num=87887 # Chat with custom store number
245
- dao-ai chat -c config/prod.yaml --user-id john123 # Chat with specific user ID
272
+ dao-ai chat -c config/prod.yaml --user-id john.doe@company.com # Chat with specific user ID
246
273
  dao-ai chat -c config/retail.yaml --custom-input store_num=123 --custom-input region=west # Multiple custom inputs
247
274
  """,
248
275
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -264,9 +291,9 @@ Examples:
264
291
  chat_parser.add_argument(
265
292
  "--user-id",
266
293
  type=str,
267
- default="my_user_id",
294
+ default=None, # Will be set to actual user in handle_chat_command
268
295
  metavar="ID",
269
- help="User ID for the chat session (default: my_user_id)",
296
+ help="User ID for the chat session (default: current Databricks user or local username)",
270
297
  )
271
298
  chat_parser.add_argument(
272
299
  "--thread-id",
@@ -292,6 +319,10 @@ def handle_chat_command(options: Namespace) -> None:
292
319
  logger.debug("Starting chat session with DAO AI system...")
293
320
 
294
321
  try:
322
+ # Set default user_id if not provided
323
+ if options.user_id is None:
324
+ options.user_id = get_default_user_id()
325
+
295
326
  config: AppConfig = AppConfig.from_file(options.config)
296
327
  app = create_dao_ai_graph(config)
297
328
 
@@ -354,12 +385,20 @@ def handle_chat_command(options: Namespace) -> None:
354
385
  )
355
386
  continue
356
387
 
357
- # Prepare custom inputs for the agent
358
- custom_inputs = {"configurable": configurable}
388
+ # Create Context object from configurable dict
389
+ from dao_ai.state import Context
390
+
391
+ context = Context(**configurable)
392
+
393
+ # Prepare config with thread_id for checkpointer
394
+ # Note: thread_id is needed in config for checkpointer/memory
395
+ config = {"configurable": {"thread_id": options.thread_id}}
359
396
 
360
397
  # Invoke the graph and handle interrupts (HITL)
361
398
  # Wrap in async function to maintain connection pool throughout
362
399
  logger.debug(f"Invoking graph with {len(messages)} messages")
400
+ logger.debug(f"Context: {context}")
401
+ logger.debug(f"Config: {config}")
363
402
 
364
403
  import asyncio
365
404
 
@@ -369,7 +408,8 @@ def handle_chat_command(options: Namespace) -> None:
369
408
  """Invoke graph and handle HITL interrupts in single async context."""
370
409
  result = await app.ainvoke(
371
410
  {"messages": messages},
372
- config=custom_inputs,
411
+ config=config,
412
+ context=context, # Pass context as separate parameter
373
413
  )
374
414
 
375
415
  # Check for interrupts (Human-in-the-Loop) using __interrupt__
@@ -471,7 +511,8 @@ def handle_chat_command(options: Namespace) -> None:
471
511
  logger.debug(f"Resuming with {len(decisions)} decision(s)")
472
512
  result = await app.ainvoke(
473
513
  Command(resume={"decisions": decisions}),
474
- config=custom_inputs,
514
+ config=config,
515
+ context=context,
475
516
  )
476
517
 
477
518
  return result
@@ -613,7 +654,6 @@ def handle_validate_command(options: Namespace) -> None:
613
654
 
614
655
 
615
656
  def setup_logging(verbosity: int) -> None:
616
- logger.remove()
617
657
  levels: dict[int, str] = {
618
658
  0: "ERROR",
619
659
  1: "WARNING",
@@ -622,7 +662,7 @@ def setup_logging(verbosity: int) -> None:
622
662
  4: "TRACE",
623
663
  }
624
664
  level: str = levels.get(verbosity, "TRACE")
625
- logger.add(sys.stderr, level=level)
665
+ configure_logging(level=level)
626
666
 
627
667
 
628
668
  def generate_bundle_from_template(config_path: Path, app_name: str) -> Path:
@@ -675,7 +715,15 @@ def run_databricks_command(
675
715
  target: Optional[str] = None,
676
716
  dry_run: bool = False,
677
717
  ) -> None:
678
- """Execute a databricks CLI command with optional profile and target."""
718
+ """Execute a databricks CLI command with optional profile and target.
719
+
720
+ Args:
721
+ command: The databricks CLI command to execute (e.g., ["bundle", "deploy"])
722
+ profile: Optional Databricks CLI profile name
723
+ config: Optional path to the configuration file
724
+ target: Optional bundle target name
725
+ dry_run: If True, print the command without executing
726
+ """
679
727
  config_path = Path(config) if config else None
680
728
 
681
729
  if config_path and not config_path.exists():
@@ -697,15 +745,17 @@ def run_databricks_command(
697
745
  logger.debug(f"Using app-specific target: {target}")
698
746
 
699
747
  # Build databricks command (no -c flag needed, uses databricks.yaml in current dir)
748
+ # Note: --profile is a global flag, but --target is a subcommand flag for 'bundle'
700
749
  cmd = ["databricks"]
701
750
  if profile:
702
751
  cmd.extend(["--profile", profile])
703
752
 
753
+ cmd.extend(command)
754
+
755
+ # --target must come after the bundle subcommand (it's a subcommand-specific flag)
704
756
  if target:
705
757
  cmd.extend(["--target", target])
706
758
 
707
- cmd.extend(command)
708
-
709
759
  # Add config_path variable for notebooks
710
760
  if config_path and app_config:
711
761
  # Calculate relative path from notebooks directory to config file