Fix critical LLM provider bugs, Redis config, and Streamlit null safety issues
Browse files- README.md +0 -39
- api/main.py +10 -0
- app.py +37 -24
- core/llm.py +11 -13
- ngrok.yml.txt +9 -0
- test_setup.py +33 -0
- utils/__pycache__/config.cpython-313.pyc +0 -0
- utils/config.py +8 -3
README.md
CHANGED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: AI Life Coach
|
| 3 |
-
emoji: 🧘
|
| 4 |
-
colorFrom: blue
|
| 5 |
-
colorTo: green
|
| 6 |
-
sdk: streamlit
|
| 7 |
-
sdk_version: 1.24.0
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
license: apache-2.0
|
| 11 |
-
---
|
| 12 |
-
|
| 13 |
-
# AI Life Coach
|
| 14 |
-
|
| 15 |
-
A personal development assistant powered by LLMs.
|
| 16 |
-
|
| 17 |
-
## Features
|
| 18 |
-
|
| 19 |
-
- Ollama & Hugging Face models
|
| 20 |
-
- Redis-based memory
|
| 21 |
-
- User sessions (Rob & Sarah)
|
| 22 |
-
- FastAPI backend
|
| 23 |
-
- Streamlit UI
|
| 24 |
-
|
| 25 |
-
## Deployment
|
| 26 |
-
|
| 27 |
-
The application is designed to work in Hugging Face Spaces environment. For local LLM inference, it connects to a remote Ollama instance via ngrok tunnel at `https://ace32bd59aef.ngrok-free.app`. This allows the application to access powerful local models without requiring them to be installed directly in the Space.
|
| 28 |
-
|
| 29 |
-
In case the remote Ollama instance is unavailable, the system gracefully falls back to checking a local instance, and handles unavailability by showing appropriate status messages in the UI.
|
| 30 |
-
|
| 31 |
-
## Troubleshooting
|
| 32 |
-
|
| 33 |
-
If you're experiencing connection issues with Ollama, you can run the diagnostic script:
|
| 34 |
-
|
| 35 |
-
```
|
| 36 |
-
python diagnose_ollama.py
|
| 37 |
-
```
|
| 38 |
-
|
| 39 |
-
This will test connectivity to your configured Ollama host and provide detailed information about any connection problems.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
api/main.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from fastapi import FastAPI
|
| 2 |
from api.status import router as status_router
|
| 3 |
from api.chat import router as chat_router
|
|
|
|
| 4 |
|
| 5 |
app = FastAPI()
|
| 6 |
|
|
@@ -11,3 +12,12 @@ app.include_router(chat_router, prefix="/api")
|
|
| 11 |
@app.get("/")
|
| 12 |
async def root():
|
| 13 |
return {"message": "AI Life Coach API is running"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from fastapi import FastAPI
|
| 2 |
from api.status import router as status_router
|
| 3 |
from api.chat import router as chat_router
|
| 4 |
+
from core.memory import check_redis_health
|
| 5 |
|
| 6 |
app = FastAPI()
|
| 7 |
|
|
|
|
| 12 |
@app.get("/")
|
| 13 |
async def root():
|
| 14 |
return {"message": "AI Life Coach API is running"}
|
| 15 |
+
|
| 16 |
+
@app.get("/health")
|
| 17 |
+
async def health_check():
|
| 18 |
+
"""Health check endpoint"""
|
| 19 |
+
redis_healthy = check_redis_health()
|
| 20 |
+
return {
|
| 21 |
+
"status": "healthy" if redis_healthy else "degraded",
|
| 22 |
+
"redis": "healthy" if redis_healthy else "unhealthy"
|
| 23 |
+
}
|
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# Force redeploy trigger - version 1.
|
| 2 |
import streamlit as st
|
| 3 |
from utils.config import config
|
| 4 |
import requests
|
|
@@ -12,13 +12,10 @@ st.set_page_config(page_title="AI Life Coach", page_icon="🧘", layout="centere
|
|
| 12 |
# Initialize session state
|
| 13 |
if 'ngrok_url' not in st.session_state:
|
| 14 |
st.session_state.ngrok_url = config.ollama_host
|
| 15 |
-
|
| 16 |
if 'model_status' not in st.session_state:
|
| 17 |
st.session_state.model_status = "checking"
|
| 18 |
-
|
| 19 |
if 'available_models' not in st.session_state:
|
| 20 |
st.session_state.available_models = []
|
| 21 |
-
|
| 22 |
if 'selected_model' not in st.session_state:
|
| 23 |
st.session_state.selected_model = config.local_model_name
|
| 24 |
|
|
@@ -47,7 +44,7 @@ NGROK_HEADERS = {
|
|
| 47 |
def fetch_available_models(ngrok_url):
|
| 48 |
try:
|
| 49 |
response = requests.get(
|
| 50 |
-
f"{ngrok_url}/api/tags",
|
| 51 |
headers=NGROK_HEADERS,
|
| 52 |
timeout=5
|
| 53 |
)
|
|
@@ -72,11 +69,11 @@ st.sidebar.markdown("---")
|
|
| 72 |
st.sidebar.subheader("Model Selection")
|
| 73 |
if st.session_state.available_models:
|
| 74 |
selected_model = st.sidebar.selectbox(
|
| 75 |
-
"Select Model",
|
| 76 |
st.session_state.available_models,
|
| 77 |
-
index=st.session_state.available_models.index(st.session_state.selected_model)
|
| 78 |
-
|
| 79 |
-
|
| 80 |
)
|
| 81 |
st.session_state.selected_model = selected_model
|
| 82 |
else:
|
|
@@ -94,7 +91,7 @@ IS_HF_SPACE = bool(BASE_URL)
|
|
| 94 |
def get_ollama_status(ngrok_url):
|
| 95 |
try:
|
| 96 |
response = requests.get(
|
| 97 |
-
f"{ngrok_url}/api/tags",
|
| 98 |
headers=NGROK_HEADERS,
|
| 99 |
timeout=10
|
| 100 |
)
|
|
@@ -106,7 +103,7 @@ def get_ollama_status(ngrok_url):
|
|
| 106 |
if models:
|
| 107 |
selected_model_available = st.session_state.selected_model in model_names
|
| 108 |
return {
|
| 109 |
-
"running": True,
|
| 110 |
"model_loaded": st.session_state.selected_model if selected_model_available else model_names[0],
|
| 111 |
"remote_host": ngrok_url,
|
| 112 |
"available_models": model_names,
|
|
@@ -115,15 +112,23 @@ def get_ollama_status(ngrok_url):
|
|
| 115 |
else:
|
| 116 |
st.session_state.model_status = "no_models"
|
| 117 |
return {
|
| 118 |
-
"running": False,
|
| 119 |
"model_loaded": None,
|
| 120 |
"remote_host": ngrok_url,
|
| 121 |
"message": "Connected to Ollama but no models found"
|
| 122 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
except Exception as e:
|
| 124 |
st.session_state.model_status = "unreachable"
|
| 125 |
return {
|
| 126 |
-
"running": False,
|
| 127 |
"model_loaded": None,
|
| 128 |
"error": str(e),
|
| 129 |
"remote_host": ngrok_url
|
|
@@ -139,9 +144,18 @@ def get_conversation_history(user_id):
|
|
| 139 |
st.warning(f"Could not load conversation history: {e}")
|
| 140 |
return []
|
| 141 |
|
| 142 |
-
#
|
| 143 |
ollama_status = get_ollama_status(st.session_state.ngrok_url)
|
| 144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
# Update model status
|
| 146 |
if ollama_status.get("running", False):
|
| 147 |
if ollama_status.get("available_models"):
|
|
@@ -151,9 +165,13 @@ if ollama_status.get("running", False):
|
|
| 151 |
else:
|
| 152 |
st.session_state.model_status = "unreachable"
|
| 153 |
|
| 154 |
-
#
|
|
|
|
|
|
|
|
|
|
| 155 |
use_fallback = not ollama_status.get("running", False) or config.use_fallback
|
| 156 |
|
|
|
|
| 157 |
if use_fallback:
|
| 158 |
st.sidebar.warning("🌐 Using Hugging Face fallback (Ollama not available)")
|
| 159 |
if "error" in ollama_status:
|
|
@@ -213,14 +231,12 @@ def send_to_ollama(user_input, conversation_history, ngrok_url, model_name):
|
|
| 213 |
"top_p": 0.9
|
| 214 |
}
|
| 215 |
}
|
| 216 |
-
|
| 217 |
response = requests.post(
|
| 218 |
f"{ngrok_url}/api/chat",
|
| 219 |
json=payload,
|
| 220 |
headers=NGROK_HEADERS,
|
| 221 |
timeout=60
|
| 222 |
)
|
| 223 |
-
|
| 224 |
if response.status_code == 200:
|
| 225 |
response_data = response.json()
|
| 226 |
return response_data.get("message", {}).get("content", "")
|
|
@@ -237,7 +253,6 @@ def send_to_hf(user_input, conversation_history):
|
|
| 237 |
try:
|
| 238 |
from core.llm import LLMClient
|
| 239 |
llm_client = LLMClient(provider="huggingface")
|
| 240 |
-
|
| 241 |
# Format for HF
|
| 242 |
prompt = "You are a helpful life coach. "
|
| 243 |
for msg in conversation_history:
|
|
@@ -246,7 +261,6 @@ def send_to_hf(user_input, conversation_history):
|
|
| 246 |
elif msg["role"] == "assistant":
|
| 247 |
prompt += f"Assistant: {msg['content']} "
|
| 248 |
prompt += "Assistant:"
|
| 249 |
-
|
| 250 |
response = llm_client.generate(prompt, max_tokens=500, stream=False)
|
| 251 |
return response
|
| 252 |
except Exception as e:
|
|
@@ -268,10 +282,9 @@ if st.button("Send"):
|
|
| 268 |
else:
|
| 269 |
# Display user message
|
| 270 |
st.markdown(f"**You:** {user_input}")
|
| 271 |
-
|
| 272 |
# Prepare conversation history
|
| 273 |
-
conversation_history = [{"role": msg["role"], "content": msg["content"]}
|
| 274 |
-
for msg in conversation[-5:]]
|
| 275 |
conversation_history.append({"role": "user", "content": user_input})
|
| 276 |
|
| 277 |
# Send to appropriate backend
|
|
@@ -281,8 +294,8 @@ if st.button("Send"):
|
|
| 281 |
backend_used = "Hugging Face"
|
| 282 |
else:
|
| 283 |
ai_response = send_to_ollama(
|
| 284 |
-
user_input,
|
| 285 |
-
conversation_history,
|
| 286 |
st.session_state.ngrok_url,
|
| 287 |
st.session_state.selected_model
|
| 288 |
)
|
|
|
|
| 1 |
+
# Force redeploy trigger - version 1.9
|
| 2 |
import streamlit as st
|
| 3 |
from utils.config import config
|
| 4 |
import requests
|
|
|
|
| 12 |
# Initialize session state
|
| 13 |
if 'ngrok_url' not in st.session_state:
|
| 14 |
st.session_state.ngrok_url = config.ollama_host
|
|
|
|
| 15 |
if 'model_status' not in st.session_state:
|
| 16 |
st.session_state.model_status = "checking"
|
|
|
|
| 17 |
if 'available_models' not in st.session_state:
|
| 18 |
st.session_state.available_models = []
|
|
|
|
| 19 |
if 'selected_model' not in st.session_state:
|
| 20 |
st.session_state.selected_model = config.local_model_name
|
| 21 |
|
|
|
|
| 44 |
def fetch_available_models(ngrok_url):
|
| 45 |
try:
|
| 46 |
response = requests.get(
|
| 47 |
+
f"{ngrok_url}/api/tags",
|
| 48 |
headers=NGROK_HEADERS,
|
| 49 |
timeout=5
|
| 50 |
)
|
|
|
|
| 69 |
st.sidebar.subheader("Model Selection")
|
| 70 |
if st.session_state.available_models:
|
| 71 |
selected_model = st.sidebar.selectbox(
|
| 72 |
+
"Select Model",
|
| 73 |
st.session_state.available_models,
|
| 74 |
+
index=st.session_state.available_models.index(st.session_state.selected_model)
|
| 75 |
+
if st.session_state.selected_model in st.session_state.available_models
|
| 76 |
+
else 0
|
| 77 |
)
|
| 78 |
st.session_state.selected_model = selected_model
|
| 79 |
else:
|
|
|
|
| 91 |
def get_ollama_status(ngrok_url):
|
| 92 |
try:
|
| 93 |
response = requests.get(
|
| 94 |
+
f"{ngrok_url}/api/tags",
|
| 95 |
headers=NGROK_HEADERS,
|
| 96 |
timeout=10
|
| 97 |
)
|
|
|
|
| 103 |
if models:
|
| 104 |
selected_model_available = st.session_state.selected_model in model_names
|
| 105 |
return {
|
| 106 |
+
"running": True,
|
| 107 |
"model_loaded": st.session_state.selected_model if selected_model_available else model_names[0],
|
| 108 |
"remote_host": ngrok_url,
|
| 109 |
"available_models": model_names,
|
|
|
|
| 112 |
else:
|
| 113 |
st.session_state.model_status = "no_models"
|
| 114 |
return {
|
| 115 |
+
"running": False,
|
| 116 |
"model_loaded": None,
|
| 117 |
"remote_host": ngrok_url,
|
| 118 |
"message": "Connected to Ollama but no models found"
|
| 119 |
}
|
| 120 |
+
else:
|
| 121 |
+
st.session_state.model_status = "unreachable"
|
| 122 |
+
return {
|
| 123 |
+
"running": False,
|
| 124 |
+
"model_loaded": None,
|
| 125 |
+
"error": f"HTTP {response.status_code}",
|
| 126 |
+
"remote_host": ngrok_url
|
| 127 |
+
}
|
| 128 |
except Exception as e:
|
| 129 |
st.session_state.model_status = "unreachable"
|
| 130 |
return {
|
| 131 |
+
"running": False,
|
| 132 |
"model_loaded": None,
|
| 133 |
"error": str(e),
|
| 134 |
"remote_host": ngrok_url
|
|
|
|
| 144 |
st.warning(f"Could not load conversation history: {e}")
|
| 145 |
return []
|
| 146 |
|
| 147 |
+
# Get Ollama status with null safety
|
| 148 |
ollama_status = get_ollama_status(st.session_state.ngrok_url)
|
| 149 |
|
| 150 |
+
# Add null safety check
|
| 151 |
+
if ollama_status is None:
|
| 152 |
+
ollama_status = {
|
| 153 |
+
"running": False,
|
| 154 |
+
"model_loaded": None,
|
| 155 |
+
"error": "Failed to get Ollama status",
|
| 156 |
+
"remote_host": st.session_state.ngrok_url
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
# Update model status
|
| 160 |
if ollama_status.get("running", False):
|
| 161 |
if ollama_status.get("available_models"):
|
|
|
|
| 165 |
else:
|
| 166 |
st.session_state.model_status = "unreachable"
|
| 167 |
|
| 168 |
+
# Ensure ollama_status is a dict even if None
|
| 169 |
+
ollama_status = ollama_status or {}
|
| 170 |
+
|
| 171 |
+
# Determine if we should use fallback
|
| 172 |
use_fallback = not ollama_status.get("running", False) or config.use_fallback
|
| 173 |
|
| 174 |
+
# Display Ollama status
|
| 175 |
if use_fallback:
|
| 176 |
st.sidebar.warning("🌐 Using Hugging Face fallback (Ollama not available)")
|
| 177 |
if "error" in ollama_status:
|
|
|
|
| 231 |
"top_p": 0.9
|
| 232 |
}
|
| 233 |
}
|
|
|
|
| 234 |
response = requests.post(
|
| 235 |
f"{ngrok_url}/api/chat",
|
| 236 |
json=payload,
|
| 237 |
headers=NGROK_HEADERS,
|
| 238 |
timeout=60
|
| 239 |
)
|
|
|
|
| 240 |
if response.status_code == 200:
|
| 241 |
response_data = response.json()
|
| 242 |
return response_data.get("message", {}).get("content", "")
|
|
|
|
| 253 |
try:
|
| 254 |
from core.llm import LLMClient
|
| 255 |
llm_client = LLMClient(provider="huggingface")
|
|
|
|
| 256 |
# Format for HF
|
| 257 |
prompt = "You are a helpful life coach. "
|
| 258 |
for msg in conversation_history:
|
|
|
|
| 261 |
elif msg["role"] == "assistant":
|
| 262 |
prompt += f"Assistant: {msg['content']} "
|
| 263 |
prompt += "Assistant:"
|
|
|
|
| 264 |
response = llm_client.generate(prompt, max_tokens=500, stream=False)
|
| 265 |
return response
|
| 266 |
except Exception as e:
|
|
|
|
| 282 |
else:
|
| 283 |
# Display user message
|
| 284 |
st.markdown(f"**You:** {user_input}")
|
| 285 |
+
|
| 286 |
# Prepare conversation history
|
| 287 |
+
conversation_history = [{"role": msg["role"], "content": msg["content"]} for msg in conversation[-5:]]
|
|
|
|
| 288 |
conversation_history.append({"role": "user", "content": user_input})
|
| 289 |
|
| 290 |
# Send to appropriate backend
|
|
|
|
| 294 |
backend_used = "Hugging Face"
|
| 295 |
else:
|
| 296 |
ai_response = send_to_ollama(
|
| 297 |
+
user_input,
|
| 298 |
+
conversation_history,
|
| 299 |
st.session_state.ngrok_url,
|
| 300 |
st.session_state.selected_model
|
| 301 |
)
|
core/llm.py
CHANGED
|
@@ -8,7 +8,6 @@ from utils.config import config
|
|
| 8 |
|
| 9 |
class LLMProvider(ABC):
|
| 10 |
"""Abstract base class for all LLM providers"""
|
| 11 |
-
|
| 12 |
def __init__(self, model_name: str, timeout: int = 30, retries: int = 3):
|
| 13 |
self.model_name = model_name
|
| 14 |
self.timeout = timeout
|
|
@@ -29,10 +28,9 @@ class LLMProvider(ABC):
|
|
| 29 |
last_exception = e
|
| 30 |
if attempt < self.retries:
|
| 31 |
time.sleep(1 * (2 ** attempt)) # Exponential backoff
|
| 32 |
-
|
| 33 |
raise last_exception
|
| 34 |
|
| 35 |
-
|
| 36 |
class OllamaProvider(LLMProvider):
|
| 37 |
def __init__(self, model_name: str, host: str = None, timeout: int = 30, retries: int = 3):
|
| 38 |
super().__init__(model_name, timeout, retries)
|
|
@@ -78,10 +76,10 @@ class OllamaProvider(LLMProvider):
|
|
| 78 |
return stream_response()
|
| 79 |
else:
|
| 80 |
return response.json()["response"]
|
| 81 |
-
|
|
|
|
| 82 |
return self._retry_request(_make_request)
|
| 83 |
|
| 84 |
-
|
| 85 |
class HuggingFaceProvider(LLMProvider):
|
| 86 |
def __init__(self, model_name: str, timeout: int = 30, retries: int = 3):
|
| 87 |
super().__init__(model_name, timeout, retries)
|
|
@@ -108,10 +106,10 @@ class HuggingFaceProvider(LLMProvider):
|
|
| 108 |
return stream_response()
|
| 109 |
else:
|
| 110 |
return response.choices[0].message.content
|
| 111 |
-
|
|
|
|
| 112 |
return self._retry_request(_make_request)
|
| 113 |
|
| 114 |
-
|
| 115 |
class OpenAIProvider(LLMProvider):
|
| 116 |
def __init__(self, model_name: str, api_key: str = None, timeout: int = 30, retries: int = 3):
|
| 117 |
super().__init__(model_name, timeout, retries)
|
|
@@ -135,17 +133,17 @@ class OpenAIProvider(LLMProvider):
|
|
| 135 |
return stream_response()
|
| 136 |
else:
|
| 137 |
return response.choices[0].message.content
|
| 138 |
-
|
|
|
|
| 139 |
return self._retry_request(_make_request)
|
| 140 |
|
| 141 |
-
|
| 142 |
class LLMClient:
|
| 143 |
PROVIDER_MAP = {
|
| 144 |
"ollama": OllamaProvider,
|
| 145 |
"huggingface": HuggingFaceProvider,
|
| 146 |
"openai": OpenAIProvider
|
| 147 |
}
|
| 148 |
-
|
| 149 |
def __init__(self, provider: str = "ollama", model_name: str = None, **provider_kwargs):
|
| 150 |
self.provider_name = provider.lower()
|
| 151 |
self.model_name = model_name or self._get_default_model()
|
|
@@ -155,7 +153,7 @@ class LLMClient:
|
|
| 155 |
|
| 156 |
provider_class = self.PROVIDER_MAP[self.provider_name]
|
| 157 |
self.provider = provider_class(self.model_name, **provider_kwargs)
|
| 158 |
-
|
| 159 |
def _get_default_model(self) -> str:
|
| 160 |
"""Get default model based on provider"""
|
| 161 |
defaults = {
|
|
@@ -164,11 +162,11 @@ class LLMClient:
|
|
| 164 |
"openai": "gpt-3.5-turbo"
|
| 165 |
}
|
| 166 |
return defaults.get(self.provider_name, "mistral")
|
| 167 |
-
|
| 168 |
def generate(self, prompt: str, max_tokens: int = 500, stream: bool = False) -> Union[str, Generator[str, None, None]]:
|
| 169 |
"""Unified generate method that delegates to provider"""
|
| 170 |
return self.provider.generate(prompt, max_tokens, stream)
|
| 171 |
-
|
| 172 |
@classmethod
|
| 173 |
def get_available_providers(cls) -> list:
|
| 174 |
"""Return list of supported providers"""
|
|
|
|
| 8 |
|
| 9 |
class LLMProvider(ABC):
|
| 10 |
"""Abstract base class for all LLM providers"""
|
|
|
|
| 11 |
def __init__(self, model_name: str, timeout: int = 30, retries: int = 3):
|
| 12 |
self.model_name = model_name
|
| 13 |
self.timeout = timeout
|
|
|
|
| 28 |
last_exception = e
|
| 29 |
if attempt < self.retries:
|
| 30 |
time.sleep(1 * (2 ** attempt)) # Exponential backoff
|
| 31 |
+
continue
|
| 32 |
raise last_exception
|
| 33 |
|
|
|
|
| 34 |
class OllamaProvider(LLMProvider):
|
| 35 |
def __init__(self, model_name: str, host: str = None, timeout: int = 30, retries: int = 3):
|
| 36 |
super().__init__(model_name, timeout, retries)
|
|
|
|
| 76 |
return stream_response()
|
| 77 |
else:
|
| 78 |
return response.json()["response"]
|
| 79 |
+
|
| 80 |
+
# Fixed: Moved return outside the _make_request function
|
| 81 |
return self._retry_request(_make_request)
|
| 82 |
|
|
|
|
| 83 |
class HuggingFaceProvider(LLMProvider):
|
| 84 |
def __init__(self, model_name: str, timeout: int = 30, retries: int = 3):
|
| 85 |
super().__init__(model_name, timeout, retries)
|
|
|
|
| 106 |
return stream_response()
|
| 107 |
else:
|
| 108 |
return response.choices[0].message.content
|
| 109 |
+
|
| 110 |
+
# Fixed: Moved return outside the _make_request function
|
| 111 |
return self._retry_request(_make_request)
|
| 112 |
|
|
|
|
| 113 |
class OpenAIProvider(LLMProvider):
|
| 114 |
def __init__(self, model_name: str, api_key: str = None, timeout: int = 30, retries: int = 3):
|
| 115 |
super().__init__(model_name, timeout, retries)
|
|
|
|
| 133 |
return stream_response()
|
| 134 |
else:
|
| 135 |
return response.choices[0].message.content
|
| 136 |
+
|
| 137 |
+
# Fixed: Moved return outside the _make_request function
|
| 138 |
return self._retry_request(_make_request)
|
| 139 |
|
|
|
|
| 140 |
class LLMClient:
|
| 141 |
PROVIDER_MAP = {
|
| 142 |
"ollama": OllamaProvider,
|
| 143 |
"huggingface": HuggingFaceProvider,
|
| 144 |
"openai": OpenAIProvider
|
| 145 |
}
|
| 146 |
+
|
| 147 |
def __init__(self, provider: str = "ollama", model_name: str = None, **provider_kwargs):
|
| 148 |
self.provider_name = provider.lower()
|
| 149 |
self.model_name = model_name or self._get_default_model()
|
|
|
|
| 153 |
|
| 154 |
provider_class = self.PROVIDER_MAP[self.provider_name]
|
| 155 |
self.provider = provider_class(self.model_name, **provider_kwargs)
|
| 156 |
+
|
| 157 |
def _get_default_model(self) -> str:
|
| 158 |
"""Get default model based on provider"""
|
| 159 |
defaults = {
|
|
|
|
| 162 |
"openai": "gpt-3.5-turbo"
|
| 163 |
}
|
| 164 |
return defaults.get(self.provider_name, "mistral")
|
| 165 |
+
|
| 166 |
def generate(self, prompt: str, max_tokens: int = 500, stream: bool = False) -> Union[str, Generator[str, None, None]]:
|
| 167 |
"""Unified generate method that delegates to provider"""
|
| 168 |
return self.provider.generate(prompt, max_tokens, stream)
|
| 169 |
+
|
| 170 |
@classmethod
|
| 171 |
def get_available_providers(cls) -> list:
|
| 172 |
"""Return list of supported providers"""
|
ngrok.yml.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: "2"
|
| 2 |
+
authtoken: 32HaXMF3tuRxfas1siT3CIhLjH4_2AXbGGma38NnCF1tjyJNZ
|
| 3 |
+
tunnels:
|
| 4 |
+
ai-coach-api:
|
| 5 |
+
addr: 8000
|
| 6 |
+
proto: http
|
| 7 |
+
ai-coach-ui:
|
| 8 |
+
addr: 8501
|
| 9 |
+
proto: http
|
test_setup.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import os
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
|
| 5 |
+
load_dotenv()
|
| 6 |
+
|
| 7 |
+
# Test Ollama
|
| 8 |
+
ollama_host = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
| 9 |
+
model_name = os.getenv("LOCAL_MODEL_NAME", "mistral:latest")
|
| 10 |
+
|
| 11 |
+
print(f"Testing Ollama at: {ollama_host}")
|
| 12 |
+
try:
|
| 13 |
+
response = requests.get(f"{ollama_host}/api/tags")
|
| 14 |
+
print(f"Ollama Status: {response.status_code}")
|
| 15 |
+
print(f"Models available: {response.json()}")
|
| 16 |
+
except Exception as e:
|
| 17 |
+
print(f"Ollama Error: {e}")
|
| 18 |
+
|
| 19 |
+
# Test model generation
|
| 20 |
+
print(f"\nTesting model: {model_name}")
|
| 21 |
+
try:
|
| 22 |
+
response = requests.post(f"{ollama_host}/api/generate", json={
|
| 23 |
+
"model": model_name,
|
| 24 |
+
"prompt": "Hello, world!",
|
| 25 |
+
"stream": False
|
| 26 |
+
})
|
| 27 |
+
print(f"Model Test Status: {response.status_code}")
|
| 28 |
+
if response.status_code == 200:
|
| 29 |
+
print("✅ Ollama and model are working correctly!")
|
| 30 |
+
else:
|
| 31 |
+
print(f"❌ Model test failed: {response.text}")
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f"Model Test Error: {e}")
|
utils/__pycache__/config.cpython-313.pyc
ADDED
|
Binary file (2.34 kB). View file
|
|
|
utils/config.py
CHANGED
|
@@ -6,15 +6,20 @@ class Config:
|
|
| 6 |
load_dotenv()
|
| 7 |
self.hf_token = os.getenv("HF_TOKEN")
|
| 8 |
self.hf_api_url = os.getenv("HF_API_ENDPOINT_URL")
|
|
|
|
| 9 |
self.tavily_api_key = os.getenv("TAVILY_API_KEY")
|
| 10 |
self.openweather_api_key = os.getenv("OPENWEATHER_API_KEY")
|
| 11 |
self.nasa_api_key = os.getenv("NASA_API_KEY")
|
| 12 |
-
|
|
|
|
|
|
|
| 13 |
self.redis_port = int(os.getenv("REDIS_PORT", "6379"))
|
| 14 |
self.redis_username = os.getenv("REDIS_USERNAME")
|
| 15 |
self.redis_password = os.getenv("REDIS_PASSWORD")
|
| 16 |
-
self.
|
| 17 |
-
|
|
|
|
|
|
|
| 18 |
self.ollama_host = os.getenv("OLLAMA_HOST", "https://ace32bd59aef.ngrok-free.app")
|
| 19 |
|
| 20 |
config = Config()
|
|
|
|
| 6 |
load_dotenv()
|
| 7 |
self.hf_token = os.getenv("HF_TOKEN")
|
| 8 |
self.hf_api_url = os.getenv("HF_API_ENDPOINT_URL")
|
| 9 |
+
self.use_fallback = os.getenv("USE_FALLBACK", "false").lower() == "true"
|
| 10 |
self.tavily_api_key = os.getenv("TAVILY_API_KEY")
|
| 11 |
self.openweather_api_key = os.getenv("OPENWEATHER_API_KEY")
|
| 12 |
self.nasa_api_key = os.getenv("NASA_API_KEY")
|
| 13 |
+
|
| 14 |
+
# Redis configuration with proper defaults
|
| 15 |
+
self.redis_host = os.getenv("REDIS_HOST", "localhost")
|
| 16 |
self.redis_port = int(os.getenv("REDIS_PORT", "6379"))
|
| 17 |
self.redis_username = os.getenv("REDIS_USERNAME")
|
| 18 |
self.redis_password = os.getenv("REDIS_PASSWORD")
|
| 19 |
+
self.redis_retries = int(os.getenv("REDIS_RETRIES", "3"))
|
| 20 |
+
self.redis_retry_delay = int(os.getenv("REDIS_RETRY_DELAY", "1"))
|
| 21 |
+
|
| 22 |
+
self.local_model_name = os.getenv("LOCAL_MODEL_NAME", "mistral")
|
| 23 |
self.ollama_host = os.getenv("OLLAMA_HOST", "https://ace32bd59aef.ngrok-free.app")
|
| 24 |
|
| 25 |
config = Config()
|