ZOTHEOS commited on
Commit
e233283
·
verified ·
1 Parent(s): d92a09c

Upload 5 files

Browse files
Files changed (5) hide show
  1. LICENSE.txt +51 -0
  2. README.md +16 -14
  3. main_web.py +36 -0
  4. requirements.txt +3 -0
  5. zotheos_interface_public.py +855 -0
LICENSE.txt ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ZOTHEOS: Ethical Fusion AI for Synthesized Intelligence
2
+ Copyright (c) 2025, ZOTHEOS LLC — Created by David Alanzo Garcia
3
+
4
+ This software, ZOTHEOS, is a composite work. The original fusion logic, orchestration architecture, user interface design, memory system, and other unique components developed specifically for ZOTHEOS are the intellectual property of David Alanzo Garcia / ZOTHEOS LLC and are protected by copyright.
5
+
6
+ ZOTHEOS incorporates and relies upon several open-source models and technologies, each governed by its own respective license. Use and distribution of these components must comply with their individual licenses. Key open-source components include:
7
+
8
+ MODELS:
9
+ • Qwen Model Series (e.g., Qwen1.5-7B-Chat-GGUF):
10
+ - Licensed under the Tongyi Qianwen LICENSE (Apache 2.0 based).
11
+ - Copyright (c) Alibaba Cloud.
12
+ - Full license details: https://github.com/QwenLM/Qwen/blob/main/Tongyi%20Qianwen%20LICENSE%20AGREEMENT
13
+ - Note: Specific model versions may have variations; users should verify the license for the exact model file used.
14
+
15
+ • Mistral Model Series (e.g., Mistral-7B-Instruct-v0.2.Q5_K_M.gguf):
16
+ - Licensed under the Apache 2.0 License.
17
+ - Copyright (c) Mistral AI.
18
+ - Full license details: https://www.apache.org/licenses/LICENSE-2.0
19
+
20
+ • Gemma Model Series (e.g., gemma-7b-it-Q5_K_M.gguf):
21
+ - Licensed under the Gemma Tems of Use (typically permissive, similar to Apache 2.0 for many uses but with specific conditions).
22
+ - Copyright (c) Google LLC.
23
+ - Users must review and agree to the Gemma Terms of Use available at ai.google.dev/gemma/terms.
24
+
25
+ LIBRARIES & FRAMEWORKS:
26
+ • llama-cpp-python:
27
+ - Licensed under the MIT License.
28
+ - Copyright (c) 2023 Andrei CC & The llama-cpp-python Authors.
29
+ - Full license details: https://github.com/abetlen/llama-cpp-python/blob/main/LICENSE
30
+
31
+ • Gradio:
32
+ - Licensed under the Apache 2.0 License.
33
+ - Copyright (c. 2019) Gradio Team.
34
+ - Full license details: https://github.com/gradio-app/gradio/blob/main/LICENSE
35
+
36
+ • Python and its standard libraries:
37
+ - Python is available under the Python Software Foundation License (PSFL), which is GPL-compatible.
38
+
39
+ REQUIREMENTS FOR DISTRIBUTION:
40
+ Distribution of ZOTHEOS or any derivative works must:
41
+ 1. Include this `LICENSE.txt` file in its entirety.
42
+ 2. Clearly attribute the source of the open-source models and libraries as listed above.
43
+ 3. Comply with all individual licenses of the incorporated components.
44
+ 4. Retain the copyright notice for the original ZOTHEOS components.
45
+
46
+ DISCLAIMER:
47
+ This software is provided "as-is", without warranty of any kind, express or implied. The authors and copyright holders are not liable for any claim, damages, or other liability arising from the use of this software.
48
+
49
+ By using, modifying, or distributing ZOTHEOS, you agree to the terms outlined herein and the respective licenses of all incorporated components.
50
+
51
+ For inquiries, please contact: [Your Contact Email or ZOTHEOS LLC Contact Info]
README.md CHANGED
@@ -1,14 +1,16 @@
1
- ---
2
- title: ZOTHEOS App
3
- emoji: 🏢
4
- colorFrom: indigo
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.33.0
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- short_description: Ethical Fusion AI for Synthesized Intelligence
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
+ ---
2
+ title: ZOTHEOS - Ethical Fusion AI
3
+ emoji: 🧠
4
+ colorFrom: gray # <--- THIS IS THE FIX
5
+ colorTo: gray
6
+ sdk: gradio
7
+ sdk_version: 3.50.2
8
+ app_file: main_web.py
9
+ pinned: false
10
+ ---
11
+
12
+ # ZOTHEOS - Ethical Fusion AI
13
+
14
+ Welcome to the Zotheos project. This is a live web application demonstrating Ethical Fusion AI for synthesized intelligence.
15
+
16
+ Powered by open-source models and Gradio.
main_web.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main_web.py — ZOTHEOS Hugging Face Entry Point
2
+
3
+ import logging
4
+ from zotheos_interface_public import (
5
+ build_interface,
6
+ logo_path_verified,
7
+ favicon_path_verified,
8
+ APP_TITLE
9
+ )
10
+
11
+ # Set up logging
12
+ logging.basicConfig(
13
+ level=logging.INFO,
14
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
15
+ )
16
+ logger = logging.getLogger("ZOTHEOS_WebApp_HF")
17
+
18
+ # --- CRUCIAL FINAL CHECK ---
19
+ # Ensure your model loading code (likely in another file like 'modules/main_fusion_public.py')
20
+ # has been updated to use hf_hub_download.
21
+ # The server will download the models, so your code must not look for a local 'models/' folder.
22
+ logger.info("Verifying model loading strategy for web deployment...")
23
+ # (This is just a log message, the actual code change is in your backend file)
24
+
25
+ # --- Build and Launch the App ---
26
+ logger.info(f"Building Gradio UI for '{APP_TITLE}'...")
27
+ # Build the interface by calling the function from your other script
28
+ zotheos_app = build_interface(logo_path_verified, favicon_path_verified)
29
+
30
+ logger.info("UI built. Preparing to launch on Hugging Face Spaces...")
31
+ # The .queue() is important for handling multiple users.
32
+ # The .launch() command without arguments is what Hugging Face expects.
33
+ # It will handle the server and networking for you.
34
+ zotheos_app.queue().launch()
35
+
36
+ logger.info("ZOTHEOS app has been launched by the Hugging Face environment.")
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio==3.50.2
2
+ llama-cpp-python
3
+ huggingface-hub
zotheos_interface_public.py ADDED
@@ -0,0 +1,855 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import asyncio
3
+ import logging
4
+ import os
5
+ import sys
6
+ import time
7
+ import random
8
+ from typing import Optional, Dict, Any, List, Union, Tuple
9
+ import html
10
+ import json
11
+ import tempfile
12
+
13
+ # --- Constants ---
14
+ APP_TITLE = "ZOTHEOS - Ethical Fusion AI" # This can be imported by main_web.py if needed
15
+ FAVICON_FILENAME = "favicon_blackBW.ico"
16
+ LOGO_FILENAME = "zotheos_logo.png"
17
+
18
+ # --- Logger Setup ---
19
+ def init_logger():
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
23
+ datefmt="%Y-%m-%d %H:%M:%S"
24
+ )
25
+ logger_instance = logging.getLogger("ZOTHEOS_Interface")
26
+ logger_instance.propagate = False
27
+ if not logger_instance.handlers:
28
+ handler = logging.StreamHandler(sys.stdout)
29
+ formatter = logging.Formatter(
30
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
31
+ datefmt="%Y-%m-%d %H:%M:%S"
32
+ )
33
+ handler.setFormatter(formatter)
34
+ logger_instance.addHandler(handler)
35
+ return logger_instance
36
+ logger = init_logger() # logger is defined at module level
37
+
38
+ # --- Asset Path Helper (Corrected for PyInstaller) ---
39
+ def get_asset_path(filename: str) -> Optional[str]:
40
+ if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
41
+ bundle_dir = sys._MEIPASS # type: ignore
42
+ possible_path = os.path.join(bundle_dir, 'assets', filename)
43
+ if os.path.exists(possible_path):
44
+ logger.info(f"Asset found in PyInstaller bundle: '{filename}' -> '{possible_path}'")
45
+ return possible_path
46
+ possible_path_root = os.path.join(bundle_dir, filename)
47
+ if os.path.exists(possible_path_root):
48
+ logger.info(f"Asset found at PyInstaller bundle root: '{filename}' -> '{possible_path_root}'")
49
+ return possible_path_root
50
+ else:
51
+ script_dir = os.path.dirname(os.path.abspath(__file__))
52
+ path_in_assets = os.path.join(script_dir, "assets", filename)
53
+ if os.path.exists(path_in_assets):
54
+ logger.info(f"Asset found in local ./assets: '{filename}' -> '{path_in_assets}'")
55
+ return path_in_assets
56
+ path_in_root = os.path.join(script_dir, filename)
57
+ if os.path.exists(path_in_root):
58
+ logger.info(f"Asset found in local script dir: '{filename}' -> '{path_in_root}'")
59
+ return path_in_root
60
+ logger.warning(f"Asset NOT FOUND: '{filename}'.")
61
+ return None
62
+
63
+ # --- Define Asset Paths at Module Level for Import by main_web.py ---
64
+ logo_path_verified = get_asset_path(LOGO_FILENAME)
65
+ favicon_path_verified = get_asset_path(FAVICON_FILENAME)
66
+
67
+ # --- Core Logic Import Attempt ---
68
+ MainFusionPublic = None
69
+ initialization_error: Optional[Exception] = None
70
+ DEFAULT_INFERENCE_PRESET_INTERFACE = "balanced"
71
+ get_user_tier_fallback = lambda token: "error (auth module failed)"
72
+ get_user_tier = get_user_tier_fallback
73
+
74
+ try:
75
+ script_dir = os.path.dirname(os.path.abspath(__file__))
76
+ project_root = script_dir
77
+ if project_root not in sys.path:
78
+ sys.path.insert(0, project_root)
79
+ logger.info(f"ADDED project root directory '{project_root}' to sys.path for module resolution.")
80
+ from modules.main_fusion_public import MainFusionPublic # type: ignore
81
+ try:
82
+ from modules.config_settings_public import DEFAULT_INFERENCE_PRESET as CONFIG_DEFAULT_PRESET # type: ignore
83
+ DEFAULT_INFERENCE_PRESET_INTERFACE = CONFIG_DEFAULT_PRESET
84
+ logger.info(f"Imported DEFAULT_INFERENCE_PRESET ('{DEFAULT_INFERENCE_PRESET_INTERFACE}') from config.")
85
+ except ImportError:
86
+ logger.warning("Could not import DEFAULT_INFERENCE_PRESET. Using interface default: 'balanced'")
87
+ from modules.user_auth import get_user_tier as imported_get_user_tier # type: ignore
88
+ get_user_tier = imported_get_user_tier
89
+ logger.info("✅ Successfully imported MainFusionPublic and get_user_tier from modules.user_auth.")
90
+ except Exception as e:
91
+ logger.error(f"❌ Error importing modules (MainFusionPublic or user_auth): {e}", exc_info=True)
92
+ initialization_error = e
93
+ if 'imported_get_user_tier' not in globals():
94
+ get_user_tier = get_user_tier_fallback
95
+ logger.info("Using fallback 'get_user_tier' due to specific import error for get_user_tier.")
96
+
97
+ # --- Initialize AI System ---
98
+ ai_system: Optional[MainFusionPublic] = None
99
+ if 'MainFusionPublic' in globals() and MainFusionPublic is not None and initialization_error is None:
100
+ try:
101
+ logger.info("Initializing ZOTHEOS AI System...")
102
+ ai_system = MainFusionPublic() # type: ignore
103
+ if not hasattr(ai_system, 'process_query_with_fusion'):
104
+ missing_method_error = AttributeError("AI System lacks 'process_query_with_fusion' method.")
105
+ logger.error(f"❌ AI System Config Error: {missing_method_error}")
106
+ initialization_error = missing_method_error; ai_system = None
107
+ else: logger.info("✅ ZOTHEOS AI System Initialized.")
108
+ except Exception as e_init:
109
+ initialization_error = e_init; logger.error(f"❌ AI System Init Failed: {e_init}", exc_info=True); ai_system = None
110
+ elif initialization_error is None and ('MainFusionPublic' not in globals() or MainFusionPublic is None) :
111
+ initialization_error = ModuleNotFoundError("MainFusionPublic module could not be imported or was not defined.")
112
+ logger.error(f"❌ {initialization_error}")
113
+
114
+ # --- TIER_FEATURES Dictionary ---
115
+ TIER_FEATURES = {
116
+ "free": {"display_name": "Free Tier", "memory_enabled": False, "export_enabled": False, "settings_access": False, "emoji_map": {"memory": "❌", "export": "❌", "settings": "❌"}},
117
+ "starter": {"display_name": "Starter Tier", "memory_enabled": True, "export_enabled": False, "settings_access": False, "emoji_map": {"memory": "✅", "export": "❌", "settings": "❌"}},
118
+ "pro": {"display_name": "Pro Tier", "memory_enabled": True, "export_enabled": True, "settings_access": True, "emoji_map": {"memory": "✅", "export": "✅", "settings": "✅"}},
119
+ "error (auth module failed)": {"display_name": "Error Resolving Tier", "memory_enabled": False, "export_enabled": False, "settings_access": False, "emoji_map": {"memory": "⚠️", "export": "⚠️", "settings": "⚠️"}}
120
+ }
121
+
122
+ zotheos_base_css = """
123
+ @import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&family=Staatliches&family=Inconsolata:wght@400;700&display=swap');
124
+
125
+ :root {
126
+ --background-main: #000000;
127
+ --background-card: #0f0f0f;
128
+ --input-bg: #121212;
129
+ --text-color-light: #ffffff;
130
+ --text-color-medium: #b3b3b3;
131
+ --text-color-heading: #ffffff;
132
+ --border-color: #2a2a2a;
133
+ --input-border: var(--border-color);
134
+ --input-focus-border: #888888;
135
+ --accent-primary: #dcdcdc;
136
+ --accent-secondary: #444444;
137
+
138
+ --font-body: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
139
+ --font-heading: 'Staatliches', cursive;
140
+ --font-mono: 'Inconsolata', monospace;
141
+
142
+ --button-primary-bg: var(--accent-primary);
143
+ --button-primary-text: #000000;
144
+ --button-primary-hover-bg: #c0c0c0;
145
+ --button-primary-focus-ring: #b0b0b0;
146
+
147
+ --button-secondary-bg: var(--accent-secondary);
148
+ --button-secondary-text: var(--text-color-light);
149
+ --button-secondary-hover-bg: #555555;
150
+ --button-secondary-focus-ring: #666666;
151
+ }
152
+
153
+ /* Base dark mode and pixelated rendering for a sharp, retro feel */
154
+ html, body {
155
+ color-scheme: dark;
156
+ image-rendering: pixelated;
157
+ }
158
+
159
+ /* 🧼 CANVAS CLEANUP (Gradio branding block removal) */
160
+ canvas {
161
+ display: none !important;
162
+ }
163
+
164
+ /* 💥 FULL GRADIO FOOTER REMOVE */
165
+ footer {
166
+ display: none !important;
167
+ }
168
+
169
+ body, html {
170
+ background-color: var(--background-main) !important;
171
+ margin: 0;
172
+ padding: 0;
173
+ font-family: var(--font-body);
174
+ color: var(--text-color-light);
175
+ line-height: 1.6;
176
+ overflow-x: hidden;
177
+ }
178
+
179
+ body, .gradio-container {
180
+ display: flex;
181
+ flex-direction: column;
182
+ align-items: center;
183
+ justify-content: flex-start;
184
+ }
185
+
186
+ /* 💡 LIMIT WIDTH FOR MOBILE AND KEEP CENTERED */
187
+ .container, .gradio-container, .main, body {
188
+ max-width: 720px !important;
189
+ margin: 0 auto !important;
190
+ }
191
+
192
+ .gradio-container {
193
+ background-color: transparent !important;
194
+ box-shadow: none !important;
195
+ padding: 0 !important;
196
+ }
197
+
198
+ .centered-container {
199
+ max-width: 1080px;
200
+ padding: 20px 15px;
201
+ display: flex;
202
+ flex-direction: column;
203
+ align-items: center;
204
+ }
205
+
206
+ h1, h2, h3 {
207
+ font-family: var(--font-heading);
208
+ font-size: 1.5rem;
209
+ text-transform: uppercase;
210
+ color: var(--text-color-heading);
211
+ letter-spacing: 1px;
212
+ }
213
+
214
+ #header_column {
215
+ width: 100%;
216
+ display: flex;
217
+ flex-direction: column;
218
+ align-items: center;
219
+ text-align: center;
220
+ margin-bottom: 1rem;
221
+ padding-bottom: 10px;
222
+ }
223
+
224
+ #header_logo img {
225
+ max-height: 100px;
226
+ margin: 0.5rem auto;
227
+ background-color: #000000;
228
+ padding: 10px;
229
+ border-radius: 12px;
230
+ }
231
+
232
+ #header_subtitle p {
233
+ font-family: var(--font-body) !important;
234
+ font-size: 1.1rem;
235
+ color: var(--text-color-medium);
236
+ text-align: center;
237
+ margin: 0.25rem 0 1rem 0;
238
+ }
239
+
240
+ .interface-section,
241
+ #status_indicator,
242
+ #tools_accordion {
243
+ width: 100% !important;
244
+ max-width: 100% !important;
245
+ box-sizing: border-box !important;
246
+ }
247
+
248
+ #query_input textarea {
249
+ min-height: 160px !important;
250
+ font-size: 1.05rem !important;
251
+ background-color: var(--input-bg) !important;
252
+ color: var(--text-color-light) !important;
253
+ border: 1px solid var(--input-border) !important;
254
+ border-radius: 10px !important;
255
+ padding: 14px !important;
256
+ line-height: 1.6 !important;
257
+ font-family: var(--font-body) !important;
258
+ }
259
+
260
+ #fusion_output h1, #fusion_output h2, #fusion_output h3 {
261
+ font-family: var(--font-heading) !important;
262
+ font-size: clamp(1.3rem, 2vw, 1.8rem) !important;
263
+ color: var(--accent-primary);
264
+ border-bottom: 1px solid var(--border-color);
265
+ padding-bottom: 0.3em;
266
+ margin-top: 1em;
267
+ margin-bottom: 0.5em;
268
+ }
269
+
270
+ #synthesized_summary_output h2 {
271
+ font-family: var(--font-heading) !important;
272
+ color: var(--accent-primary) !important;
273
+ font-size: 1.5rem !important;
274
+ border-bottom: 1px solid var(--border-color);
275
+ padding-bottom: 0.3em;
276
+ margin-bottom: 0.8em;
277
+ }
278
+
279
+ #synthesized_summary_output h3 {
280
+ font-family: var(--font-heading) !important;
281
+ color: var(--text-color-heading) !important;
282
+ font-size: 1.1rem !important;
283
+ margin-top: 0.8em;
284
+ margin-bottom: 0.3em;
285
+ }
286
+
287
+ #memory_log textarea {
288
+ font-family: var(--font-mono) !important;
289
+ font-size: 0.8rem !important;
290
+ line-height: 1.5 !important;
291
+ background-color: #101015 !important;
292
+ color: var(--text-color-medium) !important;
293
+ border: 1px solid var(--border-color) !important;
294
+ border-radius: 8px !important;
295
+ padding: 10px !important;
296
+ }
297
+
298
+ /* Buttons */
299
+ .gradio-button.primary, button#submit_button.gradio-button {
300
+ background: var(--button-primary-bg) !important;
301
+ color: var(--button-primary-text) !important;
302
+ border: 1px solid var(--button-primary-bg) !important;
303
+ box-shadow: none !important;
304
+ }
305
+ .gradio-button.primary:hover, button#submit_button.gradio-button:hover {
306
+ background: var(--button-primary-hover-bg) !important;
307
+ border-color: var(--button-primary-hover-bg) !important;
308
+ }
309
+ .gradio-button.primary:focus, button#submit_button.gradio-button:focus {
310
+ box-shadow: 0 0 0 2px var(--background-main),
311
+ 0 0 0 4px var(--button-primary-focus-ring) !important;
312
+ }
313
+
314
+ .gradio-button.secondary, button#clear_button.gradio-button {
315
+ background: var(--button-secondary-bg) !important;
316
+ color: var(--button-secondary-text) !important;
317
+ border: 1px solid var(--button-secondary-bg) !important;
318
+ box-shadow: none !important;
319
+ }
320
+ .gradio-button.secondary:hover, button#clear_button.gradio-button:hover {
321
+ background: var(--button-secondary-hover-bg) !important;
322
+ border-color: var(--button-secondary-hover-bg) !important;
323
+ }
324
+ .gradio-button.secondary:focus, button#clear_button.gradio-button:focus {
325
+ box-shadow: 0 0 0 2px var(--background-main),
326
+ 0 0 0 4px var(--button-secondary-focus-ring) !important;
327
+ }
328
+
329
+ /* Pro Button Styling */
330
+ #pro_features_row .gradio-button {
331
+ font-size: 0.85rem !important;
332
+ padding: 8px 16px !important;
333
+ flex-grow: 0 !important;
334
+ background-color: var(--accent-secondary) !important;
335
+ color: var(--text-color-medium) !important;
336
+ opacity: 0.7;
337
+ border: 1px solid var(--accent-secondary) !important;
338
+ }
339
+ #pro_features_row .gradio-button.interactive_button_enabled {
340
+ background-color: var(--button-secondary-bg) !important;
341
+ color: var(--button-secondary-text) !important;
342
+ opacity: 1.0;
343
+ border: 1px solid var(--button-secondary-bg) !important;
344
+ }
345
+ #pro_features_row .gradio-button.interactive_button_enabled:hover {
346
+ background-color: var(--button-secondary-hover-bg) !important;
347
+ border-color: var(--button-secondary-hover-bg) !important;
348
+ }
349
+ #pro_features_row .gradio-button:disabled,
350
+ #pro_features_row .gradio-button[disabled] {
351
+ opacity: 0.4 !important;
352
+ cursor: not-allowed !important;
353
+ background-color: #202020 !important;
354
+ color: #555 !important;
355
+ border: 1px solid #202020 !important;
356
+ }
357
+
358
+ /* Attribution footer */
359
+ #footer_attribution {
360
+ font-size: 0.75rem;
361
+ color: var(--text-color-medium);
362
+ text-align: center;
363
+ padding-top: 1rem;
364
+ margin-top: 2rem;
365
+ opacity: 0.8;
366
+ line-height: 1.5;
367
+ }
368
+
369
+ /* ✅ FORCE FULL RESPONSIVE LAYOUT ON MOBILE */
370
+ html, body {
371
+ max-width: 100vw !important;
372
+ overflow-x: hidden !important;
373
+ }
374
+
375
+ .gradio-container,
376
+ .container,
377
+ .block,
378
+ .row,
379
+ .column,
380
+ .interface-section,
381
+ .centered-container,
382
+ #header_column,
383
+ #query_input,
384
+ #synthesized_summary_output,
385
+ #fusion_output,
386
+ #user_token_input,
387
+ #tier_status_display,
388
+ #memory_log,
389
+ #memory_display_panel,
390
+ #pro_features_row {
391
+ width: 100% !important;
392
+ max-width: 100% !important;
393
+ box-sizing: border-box !important;
394
+ overflow-wrap: break-word !important;
395
+ }
396
+
397
+ textarea, input, button {
398
+ max-width: 100% !important;
399
+ }
400
+
401
+ /* ✅🧠 CORRECTED: Force logo image to grayscale. Emojis (text) are unaffected. */
402
+ img {
403
+ max-width: 100%;
404
+ height: auto;
405
+ display: block;
406
+ filter: grayscale(100%);
407
+ }
408
+
409
+ /* ✅ FIX TEXTBOX TRUNCATION */
410
+ textarea {
411
+ resize: vertical !important;
412
+ }
413
+
414
+ /* ✅ UNIFY FONT SIZE & TIGHTEN UI ON SMALL DEVICES */
415
+ @media screen and (max-width: 480px) {
416
+ html, body {
417
+ font-size: 15px !important;
418
+ }
419
+
420
+ /* 🔧 TIGHTEN GRADIO UI SCALE ON MOBILE */
421
+ .gradio-container,
422
+ .block,
423
+ .row,
424
+ .column,
425
+ textarea,
426
+ input,
427
+ button {
428
+ transform: scale(0.95);
429
+ transform-origin: top center;
430
+ }
431
+
432
+ h1, h2, h3 {
433
+ font-size: 1.2rem !important;
434
+ }
435
+
436
+ #header_logo img {
437
+ max-height: 80px !important;
438
+ padding: 8px !important;
439
+ }
440
+
441
+ #footer_attribution {
442
+ font-size: 0.65rem !important;
443
+ }
444
+ }
445
+
446
+ /* Loading Indicator Styles */
447
+ .cosmic-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; text-align: center; }
448
+ .orbital-spinner { width: 40px; height: 40px; border: 4px solid var(--text-color-medium); border-top: 4px solid var(--text-color-light); border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 15px; }
449
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
450
+ .thinking-text { font-family: var(--font-body); color: var(--text-color-light); font-size: 0.9rem; }
451
+
452
+ /* Authentication Row and Tier Status Display */
453
+ #auth_row { margin-bottom: 1rem; align-items: center; }
454
+ #user_token_input textarea {
455
+ background-color: var(--input-bg) !important; color: var(--text-color-light) !important;
456
+ border: 1px solid var(--input-border) !important; border-radius: 8px !important;
457
+ padding: 0.75em !important;
458
+ }
459
+ #tier_status_display {
460
+ padding: 0.60rem 0.5rem !important; text-align: left; font-size: 0.9rem;
461
+ color: var(--text-color-medium); display: flex; align-items: flex-start;
462
+ min-height: 40px; line-height: 1.4;
463
+ }
464
+ #tier_status_display p { margin: 0 !important; }
465
+ #tier_status_display small { font-size: 0.9em; opacity: 0.9; }
466
+
467
+ /* Footer */
468
+ #footer_attribution {
469
+ font-size: 0.75rem; color: var(--text-color-medium); text-align: center;
470
+ margin-top: 2.5rem; padding-bottom: 1rem; opacity: 0.8; line-height: 1.5;
471
+ }
472
+ #footer_attribution strong { font-weight: 600; color: var(--text-color-light); }
473
+
474
+ /* Toast / Notification Styling */
475
+ .gradio-toast {
476
+ background-color: #1e1e1e !important;
477
+ color: var(--text-color-light) !important;
478
+ border: 1px solid var(--border-color) !important;
479
+ border-radius: 6px !important;
480
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
481
+ }
482
+ .gradio-toast .toast-body {
483
+ color: var(--text-color-light) !important;
484
+ }
485
+ .gradio-toast .toast-body svg { /* Style the icon if present */
486
+ fill: var(--text-color-light) !important;
487
+ }
488
+ .gradio-toast.toast-info, .gradio-toast.toast-error, .gradio-toast.toast-warning, .gradio-toast.toast-success {
489
+ /* General styles already applied, these are for any minor specific tweaks if needed */
490
+ }
491
+
492
+ /* Scrollable Output Boxes CSS */
493
+ #fusion_output, #synthesized_summary_output {
494
+ max-height: 70vh;
495
+ overflow-y: auto !important;
496
+ padding: 1em;
497
+ scroll-behavior: smooth;
498
+ border: 1px solid var(--border-color) !important;
499
+ border-radius: 10px !important;
500
+ background-color: var(--input-bg) !important;
501
+ word-wrap: break-word;
502
+ white-space: pre-wrap;
503
+ }
504
+
505
+ /* Memory Display Panel Scrollable CSS */
506
+ #memory_display_panel {
507
+ font-size: 0.85rem;
508
+ color: var(--text-color-medium);
509
+ background-color: #0A0A0A; padding: 0.75em;
510
+ border-radius: 6px; border: 1px solid var(--border-color); max-height: 400px; overflow-y: auto;
511
+ }
512
+ #memory_display_panel .memory-entry { margin-bottom: 1rem; padding: 0.75rem; border: 1px solid #333; border-radius: 6px; background-color: #101010; }
513
+ #memory_display_panel .memory-entry strong { color: var(--text-color-light); font-weight: bold; }
514
+ #memory_display_panel .memory-entry em { color: var(--text-color-medium); font-style: normal; }
515
+ #memory_display_panel .memory-entry .query-text,
516
+ #memory_display_panel .memory-entry .summary-text { display: block; white-space: pre-wrap; word-break: break-word; margin-top: 0.25em; background-color: #181818; padding: 0.3em 0.5em; border-radius: 4px; }
517
+
518
+ /* A general scrollable container class, if needed elsewhere */
519
+ .scrollable-container { max-height: 75vh; overflow-y: auto; }
520
+ """
521
+
522
+ # --- render_memory_entries_as_html ---
523
+ def render_memory_entries_as_html(memory_entries: List[dict]) -> str:
524
+ if not memory_entries: return "<div class='memory-entry'>No stored memory entries or memory disabled.</div>"
525
+ html_blocks = [];
526
+ for entry in reversed(memory_entries[-5:]):
527
+ query = html.escape(entry.get("query", "N/A")); ts_iso = entry.get("metadata", {}).get("timestamp_iso", "Unknown");
528
+ try: dt_obj = time.strptime(ts_iso, '%Y-%m-%dT%H:%M:%SZ'); formatted_ts = time.strftime('%Y-%m-%d %H:%M UTC', dt_obj)
529
+ except: formatted_ts = html.escape(ts_iso)
530
+ models_list = entry.get("metadata", {}).get("active_models_queried", []); models_str = html.escape(", ".join(models_list)) if models_list else "N/A"
531
+ summary_raw = entry.get("metadata", {}).get("synthesized_summary_text", entry.get("response", "No summary.")[:500]); summary = html.escape(summary_raw[:300]) + ("..." if len(summary_raw) > 300 else "")
532
+ html_blocks.append(f"<div class='memory-entry'><p><strong>🕒 TS:</strong> {formatted_ts}</p><p><strong>🧠 Models:</strong> {models_str}</p><div><strong>❓ Q:</strong><div class='query-text'>{query}</div></div><div><strong>📌 Sum:</strong><div class='summary-text'>{summary}</div></div></div>")
533
+ return "\n".join(html_blocks) if html_blocks else "<div class='memory-entry'>No processable entries.</div>"
534
+
535
+ # --- Format Tier Status for UI ---
536
+ def format_tier_status_for_ui(tier_name: str) -> str:
537
+ features = TIER_FEATURES.get(tier_name, TIER_FEATURES["error (auth module failed)"])
538
+ status_md = f"**{features['display_name']}**\n"
539
+ status_md += f"<small>Memory: {features['emoji_map']['memory']} | "
540
+ status_md += f"Export: {features['emoji_map']['export']} | "
541
+ model_limit_from_backend = "N/A"
542
+ if ai_system and hasattr(ai_system, 'config') and isinstance(ai_system.config, dict) and \
543
+ 'TIER_CONFIG' in ai_system.config and isinstance(ai_system.config['TIER_CONFIG'], dict) and \
544
+ tier_name in ai_system.config['TIER_CONFIG'] and isinstance(ai_system.config['TIER_CONFIG'][tier_name], dict):
545
+ backend_limit = ai_system.config['TIER_CONFIG'][tier_name].get('model_limit', 'N/A')
546
+ if isinstance(backend_limit, int) and backend_limit >= 999 :
547
+ model_limit_from_backend = "All"
548
+ else: model_limit_from_backend = str(backend_limit)
549
+ status_md += f"Models: Up to {model_limit_from_backend}"
550
+ status_md += "</small>"
551
+ return status_md
552
+
553
+ # --- Update Tier Display and Features UI ---
554
+ def update_tier_display_and_features_ui(user_token_str: Optional[str]) -> Tuple:
555
+ tier_name = get_user_tier(user_token_str if user_token_str else "")
556
+ tier_features = TIER_FEATURES.get(tier_name, TIER_FEATURES["free"])
557
+ logger.info(f"UI: Token changed. Tier: '{tier_name}'. Features: {tier_features}")
558
+ formatted_status = format_tier_status_for_ui(tier_name)
559
+ export_button_interactive = tier_features["export_enabled"]
560
+ export_button_classes = "interactive_button_enabled" if export_button_interactive else ""
561
+ return (gr.update(value=formatted_status), gr.update(interactive=export_button_interactive, elem_classes=export_button_classes))
562
+
563
+ # --- Clear All Fields Function ---
564
+ def clear_all_fields():
565
+ logger.info("Clearing all input fields and output areas.")
566
+ default_tier_name = get_user_tier("")
567
+ default_tier_features = TIER_FEATURES.get(default_tier_name, TIER_FEATURES["free"])
568
+ return (
569
+ "", # query_input_component
570
+ "", # user_token_input
571
+ gr.update(value=format_tier_status_for_ui(default_tier_name)), # tier_status_display
572
+ "Detailed perspectives will appear here...", # fused_response_output_component
573
+ "Synthesized insight will appear here...", # synthesized_summary_component
574
+ gr.update(value="**Status:** Awaiting Input"), # active_models_display_component
575
+ gr.update(value="<div class='memory-entry'>No memory entries loaded yet.</div>"), # memory_display_panel_html
576
+ gr.update(interactive=default_tier_features["export_enabled"],
577
+ elem_classes="interactive_button_enabled" if default_tier_features["export_enabled"] else ""),
578
+ None
579
+ )
580
+
581
+ # --- Function to prepare memory data for client-side download ---
582
+ async def export_user_memory_data_for_download(user_token_str: Optional[str]) -> Optional[str]:
583
+ try:
584
+ tier_name = get_user_tier(user_token_str if user_token_str else "")
585
+ tier_f = TIER_FEATURES.get(tier_name, TIER_FEATURES["free"])
586
+
587
+ if not tier_f["export_enabled"]:
588
+ gr.Warning("🚫 Export Memory is a Pro Tier feature. Please upgrade your plan.")
589
+ return None
590
+
591
+ if not ai_system or not hasattr(ai_system, 'memory_bank'):
592
+ gr.Error("⚠️ Memory system is not available in the backend.")
593
+ return None
594
+
595
+ memory_data = None
596
+ if hasattr(ai_system.memory_bank, 'get_all_memories_for_export_async'):
597
+ logger.info("Using asynchronous get_all_memories_for_export_async.")
598
+ memory_data = await ai_system.memory_bank.get_all_memories_for_export_async()
599
+ elif hasattr(ai_system.memory_bank, 'get_all_memories_for_export'):
600
+ logger.info("Using synchronous get_all_memories_for_export.")
601
+ memory_data = ai_system.memory_bank.get_all_memories_for_export()
602
+ else:
603
+ gr.Error("⚠️ Memory export data retrieval method not available in backend MemoryBank.")
604
+ return None
605
+
606
+ if not memory_data:
607
+ gr.Info("ℹ️ No memory entries to export.")
608
+ return None
609
+
610
+ json_data = json.dumps(memory_data, indent=2)
611
+
612
+ with tempfile.NamedTemporaryFile(delete=False, mode="w", suffix=".json", encoding="utf-8", prefix="zotheos_export_") as tmp_file:
613
+ tmp_file.write(json_data)
614
+ tmp_file_path = tmp_file.name
615
+
616
+ logger.info(f"✅ Memory data prepared for client download: {tmp_file_path}")
617
+ gr.Info(f"✅ Memory export ready. Download: {os.path.basename(tmp_file_path)}")
618
+ return tmp_file_path
619
+ except Exception as e:
620
+ logger.error(f"❌ Error preparing memory export for client: {e}", exc_info=True)
621
+ gr.Error(f"⚠️ Error preparing export: {str(e)}")
622
+ return None
623
+
624
+ # --- Build Gradio Interface ---
625
+ def build_interface(logo_path_param: Optional[str], favicon_path_param: Optional[str]) -> gr.Blocks:
626
+ # ✅🧠 DEFINITIVE FIX: Use a true neutral (grayscale) theme as the foundation.
627
+ theme = gr.themes.Base(
628
+ primary_hue=gr.themes.colors.neutral, # Changed from "slate"
629
+ secondary_hue=gr.themes.colors.neutral, # Changed from "gray"
630
+ neutral_hue=gr.themes.colors.neutral, # Changed from "gray"
631
+ font=[gr.themes.GoogleFont("Lato"),"ui-sans-serif"],
632
+ font_mono=[gr.themes.GoogleFont("Inconsolata"),"ui-monospace"]
633
+ ).set(
634
+ body_background_fill="var(--background-main)",
635
+ )
636
+
637
+ initial_tier_name = get_user_tier("")
638
+ initial_tier_features = TIER_FEATURES.get(initial_tier_name, TIER_FEATURES["free"])
639
+
640
+ with gr.Blocks(theme=theme, css=zotheos_base_css, title=APP_TITLE) as demo:
641
+ with gr.Column(elem_classes="centered-container"):
642
+ with gr.Column(elem_id="header_column"):
643
+ if logo_path_param:
644
+ gr.Image(value=logo_path_param, elem_id="header_logo", show_label=False, container=False, interactive=False)
645
+ gr.Markdown("Ethical Fusion AI for Synthesized Intelligence", elem_id="header_subtitle")
646
+ gr.Markdown("### 💡 Fusing perspectives for deeper truth.", elem_id="welcome_message")
647
+
648
+ with gr.Row(elem_id="auth_row", elem_classes="interface-section", equal_height=False):
649
+ user_token_input = gr.Textbox(label="🔑 Access Token", placeholder="Enter token for tiered features", type="password", elem_id="user_token_input", scale=3, container=False)
650
+ tier_status_display = gr.Markdown(value=format_tier_status_for_ui(initial_tier_name), elem_id="tier_status_display")
651
+
652
+ with gr.Group(elem_classes="interface-section"):
653
+ query_input_component = gr.Textbox(label="Your Inquiry:", placeholder="e.g., Analyze the ethical implications of AI in art...", lines=6, elem_id="query_input")
654
+ status_indicator_component = gr.HTML(elem_id="status_indicator", visible=False)
655
+ with gr.Row(elem_classes="button-row"):
656
+ submit_button_component = gr.Button("Process Inquiry", elem_id="submit_button", variant="primary", scale=2)
657
+ clear_button_component = gr.Button("Clear All", elem_id="clear_button", variant="secondary", scale=1)
658
+
659
+ with gr.Group(elem_classes="interface-section"):
660
+ synthesized_summary_component = gr.Markdown(elem_id="synthesized_summary_output", value="Synthesized insight will appear here...", visible=True)
661
+ fused_response_output_component = gr.Markdown(elem_id="fusion_output", value="Detailed perspectives will appear here...", visible=True)
662
+
663
+ with gr.Row(elem_id="pro_features_row", elem_classes="interface-section", visible=True):
664
+ export_memory_button = gr.Button(
665
+ "⬇️ Export Memory Log",
666
+ interactive=initial_tier_features["export_enabled"],
667
+ elem_classes="interactive_button_enabled"
668
+ )
669
+
670
+ with gr.Accordion("💡 System Status & Active Models", open=False, elem_id="status_accordion"):
671
+ active_models_display_component = gr.Markdown("**Status:** Initializing... ", elem_id="active_models_info")
672
+ with gr.Accordion("🧠 Recent Interaction Chronicle (Last 5)", open=False, elem_id="memory_viewer_section"):
673
+ memory_display_panel_html = gr.HTML(elem_id="memory_display_panel", value="<div class='memory-entry'>No memory entries yet.</div>")
674
+
675
+ gr.Markdown(
676
+ """---
677
+ ZOTHEOS © 2025 ZOTHEOS LLC. System Architect: David A. Garcia.
678
+ Built with open-source models: **Qwen** (Alibaba/Tongyi Qianwen License), **Mistral** (Mistral AI/Apache 2.0), **Gemma** (Google/Gemma T.O.U).
679
+ Powered by **llama-cpp-python** (MIT) & **Gradio** (Apache 2.0). Please see `LICENSE.txt` for full details.
680
+ Ethical Fusion AI for Humanity.
681
+ """, elem_id="footer_attribution"
682
+ )
683
+
684
+ async def process_query_wrapper_internal(query_text_internal: str, user_token_from_ui: Optional[str]) -> Tuple:
685
+ current_tier_name = get_user_tier(user_token_from_ui if user_token_from_ui else "")
686
+ tier_display_name = TIER_FEATURES.get(current_tier_name, TIER_FEATURES["free"])["display_name"]
687
+ loading_html = f"<div class='cosmic-loading'><div class='orbital-spinner'></div><div class='thinking-text'>ZOTHEOS is synthesizing (Tier: {tier_display_name})...</div></div>"
688
+ yield (
689
+ gr.update(value=loading_html, visible=True),
690
+ gr.update(value="Processing...", interactive=False),
691
+ gr.update(interactive=False),
692
+ gr.update(),
693
+ gr.update(value=""),
694
+ gr.update(value=""),
695
+ gr.update(),
696
+ gr.update(value="<div class='memory-entry'>Loading...</div>")
697
+ )
698
+
699
+ if not ai_system or initialization_error:
700
+ error_msg_html = f"<div style='color: var(--text-color-light); font-weight: bold; padding: 10px; border: 1px solid var(--border-color); background-color: #1a1a1a; border-radius: 6px;'>🚫 Core System Unresponsive. Details: {html.escape(str(initialization_error or 'Unknown Reason'))}. Please check logs or restart.</div>"
701
+ yield (
702
+ gr.update(value=error_msg_html,visible=True),
703
+ gr.update(value="Offline",interactive=False),
704
+ gr.update(interactive=True),
705
+ gr.update(),
706
+ gr.update(value="System error occurred."),
707
+ gr.update(value="Insight unavailable due to system error."),
708
+ gr.update(value="**Status:** `Offline - Critical Error`"),
709
+ gr.update(value="<div class='memory-entry'>Memory unavailable due to system error.</div>")
710
+ ); return
711
+ query = query_text_internal.strip();
712
+ if not query:
713
+ yield (
714
+ gr.update(value="",visible=False),
715
+ gr.update(value="🔍 Process",interactive=True),
716
+ gr.update(interactive=True),
717
+ gr.update(),
718
+ gr.update(value="Enter query."),
719
+ gr.update(value="Insight awaits..."),
720
+ gr.update(value="**Status:** Awaiting Input"),
721
+ gr.update(value="<div class='memory-entry'>No query entered.</div>")
722
+ ); return
723
+
724
+ fused_perspectives_content = ""; synthesized_summary_content = ""; rendered_memory_html = "<div class='memory-entry'>Memory processing...</div>"
725
+ try:
726
+ logger.info(f"UI: Query: '{query[:60]}...' Token: '{user_token_from_ui[:3] if user_token_from_ui else 'N/A'}...'")
727
+ current_mode_val = DEFAULT_INFERENCE_PRESET_INTERFACE
728
+ full_response_content = await ai_system.process_query_with_fusion(query, user_token=user_token_from_ui, fusion_mode_override=current_mode_val)
729
+
730
+ overall_header_marker = "## 🧠 ZOTHEOS Fused Perspectives 🧠"
731
+ final_insight_marker = "## ✨ ZOTHEOS Final Synthesized Insight ✨"
732
+ detailed_perspectives_marker = "### 💬 Detailed Individual Perspectives"
733
+
734
+ overall_header = ""
735
+ content_after_overall_header = full_response_content
736
+
737
+ if full_response_content.startswith(overall_header_marker):
738
+ first_double_newline = full_response_content.find("\n\n")
739
+ if first_double_newline != -1:
740
+ overall_header = full_response_content[:first_double_newline + 2]
741
+ content_after_overall_header = full_response_content[first_double_newline + 2:]
742
+ else:
743
+ overall_header = full_response_content
744
+ content_after_overall_header = ""
745
+
746
+ idx_final_insight = content_after_overall_header.find(final_insight_marker)
747
+
748
+ if idx_final_insight != -1 :
749
+ synthesized_summary_content = overall_header + content_after_overall_header[idx_final_insight:]
750
+ temp_content_before_insight = content_after_overall_header[:idx_final_insight]
751
+ idx_detailed_perspectives_in_prefix = temp_content_before_insight.rfind(detailed_perspectives_marker)
752
+
753
+ if idx_detailed_perspectives_in_prefix != -1:
754
+ fused_perspectives_content = overall_header + temp_content_before_insight[idx_detailed_perspectives_in_prefix:]
755
+ else:
756
+ fused_perspectives_content = overall_header + "Detailed perspectives are integrated within the main insight or not separately provided."
757
+ elif full_response_content:
758
+ synthesized_summary_content = full_response_content
759
+ fused_perspectives_content = "See main insight above for details."
760
+ else:
761
+ synthesized_summary_content = overall_header + "No insight generated."
762
+ fused_perspectives_content = overall_header + "No perspectives generated."
763
+
764
+ tier_features_for_query = TIER_FEATURES.get(current_tier_name, TIER_FEATURES["free"])
765
+ if tier_features_for_query["memory_enabled"]:
766
+ if ai_system and hasattr(ai_system, 'memory_bank') and ai_system.memory_bank:
767
+ memory_data_list = []
768
+ if hasattr(ai_system.memory_bank, 'retrieve_recent_memories_async'):
769
+ memory_data_list = await ai_system.memory_bank.retrieve_recent_memories_async(limit=10)
770
+ elif hasattr(ai_system.memory_bank, 'retrieve_recent_memories'):
771
+ possible_coro = ai_system.memory_bank.retrieve_recent_memories(limit=10)
772
+ if asyncio.iscoroutine(possible_coro): memory_data_list = await possible_coro
773
+ else: memory_data_list = possible_coro # type: ignore
774
+ rendered_memory_html = render_memory_entries_as_html(memory_data_list)
775
+ else: rendered_memory_html = "<div class='memory-entry'>Memory system error.</div>"
776
+ else: rendered_memory_html = "<div class='memory-entry'>Memory logging disabled for tier.</div>"
777
+ except Exception as e:
778
+ synthesized_summary_content = f"⚠️ Anomaly during processing: {html.escape(type(e).__name__)}. Please check logs."; fused_perspectives_content=""
779
+ rendered_memory_html = f"<div class='memory-entry'>Error during processing: {html.escape(str(e))}</div>"
780
+ logger.error(f"❌ Anomaly during query processing: {e}", exc_info=True)
781
+
782
+ active_models_md = "**Status:** `Ready`";
783
+ if ai_system and hasattr(ai_system, 'get_status_report') and callable(ai_system.get_status_report):
784
+ try:
785
+ status_report = await ai_system.get_status_report()
786
+ models_queried_for_this_request = status_report.get("models_last_queried_for_perspectives", [])
787
+ active_cores_str = ", ".join(models_queried_for_this_request) if models_queried_for_this_request else "N/A"
788
+ actual_models_used_count_display = len(models_queried_for_this_request)
789
+ active_models_md = f"**Active Cores (Used for Query):** `{active_cores_str}` ({actual_models_used_count_display} models) | **Status:** `Online`"
790
+ except Exception as e_status:
791
+ logger.warning(f"Status report error: {e_status}"); active_models_md = "**Status:** `Report Unavailable`"
792
+
793
+ yield (
794
+ gr.update(value="", visible=False),
795
+ gr.update(value="Process Inquiry", interactive=True),
796
+ gr.update(interactive=True),
797
+ gr.update(),
798
+ gr.update(value=fused_perspectives_content, visible=True),
799
+ gr.update(value=synthesized_summary_content, visible=True),
800
+ gr.update(value=active_models_md),
801
+ gr.update(value=rendered_memory_html)
802
+ )
803
+
804
+ clear_fn_outputs_ordered = [query_input_component, user_token_input, tier_status_display, fused_response_output_component, synthesized_summary_component, active_models_display_component, memory_display_panel_html, export_memory_button]
805
+ submit_fn_outputs_ordered = [status_indicator_component, submit_button_component, clear_button_component, query_input_component, fused_response_output_component, synthesized_summary_component, active_models_display_component, memory_display_panel_html]
806
+
807
+ submit_button_component.click(fn=process_query_wrapper_internal, inputs=[query_input_component, user_token_input], outputs=submit_fn_outputs_ordered, show_progress="hidden")
808
+ clear_button_component.click(fn=clear_all_fields, inputs=[], outputs=clear_fn_outputs_ordered, show_progress="hidden")
809
+
810
+ token_change_outputs = [tier_status_display, export_memory_button]
811
+ user_token_input.change(fn=update_tier_display_and_features_ui, inputs=[user_token_input], outputs=token_change_outputs, show_progress="hidden")
812
+
813
+ export_memory_button.click(fn=export_user_memory_data_for_download, inputs=[user_token_input], outputs=[])
814
+
815
+ return demo
816
+
817
+ # --- Main Execution Block (for direct run of this file) ---
818
+ if __name__ == "__main__":
819
+ logger.info("--- Initializing ZOTHEOS Gradio Interface (V11 - Direct Run) ---")
820
+
821
+ logger.info("Building ZOTHEOS Gradio UI...")
822
+ zotheos_interface = build_interface(logo_path_verified, favicon_path_verified)
823
+ logger.info("✅ ZOTHEOS Gradio UI built.")
824
+
825
+ logger.info("🚀 Launching ZOTHEOS Gradio application...")
826
+ print("\n" + "="*60 + f"\n ZOTHEOS Interface ({APP_TITLE} - V11)\n" + "="*60)
827
+ if initialization_error:
828
+ print(f"‼️ WARNING: ZOTHEOS AI System failed to initialize fully: {type(initialization_error).__name__} - {initialization_error}")
829
+ if 'get_user_tier' not in globals() or get_user_tier == get_user_tier_fallback:
830
+ print(" Additionally, user authentication module might have failed to load. Using fallback tier logic.")
831
+ elif not ai_system:
832
+ print(f"‼️ WARNING: ZOTHEOS AI System object (ai_system) is None. Backend will not function.")
833
+ else:
834
+ print("✅ ZOTHEOS AI System appears initialized.")
835
+
836
+ try:
837
+ import llama_cpp # type: ignore
838
+ print("✅ llama-cpp-python library available.")
839
+ except ImportError:
840
+ print("\n‼️ CRITICAL WARNING: 'llama-cpp-python' not found! ZOTHEOS backend will fail.\n Install via: pip install llama-cpp-python --force-reinstall --upgrade --no-cache-dir --verbose")
841
+
842
+ print("\nZOTHEOS Interface starting. Access via URL below.")
843
+ print("="*60 + "\n")
844
+
845
+ launch_kwargs = {
846
+ "server_name": "0.0.0.0",
847
+ "server_port": int(os.getenv("PORT", 7860)),
848
+ "share": os.getenv("GRADIO_SHARE", "False").lower() == "true",
849
+ "inbrowser": True,
850
+ "show_api": False,
851
+ "favicon_path": favicon_path_verified
852
+ }
853
+
854
+ zotheos_interface.queue().launch(**launch_kwargs)
855
+ logger.info("--- ZOTHEOS Gradio Interface Shutdown ---")