signalpilot-ai-internal 0.10.0__py3-none-any.whl → 0.11.24__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 (85) hide show
  1. signalpilot_ai_internal/__init__.py +1 -0
  2. signalpilot_ai_internal/_version.py +1 -1
  3. signalpilot_ai_internal/cache_service.py +22 -21
  4. signalpilot_ai_internal/composio_handlers.py +224 -0
  5. signalpilot_ai_internal/composio_service.py +511 -0
  6. signalpilot_ai_internal/database_config_handlers.py +182 -0
  7. signalpilot_ai_internal/database_config_service.py +166 -0
  8. signalpilot_ai_internal/databricks_schema_service.py +907 -0
  9. signalpilot_ai_internal/file_scanner_service.py +5 -146
  10. signalpilot_ai_internal/handlers.py +388 -9
  11. signalpilot_ai_internal/integrations_config.py +256 -0
  12. signalpilot_ai_internal/log_utils.py +31 -0
  13. signalpilot_ai_internal/mcp_handlers.py +532 -0
  14. signalpilot_ai_internal/mcp_server_manager.py +298 -0
  15. signalpilot_ai_internal/mcp_service.py +1255 -0
  16. signalpilot_ai_internal/oauth_token_store.py +141 -0
  17. signalpilot_ai_internal/schema_search_config.yml +17 -11
  18. signalpilot_ai_internal/schema_search_service.py +85 -4
  19. signalpilot_ai_internal/signalpilot_home.py +961 -0
  20. signalpilot_ai_internal/snowflake_schema_service.py +2 -0
  21. signalpilot_ai_internal/test_dbt_mcp_server.py +180 -0
  22. signalpilot_ai_internal/unified_database_schema_service.py +2 -0
  23. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/package.json.orig → signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/package.json +15 -48
  24. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/package.json → signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/package.json.orig +9 -52
  25. {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/plugin.json +7 -1
  26. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/122.bab318d6caadb055e29c.js +1 -0
  27. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/129.868ca665e6fc225c20a0.js +1 -0
  28. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/179.fd45a2e75d471d0aa3b9.js +7 -0
  29. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/220.81105a94aa873fc51a94.js +1 -0
  30. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/262.a002dd4630d3b6404a90.js +1 -0
  31. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/353.cc6f6ecacd703bcdb468.js +1 -0
  32. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/364.817a883549d55a0e0576.js +1 -0
  33. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/384.a4daecd44f1e9364e44a.js +1 -0
  34. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/439.667225aab294fb5ed161.js +1 -0
  35. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/447.8138af2522716e5a926f.js +1 -0
  36. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/476.925c73e32f3c07448da0.js +1 -0
  37. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/477.aaa4cc9e87801fb45f5b.js +1 -0
  38. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/481.370056149a59022b700c.js +1 -0
  39. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/510.868ca665e6fc225c20a0.js +1 -0
  40. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/512.835f97f7ccfc70ff5c93.js +1 -0
  41. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/57.6c13335f73de089d6b1e.js +1 -0
  42. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/574.ad2709e91ebcac5bbe68.js +1 -0
  43. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/635.bddbab8e464fe31f0393.js +1 -0
  44. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/713.fda1bcdb10497b0a6ade.js +1 -0
  45. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/741.d046701f475fcbf6697d.js +1 -0
  46. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/785.c306dffd4cfe8a613d13.js +1 -0
  47. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/801.e39898b6f336539f228c.js +1 -0
  48. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/880.77cc0ca10a1860df1b52.js +1 -0
  49. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/936.4e2850b2af985ed0d378.js +1 -0
  50. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/956.eeffe67d7781fd63ef4b.js +2 -0
  51. signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/remoteEntry.055f50d20a31f3068c72.js +1 -0
  52. {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/third-party-licenses.json +47 -29
  53. {signalpilot_ai_internal-0.10.0.dist-info → signalpilot_ai_internal-0.11.24.dist-info}/METADATA +14 -31
  54. signalpilot_ai_internal-0.11.24.dist-info/RECORD +66 -0
  55. signalpilot_ai_internal-0.11.24.dist-info/licenses/LICENSE +7 -0
  56. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/122.e2dadf63dc64d7b5f1ee.js +0 -1
  57. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/220.328403b5545f268b95c6.js +0 -1
  58. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/262.726e1da31a50868cb297.js +0 -1
  59. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/330.af2e9cb5def5ae2b84d5.js +0 -1
  60. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/353.972abe1d2d66f083f9cc.js +0 -1
  61. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/364.dbec4c2dc12e7b050dcc.js +0 -1
  62. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/384.fa432bdb7fb6b1c95ad6.js +0 -1
  63. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/439.37e271d7a80336daabe2.js +0 -1
  64. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/476.ad22ccddd74ee306fb56.js +0 -1
  65. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/481.73c7a9290b7d35a8b9c1.js +0 -1
  66. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/512.b58fc0093d080b8ee61c.js +0 -1
  67. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/553.b4042a795c91d9ff71ef.js +0 -2
  68. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/57.e9acd2e1f9739037f1ab.js +0 -1
  69. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/635.9720593ee20b768da3ca.js +0 -1
  70. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/713.8e6edc9a965bdd578ca7.js +0 -1
  71. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/741.dc49867fafb03ea2ba4d.js +0 -1
  72. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/742.91e7b516c8699eea3373.js +0 -1
  73. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/785.2d75de1a8d2c3131a8db.js +0 -1
  74. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/786.770dc7bcab77e14cc135.js +0 -7
  75. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/801.ca9e114a30896b669a3c.js +0 -1
  76. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/880.25ddd15aca09421d3765.js +0 -1
  77. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/888.34054db17bcf6e87ec95.js +0 -1
  78. signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/remoteEntry.b05b2f0c9617ba28370d.js +0 -1
  79. signalpilot_ai_internal-0.10.0.dist-info/RECORD +0 -50
  80. signalpilot_ai_internal-0.10.0.dist-info/licenses/LICENSE +0 -29
  81. {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/etc/jupyter/jupyter_server_config.d/signalpilot_ai.json +0 -0
  82. {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/install.json +0 -0
  83. /signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/553.b4042a795c91d9ff71ef.js.LICENSE.txt → /signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/956.eeffe67d7781fd63ef4b.js.LICENSE.txt +0 -0
  84. {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/style.js +0 -0
  85. {signalpilot_ai_internal-0.10.0.dist-info → signalpilot_ai_internal-0.11.24.dist-info}/WHEEL +0 -0
@@ -0,0 +1,256 @@
1
+ """
2
+ Integration Configuration for Composio OAuth Apps
3
+ This file contains static configuration for integrations with MCP servers.
4
+ IMPORTANT: This file is NOT exposed to the frontend for security reasons.
5
+ """
6
+
7
+ import base64
8
+ import json
9
+ import logging
10
+ from typing import Dict, List, Any
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Integration configuration - defines how to set up MCP servers after OAuth
15
+ INTEGRATION_CONFIG: Dict[str, Dict[str, Any]] = {
16
+ 'notion': {
17
+ 'id': 'notion',
18
+ 'name': 'Notion',
19
+ 'description': 'Connect to Notion workspaces for reading and searching pages',
20
+ 'mcp_server_id': 'notion-integration',
21
+ 'mcp_server_name': 'Notion (Composio)',
22
+ 'mcp_command': 'npx',
23
+ 'mcp_args': ['-y', '@notionhq/notion-mcp-server'],
24
+ # Maps Composio credential fields to MCP server env vars
25
+ 'env_mapping': {
26
+ 'access_token': 'NOTION_TOKEN',
27
+ },
28
+ 'whitelisted_tools': [
29
+ 'API-post-search',
30
+ 'API-get-block-children',
31
+ 'API-retrieve-a-page',
32
+ 'API-retrieve-a-database',
33
+ 'API-post-database-query',
34
+ ],
35
+ },
36
+ 'slack': {
37
+ 'id': 'slack',
38
+ 'name': 'Slack',
39
+ 'description': 'Connect to Slack workspaces for searching and reading messages',
40
+ 'mcp_server_id': 'slack-integration',
41
+ 'mcp_server_name': 'Slack (Composio)',
42
+ # Note: This requires the slack-mcp-server to be installed
43
+ # Using npx with a placeholder - user may need to adjust based on their setup
44
+ 'mcp_command': 'npx',
45
+ 'mcp_args': ['-y', 'slack-mcp-server@latest', '--transport', 'stdio'],
46
+ # Maps Composio credential fields to MCP server env vars
47
+ 'env_mapping': {
48
+ 'access_token': 'SLACK_MCP_XOXP_TOKEN',
49
+ },
50
+ 'whitelisted_tools': [
51
+ 'conversations_search_messages',
52
+ 'conversations_history',
53
+ 'conversations_replies',
54
+ 'channels_list',
55
+ ],
56
+ },
57
+ 'google': {
58
+ 'id': 'google',
59
+ 'name': 'Google Docs',
60
+ 'description': 'Connect to Google Drive and Docs for searching and reading documents',
61
+ 'mcp_server_id': 'google-integration',
62
+ 'mcp_server_name': 'Google Docs (Composio)',
63
+ # Note: This requires uvx to be installed
64
+ # --single-user makes USER_GOOGLE_EMAIL the default, skipping email in tool calls
65
+ 'mcp_command': 'uvx',
66
+ 'mcp_args': ['workspace-mcp', '--tools', 'drive', 'docs', '--single-user'],
67
+ # Maps Composio credential fields to MCP server env vars
68
+ 'env_mapping': {
69
+ 'client_id': 'GOOGLE_OAUTH_CLIENT_ID',
70
+ 'client_secret': 'GOOGLE_OAUTH_CLIENT_SECRET',
71
+ 'access_token': 'GOOGLE_ACCESS_TOKEN',
72
+ 'refresh_token': 'GOOGLE_REFRESH_TOKEN',
73
+ 'email': 'USER_GOOGLE_EMAIL', # User's Google email from OAuth
74
+ },
75
+ 'whitelisted_tools': [
76
+ 'start_google_auth',
77
+ 'search_docs',
78
+ 'get_doc_content',
79
+ 'list_docs_in_folder',
80
+ 'inspect_doc_structure',
81
+ 'read_document_comments',
82
+ 'create_document_comment',
83
+ 'reply_to_document_comment',
84
+ 'resolve_document_comment',
85
+ 'search_drive_files',
86
+ 'list_drive_items',
87
+ 'get_drive_file_content',
88
+ 'get_drive_file_download_url',
89
+ 'list_drive_items_in_folder',
90
+ ],
91
+ },
92
+ }
93
+
94
+
95
+ def get_integration_config(integration_id: str) -> Dict[str, Any] | None:
96
+ """Get configuration for a specific integration."""
97
+ return INTEGRATION_CONFIG.get(integration_id)
98
+
99
+
100
+ def get_all_integration_ids() -> List[str]:
101
+ """Get list of all integration IDs."""
102
+ return list(INTEGRATION_CONFIG.keys())
103
+
104
+
105
+ def get_integration_info_for_frontend() -> List[Dict[str, str]]:
106
+ """
107
+ Get integration info safe to expose to frontend.
108
+ Only includes id, name, and description - no MCP commands or env mappings.
109
+ """
110
+ return [
111
+ {
112
+ 'id': config['id'],
113
+ 'name': config['name'],
114
+ 'description': config['description'],
115
+ }
116
+ for config in INTEGRATION_CONFIG.values()
117
+ ]
118
+
119
+
120
+ def get_mcp_server_config(integration_id: str, credentials: Dict[str, Any]) -> Dict[str, Any] | None:
121
+ """
122
+ Generate MCP server configuration for an integration.
123
+ Maps credentials from Composio to the appropriate env vars for the MCP server.
124
+
125
+ DEPRECATED: Use get_mcp_server_config_for_storage() and store tokens separately.
126
+ """
127
+ config = INTEGRATION_CONFIG.get(integration_id)
128
+ if not config:
129
+ return None
130
+
131
+ # Build environment variables from credentials
132
+ env = {}
133
+ for cred_field, env_var in config['env_mapping'].items():
134
+ if cred_field in credentials:
135
+ env[env_var] = credentials[cred_field]
136
+
137
+ return {
138
+ 'id': config['mcp_server_id'],
139
+ 'name': config['mcp_server_name'],
140
+ 'command': config['mcp_command'],
141
+ 'args': config['mcp_args'],
142
+ 'env': env,
143
+ 'enabled': True,
144
+ 'enabledTools': config['whitelisted_tools'],
145
+ }
146
+
147
+
148
+ def get_mcp_server_config_for_storage(integration_id: str) -> Dict[str, Any] | None:
149
+ """
150
+ Generate MCP server configuration for storage (WITHOUT credentials).
151
+ Tokens should be stored separately using OAuthTokenStore and injected at runtime.
152
+
153
+ Returns config suitable for mcp.json without sensitive data.
154
+ """
155
+ config = INTEGRATION_CONFIG.get(integration_id)
156
+ if not config:
157
+ return None
158
+
159
+ return {
160
+ 'id': config['mcp_server_id'],
161
+ 'name': config['mcp_server_name'],
162
+ 'command': config['mcp_command'],
163
+ 'args': config['mcp_args'],
164
+ # No env vars with tokens - they're stored securely elsewhere
165
+ 'enabled': True,
166
+ 'enabledTools': config['whitelisted_tools'],
167
+ # Mark as OAuth integration so MCP service knows to look up tokens
168
+ 'isOAuthIntegration': True,
169
+ }
170
+
171
+
172
+ def _extract_email_from_id_token(id_token: str) -> str | None:
173
+ """
174
+ Extract email from a Google OAuth id_token (JWT).
175
+
176
+ The id_token is a JWT with three parts: header.payload.signature
177
+ We decode the payload to get the email claim.
178
+ """
179
+ try:
180
+ # JWT has 3 parts separated by dots
181
+ parts = id_token.split('.')
182
+ if len(parts) != 3:
183
+ return None
184
+
185
+ # Decode the payload (second part)
186
+ # Add padding if needed for base64 decoding
187
+ payload_b64 = parts[1]
188
+ padding = 4 - len(payload_b64) % 4
189
+ if padding != 4:
190
+ payload_b64 += '=' * padding
191
+
192
+ payload_json = base64.urlsafe_b64decode(payload_b64)
193
+ payload = json.loads(payload_json)
194
+
195
+ # Extract email from claims
196
+ email = payload.get('email')
197
+ if email:
198
+ logger.debug(f"[Integrations] Extracted email from id_token: {email}")
199
+ return email
200
+
201
+ return None
202
+ except Exception as e:
203
+ logger.warning(f"[Integrations] Failed to extract email from id_token: {e}")
204
+ return None
205
+
206
+
207
+ def build_env_from_credentials(integration_id: str, credentials: Dict[str, Any]) -> Dict[str, str]:
208
+ """
209
+ Build environment variables from Composio credentials.
210
+
211
+ Args:
212
+ integration_id: The integration ID
213
+ credentials: Credentials from Composio
214
+
215
+ Returns:
216
+ Dict of environment variable name -> value
217
+ """
218
+ config = INTEGRATION_CONFIG.get(integration_id)
219
+ if not config:
220
+ return {}
221
+
222
+ env = {}
223
+ for cred_field, env_var in config['env_mapping'].items():
224
+ if cred_field in credentials:
225
+ env[env_var] = credentials[cred_field]
226
+
227
+ # For Google integration, try to extract email from various sources if not directly available
228
+ if integration_id == 'google' and 'USER_GOOGLE_EMAIL' not in env:
229
+ email = None
230
+
231
+ # Try common field names for email
232
+ for field_name in ['email', 'userEmail', 'user_email', 'Email']:
233
+ if field_name in credentials:
234
+ email = credentials[field_name]
235
+ break
236
+
237
+ # Try to extract from id_token if not found directly
238
+ if not email:
239
+ id_token = credentials.get('id_token')
240
+ if id_token:
241
+ email = _extract_email_from_id_token(id_token)
242
+
243
+ if email:
244
+ env['USER_GOOGLE_EMAIL'] = email
245
+ else:
246
+ logger.warning(f"[Integrations] Could not find email in Google credentials. Keys: {list(credentials.keys())}")
247
+
248
+ return env
249
+
250
+
251
+ def get_mcp_server_id_for_integration(integration_id: str) -> str | None:
252
+ """Get the MCP server ID for an integration."""
253
+ config = INTEGRATION_CONFIG.get(integration_id)
254
+ if config:
255
+ return config['mcp_server_id']
256
+ return None
@@ -0,0 +1,31 @@
1
+ """
2
+ Logging utilities for SignalPilot AI.
3
+ Provides controlled print function that respects SIGNALPILOT_DEBUG environment variable.
4
+ """
5
+
6
+ import builtins
7
+ import os
8
+
9
+ # Debug mode controlled by environment variable
10
+ # Set SIGNALPILOT_DEBUG=1 to enable verbose logging (disabled by default)
11
+ _DEBUG = os.environ.get('SIGNALPILOT_DEBUG', '0').lower() not in ('0', 'false', 'no')
12
+
13
+ # Store the original print function
14
+ _original_print = builtins.print
15
+
16
+
17
+ def print(*args, **kwargs) -> None:
18
+ """Conditionally print messages based on debug mode.
19
+
20
+ Only prints when SIGNALPILOT_DEBUG environment variable is not set to 0/false/no.
21
+ Error messages (containing 'ERROR') are always printed regardless of debug mode.
22
+ """
23
+ message = ' '.join(str(arg) for arg in args)
24
+ # Always print errors, otherwise only print in debug mode
25
+ if 'ERROR' in message or _DEBUG:
26
+ _original_print(*args, **kwargs)
27
+
28
+
29
+ def is_debug_enabled() -> bool:
30
+ """Check if debug mode is enabled."""
31
+ return _DEBUG