File size: 9,828 Bytes
92eb899
742b2a5
92eb899
 
 
73f9fb2
 
742b2a5
 
 
 
 
 
 
3f5b0f9
 
 
92eb899
4a45067
742b2a5
73f9fb2
 
 
4a45067
 
73f9fb2
4a45067
f1fb766
4a45067
92eb899
3f5b0f9
 
 
 
4a45067
3f5b0f9
 
 
 
 
 
 
73f9fb2
3f5b0f9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a45067
f1fb766
4a45067
f1fb766
4a45067
 
 
92eb899
 
 
f1fb766
4a45067
f1fb766
4a45067
92eb899
 
f1fb766
92eb899
 
03df531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742b2a5
 
03df531
742b2a5
 
 
f1fb766
 
742b2a5
 
 
 
f1fb766
742b2a5
f1fb766
742b2a5
 
f1fb766
742b2a5
 
 
 
 
 
f1fb766
 
742b2a5
 
 
92eb899
f1fb766
92eb899
f1fb766
 
 
92eb899
f1fb766
742b2a5
 
 
 
 
 
 
 
 
 
 
f1fb766
 
 
742b2a5
 
92eb899
f1fb766
92eb899
 
3f5b0f9
f1fb766
3f5b0f9
 
92eb899
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# modules/analyzer.py
from openai import OpenAI
import requests
import time
import logging
from modules.server_cache import RedisServerStatusCache
from modules.status_logger import log_server_status

class Analyzer:
    def __init__(self, base_url, api_key):
        self.client = OpenAI(
            base_url=base_url,
            api_key=api_key
        )
        self.base_url = base_url.rstrip('/')
        self.health_check_url = self.base_url + "/health"
        self.models_url = self.base_url + "/models"
        self.headers = {"Authorization": f"Bearer {api_key}"}
        self.cache_key = f"server_status_{base_url}"
        
        # Connect to Redis cache
        self.cache = RedisServerStatusCache()
        
    def is_server_ready(self):
        # Check cache first
        cached_status = self.cache.get(self.cache_key)
        if cached_status is not None:
            logging.info(f"Using cached server status: {cached_status}")
            return cached_status
            
        # Try multiple approaches to check if server is ready
        is_ready = False
        
        # Approach 1: Try /models endpoint (commonly available)
        try:
            logging.info(f"Checking server models at: {self.models_url}")
            response = requests.get(self.models_url, headers=self.headers, timeout=10)
            if response.status_code in [200, 401, 403]:  # 401/403 means auth required but endpoint exists
                is_ready = True
                logging.info(f"Server models check response: {response.status_code}")
            else:
                logging.info(f"Server models check response: {response.status_code}")
        except requests.exceptions.RequestException as e:
            logging.info(f"Models endpoint check failed: {str(e)}")
        
        # Approach 2: Try a lightweight API call if models endpoint failed
        if not is_ready:
            try:
                logging.info("Trying lightweight API call to test server availability")
                # Make a request to list models (doesn't consume tokens)
                response = requests.get(f"{self.base_url}/models", headers=self.headers, timeout=10)
                if response.status_code in [200, 401, 403]:
                    is_ready = True
                    logging.info(f"Lightweight API call response: {response.status_code}")
                else:
                    logging.info(f"Lightweight API call response: {response.status_code}")
            except requests.exceptions.RequestException as e:
                logging.info(f"Lightweight API call failed: {str(e)}")
        
        # Cache the result for a shorter time since we're not using a proper health endpoint
        self.cache.set(self.cache_key, is_ready, ttl=60)  # Cache for 1 minute
        log_server_status(self.cache_key, is_ready)
        return is_ready

    def wait_for_server(self, timeout=180, interval=15):
        if self.is_server_ready():
            logging.info("✅ Server is already ready (from cache or direct check).")
            return True
            
        logging.info("⏳ Server not ready. Starting polling...")
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            logging.info(f"Polling server... ({int(time.time() - start_time)}s elapsed)")
            if self.is_server_ready():
                logging.info("✅ Server is now ready!")
                return True
            time.sleep(interval)
        
        logging.warning("⏰ Server initialization timeout reached")
        return False

    def analyze_stream(self, query, search_results):
        """
        Analyze search results using the custom LLM with streaming output
        Yields chunks of the response as they arrive
        """
        # Prepare context from search results
        context = "\n\n".join([
            f"Source: {result.get('url', 'N/A')}\nTitle: {result.get('title', 'N/A')}\nContent: {result.get('content', 'N/A')}"
            for result in search_results[:5]  # Limit to top 5 for context
        ])
        
        prompt = f"""
        You are an expert research analyst. Analyze the following query and information to provide a comprehensive summary.

        Query: {query}

        Information:
        {context}

        Please provide:
        1. A brief overview of the topic
        2. Key findings or developments
        3. Different perspectives or approaches
        4. Potential implications or future directions
        5. Any controversies or conflicting viewpoints

        Structure your response clearly with these sections. If there is insufficient information, state that clearly.
        """
        
        try:
            # First check if server is ready
            logging.info("Checking if server is ready for analysis...")
            if not self.wait_for_server(timeout=180):  # 3 minutes timeout
                error_msg = "⚠️ The AI model server is still initializing. Please try again in a few minutes."
                logging.warning(error_msg)
                yield error_msg
                return
            
            logging.info("Server is ready. Sending streaming request to AI model...")
            
            # Send streaming request
            response = self.client.chat.completions.create(
                model="DavidAU/OpenAi-GPT-oss-20b-abliterated-uncensored-NEO-Imatrix-gguf",
                messages=[
                    {"role": "system", "content": "You are a helpful research assistant that provides structured, analytical responses."},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.7,
                max_tokens=1500,
                stream=True  # Enable streaming
            )
            
            # Yield chunks as they arrive
            for chunk in response:
                if chunk.choices[0].delta.content:
                    yield chunk.choices[0].delta.content
                    
            logging.info("Analysis streaming completed successfully")
            
        except Exception as e:
            error_msg = str(e)
            logging.error(f"Analysis streaming failed: {error_msg}")
            if "503" in error_msg or "Service Unavailable" in error_msg:
                yield "⚠️ The AI model server is currently unavailable. It may be initializing. Please try again in a few minutes."
            elif "timeout" in error_msg.lower() or "read timeout" in error_msg.lower():
                yield "⚠️ The AI model request timed out. The server may be overloaded or initializing. Please try again in a few minutes."
            elif "404" in error_msg:
                yield "⚠️ The AI model endpoint was not found. Please check the configuration."
            else:
                yield f"Analysis failed: {str(e)}"

    def analyze(self, query, search_results):
        """
        Non-streaming version for compatibility
        """
        # Prepare context from search results
        context = "\n\n".join([
            f"Source: {result.get('url', 'N/A')}\nTitle: {result.get('title', 'N/A')}\nContent: {result.get('content', 'N/A')}"
            for result in search_results[:5]  # Limit to top 5 for context
        ])
        
        prompt = f"""
        You are an expert research analyst. Analyze the following query and information to provide a comprehensive summary.

        Query: {query}

        Information:
        {context}

        Please provide:
        1. A brief overview of the topic
        2. Key findings or developments
        3. Different perspectives or approaches
        4. Potential implications or future directions
        5. Any controversies or conflicting viewpoints

        Structure your response clearly with these sections. If there is insufficient information, state that clearly.
        """
        
        try:
            # First check if server is ready
            logging.info("Checking if server is ready for analysis...")
            if not self.wait_for_server(timeout=180):  # 3 minutes timeout
                error_msg = "⚠️ The AI model server is still initializing. Please try again in a few minutes."
                logging.warning(error_msg)
                return error_msg
            
            logging.info("Server is ready. Sending request to AI model...")
            response = self.client.chat.completions.create(
                model="DavidAU/OpenAi-GPT-oss-20b-abliterated-uncensored-NEO-Imatrix-gguf",
                messages=[
                    {"role": "system", "content": "You are a helpful research assistant that provides structured, analytical responses."},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.7,
                max_tokens=1500,
                stream=False
            )
            
            result_content = response.choices[0].message.content
            logging.info("Analysis completed successfully")
            return result_content
            
        except Exception as e:
            error_msg = str(e)
            logging.error(f"Analysis failed: {error_msg}")
            if "503" in error_msg or "Service Unavailable" in error_msg:
                return "⚠️ The AI model server is currently unavailable. It may be initializing. Please try again in a few minutes."
            elif "timeout" in error_msg.lower() or "read timeout" in error_msg.lower():
                return "⚠️ The AI model request timed out. The server may be overloaded or initializing. Please try again in a few minutes."
            elif "404" in error_msg:
                return "⚠️ The AI model endpoint was not found. Please check the configuration."
            return f"Analysis failed: {str(e)}"