aixtools 0.0.0__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.

Potentially problematic release.


This version of aixtools might be problematic. Click here for more details.

Files changed (88) hide show
  1. aixtools/.chainlit/config.toml +113 -0
  2. aixtools/.chainlit/translations/bn.json +214 -0
  3. aixtools/.chainlit/translations/en-US.json +214 -0
  4. aixtools/.chainlit/translations/gu.json +214 -0
  5. aixtools/.chainlit/translations/he-IL.json +214 -0
  6. aixtools/.chainlit/translations/hi.json +214 -0
  7. aixtools/.chainlit/translations/ja.json +214 -0
  8. aixtools/.chainlit/translations/kn.json +214 -0
  9. aixtools/.chainlit/translations/ml.json +214 -0
  10. aixtools/.chainlit/translations/mr.json +214 -0
  11. aixtools/.chainlit/translations/nl.json +214 -0
  12. aixtools/.chainlit/translations/ta.json +214 -0
  13. aixtools/.chainlit/translations/te.json +214 -0
  14. aixtools/.chainlit/translations/zh-CN.json +214 -0
  15. aixtools/__init__.py +11 -0
  16. aixtools/_version.py +34 -0
  17. aixtools/a2a/app.py +126 -0
  18. aixtools/a2a/google_sdk/__init__.py +0 -0
  19. aixtools/a2a/google_sdk/card.py +27 -0
  20. aixtools/a2a/google_sdk/pydantic_ai_adapter/agent_executor.py +199 -0
  21. aixtools/a2a/google_sdk/pydantic_ai_adapter/storage.py +26 -0
  22. aixtools/a2a/google_sdk/remote_agent_connection.py +88 -0
  23. aixtools/a2a/google_sdk/utils.py +59 -0
  24. aixtools/a2a/utils.py +115 -0
  25. aixtools/agents/__init__.py +12 -0
  26. aixtools/agents/agent.py +164 -0
  27. aixtools/agents/agent_batch.py +71 -0
  28. aixtools/agents/prompt.py +97 -0
  29. aixtools/app.py +143 -0
  30. aixtools/chainlit.md +14 -0
  31. aixtools/compliance/__init__.py +9 -0
  32. aixtools/compliance/private_data.py +138 -0
  33. aixtools/context.py +17 -0
  34. aixtools/db/__init__.py +17 -0
  35. aixtools/db/database.py +110 -0
  36. aixtools/db/vector_db.py +115 -0
  37. aixtools/google/client.py +25 -0
  38. aixtools/log_view/__init__.py +17 -0
  39. aixtools/log_view/app.py +195 -0
  40. aixtools/log_view/display.py +285 -0
  41. aixtools/log_view/export.py +51 -0
  42. aixtools/log_view/filters.py +41 -0
  43. aixtools/log_view/log_utils.py +26 -0
  44. aixtools/log_view/node_summary.py +229 -0
  45. aixtools/logfilters/__init__.py +7 -0
  46. aixtools/logfilters/context_filter.py +67 -0
  47. aixtools/logging/__init__.py +30 -0
  48. aixtools/logging/log_objects.py +227 -0
  49. aixtools/logging/logging_config.py +161 -0
  50. aixtools/logging/mcp_log_models.py +102 -0
  51. aixtools/logging/mcp_logger.py +172 -0
  52. aixtools/logging/model_patch_logging.py +87 -0
  53. aixtools/logging/open_telemetry.py +36 -0
  54. aixtools/mcp/__init__.py +9 -0
  55. aixtools/mcp/client.py +375 -0
  56. aixtools/mcp/example_client.py +30 -0
  57. aixtools/mcp/example_server.py +22 -0
  58. aixtools/mcp/fast_mcp_log.py +31 -0
  59. aixtools/mcp/faulty_mcp.py +319 -0
  60. aixtools/model_patch/model_patch.py +63 -0
  61. aixtools/server/__init__.py +29 -0
  62. aixtools/server/app_mounter.py +90 -0
  63. aixtools/server/path.py +72 -0
  64. aixtools/server/utils.py +70 -0
  65. aixtools/server/workspace_privacy.py +65 -0
  66. aixtools/testing/__init__.py +9 -0
  67. aixtools/testing/aix_test_model.py +149 -0
  68. aixtools/testing/mock_tool.py +66 -0
  69. aixtools/testing/model_patch_cache.py +279 -0
  70. aixtools/tools/doctor/__init__.py +3 -0
  71. aixtools/tools/doctor/tool_doctor.py +61 -0
  72. aixtools/tools/doctor/tool_recommendation.py +44 -0
  73. aixtools/utils/__init__.py +35 -0
  74. aixtools/utils/chainlit/cl_agent_show.py +82 -0
  75. aixtools/utils/chainlit/cl_utils.py +168 -0
  76. aixtools/utils/config.py +131 -0
  77. aixtools/utils/config_util.py +69 -0
  78. aixtools/utils/enum_with_description.py +37 -0
  79. aixtools/utils/files.py +17 -0
  80. aixtools/utils/persisted_dict.py +99 -0
  81. aixtools/utils/utils.py +167 -0
  82. aixtools/vault/__init__.py +7 -0
  83. aixtools/vault/vault.py +137 -0
  84. aixtools-0.0.0.dist-info/METADATA +669 -0
  85. aixtools-0.0.0.dist-info/RECORD +88 -0
  86. aixtools-0.0.0.dist-info/WHEEL +5 -0
  87. aixtools-0.0.0.dist-info/entry_points.txt +2 -0
  88. aixtools-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,167 @@
1
+ """
2
+ General utility functions for string manipulation, logging, and data handling.
3
+ """
4
+
5
+ import json
6
+ import uuid
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ import pandas as pd
11
+
12
+ DF_SHOW_MAX_ROWS = 20
13
+
14
+
15
+ def escape_newline(s, max_length: int = 300) -> str:
16
+ """Escape newlines in a string."""
17
+ s = str(s)
18
+ ss = "\\n".join(s.split("\n"))
19
+ if len(ss) <= max_length:
20
+ return ss
21
+ return "".join(ss[:max_length]) + "..."
22
+
23
+
24
+ def escape_backticks(s) -> str:
25
+ """Escape backticks in a string."""
26
+ s = f"{s}"
27
+ return s.replace("`", "\\`")
28
+
29
+
30
+ def find_file(path: Path, glob="*.pdf"):
31
+ """Recursively find all files matching the glob pattern in a directory"""
32
+ yield from path.rglob(glob)
33
+
34
+
35
+ def prepend_all_lines(msg, prepend="\t", skip_first_line: bool = False) -> str:
36
+ """Prepend all lines of a message with a prepend."""
37
+ out = ""
38
+ for i, line in enumerate(str(msg).split("\n")):
39
+ if i == 0 and skip_first_line:
40
+ out += f"{line}\n"
41
+ else:
42
+ out += f"{prepend}{line}\n"
43
+ return out
44
+
45
+
46
+ def remove_quotes(s):
47
+ """
48
+ Remove all quotes (including triple backticks with language specifications) surrounding a string.
49
+
50
+ This function strips the input string of all surrounding quotes, including single quotes, double quotes,
51
+ backticks, and triple backticks with or without language specifications. It continues to remove quotes
52
+ until none are left.
53
+
54
+ Args:
55
+ s (str): The input string potentially surrounded by quotes.
56
+
57
+ Returns:
58
+ str: The string with all surrounding quotes removed. If the input is None, returns None.
59
+ """
60
+ if s is None:
61
+ return None
62
+ s = s.strip()
63
+ while (
64
+ (s.startswith('"') and s.endswith('"'))
65
+ or (s.startswith("'") and s.endswith("'"))
66
+ or (s.startswith("`") and s.endswith("`"))
67
+ or ("```" in s)
68
+ ):
69
+ if "```" in s:
70
+ s = tripple_quote_strip(s)
71
+ else:
72
+ # Single quotes
73
+ s = s[1:-1].strip()
74
+ # Remove spaces
75
+ s = s.strip()
76
+ return s
77
+
78
+
79
+ def tabit(s: str, prefix="\t|") -> str:
80
+ """Add a prefix to each line of a string for improved readability."""
81
+ return prefix + str(s).replace("\n", f"\n{prefix}")
82
+
83
+
84
+ def to_str(data) -> str:
85
+ """Convert any data type to a readable string representation."""
86
+ # Primitive values, just use str()
87
+ if isinstance(data, str):
88
+ return f"'{str}'"
89
+ if isinstance(data, (bool, bytes, float, int)):
90
+ return str(data)
91
+ # Dataframes
92
+ if isinstance(data, pd.DataFrame):
93
+ if data.shape[0] > DF_SHOW_MAX_ROWS:
94
+ return f"Showing only the first {DF_SHOW_MAX_ROWS} rows out of {data.shape[0]}:\n" + data.head(
95
+ DF_SHOW_MAX_ROWS
96
+ ).to_markdown(index=False)
97
+ return data.to_markdown(index=False)
98
+ # Use json for list, dict, etc.
99
+ if isinstance(data, (dict, list, tuple)):
100
+ return json.dumps(data, indent=2, default=str)
101
+ return str(data)
102
+
103
+
104
+ def truncate(s: str, max_len=76, ellipsis="...") -> str:
105
+ """Truncate a string to a maximum length, adding ellipsis if needed."""
106
+ s = str(s)
107
+ if len(s) > max_len:
108
+ return s[: max_len - len(ellipsis)] + ellipsis
109
+ return s
110
+
111
+
112
+ def tripple_quote_strip(s):
113
+ """
114
+ Remove triple quotes from a string, including those with language specifications.
115
+
116
+ Eexamples:
117
+ ```sql SELECT * from table;```
118
+
119
+ ```Here is your code ```python c = a + b ``` This code will perform addition```
120
+
121
+ Args:
122
+ s (str): The input string potentially containing triple quotes.
123
+
124
+ Returns:
125
+ str: The string with triple quotes removed, if present.
126
+ """
127
+ if "```" not in s:
128
+ return s
129
+ left_pos, right_pos = len(s), s.rfind("```")
130
+ s_lower = s.lower()
131
+ pre_matched = ""
132
+ for lang in ["python3", "python2", "python", "bash", "json", "sql", ""]:
133
+ pre = f"```{lang}"
134
+ idx = s_lower.find(pre)
135
+ if idx != -1 and idx < left_pos:
136
+ left_pos = idx
137
+ pre_matched = pre
138
+ if left_pos < right_pos:
139
+ s = s[left_pos + len(pre_matched) : right_pos].strip()
140
+ return s
141
+
142
+
143
+ def timestamp_with_uuid() -> str:
144
+ """Get a timestamp string + a UUID string (first 8 chars)."""
145
+ (yyy, hh, uu) = timestamp_uuid_tuple()
146
+ return f"{yyy}.{hh}.{uu[:8]}"
147
+
148
+
149
+ def timestamp_uuid_tuple() -> tuple[str, str, str]:
150
+ """
151
+ Get a tuple of timestamp + a UUID: (YYYY-MM-DD, HH:MM:SS, UUID)
152
+ """
153
+ now = datetime.now()
154
+ return (now.strftime("%Y-%m-%d"), now.strftime("%H:%M:%S"), str(uuid.uuid4()))
155
+
156
+
157
+ def str2bool(v: str | None) -> bool:
158
+ """Convert a string to a boolean value."""
159
+ if not v:
160
+ return False
161
+ return str(v).lower() in ("yes", "true", "on", "1")
162
+
163
+
164
+ async def async_iter(items):
165
+ """Asynchronously iterate over items."""
166
+ for item in items:
167
+ yield item
@@ -0,0 +1,7 @@
1
+ """
2
+ Provides a Vault client for storing and retrieving user service api keys.
3
+ """
4
+
5
+ from .vault import VaultClient
6
+
7
+ __all__ = ["VaultClient"]
@@ -0,0 +1,137 @@
1
+ # ruff: noqa: PLR0913
2
+ """
3
+ Provides a Vault client for storing and retrieving user service api keys.
4
+ """
5
+
6
+ import logging
7
+ from typing import Dict, Optional
8
+
9
+ import hvac
10
+ from hvac.exceptions import InvalidPath
11
+
12
+ from aixtools.utils import config
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class VaultAuthError(Exception):
18
+ """Exception raised for vault authentication errors."""
19
+
20
+
21
+ class VaultClient:
22
+ """Vault client for storing and retrieving user service api keys."""
23
+
24
+ def __init__(self):
25
+ self.client = hvac.Client(url=config.VAULT_ADDRESS, token=config.VAULT_TOKEN)
26
+
27
+ if not self.client.is_authenticated():
28
+ raise VaultAuthError("Vault client authentication failed. Check vault_token.")
29
+
30
+ def _get_secret_path(self, user_id: str, service_name: Optional[str] = None) -> str:
31
+ """Generate the vault secret path for a user and optionally a service."""
32
+ if service_name:
33
+ return f"{config.VAULT_PATH_PREFIX}/{config.VAULT_ENV}/{user_id}/{service_name}"
34
+ return f"{config.VAULT_PATH_PREFIX}/{config.VAULT_ENV}/{user_id}"
35
+
36
+ def store_user_service_api_key(self, *, user_id: str, service_name: str, user_api_key: str):
37
+ """
38
+ Store user's service api key in the Vault at the specified vault mount
39
+ point, where the path is <path_prefix>/<env>/<user_id>/<service_name>.
40
+
41
+ This is a convenience method for storing a single API key.
42
+ For storing multiple secrets, use store_user_service_secret().
43
+ """
44
+ secret_dict = {"user-api-key": user_api_key}
45
+ self.store_user_service_secret(user_id=user_id, service_name=service_name, secret_data=secret_dict)
46
+
47
+ def read_user_service_api_key(self, *, user_id: str, service_name: str) -> Optional[str]:
48
+ """
49
+ Read user's service api key in from vault at the specified mount point,
50
+ where the path is <path_prefix>/<env>/<user_id>/<service_name>.
51
+
52
+ This is a convenience method for reading a single API key.
53
+ For reading multiple secrets, use read_user_service_secret().
54
+ """
55
+ secret_data = self.read_user_service_secret(user_id=user_id, service_name=service_name)
56
+ if secret_data is None:
57
+ return None
58
+ return secret_data.get("user-api-key")
59
+
60
+ def store_user_service_secret(self, *, user_id: str, service_name: str, secret_data: Dict[str, str]):
61
+ """
62
+ Store complete user service secret with multiple key-value pairs in the Vault
63
+ at the specified vault mount point, where the path is <path_prefix>/<env>/<user_id>/<service_name>.
64
+ """
65
+ secret_path = None
66
+ try:
67
+ secret_path = self._get_secret_path(user_id, service_name)
68
+ logger.info("Writing complete secret to path %s", secret_path)
69
+ self.client.secrets.kv.v2.create_or_update_secret(
70
+ secret_path, secret=secret_data, mount_point=config.VAULT_MOUNT_POINT
71
+ )
72
+
73
+ logger.info("Complete secret written to path %s", secret_path)
74
+ except Exception as e:
75
+ logger.error("Failed to write complete secret to path %s: %s", secret_path, str(e))
76
+ raise VaultAuthError(e) from e
77
+
78
+ def read_user_service_secret(self, *, user_id: str, service_name: str) -> Optional[Dict[str, str]]:
79
+ """
80
+ Read complete user service secret from vault at the specified mount point,
81
+ where the path is <path_prefix>/<env>/<user_id>/<service_name>.
82
+ Returns all key-value pairs in the secret or None if the secret doesn't exist.
83
+ """
84
+ secret_path = None
85
+
86
+ try:
87
+ secret_path = self._get_secret_path(user_id, service_name)
88
+ logger.info("Reading complete secret from path %s", secret_path)
89
+ response = self.client.secrets.kv.v2.read_secret_version(
90
+ secret_path, mount_point=config.VAULT_MOUNT_POINT, raise_on_deleted_version=True
91
+ )
92
+ secret_data = response["data"]["data"]
93
+ logger.info("Complete secret read from path %s", secret_path)
94
+ return secret_data
95
+ except InvalidPath:
96
+ # Secret path does not exist
97
+ logger.warning("Secret path does not exist %s", secret_path)
98
+ return None
99
+
100
+ except Exception as e:
101
+ logger.error("Failed to read complete secret from path %s: %s", secret_path, str(e))
102
+ raise VaultAuthError(e) from e
103
+
104
+ def list_user_secret_keys(self, *, user_id: str) -> list[str]:
105
+ """
106
+ List all secret keys (service names) for a user, optionally filtered by service name.
107
+
108
+ Args:
109
+ user_id: The user ID to list secrets for
110
+ service_name: Optional service name to filter results. If provided, returns only this service if it exists.
111
+
112
+ Returns:
113
+ List of service names (secret keys) for the user. Empty list if no secrets exist.
114
+ """
115
+ try:
116
+ # List all services for user
117
+ user_path = self._get_secret_path(user_id)
118
+ logger.info("Listing secret keys for user at path %s", user_path)
119
+
120
+ response = self.client.secrets.kv.v2.list_secrets(path=user_path, mount_point=config.VAULT_MOUNT_POINT)
121
+
122
+ if response and "data" in response and "keys" in response["data"]:
123
+ secret_keys = response["data"]["keys"]
124
+ # Remove trailing slashes from directory names if any
125
+ secret_keys = [key.rstrip("/") for key in secret_keys]
126
+ logger.info("Found %d secret keys for user %s", len(secret_keys), user_id)
127
+ return secret_keys
128
+ logger.info("No secret keys found for user %s", user_id)
129
+ return []
130
+
131
+ except InvalidPath:
132
+ # User path does not exist
133
+ logger.warning("User path does not exist for user %s", user_id)
134
+ return []
135
+ except Exception as e:
136
+ logger.error("Failed to list secret keys for user %s: %s", user_id, str(e))
137
+ raise VaultAuthError(e) from e