atlas-chat 0.1.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.
- atlas/__init__.py +40 -0
- atlas/application/__init__.py +7 -0
- atlas/application/chat/__init__.py +7 -0
- atlas/application/chat/agent/__init__.py +10 -0
- atlas/application/chat/agent/act_loop.py +179 -0
- atlas/application/chat/agent/factory.py +142 -0
- atlas/application/chat/agent/protocols.py +46 -0
- atlas/application/chat/agent/react_loop.py +338 -0
- atlas/application/chat/agent/think_act_loop.py +171 -0
- atlas/application/chat/approval_manager.py +151 -0
- atlas/application/chat/elicitation_manager.py +191 -0
- atlas/application/chat/events/__init__.py +1 -0
- atlas/application/chat/events/agent_event_relay.py +112 -0
- atlas/application/chat/modes/__init__.py +1 -0
- atlas/application/chat/modes/agent.py +125 -0
- atlas/application/chat/modes/plain.py +74 -0
- atlas/application/chat/modes/rag.py +81 -0
- atlas/application/chat/modes/tools.py +179 -0
- atlas/application/chat/orchestrator.py +213 -0
- atlas/application/chat/policies/__init__.py +1 -0
- atlas/application/chat/policies/tool_authorization.py +99 -0
- atlas/application/chat/preprocessors/__init__.py +1 -0
- atlas/application/chat/preprocessors/message_builder.py +92 -0
- atlas/application/chat/preprocessors/prompt_override_service.py +104 -0
- atlas/application/chat/service.py +454 -0
- atlas/application/chat/utilities/__init__.py +6 -0
- atlas/application/chat/utilities/error_handler.py +367 -0
- atlas/application/chat/utilities/event_notifier.py +546 -0
- atlas/application/chat/utilities/file_processor.py +613 -0
- atlas/application/chat/utilities/tool_executor.py +789 -0
- atlas/atlas_chat_cli.py +347 -0
- atlas/atlas_client.py +238 -0
- atlas/core/__init__.py +0 -0
- atlas/core/auth.py +205 -0
- atlas/core/authorization_manager.py +27 -0
- atlas/core/capabilities.py +123 -0
- atlas/core/compliance.py +215 -0
- atlas/core/domain_whitelist.py +147 -0
- atlas/core/domain_whitelist_middleware.py +82 -0
- atlas/core/http_client.py +28 -0
- atlas/core/log_sanitizer.py +102 -0
- atlas/core/metrics_logger.py +59 -0
- atlas/core/middleware.py +131 -0
- atlas/core/otel_config.py +242 -0
- atlas/core/prompt_risk.py +200 -0
- atlas/core/rate_limit.py +0 -0
- atlas/core/rate_limit_middleware.py +64 -0
- atlas/core/security_headers_middleware.py +51 -0
- atlas/domain/__init__.py +37 -0
- atlas/domain/chat/__init__.py +1 -0
- atlas/domain/chat/dtos.py +85 -0
- atlas/domain/errors.py +96 -0
- atlas/domain/messages/__init__.py +12 -0
- atlas/domain/messages/models.py +160 -0
- atlas/domain/rag_mcp_service.py +664 -0
- atlas/domain/sessions/__init__.py +7 -0
- atlas/domain/sessions/models.py +36 -0
- atlas/domain/unified_rag_service.py +371 -0
- atlas/infrastructure/__init__.py +10 -0
- atlas/infrastructure/app_factory.py +135 -0
- atlas/infrastructure/events/__init__.py +1 -0
- atlas/infrastructure/events/cli_event_publisher.py +140 -0
- atlas/infrastructure/events/websocket_publisher.py +140 -0
- atlas/infrastructure/sessions/in_memory_repository.py +56 -0
- atlas/infrastructure/transport/__init__.py +7 -0
- atlas/infrastructure/transport/websocket_connection_adapter.py +33 -0
- atlas/init_cli.py +226 -0
- atlas/interfaces/__init__.py +15 -0
- atlas/interfaces/events.py +134 -0
- atlas/interfaces/llm.py +54 -0
- atlas/interfaces/rag.py +40 -0
- atlas/interfaces/sessions.py +75 -0
- atlas/interfaces/tools.py +57 -0
- atlas/interfaces/transport.py +24 -0
- atlas/main.py +564 -0
- atlas/mcp/api_key_demo/README.md +76 -0
- atlas/mcp/api_key_demo/main.py +172 -0
- atlas/mcp/api_key_demo/run.sh +56 -0
- atlas/mcp/basictable/main.py +147 -0
- atlas/mcp/calculator/main.py +149 -0
- atlas/mcp/code-executor/execution_engine.py +98 -0
- atlas/mcp/code-executor/execution_environment.py +95 -0
- atlas/mcp/code-executor/main.py +528 -0
- atlas/mcp/code-executor/result_processing.py +276 -0
- atlas/mcp/code-executor/script_generation.py +195 -0
- atlas/mcp/code-executor/security_checker.py +140 -0
- atlas/mcp/corporate_cars/main.py +437 -0
- atlas/mcp/csv_reporter/main.py +545 -0
- atlas/mcp/duckduckgo/main.py +182 -0
- atlas/mcp/elicitation_demo/README.md +171 -0
- atlas/mcp/elicitation_demo/main.py +262 -0
- atlas/mcp/env-demo/README.md +158 -0
- atlas/mcp/env-demo/main.py +199 -0
- atlas/mcp/file_size_test/main.py +284 -0
- atlas/mcp/filesystem/main.py +348 -0
- atlas/mcp/image_demo/main.py +113 -0
- atlas/mcp/image_demo/requirements.txt +4 -0
- atlas/mcp/logging_demo/README.md +72 -0
- atlas/mcp/logging_demo/main.py +103 -0
- atlas/mcp/many_tools_demo/main.py +50 -0
- atlas/mcp/order_database/__init__.py +0 -0
- atlas/mcp/order_database/main.py +369 -0
- atlas/mcp/order_database/signal_data.csv +1001 -0
- atlas/mcp/pdfbasic/main.py +394 -0
- atlas/mcp/pptx_generator/main.py +760 -0
- atlas/mcp/pptx_generator/requirements.txt +13 -0
- atlas/mcp/pptx_generator/run_test.sh +1 -0
- atlas/mcp/pptx_generator/test_pptx_generator_security.py +169 -0
- atlas/mcp/progress_demo/main.py +167 -0
- atlas/mcp/progress_updates_demo/QUICKSTART.md +273 -0
- atlas/mcp/progress_updates_demo/README.md +120 -0
- atlas/mcp/progress_updates_demo/main.py +497 -0
- atlas/mcp/prompts/main.py +222 -0
- atlas/mcp/public_demo/main.py +189 -0
- atlas/mcp/sampling_demo/README.md +169 -0
- atlas/mcp/sampling_demo/main.py +234 -0
- atlas/mcp/thinking/main.py +77 -0
- atlas/mcp/tool_planner/main.py +240 -0
- atlas/mcp/ui-demo/badmesh.png +0 -0
- atlas/mcp/ui-demo/main.py +383 -0
- atlas/mcp/ui-demo/templates/button_demo.html +32 -0
- atlas/mcp/ui-demo/templates/data_visualization.html +32 -0
- atlas/mcp/ui-demo/templates/form_demo.html +28 -0
- atlas/mcp/username-override-demo/README.md +320 -0
- atlas/mcp/username-override-demo/main.py +308 -0
- atlas/modules/__init__.py +0 -0
- atlas/modules/config/__init__.py +34 -0
- atlas/modules/config/cli.py +231 -0
- atlas/modules/config/config_manager.py +1096 -0
- atlas/modules/file_storage/__init__.py +22 -0
- atlas/modules/file_storage/cli.py +330 -0
- atlas/modules/file_storage/content_extractor.py +290 -0
- atlas/modules/file_storage/manager.py +295 -0
- atlas/modules/file_storage/mock_s3_client.py +402 -0
- atlas/modules/file_storage/s3_client.py +417 -0
- atlas/modules/llm/__init__.py +19 -0
- atlas/modules/llm/caller.py +287 -0
- atlas/modules/llm/litellm_caller.py +675 -0
- atlas/modules/llm/models.py +19 -0
- atlas/modules/mcp_tools/__init__.py +17 -0
- atlas/modules/mcp_tools/client.py +2123 -0
- atlas/modules/mcp_tools/token_storage.py +556 -0
- atlas/modules/prompts/prompt_provider.py +130 -0
- atlas/modules/rag/__init__.py +24 -0
- atlas/modules/rag/atlas_rag_client.py +336 -0
- atlas/modules/rag/client.py +129 -0
- atlas/routes/admin_routes.py +865 -0
- atlas/routes/config_routes.py +484 -0
- atlas/routes/feedback_routes.py +361 -0
- atlas/routes/files_routes.py +274 -0
- atlas/routes/health_routes.py +40 -0
- atlas/routes/mcp_auth_routes.py +223 -0
- atlas/server_cli.py +164 -0
- atlas/tests/conftest.py +20 -0
- atlas/tests/integration/test_mcp_auth_integration.py +152 -0
- atlas/tests/manual_test_sampling.py +87 -0
- atlas/tests/modules/mcp_tools/test_client_auth.py +226 -0
- atlas/tests/modules/mcp_tools/test_client_env.py +191 -0
- atlas/tests/test_admin_mcp_server_management_routes.py +141 -0
- atlas/tests/test_agent_roa.py +135 -0
- atlas/tests/test_app_factory_smoke.py +47 -0
- atlas/tests/test_approval_manager.py +439 -0
- atlas/tests/test_atlas_client.py +188 -0
- atlas/tests/test_atlas_rag_client.py +447 -0
- atlas/tests/test_atlas_rag_integration.py +224 -0
- atlas/tests/test_attach_file_flow.py +287 -0
- atlas/tests/test_auth_utils.py +165 -0
- atlas/tests/test_backend_public_url.py +185 -0
- atlas/tests/test_banner_logging.py +287 -0
- atlas/tests/test_capability_tokens_and_injection.py +203 -0
- atlas/tests/test_compliance_level.py +54 -0
- atlas/tests/test_compliance_manager.py +253 -0
- atlas/tests/test_config_manager.py +617 -0
- atlas/tests/test_config_manager_paths.py +12 -0
- atlas/tests/test_core_auth.py +18 -0
- atlas/tests/test_core_utils.py +190 -0
- atlas/tests/test_docker_env_sync.py +202 -0
- atlas/tests/test_domain_errors.py +329 -0
- atlas/tests/test_domain_whitelist.py +359 -0
- atlas/tests/test_elicitation_manager.py +408 -0
- atlas/tests/test_elicitation_routing.py +296 -0
- atlas/tests/test_env_demo_server.py +88 -0
- atlas/tests/test_error_classification.py +113 -0
- atlas/tests/test_error_flow_integration.py +116 -0
- atlas/tests/test_feedback_routes.py +333 -0
- atlas/tests/test_file_content_extraction.py +1134 -0
- atlas/tests/test_file_extraction_routes.py +158 -0
- atlas/tests/test_file_library.py +107 -0
- atlas/tests/test_file_manager_unit.py +18 -0
- atlas/tests/test_health_route.py +49 -0
- atlas/tests/test_http_client_stub.py +8 -0
- atlas/tests/test_imports_smoke.py +30 -0
- atlas/tests/test_interfaces_llm_response.py +9 -0
- atlas/tests/test_issue_access_denied_fix.py +136 -0
- atlas/tests/test_llm_env_expansion.py +836 -0
- atlas/tests/test_log_level_sensitive_data.py +285 -0
- atlas/tests/test_mcp_auth_routes.py +341 -0
- atlas/tests/test_mcp_client_auth.py +331 -0
- atlas/tests/test_mcp_data_injection.py +270 -0
- atlas/tests/test_mcp_get_authorized_servers.py +95 -0
- atlas/tests/test_mcp_hot_reload.py +512 -0
- atlas/tests/test_mcp_image_content.py +424 -0
- atlas/tests/test_mcp_logging.py +172 -0
- atlas/tests/test_mcp_progress_updates.py +313 -0
- atlas/tests/test_mcp_prompt_override_system_prompt.py +102 -0
- atlas/tests/test_mcp_prompts_server.py +39 -0
- atlas/tests/test_mcp_tool_result_parsing.py +296 -0
- atlas/tests/test_metrics_logger.py +56 -0
- atlas/tests/test_middleware_auth.py +379 -0
- atlas/tests/test_prompt_risk_and_acl.py +141 -0
- atlas/tests/test_rag_mcp_aggregator.py +204 -0
- atlas/tests/test_rag_mcp_service.py +224 -0
- atlas/tests/test_rate_limit_middleware.py +45 -0
- atlas/tests/test_routes_config_smoke.py +60 -0
- atlas/tests/test_routes_files_download_token.py +41 -0
- atlas/tests/test_routes_files_health.py +18 -0
- atlas/tests/test_runtime_imports.py +53 -0
- atlas/tests/test_sampling_integration.py +482 -0
- atlas/tests/test_security_admin_routes.py +61 -0
- atlas/tests/test_security_capability_tokens.py +65 -0
- atlas/tests/test_security_file_stats_scope.py +21 -0
- atlas/tests/test_security_header_injection.py +191 -0
- atlas/tests/test_security_headers_and_filename.py +63 -0
- atlas/tests/test_shared_session_repository.py +101 -0
- atlas/tests/test_system_prompt_loading.py +181 -0
- atlas/tests/test_token_storage.py +505 -0
- atlas/tests/test_tool_approval_config.py +93 -0
- atlas/tests/test_tool_approval_utils.py +356 -0
- atlas/tests/test_tool_authorization_group_filtering.py +223 -0
- atlas/tests/test_tool_details_in_config.py +108 -0
- atlas/tests/test_tool_planner.py +300 -0
- atlas/tests/test_unified_rag_service.py +398 -0
- atlas/tests/test_username_override_in_approval.py +258 -0
- atlas/tests/test_websocket_auth_header.py +168 -0
- atlas/version.py +6 -0
- atlas_chat-0.1.0.data/data/.env.example +253 -0
- atlas_chat-0.1.0.data/data/config/defaults/compliance-levels.json +44 -0
- atlas_chat-0.1.0.data/data/config/defaults/domain-whitelist.json +123 -0
- atlas_chat-0.1.0.data/data/config/defaults/file-extractors.json +74 -0
- atlas_chat-0.1.0.data/data/config/defaults/help-config.json +198 -0
- atlas_chat-0.1.0.data/data/config/defaults/llmconfig-buggy.yml +11 -0
- atlas_chat-0.1.0.data/data/config/defaults/llmconfig.yml +19 -0
- atlas_chat-0.1.0.data/data/config/defaults/mcp.json +138 -0
- atlas_chat-0.1.0.data/data/config/defaults/rag-sources.json +17 -0
- atlas_chat-0.1.0.data/data/config/defaults/splash-config.json +16 -0
- atlas_chat-0.1.0.dist-info/METADATA +236 -0
- atlas_chat-0.1.0.dist-info/RECORD +250 -0
- atlas_chat-0.1.0.dist-info/WHEEL +5 -0
- atlas_chat-0.1.0.dist-info/entry_points.txt +4 -0
- atlas_chat-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
"""Configuration API routes."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter, Depends
|
|
7
|
+
|
|
8
|
+
from atlas.core.auth import is_user_in_group
|
|
9
|
+
from atlas.core.log_sanitizer import get_current_user, sanitize_for_logging
|
|
10
|
+
from atlas.infrastructure.app_factory import app_factory
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
router = APIRouter(prefix="/api", tags=["config"])
|
|
15
|
+
|
|
16
|
+
# Canvas tool description constant
|
|
17
|
+
CANVAS_TOOL_DESCRIPTION = (
|
|
18
|
+
"Display final rendered content in a visual canvas panel. "
|
|
19
|
+
"Use this for: 1) Complete code (not code discussions), "
|
|
20
|
+
"2) Final reports/documents (not report discussions), "
|
|
21
|
+
"3) Data visualizations, 4) Any polished content that should be "
|
|
22
|
+
"viewed separately from the conversation."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@router.get("/banners")
|
|
27
|
+
async def get_banners(current_user: str = Depends(get_current_user)):
|
|
28
|
+
"""Get banners for the user."""
|
|
29
|
+
config_manager = app_factory.get_config_manager()
|
|
30
|
+
app_settings = config_manager.app_settings
|
|
31
|
+
|
|
32
|
+
# Check if banners are enabled
|
|
33
|
+
if not app_settings.banner_enabled:
|
|
34
|
+
return {"messages": []}
|
|
35
|
+
|
|
36
|
+
# Read messages from messages.txt file
|
|
37
|
+
try:
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
|
|
40
|
+
# Use app settings for config path
|
|
41
|
+
base = Path(app_settings.app_config_overrides)
|
|
42
|
+
|
|
43
|
+
# If relative path, resolve from project root
|
|
44
|
+
if not base.is_absolute():
|
|
45
|
+
project_root = Path(__file__).parent.parent.parent
|
|
46
|
+
base = project_root / base
|
|
47
|
+
|
|
48
|
+
messages_file = base / app_settings.messages_config_file
|
|
49
|
+
|
|
50
|
+
if messages_file.exists():
|
|
51
|
+
with open(messages_file, "r", encoding="utf-8") as f:
|
|
52
|
+
content = f.read()
|
|
53
|
+
messages = [line.strip() for line in content.splitlines() if line.strip()]
|
|
54
|
+
return {"messages": messages}
|
|
55
|
+
else:
|
|
56
|
+
return {"messages": []}
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error(f"Error reading banner messages: {e}")
|
|
59
|
+
return {"messages": []}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.get("/config")
|
|
63
|
+
async def get_config(
|
|
64
|
+
current_user: str = Depends(get_current_user),
|
|
65
|
+
compliance_level: Optional[str] = None,
|
|
66
|
+
):
|
|
67
|
+
"""Get available models, tools, and data sources for the user.
|
|
68
|
+
Only returns MCP servers and tools that the user is authorized to access.
|
|
69
|
+
"""
|
|
70
|
+
config_manager = app_factory.get_config_manager()
|
|
71
|
+
llm_config = config_manager.llm_config
|
|
72
|
+
app_settings = config_manager.app_settings
|
|
73
|
+
|
|
74
|
+
# Get RAG data sources for the user from unified RAG service
|
|
75
|
+
rag_data_sources = []
|
|
76
|
+
rag_servers = []
|
|
77
|
+
# Only attempt RAG discovery if RAG feature is enabled
|
|
78
|
+
if app_settings.feature_rag_enabled:
|
|
79
|
+
# Discover HTTP and MCP RAG sources independently (best-effort)
|
|
80
|
+
# so a failure in one type does not prevent discovery of the other
|
|
81
|
+
try:
|
|
82
|
+
unified_rag = app_factory.get_unified_rag_service()
|
|
83
|
+
if unified_rag:
|
|
84
|
+
http_rag_servers = await unified_rag.discover_data_sources(
|
|
85
|
+
current_user, user_compliance_level=compliance_level
|
|
86
|
+
)
|
|
87
|
+
rag_servers.extend(http_rag_servers)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.warning("Error discovering HTTP RAG sources: %s", e)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
rag_mcp = app_factory.get_rag_mcp_service()
|
|
93
|
+
if rag_mcp:
|
|
94
|
+
mcp_rag_servers = await rag_mcp.discover_servers(
|
|
95
|
+
current_user, user_compliance_level=compliance_level
|
|
96
|
+
)
|
|
97
|
+
rag_servers.extend(mcp_rag_servers)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.warning("Error discovering MCP RAG sources: %s", e)
|
|
100
|
+
|
|
101
|
+
# Build flat list of data sources for backward compatibility
|
|
102
|
+
# Format: "server:source_id" for qualified references
|
|
103
|
+
for server in rag_servers:
|
|
104
|
+
server_name = server.get("server", "")
|
|
105
|
+
for source in server.get("sources", []):
|
|
106
|
+
source_id = source.get("id", "")
|
|
107
|
+
if server_name and source_id:
|
|
108
|
+
rag_data_sources.append(f"{server_name}:{source_id}")
|
|
109
|
+
|
|
110
|
+
# Check if tools are enabled
|
|
111
|
+
tools_info = []
|
|
112
|
+
prompts_info = []
|
|
113
|
+
authorized_servers = []
|
|
114
|
+
|
|
115
|
+
if app_settings.feature_tools_enabled:
|
|
116
|
+
# Get MCP manager
|
|
117
|
+
mcp_manager = app_factory.get_mcp_manager()
|
|
118
|
+
|
|
119
|
+
# Get authorized servers for the user - this filters out unauthorized servers completely
|
|
120
|
+
authorized_servers = await mcp_manager.get_authorized_servers(current_user, is_user_in_group)
|
|
121
|
+
|
|
122
|
+
# Add canvas pseudo-tool to authorized servers (available to all users)
|
|
123
|
+
authorized_servers.append("canvas")
|
|
124
|
+
|
|
125
|
+
# Only build tool information for servers the user is authorized to access
|
|
126
|
+
for server_name in authorized_servers:
|
|
127
|
+
# Handle canvas pseudo-tool
|
|
128
|
+
if server_name == "canvas":
|
|
129
|
+
tools_info.append({
|
|
130
|
+
'server': 'canvas',
|
|
131
|
+
'tools': ['canvas'],
|
|
132
|
+
'tools_detailed': [{
|
|
133
|
+
'name': 'canvas',
|
|
134
|
+
'description': CANVAS_TOOL_DESCRIPTION,
|
|
135
|
+
'inputSchema': {
|
|
136
|
+
'type': 'object',
|
|
137
|
+
'properties': {
|
|
138
|
+
'content': {
|
|
139
|
+
'type': 'string',
|
|
140
|
+
'description': 'The content to display in the canvas. Can be markdown, code, or plain text.'
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
'required': ['content']
|
|
144
|
+
}
|
|
145
|
+
}],
|
|
146
|
+
'tool_count': 1,
|
|
147
|
+
'description': 'Canvas for showing final rendered content: complete code, reports, and polished documents. Use this to finalize your work. Most code and reports will be shown here.',
|
|
148
|
+
'author': 'Chat UI Team',
|
|
149
|
+
'short_description': 'Visual content display',
|
|
150
|
+
'help_email': 'support@chatui.example.com',
|
|
151
|
+
'compliance_level': 'Public'
|
|
152
|
+
})
|
|
153
|
+
elif server_name in mcp_manager.available_tools:
|
|
154
|
+
server_tools = mcp_manager.available_tools[server_name]['tools']
|
|
155
|
+
server_config = mcp_manager.available_tools[server_name]['config']
|
|
156
|
+
|
|
157
|
+
# Only include servers that have tools and user has access to
|
|
158
|
+
if server_tools: # Only show servers with actual tools
|
|
159
|
+
# Build detailed tool information including descriptions and input schemas
|
|
160
|
+
tools_detailed = []
|
|
161
|
+
for tool in server_tools:
|
|
162
|
+
tool_detail = {
|
|
163
|
+
'name': tool.name,
|
|
164
|
+
'description': tool.description or '',
|
|
165
|
+
'inputSchema': getattr(tool, 'inputSchema', {}) or {}
|
|
166
|
+
}
|
|
167
|
+
tools_detailed.append(tool_detail)
|
|
168
|
+
|
|
169
|
+
# Determine auth_type from server config
|
|
170
|
+
auth_type = server_config.get('auth_type', 'none')
|
|
171
|
+
auth_required = auth_type in ('jwt', 'bearer', 'oauth', 'api_key')
|
|
172
|
+
|
|
173
|
+
tools_info.append({
|
|
174
|
+
'server': server_name,
|
|
175
|
+
'tools': [tool.name for tool in server_tools],
|
|
176
|
+
'tools_detailed': tools_detailed,
|
|
177
|
+
'tool_count': len(server_tools),
|
|
178
|
+
'description': server_config.get('description', f'{server_name} tools'),
|
|
179
|
+
'author': server_config.get('author', 'Unknown'),
|
|
180
|
+
'short_description': server_config.get('short_description', server_config.get('description', f'{server_name} tools')),
|
|
181
|
+
'help_email': server_config.get('help_email', ''),
|
|
182
|
+
'compliance_level': server_config.get('compliance_level'),
|
|
183
|
+
'auth_type': auth_type,
|
|
184
|
+
'auth_required': auth_required
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
# Collect prompts from this server if available
|
|
188
|
+
if server_name in mcp_manager.available_prompts:
|
|
189
|
+
server_prompts = mcp_manager.available_prompts[server_name]['prompts']
|
|
190
|
+
server_config = mcp_manager.available_prompts[server_name]['config']
|
|
191
|
+
if server_prompts: # Only show servers with actual prompts
|
|
192
|
+
prompts_info.append({
|
|
193
|
+
'server': server_name,
|
|
194
|
+
'prompts': [{'name': prompt.name, 'description': prompt.description} for prompt in server_prompts],
|
|
195
|
+
'prompt_count': len(server_prompts),
|
|
196
|
+
'description': f'{server_name} custom prompts',
|
|
197
|
+
'author': server_config.get('author', 'Unknown'),
|
|
198
|
+
'short_description': server_config.get('short_description', f'{server_name} custom prompts'),
|
|
199
|
+
'help_email': server_config.get('help_email', ''),
|
|
200
|
+
'compliance_level': server_config.get('compliance_level')
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
# Read help page configuration (supports new config directory layout + legacy paths)
|
|
204
|
+
help_config = {}
|
|
205
|
+
import json
|
|
206
|
+
help_config_filename = config_manager.app_settings.help_config_file
|
|
207
|
+
help_paths = []
|
|
208
|
+
try:
|
|
209
|
+
# Reuse config manager search logic (private but acceptable for now)
|
|
210
|
+
try:
|
|
211
|
+
help_paths = config_manager._search_paths(help_config_filename) # type: ignore[attr-defined]
|
|
212
|
+
except AttributeError:
|
|
213
|
+
# Fallback minimal search if method renamed/removed
|
|
214
|
+
from pathlib import Path
|
|
215
|
+
backend_root = Path(__file__).parent.parent
|
|
216
|
+
project_root = backend_root.parent
|
|
217
|
+
help_paths = [
|
|
218
|
+
project_root / "config" / "overrides" / help_config_filename,
|
|
219
|
+
project_root / "config" / "defaults" / help_config_filename,
|
|
220
|
+
backend_root / "configfilesadmin" / help_config_filename,
|
|
221
|
+
backend_root / "configfiles" / help_config_filename,
|
|
222
|
+
backend_root / help_config_filename,
|
|
223
|
+
project_root / help_config_filename,
|
|
224
|
+
]
|
|
225
|
+
|
|
226
|
+
found_path = None
|
|
227
|
+
for p in help_paths:
|
|
228
|
+
if p.exists():
|
|
229
|
+
found_path = p
|
|
230
|
+
break
|
|
231
|
+
if found_path:
|
|
232
|
+
with open(found_path, "r", encoding="utf-8") as f:
|
|
233
|
+
help_config = json.load(f)
|
|
234
|
+
logger.info(f"Loaded help config from {found_path}")
|
|
235
|
+
else:
|
|
236
|
+
logger.warning(
|
|
237
|
+
"Help config not found in any of these locations: %s",
|
|
238
|
+
[str(p) for p in help_paths]
|
|
239
|
+
)
|
|
240
|
+
help_config = {"title": "Help & Documentation", "sections": []}
|
|
241
|
+
except Exception as e:
|
|
242
|
+
logger.warning(f"Error loading help config: {e}")
|
|
243
|
+
help_config = {"title": "Help & Documentation", "sections": []}
|
|
244
|
+
|
|
245
|
+
# Keep INFO logging concise; server lists can be very long.
|
|
246
|
+
logger.info(
|
|
247
|
+
"Config for user %s: %d authorized servers, %d tool groups",
|
|
248
|
+
sanitize_for_logging(current_user),
|
|
249
|
+
len(authorized_servers),
|
|
250
|
+
len(tools_info),
|
|
251
|
+
)
|
|
252
|
+
logger.debug(
|
|
253
|
+
"Authorized servers for user %s: %s",
|
|
254
|
+
sanitize_for_logging(current_user),
|
|
255
|
+
authorized_servers,
|
|
256
|
+
)
|
|
257
|
+
# Build models list with compliance levels
|
|
258
|
+
models_list = []
|
|
259
|
+
for model_name, model_config in llm_config.models.items():
|
|
260
|
+
model_info = {
|
|
261
|
+
"name": model_name,
|
|
262
|
+
"description": model_config.description,
|
|
263
|
+
}
|
|
264
|
+
# Include compliance_level if feature is enabled
|
|
265
|
+
if app_settings.feature_compliance_levels_enabled and model_config.compliance_level:
|
|
266
|
+
model_info["compliance_level"] = model_config.compliance_level
|
|
267
|
+
models_list.append(model_info)
|
|
268
|
+
|
|
269
|
+
# Build tool approval settings - only include tools from authorized servers
|
|
270
|
+
tool_approvals_config = config_manager.tool_approvals_config
|
|
271
|
+
filtered_tool_approvals = {}
|
|
272
|
+
|
|
273
|
+
# Get all tool names from authorized servers
|
|
274
|
+
authorized_tool_names = set()
|
|
275
|
+
for tool_group in tools_info:
|
|
276
|
+
server_name = tool_group.get('server')
|
|
277
|
+
if server_name in authorized_servers:
|
|
278
|
+
# tools is a list of strings (tool names), not dicts
|
|
279
|
+
for tool_name in tool_group.get('tools', []):
|
|
280
|
+
if isinstance(tool_name, str):
|
|
281
|
+
authorized_tool_names.add(tool_name)
|
|
282
|
+
|
|
283
|
+
# Only include approval settings for tools the user has access to
|
|
284
|
+
for tool_name, approval_config in tool_approvals_config.tools.items():
|
|
285
|
+
if tool_name in authorized_tool_names:
|
|
286
|
+
filtered_tool_approvals[tool_name] = {
|
|
287
|
+
"require_approval": approval_config.require_approval,
|
|
288
|
+
"allow_edit": approval_config.allow_edit
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
"app_name": app_settings.app_name,
|
|
293
|
+
"models": models_list,
|
|
294
|
+
"tools": tools_info, # Only authorized servers are included
|
|
295
|
+
"prompts": prompts_info, # Available prompts from authorized servers
|
|
296
|
+
"data_sources": rag_data_sources, # RAG data sources for the user
|
|
297
|
+
"rag_servers": rag_servers, # Optional richer structure for RAG UI
|
|
298
|
+
"user": current_user,
|
|
299
|
+
"is_in_admin_group": await is_user_in_group(current_user, app_settings.admin_group),
|
|
300
|
+
"active_sessions": 0, # TODO: Implement session counting in ChatService
|
|
301
|
+
"authorized_servers": authorized_servers, # Optional: expose for debugging
|
|
302
|
+
"agent_mode_available": app_settings.agent_mode_available, # Whether agent mode UI should be shown
|
|
303
|
+
"banner_enabled": app_settings.banner_enabled, # Whether banner system is enabled
|
|
304
|
+
"help_config": help_config, # Help page configuration from help-config.json
|
|
305
|
+
"tool_approvals": {
|
|
306
|
+
"require_approval_by_default": tool_approvals_config.require_approval_by_default,
|
|
307
|
+
"tools": filtered_tool_approvals
|
|
308
|
+
},
|
|
309
|
+
"features": {
|
|
310
|
+
"workspaces": app_settings.feature_workspaces_enabled,
|
|
311
|
+
"rag": app_settings.feature_rag_enabled,
|
|
312
|
+
"tools": app_settings.feature_tools_enabled,
|
|
313
|
+
"marketplace": app_settings.feature_marketplace_enabled,
|
|
314
|
+
"files_panel": app_settings.feature_files_panel_enabled,
|
|
315
|
+
"chat_history": app_settings.feature_chat_history_enabled,
|
|
316
|
+
"compliance_levels": app_settings.feature_compliance_levels_enabled,
|
|
317
|
+
"splash_screen": app_settings.feature_splash_screen_enabled,
|
|
318
|
+
"file_content_extraction": app_settings.feature_file_content_extraction_enabled
|
|
319
|
+
},
|
|
320
|
+
"file_extraction": _get_file_extraction_config(config_manager)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _get_file_extraction_config(config_manager) -> dict:
|
|
325
|
+
"""Build file extraction config for frontend."""
|
|
326
|
+
app_settings = config_manager.app_settings
|
|
327
|
+
|
|
328
|
+
if not app_settings.feature_file_content_extraction_enabled:
|
|
329
|
+
return {
|
|
330
|
+
"enabled": False,
|
|
331
|
+
"default_behavior": "none",
|
|
332
|
+
"supported_extensions": []
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
extractors_config = config_manager.file_extractors_config
|
|
337
|
+
|
|
338
|
+
# Get list of extensions with enabled extractors
|
|
339
|
+
supported_extensions = []
|
|
340
|
+
for ext, extractor_name in extractors_config.extension_mapping.items():
|
|
341
|
+
extractor = extractors_config.extractors.get(extractor_name)
|
|
342
|
+
if extractor and extractor.enabled:
|
|
343
|
+
supported_extensions.append(ext)
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
"enabled": extractors_config.enabled,
|
|
347
|
+
"default_behavior": extractors_config.default_behavior,
|
|
348
|
+
"supported_extensions": sorted(supported_extensions)
|
|
349
|
+
}
|
|
350
|
+
except Exception as e:
|
|
351
|
+
logger.warning(f"Error building file extraction config: {e}")
|
|
352
|
+
return {
|
|
353
|
+
"enabled": False,
|
|
354
|
+
"default_behavior": "none",
|
|
355
|
+
"supported_extensions": []
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@router.get("/compliance-levels")
|
|
360
|
+
async def get_compliance_levels(current_user: str = Depends(get_current_user)):
|
|
361
|
+
"""Get compliance level definitions and allowlist."""
|
|
362
|
+
try:
|
|
363
|
+
from atlas.core.compliance import get_compliance_manager
|
|
364
|
+
compliance_mgr = get_compliance_manager()
|
|
365
|
+
|
|
366
|
+
# Return level definitions for frontend use
|
|
367
|
+
levels = []
|
|
368
|
+
for name, level_obj in compliance_mgr.levels.items():
|
|
369
|
+
levels.append({
|
|
370
|
+
"name": name,
|
|
371
|
+
"description": level_obj.description,
|
|
372
|
+
"aliases": level_obj.aliases,
|
|
373
|
+
"allowed_with": level_obj.allowed_with
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
"levels": levels,
|
|
378
|
+
"mode": compliance_mgr.mode,
|
|
379
|
+
"all_level_names": compliance_mgr.get_all_levels()
|
|
380
|
+
}
|
|
381
|
+
except Exception as e:
|
|
382
|
+
logger.error(f"Error getting compliance levels: {e}", exc_info=True)
|
|
383
|
+
return {
|
|
384
|
+
"levels": [],
|
|
385
|
+
"mode": "explicit_allowlist",
|
|
386
|
+
"all_level_names": []
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
@router.get("/splash")
|
|
391
|
+
async def get_splash_config(current_user: str = Depends(get_current_user)):
|
|
392
|
+
"""Get splash screen configuration."""
|
|
393
|
+
config_manager = app_factory.get_config_manager()
|
|
394
|
+
app_settings = config_manager.app_settings
|
|
395
|
+
|
|
396
|
+
# Check if splash screen feature is enabled
|
|
397
|
+
if not app_settings.feature_splash_screen_enabled:
|
|
398
|
+
return {
|
|
399
|
+
"enabled": False,
|
|
400
|
+
"title": "",
|
|
401
|
+
"messages": [],
|
|
402
|
+
"dismissible": True,
|
|
403
|
+
"require_accept": False,
|
|
404
|
+
"dismiss_duration_days": 30,
|
|
405
|
+
"accept_button_text": "Accept",
|
|
406
|
+
"dismiss_button_text": "Dismiss",
|
|
407
|
+
"show_on_every_visit": False
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
# Read splash screen configuration
|
|
411
|
+
splash_config = {}
|
|
412
|
+
import json
|
|
413
|
+
splash_config_filename = app_settings.splash_config_file
|
|
414
|
+
splash_paths = []
|
|
415
|
+
try:
|
|
416
|
+
# Reuse config manager search logic
|
|
417
|
+
try:
|
|
418
|
+
splash_paths = config_manager._search_paths(splash_config_filename) # type: ignore[attr-defined]
|
|
419
|
+
except AttributeError:
|
|
420
|
+
# Fallback minimal search if method renamed/removed
|
|
421
|
+
from pathlib import Path
|
|
422
|
+
backend_root = Path(__file__).parent.parent
|
|
423
|
+
project_root = backend_root.parent
|
|
424
|
+
splash_paths = [
|
|
425
|
+
project_root / "config" / "overrides" / splash_config_filename,
|
|
426
|
+
project_root / "config" / "defaults" / splash_config_filename,
|
|
427
|
+
backend_root / "configfilesadmin" / splash_config_filename,
|
|
428
|
+
backend_root / "configfiles" / splash_config_filename,
|
|
429
|
+
backend_root / splash_config_filename,
|
|
430
|
+
project_root / splash_config_filename,
|
|
431
|
+
]
|
|
432
|
+
|
|
433
|
+
found_path = None
|
|
434
|
+
for p in splash_paths:
|
|
435
|
+
if p.exists():
|
|
436
|
+
found_path = p
|
|
437
|
+
break
|
|
438
|
+
if found_path:
|
|
439
|
+
with open(found_path, "r", encoding="utf-8") as f:
|
|
440
|
+
splash_config = json.load(f)
|
|
441
|
+
logger.info(f"Loaded splash config from {found_path}")
|
|
442
|
+
else:
|
|
443
|
+
logger.info(
|
|
444
|
+
"Splash config not found in any of these locations: %s",
|
|
445
|
+
[str(p) for p in splash_paths]
|
|
446
|
+
)
|
|
447
|
+
# Return default disabled config
|
|
448
|
+
splash_config = {
|
|
449
|
+
"enabled": False,
|
|
450
|
+
"title": "",
|
|
451
|
+
"messages": [],
|
|
452
|
+
"dismissible": True,
|
|
453
|
+
"require_accept": False,
|
|
454
|
+
"dismiss_duration_days": 30,
|
|
455
|
+
"accept_button_text": "Accept",
|
|
456
|
+
"dismiss_button_text": "Dismiss",
|
|
457
|
+
"show_on_every_visit": False
|
|
458
|
+
}
|
|
459
|
+
except Exception as e:
|
|
460
|
+
logger.warning(f"Error loading splash config: {e}")
|
|
461
|
+
splash_config = {
|
|
462
|
+
"enabled": False,
|
|
463
|
+
"title": "",
|
|
464
|
+
"messages": [],
|
|
465
|
+
"dismissible": True,
|
|
466
|
+
"require_accept": False,
|
|
467
|
+
"dismiss_duration_days": 30,
|
|
468
|
+
"accept_button_text": "Accept",
|
|
469
|
+
"dismiss_button_text": "Dismiss",
|
|
470
|
+
"show_on_every_visit": False
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return splash_config
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# @router.get("/sessions")
|
|
477
|
+
# async def get_session_info(current_user: str = Depends(get_current_user)):
|
|
478
|
+
# """Get session information for the current user."""
|
|
479
|
+
# # TODO: Implement session info retrieval from ChatService
|
|
480
|
+
# return {
|
|
481
|
+
# "total_sessions": 0,
|
|
482
|
+
# "user_sessions": 0,
|
|
483
|
+
# "sessions": []
|
|
484
|
+
# }
|